├── README.md ├── app.yaml ├── blog ├── __init__.py ├── context_processor.py ├── decorators.py ├── forms.py ├── hooks.py ├── model.py ├── send.py ├── templates │ ├── 404.html │ ├── 500.html │ ├── form_post.html │ ├── index.html │ ├── macro.html │ ├── read_post.html │ └── template.html ├── util.py └── views.py ├── index.yaml ├── lib ├── __init__.py ├── flask │ ├── __init__.py │ ├── app.py │ ├── config.py │ ├── ctx.py │ ├── globals.py │ ├── helpers.py │ ├── logging.py │ ├── module.py │ ├── session.py │ ├── signals.py │ ├── templating.py │ ├── testing.py │ └── wrappers.py ├── flaskext │ ├── __init__.py │ └── wtf │ │ ├── __init__.py │ │ └── recaptcha │ │ ├── __init__.py │ │ ├── fields.py │ │ ├── validators.py │ │ └── widgets.py ├── jinja2 │ ├── __init__.py │ ├── _speedups.c │ ├── _stringdefs.py │ ├── bccache.py │ ├── compiler.py │ ├── constants.py │ ├── debug.py │ ├── defaults.py │ ├── environment.py │ ├── exceptions.py │ ├── ext.py │ ├── filters.py │ ├── lexer.py │ ├── loaders.py │ ├── meta.py │ ├── nodes.py │ ├── optimizer.py │ ├── parser.py │ ├── runtime.py │ ├── sandbox.py │ ├── tests.py │ ├── testsuite │ │ ├── __init__.py │ │ ├── api.py │ │ ├── core_tags.py │ │ ├── debug.py │ │ ├── doctests.py │ │ ├── ext.py │ │ ├── filters.py │ │ ├── imports.py │ │ ├── inheritance.py │ │ ├── lexnparse.py │ │ ├── loader.py │ │ ├── regression.py │ │ ├── res │ │ │ ├── __init__.py │ │ │ └── templates │ │ │ │ ├── broken.html │ │ │ │ ├── foo │ │ │ │ └── test.html │ │ │ │ ├── syntaxerror.html │ │ │ │ └── test.html │ │ ├── security.py │ │ ├── tests.py │ │ └── utils.py │ ├── utils.py │ └── visitor.py ├── pkg_resources.py ├── simplejson │ ├── __init__.py │ ├── _speedups.c │ ├── decoder.py │ ├── encoder.py │ ├── ordered_dict.py │ ├── scanner.py │ ├── tests │ │ ├── __init__.py │ │ ├── test_check_circular.py │ │ ├── test_decimal.py │ │ ├── test_decode.py │ │ ├── test_default.py │ │ ├── test_dump.py │ │ ├── test_encode_basestring_ascii.py │ │ ├── test_encode_for_html.py │ │ ├── test_fail.py │ │ ├── test_float.py │ │ ├── test_indent.py │ │ ├── test_pass1.py │ │ ├── test_pass2.py │ │ ├── test_pass3.py │ │ ├── test_recursion.py │ │ ├── test_scanstring.py │ │ ├── test_separators.py │ │ ├── test_speedups.py │ │ └── test_unicode.py │ └── tool.py ├── werkzeug │ ├── __init__.py │ ├── _internal.py │ ├── contrib │ │ ├── __init__.py │ │ ├── atom.py │ │ ├── cache.py │ │ ├── fixers.py │ │ ├── iterio.py │ │ ├── jsrouting.py │ │ ├── kickstart.py │ │ ├── limiter.py │ │ ├── lint.py │ │ ├── profiler.py │ │ ├── securecookie.py │ │ ├── sessions.py │ │ ├── testtools.py │ │ └── wrappers.py │ ├── datastructures.py │ ├── debug │ │ ├── __init__.py │ │ ├── console.py │ │ ├── render.py │ │ ├── repr.py │ │ ├── shared │ │ │ ├── body.tmpl │ │ │ ├── codetable.tmpl │ │ │ ├── console.png │ │ │ ├── debugger.js │ │ │ ├── jquery.js │ │ │ ├── less.png │ │ │ ├── more.png │ │ │ ├── source.png │ │ │ ├── style.css │ │ │ └── vartable.tmpl │ │ ├── tbtools.py │ │ ├── templates │ │ │ ├── console.html │ │ │ ├── dump_object.html │ │ │ ├── frame.html │ │ │ ├── help_command.html │ │ │ ├── source.html │ │ │ ├── traceback_full.html │ │ │ ├── traceback_plaintext.html │ │ │ └── traceback_summary.html │ │ └── utils.py │ ├── exceptions.py │ ├── formparser.py │ ├── http.py │ ├── local.py │ ├── posixemulation.py │ ├── routing.py │ ├── script.py │ ├── security.py │ ├── serving.py │ ├── templates.py │ ├── test.py │ ├── testapp.py │ ├── urls.py │ ├── useragents.py │ ├── utils.py │ ├── wrappers.py │ └── wsgi.py └── wtforms │ ├── __init__.py │ ├── ext │ ├── __init__.py │ ├── appengine │ │ ├── __init__.py │ │ ├── db.py │ │ └── fields.py │ ├── dateutil │ │ ├── __init__.py │ │ └── fields.py │ ├── django │ │ ├── __init__.py │ │ ├── fields.py │ │ ├── orm.py │ │ └── templatetags │ │ │ ├── __init__.py │ │ │ └── wtforms.py │ └── sqlalchemy │ │ ├── __init__.py │ │ ├── fields.py │ │ └── orm.py │ ├── fields.py │ ├── form.py │ ├── validators.py │ └── widgets.py ├── main.py ├── static ├── css │ ├── style.css │ ├── template.css │ └── validationEngine.jquery.css ├── image │ ├── delicious_32.png │ ├── facebook_32.png │ ├── flickr_32.png │ ├── ln.jpg │ ├── orkut_32.png │ ├── page_bg.jpg │ ├── rss_32.png │ ├── tag.png │ └── twitter_32.png └── js │ ├── jquery-1.4.2.min.js │ ├── jquery.validationEngine-extend.js │ ├── jquery.validationEngine-pt.js │ ├── jquery.validationEngine.js │ ├── load-editor.js │ └── rlopes.js └── tests ├── __init__.py ├── test_context_processor.py ├── test_hooks.py ├── test_model.py ├── test_short_url.py ├── test_to_url.py └── test_view.py /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/riquellopes/micro-blog/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 5 | 6 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: rlopes-blog 2 | version: 1 3 | runtime: python 4 | api_version: 1 5 | 6 | skip_files: | 7 | ^(.*/)?( 8 | (.git)| 9 | (#.*#)| 10 | (.*~) 11 | (.*/.py[co])| 12 | (.*/RCS/.*)| 13 | (\..*)| 14 | (README) 15 | )$ 16 | 17 | handlers: 18 | - url: /static 19 | static_dir: static 20 | 21 | - url: .* 22 | script: main.py 23 | -------------------------------------------------------------------------------- /blog/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding:utf-8 -*- 2 | 3 | from flask import Flask 4 | 5 | import settings 6 | 7 | app = Flask('blog') 8 | app.config.from_object('blog.settings') 9 | 10 | 11 | import blog.views 12 | -------------------------------------------------------------------------------- /blog/context_processor.py: -------------------------------------------------------------------------------- 1 | # -*- encoding:utf-8 -*- 2 | """ 3 | Blog rlopes ContextProcessor 4 | ---------------------------- 5 | """ 6 | from google.appengine.api import users 7 | from blog import app 8 | 9 | @app.context_processor 10 | def admin_logged(): 11 | return dict(admin_loggend=users.is_current_user_admin()) 12 | 13 | @app.context_processor 14 | def admin_logout_url(): 15 | return dict(admin_logout_url=users.create_logout_url) 16 | 17 | @app.context_processor 18 | def admin_login_url(): 19 | return dict(admin_login_url=users.create_login_url) 20 | 21 | @app.context_processor 22 | def rss_url(): 23 | return dict(rss_url="http://twitter.com/statuses/user_timeline/riquellopes.atom") 24 | -------------------------------------------------------------------------------- /blog/decorators.py: -------------------------------------------------------------------------------- 1 | # -*- encoding:utf-8 -*- 2 | """ 3 | Blog rLopes Decorators 4 | ---------------------- 5 | """ 6 | from functools import wraps 7 | from google.appengine.api import users 8 | from flask import redirect, request 9 | 10 | def login_required(func): 11 | @wraps(func) 12 | def decorated_view(*args, **kwargs): 13 | if users.is_current_user_admin() == False: 14 | return redirect(users.create_login_url(request.url)) 15 | return func(*args, **kwargs) 16 | return decorated_view 17 | -------------------------------------------------------------------------------- /blog/forms.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Blog rLopes Forms 4 | ----------------- 5 | """ 6 | 7 | from flaskext.wtf import TextField, TextAreaField, Form, validators 8 | from model import Post, TagCategory 9 | 10 | 11 | class PostForm(Form): 12 | title = TextField('Titulo:', [ 13 | validators.Length(min=5, max=60, message="O Titulo deve ter entre 5 a 60 caracteres.") 14 | ]) 15 | text = TextAreaField('Texto:', [ 16 | validators.Length(min=5, message="O Texto deve ter no minimo 5 caracteres.") 17 | ]) 18 | tags = TextField('Tags:',[ 19 | validators.required(message="Informe uma tag.") 20 | ]) 21 | 22 | def __init__(self, model_instance = None, *args, **kwargs): 23 | """Método construtor da classe, preenche model ao criar form.""" 24 | kwargs['csrf_enabled'] = False 25 | super(PostForm, self).__init__(*args, **kwargs) 26 | self.model = None 27 | if model_instance: 28 | self.title.data = model_instance.title 29 | self.text.data = model_instance.text 30 | self.tags.data = self.get_tags( model_instance.tags ) 31 | self.model = model_instance 32 | 33 | def save(self): 34 | """Método responsável em salvar post no bigtable.""" 35 | if self.model: 36 | self.model.title = self.title.data 37 | self.model.text = self.text.data 38 | self.model.tags = self.set_tags(self.tags.data) 39 | else: 40 | self.model = Post( 41 | title = self.title.data, 42 | text = self.text.data, 43 | tags = self.set_tags(self.tags.data) 44 | ) 45 | self.model.put() 46 | return self.model 47 | 48 | def get_tags(self, tags): 49 | """Metodo que recupera valor do atributo tags.""" 50 | tstr = "" 51 | for tag in tags: 52 | tstr += "%s," % tag 53 | return tstr[:-1] 54 | 55 | def set_tags(self, tags): 56 | """Método que define valor para o atributo tags.""" 57 | tag = TagCategory() 58 | return tag.string_to_category( tags ) 59 | -------------------------------------------------------------------------------- /blog/hooks.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Blog rLopes Hooks 4 | ----------------- 5 | """ 6 | from flask import render_template 7 | from blog import app 8 | 9 | @app.errorhandler(404) 10 | def page_not_found(error): 11 | """Método responsável em tratar erros. 404""" 12 | return render_template('404.html', error=error), 404 13 | 14 | @app.errorhandler(500) 15 | def internal_server_error(error): 16 | """Método responsável em tratar erros. 500""" 17 | return render_template("500.html", error=error), 500 18 | -------------------------------------------------------------------------------- /blog/model.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Blog rlopes Models 4 | ------------------ 5 | """ 6 | 7 | import string 8 | from google.appengine.ext import db 9 | from util import slug, short_url 10 | 11 | class TagCategory: 12 | 13 | def string_to_category(self, tag_string): 14 | """ 15 | A function to validate a comma-separated list of tags. 16 | 17 | try: 18 | tags = validate_category_list("ai, computer science, lisp") 19 | except ValueError: 20 | # Display error to user that the submitted tags were not formated correctly 21 | pass 22 | except: 23 | # Something bad happened, handle it 24 | pass 25 | 26 | @string list of category items 27 | @string optional regex for validating each list item 28 | """ 29 | 30 | # Only accept a parameter of type string or type unicode 31 | if not type(tag_string) in [unicode, str]: 32 | raise ValueError("Passed category list must be of type string or unicode.") 33 | 34 | # Split comma-separated list into an array 35 | tags = tag_string.split(',') 36 | 37 | # Remove whitespace from each list item 38 | tags = map(string.strip, tags) 39 | 40 | # Remove duplicate categories 41 | tags = {}.fromkeys(tags).keys() 42 | 43 | # When a user enters a comma at the end of a line a blank string is 44 | # inserted into the list. 45 | # Example: ['ai', 'computer science', 'lisp', ''] 46 | # This removes that empty string 47 | try: 48 | tags.remove(" ") 49 | except: pass 50 | 51 | # Sort list alphabetically 52 | tags.sort() 53 | 54 | # Return list as an array of db.Category items 55 | # Example: [db.Category('ai'), db.Category('computer science'), db.Category('lisp')] 56 | return map(db.Category, tags) 57 | 58 | class Post(db.Model): 59 | title = db.StringProperty(required = True) 60 | text = db.TextProperty(required = True) 61 | tags = db.ListProperty(db.Category) 62 | user = db.UserProperty() 63 | url = db.StringProperty(); 64 | date_create = db.DateTimeProperty(auto_now_add = True) 65 | INDEX_ONLY = ['url', 'title', 'tags'] 66 | 67 | def put(self): 68 | """ 69 | Método responsável em salvar informações no bigtable. 70 | Após conteúdo ser salvo, url é envida para o twitter, 71 | informando que existe um novo post no blog. 72 | """ 73 | self.url = slug(self.title) 74 | return super(Post, self).put() 75 | -------------------------------------------------------------------------------- /blog/send.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | from google.appengine.api.mail import EmailMessage 4 | from flaskext.wtf import TextAreaField, TextField, Form, validators 5 | ValidationError = validators.ValidationError 6 | from datetime import datetime 7 | 8 | __email__ = 'riquellopes@gmail.com' 9 | validation_engine = [] 10 | 11 | class Contact(Form): 12 | nome = TextField() 13 | email = TextField([ 14 | validators.email() 15 | ]) 16 | mensagem = TextAreaField() 17 | 18 | def __init__(self, *args, **kwargs): 19 | validation_engine = [] 20 | super(Contact, self).__init__(*args, **kwargs) 21 | 22 | def send(self): 23 | """Método que envia email para destinatário.""" 24 | email = EmailMessage() 25 | 26 | email.sender = '%s <%s>' % ("rlopes-blog", __email__) 27 | email.subject = 'Fale comigo - rlopes' 28 | email.to = __email__ 29 | email.body = """ 30 | Nome: %(nome)s 31 | Data Contato: %(data)s 32 | Email: %(email)s 33 | %(messagem)s 34 | 35 | """ % {'nome':self.nome.data, 36 | 'data':datetime.today().strftime('%d/%m/%Y'), 37 | 'messagem':self.mensagem.data, 38 | 'email':self.email.data} 39 | 40 | email.send() 41 | 42 | def validate_nome(form, field): 43 | """ 44 | Método que valida nome deacordo com as regras do blog. 45 | """ 46 | if field.data == "Digite seu nome" or len(field.data) == 0: 47 | validation_engine.append("['#nome', 'false', 'Digite seu nome']") 48 | raise ValidationError("Digite seu nome.") 49 | 50 | def validate_mensagem(form, field): 51 | """ 52 | Método que valida mensagem deacordo com as regras do blog. 53 | """ 54 | if field.data == "Escreva sua mensagem" or len(field.data) == 0: 55 | validation_engine.append("['#mensagem', 'false', 'Digite sua mensagem']") 56 | raise ValidationError("Escreva sua mensagem") 57 | 58 | def get_validation_engine(self): 59 | """ 60 | Método que cria um string com os campos que não atendem 61 | os critérios de validação do formulário de contato. 62 | """ 63 | return "[%s]" % (",".join(validation_engine)) 64 | -------------------------------------------------------------------------------- /blog/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "template.html" %} 2 | {% block title %} Ops! Error 404 {% endblock %} 3 | {% block container %} 4 |

Ops! Erro 404

5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /blog/templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends "template.html" %} 2 | {% block title %} Ops! Error 500 {% endblock %} 3 | {% block container %} 4 |

Opes! Erro 500

5 |

{{ error }}

6 | {% endblock%} 7 | -------------------------------------------------------------------------------- /blog/templates/form_post.html: -------------------------------------------------------------------------------- 1 | {% extends "template.html" %} 2 | {% block script %} 3 | 4 | 5 | 6 | 7 | {% endblock %} 8 | {% block title %} {{title}} {% endblock%} 9 | {% block body %}class="yui-skin-sam"{% endblock%} 10 | {% block container %} 11 |
12 |
13 |
14 | {% from 'macro.html' import render_input ,input %} 15 | {{ form.hidden_tag() }} 16 |
17 | {{ render_input(form.title, size=40) }} 18 |
19 |
20 | {{ render_input(form.tags, text_alias='(separar tags por virgula.)') }} 21 |
22 |
23 | {{ render_input(form.text, id='text', rows=20, cols=95) }} 24 |
25 |
26 |
27 | {{ input('salve', type='submit', value="Salvar", id="form-salvar") }} 28 |
29 |
30 |
31 |
32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /blog/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "template.html" %} 2 | {% block title %} rlopes {% endblock %} 3 | {% block container %} 4 |
5 | {% from 'macro.html' import render_post %} 6 | {% for post in posts: %} 7 | {{ render_post(post, preview=True, admin_loggend=admin_loggend) }} 8 | {% endfor %} 9 |
{{ name }} 10 |
11 | {% if admin_loggend %} 12 |
13 | New post 14 |
15 | {% endif %} 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /blog/templates/macro.html: -------------------------------------------------------------------------------- 1 | {% macro input(name, value='', type='text', class="", id="", default="") %} 2 | 3 | {% endmacro %} 4 | 5 | {% macro textarea(name='', value='', rows=10, cols=40, class="", id="", default="") %} 6 | 7 | {% endmacro %} 8 | 9 | {% macro tags(tags) %} 10 | {% for tag in tags %} 11 | {{tag}}{% if not loop.last %}, {% endif %} 12 | {% endfor %} 13 | {% endmacro%} 14 | 15 | {% macro render_post(post, preview=False, admin_loggend=False) %} 16 |
17 |

{{ post.title }}

18 |
19 | Publicado {{ post.date_create.strftime("%d %B, %Y") }} em 20 | {{ tags(post.tags) }} 21 |
22 |

23 | {% if preview %}{{ post.text|truncate|safe }}{% else %}{{ post.text|safe }}{% endif %} 24 |

25 |
 
26 | {% if preview %} 27 | Leia mais 28 | {% endif %} 29 | {% if not preview %} 30 |
31 | 32 | 33 | 34 |
35 | {% endif %} 36 | {% if admin_loggend %} 37 |
38 | Edit 39 | Delete 40 |
41 | {% endif %} 42 |
 
43 |
44 | {% endmacro %} 45 | 46 | {% macro render_input(input, text_alias=None) %} 47 | {% if text_alias %} {{ text_alias }} {% endif %}
48 | {{ input(**kwargs)|safe }} 49 | {% if input.errors %} 50 | 55 | {% endif %} 56 | {% endmacro %} 57 | -------------------------------------------------------------------------------- /blog/templates/read_post.html: -------------------------------------------------------------------------------- 1 | {% extends "template.html" %} 2 | {% block title %} {{post.title}} {% endblock %} 3 | {% block css %} 4 | 5 | {% endblock %} 6 | {% block script %} 7 | 8 | {% endblock%} 9 | {% block body %} onload='prettyPrint()' {% endblock %} 10 | {% block container %} 11 |
12 | {% from 'macro.html' import render_post %} 13 | {{ render_post(post, admin_loggend=admin_loggend) }} 14 |
15 | {% endblock %} 16 | -------------------------------------------------------------------------------- /blog/templates/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {% block title %}{% endblock %}| Henrique Lopes 10 | 11 | {% block css %}{% endblock %} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% block script %}{% endblock %} 20 | 21 | 22 | Fork me on GitHub 23 |
24 |
25 | 28 | 34 |
35 | {% with messages = get_flashed_messages() %} 36 | {% if messages %} 37 |
38 | {% for message in messages: %} 39 |

{{ message }}

40 | {% endfor %} 41 |
42 | {% endif %} 43 | {% endwith %} 44 | {% block container %}{% endblock %} 45 |
46 |
47 |
48 |
49 |
50 |
51 |

Siga me:

52 |
53 | 60 |
 
61 |
 
62 |
63 |
 
64 |
65 |
66 |

Fale comigo:

67 |
68 | {% from 'macro.html' import input, textarea %} 69 |
70 |
71 | {{ input('nome', 'Digite seu nome', class="validate[required] text-input", id='nome', default='Digite seu nome') }} 72 |
73 |
74 | {{ input('email', 'Digite seu email', class="validate[required,custom[email]] text-input", id='email', default='Digite seu email') }} 75 |
76 |
77 | {{ textarea('mensagem', class="validate[required,length[6,300]] text-input", value="Escreva sua mensagem", id='mensagem', default='Escreva sua message') }} 78 |
79 | {{ input('acao', 'Enviar', class="submit rounde", type="submit", id="send-contact") }} 80 |
81 |
82 |
83 |
84 |
 
85 |
86 | 100 |
102 | 103 | -------------------------------------------------------------------------------- /blog/util.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | #! /usr/bin/python 3 | 4 | import re 5 | from unicodedata import normalize 6 | import urllib 7 | 8 | _punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+') 9 | 10 | def slug(text, delim=u'-'): 11 | """ 12 | Code snippets: 13 | http://flask.pocoo.org/snippets/5/ 14 | 15 | Generates an ASCII-only slug. 16 | """ 17 | result = [] 18 | for word in _punct_re.split(text.lower()): 19 | word = normalize('NFKD', word.decode('utf-8')).encode('ASCII', 'ignore') 20 | if word: 21 | result.append(word) 22 | return unicode(delim.join(result)) 23 | 24 | def to_url(string): 25 | """Função responsável em parsear string para url valida.""" 26 | string = normalize('NFKD', string.decode('utf-8')).encode('ASCII', 'ignore') 27 | string = re.sub(r"[^\w]+", "-", string) 28 | string = string.lower() 29 | 30 | return string 31 | 32 | def short_url(url=None): 33 | """Função responsável em encurtar tamanho de uma url""" 34 | short = urllib.urlopen("http://tinyurl.com/api-create.php?url=%s" % urllib.quote( url ) ).read() 35 | return short 36 | -------------------------------------------------------------------------------- /blog/views.py: -------------------------------------------------------------------------------- 1 | # -*- encoding:utf-8 -*- 2 | 3 | """ 4 | Blog rlopes Views 5 | ----------------- 6 | 7 | rLopes - Blog Engine. A blog engine written with Flask and Gae. 8 | 9 | """ 10 | from blog import app 11 | from flask import render_template, request, flash, url_for, redirect,\ 12 | session, abort 13 | 14 | from model import Post 15 | from forms import PostForm 16 | from decorators import login_required 17 | from context_processor import admin_logged 18 | from hooks import page_not_found, internal_server_error 19 | from send import Contact 20 | 21 | @app.route('/') 22 | @app.route('/') 23 | def get(name=None): 24 | if name == None: 25 | posts = Post.all() 26 | posts.order('-date_create') 27 | return render_template('index.html', posts=posts) 28 | else: 29 | try: 30 | post = Post.all().filter('url', name).get() 31 | return render_template('read_post.html', post=post) 32 | except: 33 | abort(404) 34 | 35 | @app.route('/tag/') 36 | def tag(name): 37 | """Método responsável em buscar post com a hash tag informada.""" 38 | posts = Post.all() 39 | posts.filter('tags', name) 40 | posts.order('-date_create') 41 | return render_template('index.html', posts=posts) 42 | 43 | 44 | @app.route('/contact', methods=['POST']) 45 | def contact(): 46 | """Metodo responsável em solicitar envio de email.""" 47 | contact = Contact() 48 | 49 | if contact.validate_on_submit(): 50 | contact.send() 51 | return 'true' 52 | return contact.get_validation_engine() 53 | 54 | @app.route('//form') 55 | @login_required 56 | def form_update_post(id): 57 | """Método responsável em criar form update para o blog.""" 58 | try: 59 | form = PostForm( Post.get_by_id(id) ) 60 | title = 'Update - %s' % form.title.data.strip() 61 | action = '%i/update' % int(id) 62 | return render_template('form_post.html', form=form, title=title, action=action) 63 | except: 64 | abort(404) 65 | 66 | @app.route('/post/form') 67 | @login_required 68 | def form_new_post(): 69 | """Método responsável em criar form new para o blog.""" 70 | form = PostForm() 71 | return render_template('form_post.html', form=form, title='Criar post', action='create') 72 | 73 | @app.route('//update', methods=['POST']) 74 | @login_required 75 | def update_post(id): 76 | post = Post.get_by_id(id) 77 | form = PostForm() 78 | title = 'Update - %s' % form.title.data 79 | action = '%i/update' % int(id) 80 | if form.validate_on_submit(): 81 | form.model = post 82 | form.save() 83 | flash('Post atualizado.') 84 | return redirect(url_for('get')) 85 | return render_template('form_post.html', form=form, title=title, action=action) 86 | 87 | @app.route('/create', methods=['POST']) 88 | @login_required 89 | def create_post(): 90 | """Método responsável em salvar informações do blog.""" 91 | form = PostForm() 92 | if form.validate_on_submit(): 93 | form.save() 94 | flash('Post foi salvo no banco de dados.') 95 | return redirect(url_for('get')) 96 | return render_template('form_post.html', form=form, title='Criar post', action="create") 97 | 98 | @app.route('//delete', methods=['GET']) 99 | @login_required 100 | def delete_post(id): 101 | """Método responsável em remover um post do blog.""" 102 | post = Post.get_by_id(id) 103 | if post: 104 | post.delete() 105 | flash("Post foi removido com sucesso.") 106 | else: 107 | flash("Erro ao tentar remover post.") 108 | return redirect(url_for('get')) 109 | -------------------------------------------------------------------------------- /index.yaml: -------------------------------------------------------------------------------- 1 | indexes: 2 | 3 | # AUTOGENERATED 4 | 5 | # This index.yaml is automatically updated whenever the dev_appserver 6 | # detects that a new type of query is run. If you want to manage the 7 | # index.yaml file manually, remove the above marker line (the line 8 | # saying "# AUTOGENERATED"). If you want to manage some indexes 9 | # manually, move them above the marker line. The index.yaml file is 10 | # automatically uploaded to the admin console when you next deploy 11 | # your application using appcfg.py. 12 | 13 | - kind: Post 14 | properties: 15 | - name: tag 16 | - name: date_create 17 | direction: desc 18 | 19 | - kind: Post 20 | properties: 21 | - name: tags 22 | - name: date_create 23 | direction: desc 24 | -------------------------------------------------------------------------------- /lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/lib/__init__.py -------------------------------------------------------------------------------- /lib/flask/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask 4 | ~~~~~ 5 | 6 | A microframework based on Werkzeug. It's extensively documented 7 | and follows best practice patterns. 8 | 9 | :copyright: (c) 2010 by Armin Ronacher. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | # utilities we import from Werkzeug and Jinja2 that are unused 14 | # in the module but are exported as public interface. 15 | from werkzeug import abort, redirect 16 | from jinja2 import Markup, escape 17 | 18 | from .app import Flask, Request, Response 19 | from .config import Config 20 | from .helpers import url_for, jsonify, json_available, flash, \ 21 | send_file, send_from_directory, get_flashed_messages, \ 22 | get_template_attribute, make_response 23 | from .globals import current_app, g, request, session, _request_ctx_stack 24 | from .module import Module 25 | from .templating import render_template, render_template_string 26 | from .session import Session 27 | 28 | # the signals 29 | from .signals import signals_available, template_rendered, request_started, \ 30 | request_finished, got_request_exception 31 | 32 | # only import json if it's available 33 | if json_available: 34 | from .helpers import json 35 | -------------------------------------------------------------------------------- /lib/flask/ctx.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.ctx 4 | ~~~~~~~~~ 5 | 6 | Implements the objects required to keep the context. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | from werkzeug.exceptions import HTTPException 13 | 14 | from .globals import _request_ctx_stack 15 | from .session import _NullSession 16 | 17 | 18 | class _RequestGlobals(object): 19 | pass 20 | 21 | 22 | class _RequestContext(object): 23 | """The request context contains all request relevant information. It is 24 | created at the beginning of the request and pushed to the 25 | `_request_ctx_stack` and removed at the end of it. It will create the 26 | URL adapter and request object for the WSGI environment provided. 27 | """ 28 | 29 | def __init__(self, app, environ): 30 | self.app = app 31 | self.request = app.request_class(environ) 32 | self.url_adapter = app.create_url_adapter(self.request) 33 | self.session = app.open_session(self.request) 34 | if self.session is None: 35 | self.session = _NullSession() 36 | self.g = _RequestGlobals() 37 | self.flashes = None 38 | 39 | try: 40 | url_rule, self.request.view_args = \ 41 | self.url_adapter.match(return_rule=True) 42 | self.request.url_rule = url_rule 43 | except HTTPException, e: 44 | self.request.routing_exception = e 45 | 46 | def push(self): 47 | """Binds the request context.""" 48 | _request_ctx_stack.push(self) 49 | 50 | def pop(self): 51 | """Pops the request context.""" 52 | _request_ctx_stack.pop() 53 | 54 | def __enter__(self): 55 | self.push() 56 | return self 57 | 58 | def __exit__(self, exc_type, exc_value, tb): 59 | # do not pop the request stack if we are in debug mode and an 60 | # exception happened. This will allow the debugger to still 61 | # access the request object in the interactive shell. Furthermore 62 | # the context can be force kept alive for the test client. 63 | # See flask.testing for how this works. 64 | if not self.request.environ.get('flask._preserve_context') and \ 65 | (tb is None or not self.app.debug): 66 | self.pop() 67 | -------------------------------------------------------------------------------- /lib/flask/globals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.globals 4 | ~~~~~~~~~~~~~ 5 | 6 | Defines all the global objects that are proxies to the current 7 | active context. 8 | 9 | :copyright: (c) 2010 by Armin Ronacher. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | from werkzeug import LocalStack, LocalProxy 14 | 15 | # context locals 16 | _request_ctx_stack = LocalStack() 17 | current_app = LocalProxy(lambda: _request_ctx_stack.top.app) 18 | request = LocalProxy(lambda: _request_ctx_stack.top.request) 19 | session = LocalProxy(lambda: _request_ctx_stack.top.session) 20 | g = LocalProxy(lambda: _request_ctx_stack.top.g) 21 | -------------------------------------------------------------------------------- /lib/flask/logging.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.logging 4 | ~~~~~~~~~~~~~ 5 | 6 | Implements the logging support for Flask. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | from __future__ import absolute_import 13 | 14 | from logging import getLogger, StreamHandler, Formatter, Logger, DEBUG 15 | 16 | 17 | def create_logger(app): 18 | """Creates a logger for the given application. This logger works 19 | similar to a regular Python logger but changes the effective logging 20 | level based on the application's debug flag. Furthermore this 21 | function also removes all attached handlers in case there was a 22 | logger with the log name before. 23 | """ 24 | 25 | class DebugLogger(Logger): 26 | def getEffectiveLevel(x): 27 | return DEBUG if app.debug else Logger.getEffectiveLevel(x) 28 | 29 | class DebugHandler(StreamHandler): 30 | def emit(x, record): 31 | StreamHandler.emit(x, record) if app.debug else None 32 | 33 | handler = DebugHandler() 34 | handler.setLevel(DEBUG) 35 | handler.setFormatter(Formatter(app.debug_log_format)) 36 | logger = getLogger(app.logger_name) 37 | # just in case that was not a new logger, get rid of all the handlers 38 | # already attached to it. 39 | del logger.handlers[:] 40 | logger.__class__ = DebugLogger 41 | logger.addHandler(handler) 42 | return logger 43 | -------------------------------------------------------------------------------- /lib/flask/session.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.session 4 | ~~~~~~~~~~~~~ 5 | 6 | Implements cookie based sessions based on Werkzeug's secure cookie 7 | system. 8 | 9 | :copyright: (c) 2010 by Armin Ronacher. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | from werkzeug.contrib.securecookie import SecureCookie 14 | 15 | 16 | class Session(SecureCookie): 17 | """Expands the session with support for switching between permanent 18 | and non-permanent sessions. 19 | """ 20 | 21 | def _get_permanent(self): 22 | return self.get('_permanent', False) 23 | 24 | def _set_permanent(self, value): 25 | self['_permanent'] = bool(value) 26 | 27 | permanent = property(_get_permanent, _set_permanent) 28 | del _get_permanent, _set_permanent 29 | 30 | 31 | class _NullSession(Session): 32 | """Class used to generate nicer error messages if sessions are not 33 | available. Will still allow read-only access to the empty session 34 | but fail on setting. 35 | """ 36 | 37 | def _fail(self, *args, **kwargs): 38 | raise RuntimeError('the session is unavailable because no secret ' 39 | 'key was set. Set the secret_key on the ' 40 | 'application to something unique and secret.') 41 | __setitem__ = __delitem__ = clear = pop = popitem = \ 42 | update = setdefault = _fail 43 | del _fail 44 | -------------------------------------------------------------------------------- /lib/flask/signals.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.signals 4 | ~~~~~~~~~~~~~ 5 | 6 | Implements signals based on blinker if available, otherwise 7 | falls silently back to a noop 8 | 9 | :copyright: (c) 2010 by Armin Ronacher. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | signals_available = False 13 | try: 14 | from blinker import Namespace 15 | signals_available = True 16 | except ImportError: 17 | class Namespace(object): 18 | def signal(self, name, doc=None): 19 | return _FakeSignal(name, doc) 20 | 21 | class _FakeSignal(object): 22 | """If blinker is unavailable, create a fake class with the same 23 | interface that allows sending of signals but will fail with an 24 | error on anything else. Instead of doing anything on send, it 25 | will just ignore the arguments and do nothing instead. 26 | """ 27 | 28 | def __init__(self, name, doc=None): 29 | self.name = name 30 | self.__doc__ = doc 31 | def _fail(self, *args, **kwargs): 32 | raise RuntimeError('signalling support is unavailable ' 33 | 'because the blinker library is ' 34 | 'not installed.') 35 | send = lambda *a, **kw: None 36 | connect = disconnect = has_receivers_for = receivers_for = \ 37 | temporarily_connected_to = _fail 38 | del _fail 39 | 40 | # the namespace for code signals. If you are not flask code, do 41 | # not put signals in here. Create your own namespace instead. 42 | _signals = Namespace() 43 | 44 | 45 | # core signals. For usage examples grep the sourcecode or consult 46 | # the API documentation in docs/api.rst as well as docs/signals.rst 47 | template_rendered = _signals.signal('template-rendered') 48 | request_started = _signals.signal('request-started') 49 | request_finished = _signals.signal('request-finished') 50 | got_request_exception = _signals.signal('got-request-exception') 51 | -------------------------------------------------------------------------------- /lib/flask/templating.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.templating 4 | ~~~~~~~~~~~~~~~~ 5 | 6 | Implements the bridge to Jinja2. 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | from jinja2 import BaseLoader, TemplateNotFound 12 | 13 | from .globals import _request_ctx_stack 14 | from .signals import template_rendered 15 | 16 | 17 | def _default_template_ctx_processor(): 18 | """Default template context processor. Injects `request`, 19 | `session` and `g`. 20 | """ 21 | reqctx = _request_ctx_stack.top 22 | return dict( 23 | config=reqctx.app.config, 24 | request=reqctx.request, 25 | session=reqctx.session, 26 | g=reqctx.g 27 | ) 28 | 29 | 30 | class _DispatchingJinjaLoader(BaseLoader): 31 | """A loader that looks for templates in the application and all 32 | the module folders. 33 | """ 34 | 35 | def __init__(self, app): 36 | self.app = app 37 | 38 | def get_source(self, environment, template): 39 | loader = None 40 | try: 41 | module, name = template.split('/', 1) 42 | loader = self.app.modules[module].jinja_loader 43 | except (ValueError, KeyError): 44 | pass 45 | # if there was a module and it has a loader, try this first 46 | if loader is not None: 47 | try: 48 | return loader.get_source(environment, name) 49 | except TemplateNotFound: 50 | pass 51 | # fall back to application loader if module failed 52 | return self.app.jinja_loader.get_source(environment, template) 53 | 54 | def list_templates(self): 55 | result = self.app.jinja_loader.list_templates() 56 | for name, module in self.app.modules.iteritems(): 57 | if module.jinja_loader is not None: 58 | for template in module.jinja_loader.list_templates(): 59 | result.append('%s/%s' % (name, template)) 60 | return result 61 | 62 | 63 | def _render(template, context, app): 64 | """Renders the template and fires the signal""" 65 | rv = template.render(context) 66 | template_rendered.send(app, template=template, context=context) 67 | return rv 68 | 69 | 70 | def render_template(template_name, **context): 71 | """Renders a template from the template folder with the given 72 | context. 73 | 74 | :param template_name: the name of the template to be rendered 75 | :param context: the variables that should be available in the 76 | context of the template. 77 | """ 78 | ctx = _request_ctx_stack.top 79 | ctx.app.update_template_context(context) 80 | return _render(ctx.app.jinja_env.get_template(template_name), 81 | context, ctx.app) 82 | 83 | 84 | def render_template_string(source, **context): 85 | """Renders a template from the given template source string 86 | with the given context. 87 | 88 | :param template_name: the sourcecode of the template to be 89 | rendered 90 | :param context: the variables that should be available in the 91 | context of the template. 92 | """ 93 | ctx = _request_ctx_stack.top 94 | ctx.app.update_template_context(context) 95 | return _render(ctx.app.jinja_env.from_string(source), 96 | context, ctx.app) 97 | -------------------------------------------------------------------------------- /lib/flask/testing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.testing 4 | ~~~~~~~~~~~~~ 5 | 6 | Implements test support helpers. This module is lazily imported 7 | and usually not used in production environments. 8 | 9 | :copyright: (c) 2010 by Armin Ronacher. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | 13 | from werkzeug import Client 14 | from flask import _request_ctx_stack 15 | 16 | 17 | class FlaskClient(Client): 18 | """Works like a regular Werkzeug test client but has some 19 | knowledge about how Flask works to defer the cleanup of the 20 | request context stack to the end of a with body when used 21 | in a with statement. 22 | """ 23 | 24 | preserve_context = context_preserved = False 25 | 26 | def open(self, *args, **kwargs): 27 | if self.context_preserved: 28 | _request_ctx_stack.pop() 29 | self.context_preserved = False 30 | kwargs.setdefault('environ_overrides', {}) \ 31 | ['flask._preserve_context'] = self.preserve_context 32 | old = _request_ctx_stack.top 33 | try: 34 | return Client.open(self, *args, **kwargs) 35 | finally: 36 | self.context_preserved = _request_ctx_stack.top is not old 37 | 38 | def __enter__(self): 39 | self.preserve_context = True 40 | return self 41 | 42 | def __exit__(self, exc_type, exc_value, tb): 43 | self.preserve_context = False 44 | if self.context_preserved: 45 | _request_ctx_stack.pop() 46 | -------------------------------------------------------------------------------- /lib/flask/wrappers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | flask.wrappers 4 | ~~~~~~~~~~~~~~ 5 | 6 | Implements the WSGI wrappers (request and response). 7 | 8 | :copyright: (c) 2010 by Armin Ronacher. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | from werkzeug import Request as RequestBase, Response as ResponseBase, \ 13 | cached_property 14 | 15 | from .helpers import json, _assert_have_json 16 | from .globals import _request_ctx_stack 17 | 18 | 19 | class Request(RequestBase): 20 | """The request object used by default in flask. Remembers the 21 | matched endpoint and view arguments. 22 | 23 | It is what ends up as :class:`~flask.request`. If you want to replace 24 | the request object used you can subclass this and set 25 | :attr:`~flask.Flask.request_class` to your subclass. 26 | """ 27 | 28 | #: the internal URL rule that matched the request. This can be 29 | #: useful to inspect which methods are allowed for the URL from 30 | #: a before/after handler (``request.url_rule.methods``) etc. 31 | #: 32 | #: .. versionadded:: 0.6 33 | url_rule = None 34 | 35 | #: a dict of view arguments that matched the request. If an exception 36 | #: happened when matching, this will be `None`. 37 | view_args = None 38 | 39 | #: if matching the URL failed, this is the exception that will be 40 | #: raised / was raised as part of the request handling. This is 41 | #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or 42 | #: something similar. 43 | routing_exception = None 44 | 45 | @property 46 | def max_content_length(self): 47 | """Read-only view of the `MAX_CONTENT_LENGTH` config key.""" 48 | ctx = _request_ctx_stack.top 49 | if ctx is not None: 50 | return ctx.app.config['MAX_CONTENT_LENGTH'] 51 | 52 | @property 53 | def endpoint(self): 54 | """The endpoint that matched the request. This in combination with 55 | :attr:`view_args` can be used to reconstruct the same or a 56 | modified URL. If an exception happened when matching, this will 57 | be `None`. 58 | """ 59 | if self.url_rule is not None: 60 | return self.url_rule.endpoint 61 | 62 | @property 63 | def module(self): 64 | """The name of the current module""" 65 | if self.url_rule and '.' in self.url_rule.endpoint: 66 | return self.url_rule.endpoint.rsplit('.', 1)[0] 67 | 68 | @cached_property 69 | def json(self): 70 | """If the mimetype is `application/json` this will contain the 71 | parsed JSON data. 72 | """ 73 | if __debug__: 74 | _assert_have_json() 75 | if self.mimetype == 'application/json': 76 | return json.loads(self.data) 77 | 78 | 79 | class Response(ResponseBase): 80 | """The response object that is used by default in flask. Works like the 81 | response object from Werkzeug but is set to have a HTML mimetype by 82 | default. Quite often you don't have to create this object yourself because 83 | :meth:`~flask.Flask.make_response` will take care of that for you. 84 | 85 | If you want to replace the response object used you can subclass this and 86 | set :attr:`~flask.Flask.response_class` to your subclass. 87 | """ 88 | default_mimetype = 'text/html' 89 | -------------------------------------------------------------------------------- /lib/flaskext/__init__.py: -------------------------------------------------------------------------------- 1 | from lib.pkg_resources import declare_namespace 2 | 3 | declare_namespace(__name__) 4 | 5 | #__import__('pkg_resources').declare_namespace(__name__) 6 | -------------------------------------------------------------------------------- /lib/flaskext/wtf/recaptcha/__init__.py: -------------------------------------------------------------------------------- 1 | from flaskext.wtf.recaptcha import fields 2 | from flaskext.wtf.recaptcha import validators 3 | from flaskext.wtf.recaptcha import widgets 4 | 5 | __all__ = fields.__all__ + validators.__all__ + widgets.__all__ 6 | -------------------------------------------------------------------------------- /lib/flaskext/wtf/recaptcha/fields.py: -------------------------------------------------------------------------------- 1 | from wtforms.fields import Field 2 | 3 | from flaskext.wtf.recaptcha import widgets 4 | from flaskext.wtf.recaptcha.validators import Recaptcha 5 | 6 | __all__ = ["RecaptchaField"] 7 | 8 | class RecaptchaField(Field): 9 | widget = widgets.RecaptchaWidget() 10 | 11 | # error message if recaptcha validation fails 12 | recaptcha_error = None 13 | 14 | def __init__(self, label='', validators=None, **kwargs): 15 | validators = validators or [Recaptcha()] 16 | super(RecaptchaField, self).__init__(label, validators, **kwargs) 17 | -------------------------------------------------------------------------------- /lib/flaskext/wtf/recaptcha/validators.py: -------------------------------------------------------------------------------- 1 | import urllib2 2 | 3 | from flask import request, current_app 4 | 5 | from wtforms import ValidationError 6 | 7 | from werkzeug import url_encode 8 | 9 | RECAPTCHA_VERIFY_SERVER = 'http://api-verify.recaptcha.net/verify' 10 | 11 | __all__ = ["Recaptcha"] 12 | 13 | class Recaptcha(object): 14 | """Validates a ReCaptcha.""" 15 | _error_codes = { 16 | 'invalid-site-public-key': 'The public key for reCAPTCHA is invalid', 17 | 'invalid-site-private-key': 'The private key for reCAPTCHA is invalid', 18 | 'invalid-referrer': 'The public key for reCAPTCHA is not valid for ' 19 | 'this domainin', 20 | 'verify-params-incorrect': 'The parameters passed to reCAPTCHA ' 21 | 'verification are incorrect', 22 | } 23 | 24 | def __init__(self, message=u'Invalid word. Please try again.'): 25 | self.message = message 26 | 27 | def __call__(self, form, field): 28 | challenge = request.form.get('recaptcha_challenge_field', '') 29 | response = request.form.get('recaptcha_response_field', '') 30 | remote_ip = request.remote_addr 31 | 32 | if not challenge or not response: 33 | raise ValidationError('This field is required.') 34 | 35 | if not self._validate_recaptcha(challenge, response, remote_ip): 36 | field.recaptcha_error = 'incorrect-captcha-sol' 37 | raise ValidationError(self.message) 38 | 39 | def _validate_recaptcha(self, challenge, response, remote_addr): 40 | """Performs the actual validation.""" 41 | 42 | try: 43 | private_key = current_app.config['RECAPTCHA_PRIVATE_KEY'] 44 | except KeyError: 45 | raise RuntimeError, "No RECAPTCHA_PRIVATE_KEY config set" 46 | 47 | data = url_encode({ 48 | 'privatekey': private_key, 49 | 'remoteip': remote_addr, 50 | 'challenge': challenge, 51 | 'response': response 52 | }) 53 | 54 | 55 | response = urllib2.urlopen(RECAPTCHA_VERIFY_SERVER, data) 56 | 57 | if response.code != 200: 58 | return False 59 | 60 | rv = [l.strip() for l in response.readlines()] 61 | 62 | if rv and rv[0] == 'true': 63 | return True 64 | 65 | if len(rv) > 1: 66 | error = rv[1] 67 | if error in self._error_codes: 68 | raise RuntimeError(self._error_codes[error]) 69 | 70 | return False 71 | -------------------------------------------------------------------------------- /lib/flaskext/wtf/recaptcha/widgets.py: -------------------------------------------------------------------------------- 1 | """ 2 | Custom widgets 3 | """ 4 | try: 5 | import json 6 | except ImportError: 7 | import simplejson as json 8 | 9 | from flask import current_app 10 | from werkzeug import url_encode 11 | 12 | # use flaskext.babel for translations, if available 13 | 14 | try: 15 | from flaskext.babel import gettext as _ 16 | except ImportError: 17 | _ = lambda(s) : s 18 | 19 | RECAPTCHA_API_SERVER = 'http://api.recaptcha.net/' 20 | RECAPTCHA_SSL_API_SERVER = 'https://api-secure.recaptcha.net/' 21 | RECAPTCHA_HTML = u''' 22 | 23 | 24 | 29 | ''' 30 | 31 | __all__ = ["RecaptchaWidget"] 32 | 33 | class RecaptchaWidget(object): 34 | 35 | def recaptcha_html(self, server, query, options): 36 | return RECAPTCHA_HTML % dict( 37 | script_url='%schallenge?%s' % (server, query), 38 | frame_url='%snoscript?%s' % (server, query), 39 | options=json.dumps(options) 40 | ) 41 | 42 | def __call__(self, field, error=None, **kwargs): 43 | """Returns the recaptcha input HTML.""" 44 | 45 | if current_app.config.get('RECAPTCHA_USE_SSL', False): 46 | 47 | server = RECAPTCHA_SSL_API_SERVER 48 | 49 | else: 50 | 51 | server = RECAPTCHA_API_SERVER 52 | 53 | try: 54 | public_key = current_app.config['RECAPTCHA_PUBLIC_KEY'] 55 | except KeyError: 56 | raise RuntimeError, "RECAPTCHA_PUBLIC_KEY config not set" 57 | query_options = dict(k=public_key) 58 | 59 | if field.recaptcha_error is not None: 60 | query_options['error'] = unicode(field.recaptcha_error) 61 | 62 | query = url_encode(query_options) 63 | 64 | options = { 65 | 'theme': 'clean', 66 | 'custom_translations': { 67 | 'visual_challenge': _('Get a visual challenge'), 68 | 'audio_challenge': _('Get an audio challenge'), 69 | 'refresh_btn': _('Get a new challenge'), 70 | 'instructions_visual': _('Type the two words:'), 71 | 'instructions_audio': _('Type what you hear:'), 72 | 'help_btn': _('Help'), 73 | 'play_again': _('Play sound again'), 74 | 'cant_hear_this': _('Download sound as MP3'), 75 | 'incorrect_try_again': _('Incorrect. Try again.'), 76 | } 77 | } 78 | 79 | options.update(current_app.config.get('RECAPTCHA_OPTIONS', {})) 80 | 81 | return self.recaptcha_html(server, query, options) 82 | -------------------------------------------------------------------------------- /lib/jinja2/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2 4 | ~~~~~~ 5 | 6 | Jinja2 is a template engine written in pure Python. It provides a 7 | Django inspired non-XML syntax but supports inline expressions and 8 | an optional sandboxed environment. 9 | 10 | Nutshell 11 | -------- 12 | 13 | Here a small example of a Jinja2 template:: 14 | 15 | {% extends 'base.html' %} 16 | {% block title %}Memberlist{% endblock %} 17 | {% block content %} 18 | 23 | {% endblock %} 24 | 25 | 26 | :copyright: (c) 2010 by the Jinja Team. 27 | :license: BSD, see LICENSE for more details. 28 | """ 29 | __docformat__ = 'restructuredtext en' 30 | try: 31 | __version__ = __import__('pkg_resources') \ 32 | .get_distribution('Jinja2').version 33 | except: 34 | __version__ = 'unknown' 35 | 36 | # high level interface 37 | from jinja2.environment import Environment, Template 38 | 39 | # loaders 40 | from jinja2.loaders import BaseLoader, FileSystemLoader, PackageLoader, \ 41 | DictLoader, FunctionLoader, PrefixLoader, ChoiceLoader, \ 42 | ModuleLoader 43 | 44 | # bytecode caches 45 | from jinja2.bccache import BytecodeCache, FileSystemBytecodeCache, \ 46 | MemcachedBytecodeCache 47 | 48 | # undefined types 49 | from jinja2.runtime import Undefined, DebugUndefined, StrictUndefined 50 | 51 | # exceptions 52 | from jinja2.exceptions import TemplateError, UndefinedError, \ 53 | TemplateNotFound, TemplatesNotFound, TemplateSyntaxError, \ 54 | TemplateAssertionError 55 | 56 | # decorators and public utilities 57 | from jinja2.filters import environmentfilter, contextfilter, \ 58 | evalcontextfilter 59 | from jinja2.utils import Markup, escape, clear_caches, \ 60 | environmentfunction, evalcontextfunction, contextfunction, \ 61 | is_undefined 62 | 63 | __all__ = [ 64 | 'Environment', 'Template', 'BaseLoader', 'FileSystemLoader', 65 | 'PackageLoader', 'DictLoader', 'FunctionLoader', 'PrefixLoader', 66 | 'ChoiceLoader', 'BytecodeCache', 'FileSystemBytecodeCache', 67 | 'MemcachedBytecodeCache', 'Undefined', 'DebugUndefined', 68 | 'StrictUndefined', 'TemplateError', 'UndefinedError', 'TemplateNotFound', 69 | 'TemplatesNotFound', 'TemplateSyntaxError', 'TemplateAssertionError', 70 | 'ModuleLoader', 'environmentfilter', 'contextfilter', 'Markup', 'escape', 71 | 'environmentfunction', 'contextfunction', 'clear_caches', 'is_undefined', 72 | 'evalcontextfilter', 'evalcontextfunction' 73 | ] 74 | -------------------------------------------------------------------------------- /lib/jinja2/defaults.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.defaults 4 | ~~~~~~~~~~~~~~~ 5 | 6 | Jinja default filters and tags. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | from jinja2.utils import generate_lorem_ipsum, Cycler, Joiner 12 | 13 | 14 | # defaults for the parser / lexer 15 | BLOCK_START_STRING = '{%' 16 | BLOCK_END_STRING = '%}' 17 | VARIABLE_START_STRING = '{{' 18 | VARIABLE_END_STRING = '}}' 19 | COMMENT_START_STRING = '{#' 20 | COMMENT_END_STRING = '#}' 21 | LINE_STATEMENT_PREFIX = None 22 | LINE_COMMENT_PREFIX = None 23 | TRIM_BLOCKS = False 24 | NEWLINE_SEQUENCE = '\n' 25 | 26 | 27 | # default filters, tests and namespace 28 | from jinja2.filters import FILTERS as DEFAULT_FILTERS 29 | from jinja2.tests import TESTS as DEFAULT_TESTS 30 | DEFAULT_NAMESPACE = { 31 | 'range': xrange, 32 | 'dict': lambda **kw: kw, 33 | 'lipsum': generate_lorem_ipsum, 34 | 'cycler': Cycler, 35 | 'joiner': Joiner 36 | } 37 | 38 | 39 | # export all constants 40 | __all__ = tuple(x for x in locals().keys() if x.isupper()) 41 | -------------------------------------------------------------------------------- /lib/jinja2/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.exceptions 4 | ~~~~~~~~~~~~~~~~~ 5 | 6 | Jinja exceptions. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | 12 | 13 | class TemplateError(Exception): 14 | """Baseclass for all template errors.""" 15 | 16 | def __init__(self, message=None): 17 | if message is not None: 18 | message = unicode(message).encode('utf-8') 19 | Exception.__init__(self, message) 20 | 21 | @property 22 | def message(self): 23 | if self.args: 24 | message = self.args[0] 25 | if message is not None: 26 | return message.decode('utf-8', 'replace') 27 | 28 | 29 | class TemplateNotFound(IOError, LookupError, TemplateError): 30 | """Raised if a template does not exist.""" 31 | 32 | # looks weird, but removes the warning descriptor that just 33 | # bogusly warns us about message being deprecated 34 | message = None 35 | 36 | def __init__(self, name, message=None): 37 | IOError.__init__(self) 38 | if message is None: 39 | message = name 40 | self.message = message 41 | self.name = name 42 | self.templates = [name] 43 | 44 | def __str__(self): 45 | return self.message.encode('utf-8') 46 | 47 | # unicode goes after __str__ because we configured 2to3 to rename 48 | # __unicode__ to __str__. because the 2to3 tree is not designed to 49 | # remove nodes from it, we leave the above __str__ around and let 50 | # it override at runtime. 51 | def __unicode__(self): 52 | return self.message 53 | 54 | 55 | class TemplatesNotFound(TemplateNotFound): 56 | """Like :class:`TemplateNotFound` but raised if multiple templates 57 | are selected. This is a subclass of :class:`TemplateNotFound` 58 | exception, so just catching the base exception will catch both. 59 | 60 | .. versionadded:: 2.2 61 | """ 62 | 63 | def __init__(self, names=(), message=None): 64 | if message is None: 65 | message = u'non of the templates given were found: ' + \ 66 | u', '.join(map(unicode, names)) 67 | TemplateNotFound.__init__(self, names and names[-1] or None, message) 68 | self.templates = list(names) 69 | 70 | 71 | class TemplateSyntaxError(TemplateError): 72 | """Raised to tell the user that there is a problem with the template.""" 73 | 74 | def __init__(self, message, lineno, name=None, filename=None): 75 | TemplateError.__init__(self, message) 76 | self.lineno = lineno 77 | self.name = name 78 | self.filename = filename 79 | self.source = None 80 | 81 | # this is set to True if the debug.translate_syntax_error 82 | # function translated the syntax error into a new traceback 83 | self.translated = False 84 | 85 | def __str__(self): 86 | return unicode(self).encode('utf-8') 87 | 88 | # unicode goes after __str__ because we configured 2to3 to rename 89 | # __unicode__ to __str__. because the 2to3 tree is not designed to 90 | # remove nodes from it, we leave the above __str__ around and let 91 | # it override at runtime. 92 | def __unicode__(self): 93 | # for translated errors we only return the message 94 | if self.translated: 95 | return self.message 96 | 97 | # otherwise attach some stuff 98 | location = 'line %d' % self.lineno 99 | name = self.filename or self.name 100 | if name: 101 | location = 'File "%s", %s' % (name, location) 102 | lines = [self.message, ' ' + location] 103 | 104 | # if the source is set, add the line to the output 105 | if self.source is not None: 106 | try: 107 | line = self.source.splitlines()[self.lineno - 1] 108 | except IndexError: 109 | line = None 110 | if line: 111 | lines.append(' ' + line.strip()) 112 | 113 | return u'\n'.join(lines) 114 | 115 | 116 | class TemplateAssertionError(TemplateSyntaxError): 117 | """Like a template syntax error, but covers cases where something in the 118 | template caused an error at compile time that wasn't necessarily caused 119 | by a syntax error. However it's a direct subclass of 120 | :exc:`TemplateSyntaxError` and has the same attributes. 121 | """ 122 | 123 | 124 | class TemplateRuntimeError(TemplateError): 125 | """A generic runtime error in the template engine. Under some situations 126 | Jinja may raise this exception. 127 | """ 128 | 129 | 130 | class UndefinedError(TemplateRuntimeError): 131 | """Raised if a template tries to operate on :class:`Undefined`.""" 132 | 133 | 134 | class SecurityError(TemplateRuntimeError): 135 | """Raised if a template tries to do something insecure if the 136 | sandbox is enabled. 137 | """ 138 | 139 | 140 | class FilterArgumentError(TemplateRuntimeError): 141 | """This error is raised if a filter was called with inappropriate 142 | arguments 143 | """ 144 | -------------------------------------------------------------------------------- /lib/jinja2/meta.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.meta 4 | ~~~~~~~~~~~ 5 | 6 | This module implements various functions that exposes information about 7 | templates that might be interesting for various kinds of applications. 8 | 9 | :copyright: (c) 2010 by the Jinja Team, see AUTHORS for more details. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | from jinja2 import nodes 13 | from jinja2.compiler import CodeGenerator 14 | 15 | 16 | class TrackingCodeGenerator(CodeGenerator): 17 | """We abuse the code generator for introspection.""" 18 | 19 | def __init__(self, environment): 20 | CodeGenerator.__init__(self, environment, '', 21 | '') 22 | self.undeclared_identifiers = set() 23 | 24 | def write(self, x): 25 | """Don't write.""" 26 | 27 | def pull_locals(self, frame): 28 | """Remember all undeclared identifiers.""" 29 | self.undeclared_identifiers.update(frame.identifiers.undeclared) 30 | 31 | 32 | def find_undeclared_variables(ast): 33 | """Returns a set of all variables in the AST that will be looked up from 34 | the context at runtime. Because at compile time it's not known which 35 | variables will be used depending on the path the execution takes at 36 | runtime, all variables are returned. 37 | 38 | >>> from jinja2 import Environment, meta 39 | >>> env = Environment() 40 | >>> ast = env.parse('{% set foo = 42 %}{{ bar + foo }}') 41 | >>> meta.find_undeclared_variables(ast) 42 | set(['bar']) 43 | 44 | .. admonition:: Implementation 45 | 46 | Internally the code generator is used for finding undeclared variables. 47 | This is good to know because the code generator might raise a 48 | :exc:`TemplateAssertionError` during compilation and as a matter of 49 | fact this function can currently raise that exception as well. 50 | """ 51 | codegen = TrackingCodeGenerator(ast.environment) 52 | codegen.visit(ast) 53 | return codegen.undeclared_identifiers 54 | 55 | 56 | def find_referenced_templates(ast): 57 | """Finds all the referenced templates from the AST. This will return an 58 | iterator over all the hardcoded template extensions, inclusions and 59 | imports. If dynamic inheritance or inclusion is used, `None` will be 60 | yielded. 61 | 62 | >>> from jinja2 import Environment, meta 63 | >>> env = Environment() 64 | >>> ast = env.parse('{% extends "layout.html" %}{% include helper %}') 65 | >>> list(meta.find_referenced_templates(ast)) 66 | ['layout.html', None] 67 | 68 | This function is useful for dependency tracking. For example if you want 69 | to rebuild parts of the website after a layout template has changed. 70 | """ 71 | for node in ast.find_all((nodes.Extends, nodes.FromImport, nodes.Import, 72 | nodes.Include)): 73 | if not isinstance(node.template, nodes.Const): 74 | # a tuple with some non consts in there 75 | if isinstance(node.template, (nodes.Tuple, nodes.List)): 76 | for template_name in node.template.items: 77 | # something const, only yield the strings and ignore 78 | # non-string consts that really just make no sense 79 | if isinstance(template_name, nodes.Const): 80 | if isinstance(template_name.value, basestring): 81 | yield template_name.value 82 | # something dynamic in there 83 | else: 84 | yield None 85 | # something dynamic we don't know about here 86 | else: 87 | yield None 88 | continue 89 | # constant is a basestring, direct template name 90 | if isinstance(node.template.value, basestring): 91 | yield node.template.value 92 | # a tuple or list (latter *should* not happen) made of consts, 93 | # yield the consts that are strings. We could warn here for 94 | # non string values 95 | elif isinstance(node, nodes.Include) and \ 96 | isinstance(node.template.value, (tuple, list)): 97 | for template_name in node.template.value: 98 | if isinstance(template_name, basestring): 99 | yield template_name 100 | # something else we don't care about, we could warn here 101 | else: 102 | yield None 103 | -------------------------------------------------------------------------------- /lib/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 | -------------------------------------------------------------------------------- /lib/jinja2/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.tests 4 | ~~~~~~~~~~~~ 5 | 6 | Jinja test functions. Used with the "is" operator. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import re 12 | from jinja2.runtime import Undefined 13 | 14 | # nose, nothing here to test 15 | __test__ = False 16 | 17 | 18 | number_re = re.compile(r'^-?\d+(\.\d+)?$') 19 | regex_type = type(number_re) 20 | 21 | 22 | try: 23 | test_callable = callable 24 | except NameError: 25 | def test_callable(x): 26 | return hasattr(x, '__call__') 27 | 28 | 29 | def test_odd(value): 30 | """Return true if the variable is odd.""" 31 | return value % 2 == 1 32 | 33 | 34 | def test_even(value): 35 | """Return true if the variable is even.""" 36 | return value % 2 == 0 37 | 38 | 39 | def test_divisibleby(value, num): 40 | """Check if a variable is divisible by a number.""" 41 | return value % num == 0 42 | 43 | 44 | def test_defined(value): 45 | """Return true if the variable is defined: 46 | 47 | .. sourcecode:: jinja 48 | 49 | {% if variable is defined %} 50 | value of variable: {{ variable }} 51 | {% else %} 52 | variable is not defined 53 | {% endif %} 54 | 55 | See the :func:`default` filter for a simple way to set undefined 56 | variables. 57 | """ 58 | return not isinstance(value, Undefined) 59 | 60 | 61 | def test_undefined(value): 62 | """Like :func:`defined` but the other way round.""" 63 | return isinstance(value, Undefined) 64 | 65 | 66 | def test_none(value): 67 | """Return true if the variable is none.""" 68 | return value is None 69 | 70 | 71 | def test_lower(value): 72 | """Return true if the variable is lowercased.""" 73 | return unicode(value).islower() 74 | 75 | 76 | def test_upper(value): 77 | """Return true if the variable is uppercased.""" 78 | return unicode(value).isupper() 79 | 80 | 81 | def test_string(value): 82 | """Return true if the object is a string.""" 83 | return isinstance(value, basestring) 84 | 85 | 86 | def test_number(value): 87 | """Return true if the variable is a number.""" 88 | return isinstance(value, (int, long, float, complex)) 89 | 90 | 91 | def test_sequence(value): 92 | """Return true if the variable is a sequence. Sequences are variables 93 | that are iterable. 94 | """ 95 | try: 96 | len(value) 97 | value.__getitem__ 98 | except: 99 | return False 100 | return True 101 | 102 | 103 | def test_sameas(value, other): 104 | """Check if an object points to the same memory address than another 105 | object: 106 | 107 | .. sourcecode:: jinja 108 | 109 | {% if foo.attribute is sameas false %} 110 | the foo attribute really is the `False` singleton 111 | {% endif %} 112 | """ 113 | return value is other 114 | 115 | 116 | def test_iterable(value): 117 | """Check if it's possible to iterate over an object.""" 118 | try: 119 | iter(value) 120 | except TypeError: 121 | return False 122 | return True 123 | 124 | 125 | def test_escaped(value): 126 | """Check if the value is escaped.""" 127 | return hasattr(value, '__html__') 128 | 129 | 130 | TESTS = { 131 | 'odd': test_odd, 132 | 'even': test_even, 133 | 'divisibleby': test_divisibleby, 134 | 'defined': test_defined, 135 | 'undefined': test_undefined, 136 | 'none': test_none, 137 | 'lower': test_lower, 138 | 'upper': test_upper, 139 | 'string': test_string, 140 | 'number': test_number, 141 | 'sequence': test_sequence, 142 | 'iterable': test_iterable, 143 | 'callable': test_callable, 144 | 'sameas': test_sameas, 145 | 'escaped': test_escaped 146 | } 147 | -------------------------------------------------------------------------------- /lib/jinja2/testsuite/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite 4 | ~~~~~~~~~~~~~~~~ 5 | 6 | All the unittests of Jinja2. These tests can be executed by 7 | either running run-tests.py using multiple Python versions at 8 | the same time. 9 | 10 | :copyright: (c) 2010 by the Jinja Team. 11 | :license: BSD, see LICENSE for more details. 12 | """ 13 | import os 14 | import re 15 | import sys 16 | import unittest 17 | from traceback import format_exception 18 | from jinja2 import loaders 19 | 20 | 21 | here = os.path.dirname(os.path.abspath(__file__)) 22 | 23 | dict_loader = loaders.DictLoader({ 24 | 'justdict.html': 'FOO' 25 | }) 26 | package_loader = loaders.PackageLoader('jinja2.testsuite.res', 'templates') 27 | filesystem_loader = loaders.FileSystemLoader(here + '/res/templates') 28 | function_loader = loaders.FunctionLoader({'justfunction.html': 'FOO'}.get) 29 | choice_loader = loaders.ChoiceLoader([dict_loader, package_loader]) 30 | prefix_loader = loaders.PrefixLoader({ 31 | 'a': filesystem_loader, 32 | 'b': dict_loader 33 | }) 34 | 35 | 36 | class JinjaTestCase(unittest.TestCase): 37 | 38 | ### use only these methods for testing. If you need standard 39 | ### unittest method, wrap them! 40 | 41 | def setup(self): 42 | pass 43 | 44 | def teardown(self): 45 | pass 46 | 47 | def setUp(self): 48 | self.setup() 49 | 50 | def tearDown(self): 51 | self.teardown() 52 | 53 | def assert_equal(self, a, b): 54 | return self.assertEqual(a, b) 55 | 56 | def assert_raises(self, *args, **kwargs): 57 | return self.assertRaises(*args, **kwargs) 58 | 59 | def assert_traceback_matches(self, callback, expected_tb): 60 | try: 61 | callback() 62 | except Exception, e: 63 | tb = format_exception(*sys.exc_info()) 64 | if re.search(expected_tb.strip(), ''.join(tb)) is None: 65 | raise self.fail('Traceback did not match:\n\n%s\nexpected:\n%s' 66 | % (''.join(tb), expected_tb)) 67 | else: 68 | self.fail('Expected exception') 69 | 70 | 71 | def suite(): 72 | from jinja2.testsuite import ext, filters, tests, core_tags, \ 73 | loader, inheritance, imports, lexnparse, security, api, \ 74 | regression, debug, utils, doctests 75 | suite = unittest.TestSuite() 76 | suite.addTest(ext.suite()) 77 | suite.addTest(filters.suite()) 78 | suite.addTest(tests.suite()) 79 | suite.addTest(core_tags.suite()) 80 | suite.addTest(loader.suite()) 81 | suite.addTest(inheritance.suite()) 82 | suite.addTest(imports.suite()) 83 | suite.addTest(lexnparse.suite()) 84 | suite.addTest(security.suite()) 85 | suite.addTest(api.suite()) 86 | suite.addTest(regression.suite()) 87 | suite.addTest(debug.suite()) 88 | suite.addTest(utils.suite()) 89 | 90 | # doctests will not run on python 3 currently. Too many issues 91 | # with that, do not test that on that platform. 92 | if sys.version_info < (3, 0): 93 | suite.addTest(doctests.suite()) 94 | 95 | return suite 96 | -------------------------------------------------------------------------------- /lib/jinja2/testsuite/debug.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.debug 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Tests the debug system. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import sys 12 | import unittest 13 | 14 | from jinja2.testsuite import JinjaTestCase, filesystem_loader 15 | 16 | from jinja2 import Environment, TemplateSyntaxError 17 | 18 | env = Environment(loader=filesystem_loader) 19 | 20 | 21 | class DebugTestCase(JinjaTestCase): 22 | 23 | if sys.version_info[:2] != (2, 4): 24 | def test_runtime_error(self): 25 | def test(): 26 | tmpl.render(fail=lambda: 1 / 0) 27 | tmpl = env.get_template('broken.html') 28 | self.assert_traceback_matches(test, r''' 29 | File ".*?broken.html", line 2, in (top-level template code|) 30 | \{\{ fail\(\) \}\} 31 | File ".*?debug.pyc?", line \d+, in 32 | tmpl\.render\(fail=lambda: 1 / 0\) 33 | ZeroDivisionError: (int(eger)? )?division (or modulo )?by zero 34 | ''') 35 | 36 | def test_syntax_error(self): 37 | # XXX: the .*? is necessary for python3 which does not hide 38 | # some of the stack frames we don't want to show. Not sure 39 | # what's up with that, but that is not that critical. Should 40 | # be fixed though. 41 | self.assert_traceback_matches(lambda: env.get_template('syntaxerror.html'), r'''(?sm) 42 | File ".*?syntaxerror.html", line 4, in (template|) 43 | \{% endif %\}.*? 44 | (jinja2\.exceptions\.)?TemplateSyntaxError: Encountered unknown tag 'endif'. Jinja was looking for the following tags: 'endfor' or 'else'. The innermost block that needs to be closed is 'for'. 45 | ''') 46 | 47 | def test_regular_syntax_error(self): 48 | def test(): 49 | raise TemplateSyntaxError('wtf', 42) 50 | self.assert_traceback_matches(test, r''' 51 | File ".*debug.pyc?", line \d+, in test 52 | raise TemplateSyntaxError\('wtf', 42\) 53 | (jinja2\.exceptions\.)?TemplateSyntaxError: wtf 54 | line 42''') 55 | 56 | 57 | def suite(): 58 | suite = unittest.TestSuite() 59 | suite.addTest(unittest.makeSuite(DebugTestCase)) 60 | return suite 61 | -------------------------------------------------------------------------------- /lib/jinja2/testsuite/doctests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.doctests 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | The doctests. Collects all tests we want to test from 7 | the Jinja modules. 8 | 9 | :copyright: (c) 2010 by the Jinja Team. 10 | :license: BSD, see LICENSE for more details. 11 | """ 12 | import unittest 13 | import doctest 14 | 15 | 16 | def suite(): 17 | from jinja2 import utils, sandbox, runtime, meta, loaders, \ 18 | ext, environment, bccache, nodes 19 | suite = unittest.TestSuite() 20 | suite.addTest(doctest.DocTestSuite(utils)) 21 | suite.addTest(doctest.DocTestSuite(sandbox)) 22 | suite.addTest(doctest.DocTestSuite(runtime)) 23 | suite.addTest(doctest.DocTestSuite(meta)) 24 | suite.addTest(doctest.DocTestSuite(loaders)) 25 | suite.addTest(doctest.DocTestSuite(ext)) 26 | suite.addTest(doctest.DocTestSuite(environment)) 27 | suite.addTest(doctest.DocTestSuite(bccache)) 28 | suite.addTest(doctest.DocTestSuite(nodes)) 29 | return suite 30 | -------------------------------------------------------------------------------- /lib/jinja2/testsuite/res/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/lib/jinja2/testsuite/res/__init__.py -------------------------------------------------------------------------------- /lib/jinja2/testsuite/res/templates/broken.html: -------------------------------------------------------------------------------- 1 | Before 2 | {{ fail() }} 3 | After 4 | -------------------------------------------------------------------------------- /lib/jinja2/testsuite/res/templates/foo/test.html: -------------------------------------------------------------------------------- 1 | FOO 2 | -------------------------------------------------------------------------------- /lib/jinja2/testsuite/res/templates/syntaxerror.html: -------------------------------------------------------------------------------- 1 | Foo 2 | {% for item in broken %} 3 | ... 4 | {% endif %} 5 | -------------------------------------------------------------------------------- /lib/jinja2/testsuite/res/templates/test.html: -------------------------------------------------------------------------------- 1 | BAR 2 | -------------------------------------------------------------------------------- /lib/jinja2/testsuite/security.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.security 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Checks the sandbox and other security features. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import os 12 | import time 13 | import tempfile 14 | import unittest 15 | 16 | from jinja2.testsuite import JinjaTestCase 17 | 18 | from jinja2 import Environment 19 | from jinja2.sandbox import SandboxedEnvironment, \ 20 | ImmutableSandboxedEnvironment, unsafe 21 | from jinja2 import Markup, escape 22 | from jinja2.exceptions import SecurityError, TemplateSyntaxError 23 | 24 | 25 | class PrivateStuff(object): 26 | 27 | def bar(self): 28 | return 23 29 | 30 | @unsafe 31 | def foo(self): 32 | return 42 33 | 34 | def __repr__(self): 35 | return 'PrivateStuff' 36 | 37 | 38 | class PublicStuff(object): 39 | bar = lambda self: 23 40 | _foo = lambda self: 42 41 | 42 | def __repr__(self): 43 | return 'PublicStuff' 44 | 45 | 46 | class SandboxTestCase(JinjaTestCase): 47 | 48 | def test_unsafe(self): 49 | env = SandboxedEnvironment() 50 | self.assert_raises(SecurityError, env.from_string("{{ foo.foo() }}").render, 51 | foo=PrivateStuff()) 52 | self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PrivateStuff()), '23') 53 | 54 | self.assert_raises(SecurityError, env.from_string("{{ foo._foo() }}").render, 55 | foo=PublicStuff()) 56 | self.assert_equal(env.from_string("{{ foo.bar() }}").render(foo=PublicStuff()), '23') 57 | self.assert_equal(env.from_string("{{ foo.__class__ }}").render(foo=42), '') 58 | self.assert_equal(env.from_string("{{ foo.func_code }}").render(foo=lambda:None), '') 59 | self.assert_raises(SecurityError, env.from_string( 60 | "{{ foo.__class__.__subclasses__() }}").render, foo=42) 61 | 62 | def test_immutable_environment(self): 63 | env = ImmutableSandboxedEnvironment() 64 | self.assert_raises(SecurityError, env.from_string( 65 | '{{ [].append(23) }}').render) 66 | self.assert_raises(SecurityError, env.from_string( 67 | '{{ {1:2}.clear() }}').render) 68 | 69 | def test_restricted(self): 70 | env = SandboxedEnvironment() 71 | self.assert_raises(TemplateSyntaxError, env.from_string, 72 | "{% for item.attribute in seq %}...{% endfor %}") 73 | self.assert_raises(TemplateSyntaxError, env.from_string, 74 | "{% for foo, bar.baz in seq %}...{% endfor %}") 75 | 76 | def test_markup_operations(self): 77 | # adding two strings should escape the unsafe one 78 | unsafe = '' 79 | safe = Markup('username') 80 | assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe) 81 | 82 | # string interpolations are safe to use too 83 | assert Markup('%s') % '' == \ 84 | '<bad user>' 85 | assert Markup('%(username)s') % { 86 | 'username': '' 87 | } == '<bad user>' 88 | 89 | # an escaped object is markup too 90 | assert type(Markup('foo') + 'bar') is Markup 91 | 92 | # and it implements __html__ by returning itself 93 | x = Markup("foo") 94 | assert x.__html__() is x 95 | 96 | # it also knows how to treat __html__ objects 97 | class Foo(object): 98 | def __html__(self): 99 | return 'awesome' 100 | def __unicode__(self): 101 | return 'awesome' 102 | assert Markup(Foo()) == 'awesome' 103 | assert Markup('%s') % Foo() == \ 104 | 'awesome' 105 | 106 | # escaping and unescaping 107 | assert escape('"<>&\'') == '"<>&'' 108 | assert Markup("Foo & Bar").striptags() == "Foo & Bar" 109 | assert Markup("<test>").unescape() == "" 110 | 111 | 112 | def test_template_data(self): 113 | env = Environment(autoescape=True) 114 | t = env.from_string('{% macro say_hello(name) %}' 115 | '

Hello {{ name }}!

{% endmacro %}' 116 | '{{ say_hello("foo") }}') 117 | escaped_out = '

Hello <blink>foo</blink>!

' 118 | assert t.render() == escaped_out 119 | assert unicode(t.module) == escaped_out 120 | assert escape(t.module) == escaped_out 121 | assert t.module.say_hello('foo') == escaped_out 122 | assert escape(t.module.say_hello('foo')) == escaped_out 123 | 124 | 125 | def test_attr_filter(self): 126 | env = SandboxedEnvironment() 127 | tmpl = env.from_string('{{ 42|attr("__class__")|attr("__subclasses__")() }}') 128 | self.assert_raises(SecurityError, tmpl.render) 129 | 130 | 131 | def suite(): 132 | suite = unittest.TestSuite() 133 | suite.addTest(unittest.makeSuite(SandboxTestCase)) 134 | return suite 135 | -------------------------------------------------------------------------------- /lib/jinja2/testsuite/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.tests 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Who tests the tests? 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import unittest 12 | from jinja2.testsuite import JinjaTestCase 13 | 14 | from jinja2 import Markup, Environment 15 | 16 | env = Environment() 17 | 18 | 19 | class TestsTestCase(JinjaTestCase): 20 | 21 | def test_defined(self): 22 | tmpl = env.from_string('{{ missing is defined }}|{{ true is defined }}') 23 | assert tmpl.render() == 'False|True' 24 | 25 | def test_even(self): 26 | tmpl = env.from_string('''{{ 1 is even }}|{{ 2 is even }}''') 27 | assert tmpl.render() == 'False|True' 28 | 29 | def test_odd(self): 30 | tmpl = env.from_string('''{{ 1 is odd }}|{{ 2 is odd }}''') 31 | assert tmpl.render() == 'True|False' 32 | 33 | def test_lower(self): 34 | tmpl = env.from_string('''{{ "foo" is lower }}|{{ "FOO" is lower }}''') 35 | assert tmpl.render() == 'True|False' 36 | 37 | def test_typechecks(self): 38 | tmpl = env.from_string(''' 39 | {{ 42 is undefined }} 40 | {{ 42 is defined }} 41 | {{ 42 is none }} 42 | {{ none is none }} 43 | {{ 42 is number }} 44 | {{ 42 is string }} 45 | {{ "foo" is string }} 46 | {{ "foo" is sequence }} 47 | {{ [1] is sequence }} 48 | {{ range is callable }} 49 | {{ 42 is callable }} 50 | {{ range(5) is iterable }} 51 | ''') 52 | assert tmpl.render().split() == [ 53 | 'False', 'True', 'False', 'True', 'True', 'False', 54 | 'True', 'True', 'True', 'True', 'False', 'True' 55 | ] 56 | 57 | def test_sequence(self): 58 | tmpl = env.from_string( 59 | '{{ [1, 2, 3] is sequence }}|' 60 | '{{ "foo" is sequence }}|' 61 | '{{ 42 is sequence }}' 62 | ) 63 | assert tmpl.render() == 'True|True|False' 64 | 65 | def test_upper(self): 66 | tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}') 67 | assert tmpl.render() == 'True|False' 68 | 69 | def test_sameas(self): 70 | tmpl = env.from_string('{{ foo is sameas false }}|' 71 | '{{ 0 is sameas false }}') 72 | assert tmpl.render(foo=False) == 'True|False' 73 | 74 | def test_no_paren_for_arg1(self): 75 | tmpl = env.from_string('{{ foo is sameas none }}') 76 | assert tmpl.render(foo=None) == 'True' 77 | 78 | def test_escaped(self): 79 | env = Environment(autoescape=True) 80 | tmpl = env.from_string('{{ x is escaped }}|{{ y is escaped }}') 81 | assert tmpl.render(x='foo', y=Markup('foo')) == 'False|True' 82 | 83 | 84 | def suite(): 85 | suite = unittest.TestSuite() 86 | suite.addTest(unittest.makeSuite(TestsTestCase)) 87 | return suite 88 | -------------------------------------------------------------------------------- /lib/jinja2/testsuite/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.utils 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Tests utilities jinja uses. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import os 12 | import gc 13 | import unittest 14 | 15 | import pickle 16 | 17 | from jinja2.testsuite import JinjaTestCase 18 | 19 | from jinja2 import Environment, Undefined, DebugUndefined, \ 20 | StrictUndefined, UndefinedError, Template, meta 21 | from jinja2.utils import LRUCache, escape, object_type_repr 22 | 23 | 24 | class LRUCacheTestCase(JinjaTestCase): 25 | 26 | def test_simple(self): 27 | d = LRUCache(3) 28 | d["a"] = 1 29 | d["b"] = 2 30 | d["c"] = 3 31 | d["a"] 32 | d["d"] = 4 33 | assert len(d) == 3 34 | assert 'a' in d and 'c' in d and 'd' in d and 'b' not in d 35 | 36 | def test_pickleable(self): 37 | cache = LRUCache(2) 38 | cache["foo"] = 42 39 | cache["bar"] = 23 40 | cache["foo"] 41 | 42 | for protocol in range(3): 43 | copy = pickle.loads(pickle.dumps(cache, protocol)) 44 | assert copy.capacity == cache.capacity 45 | assert copy._mapping == cache._mapping 46 | assert copy._queue == cache._queue 47 | 48 | 49 | class HelpersTestCase(JinjaTestCase): 50 | 51 | def test_object_type_repr(self): 52 | class X(object): 53 | pass 54 | self.assert_equal(object_type_repr(42), 'int object') 55 | self.assert_equal(object_type_repr([]), 'list object') 56 | self.assert_equal(object_type_repr(X()), 57 | 'jinja2.testsuite.utils.X object') 58 | self.assert_equal(object_type_repr(None), 'None') 59 | self.assert_equal(object_type_repr(Ellipsis), 'Ellipsis') 60 | 61 | 62 | class MarkupLeakTestCase(JinjaTestCase): 63 | 64 | def test_markup_leaks(self): 65 | counts = set() 66 | for count in xrange(20): 67 | for item in xrange(1000): 68 | escape("foo") 69 | escape("") 70 | escape(u"foo") 71 | escape(u"") 72 | counts.add(len(gc.get_objects())) 73 | assert len(counts) == 1, 'ouch, c extension seems to leak objects' 74 | 75 | 76 | def suite(): 77 | suite = unittest.TestSuite() 78 | suite.addTest(unittest.makeSuite(LRUCacheTestCase)) 79 | suite.addTest(unittest.makeSuite(HelpersTestCase)) 80 | 81 | # this test only tests the c extension 82 | if not hasattr(escape, 'func_code'): 83 | suite.addTest(unittest.makeSuite(MarkupLeakTestCase)) 84 | 85 | return suite 86 | -------------------------------------------------------------------------------- /lib/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 | -------------------------------------------------------------------------------- /lib/simplejson/ordered_dict.py: -------------------------------------------------------------------------------- 1 | """Drop-in replacement for collections.OrderedDict by Raymond Hettinger 2 | 3 | http://code.activestate.com/recipes/576693/ 4 | 5 | """ 6 | from UserDict import DictMixin 7 | 8 | # Modified from original to support Python 2.4, see 9 | # http://code.google.com/p/simplejson/issues/detail?id=53 10 | try: 11 | all 12 | except NameError: 13 | def all(seq): 14 | for elem in seq: 15 | if not elem: 16 | return False 17 | return True 18 | 19 | class OrderedDict(dict, DictMixin): 20 | 21 | def __init__(self, *args, **kwds): 22 | if len(args) > 1: 23 | raise TypeError('expected at most 1 arguments, got %d' % len(args)) 24 | try: 25 | self.__end 26 | except AttributeError: 27 | self.clear() 28 | self.update(*args, **kwds) 29 | 30 | def clear(self): 31 | self.__end = end = [] 32 | end += [None, end, end] # sentinel node for doubly linked list 33 | self.__map = {} # key --> [key, prev, next] 34 | dict.clear(self) 35 | 36 | def __setitem__(self, key, value): 37 | if key not in self: 38 | end = self.__end 39 | curr = end[1] 40 | curr[2] = end[1] = self.__map[key] = [key, curr, end] 41 | dict.__setitem__(self, key, value) 42 | 43 | def __delitem__(self, key): 44 | dict.__delitem__(self, key) 45 | key, prev, next = self.__map.pop(key) 46 | prev[2] = next 47 | next[1] = prev 48 | 49 | def __iter__(self): 50 | end = self.__end 51 | curr = end[2] 52 | while curr is not end: 53 | yield curr[0] 54 | curr = curr[2] 55 | 56 | def __reversed__(self): 57 | end = self.__end 58 | curr = end[1] 59 | while curr is not end: 60 | yield curr[0] 61 | curr = curr[1] 62 | 63 | def popitem(self, last=True): 64 | if not self: 65 | raise KeyError('dictionary is empty') 66 | # Modified from original to support Python 2.4, see 67 | # http://code.google.com/p/simplejson/issues/detail?id=53 68 | if last: 69 | key = reversed(self).next() 70 | else: 71 | key = iter(self).next() 72 | value = self.pop(key) 73 | return key, value 74 | 75 | def __reduce__(self): 76 | items = [[k, self[k]] for k in self] 77 | tmp = self.__map, self.__end 78 | del self.__map, self.__end 79 | inst_dict = vars(self).copy() 80 | self.__map, self.__end = tmp 81 | if inst_dict: 82 | return (self.__class__, (items,), inst_dict) 83 | return self.__class__, (items,) 84 | 85 | def keys(self): 86 | return list(self) 87 | 88 | setdefault = DictMixin.setdefault 89 | update = DictMixin.update 90 | pop = DictMixin.pop 91 | values = DictMixin.values 92 | items = DictMixin.items 93 | iterkeys = DictMixin.iterkeys 94 | itervalues = DictMixin.itervalues 95 | iteritems = DictMixin.iteritems 96 | 97 | def __repr__(self): 98 | if not self: 99 | return '%s()' % (self.__class__.__name__,) 100 | return '%s(%r)' % (self.__class__.__name__, self.items()) 101 | 102 | def copy(self): 103 | return self.__class__(self) 104 | 105 | @classmethod 106 | def fromkeys(cls, iterable, value=None): 107 | d = cls() 108 | for key in iterable: 109 | d[key] = value 110 | return d 111 | 112 | def __eq__(self, other): 113 | if isinstance(other, OrderedDict): 114 | return len(self)==len(other) and \ 115 | all(p==q for p, q in zip(self.items(), other.items())) 116 | return dict.__eq__(self, other) 117 | 118 | def __ne__(self, other): 119 | return not self == other 120 | -------------------------------------------------------------------------------- /lib/simplejson/scanner.py: -------------------------------------------------------------------------------- 1 | """JSON token scanner 2 | """ 3 | import re 4 | def _import_c_make_scanner(): 5 | try: 6 | from simplejson._speedups import make_scanner 7 | return make_scanner 8 | except ImportError: 9 | return None 10 | c_make_scanner = _import_c_make_scanner() 11 | 12 | __all__ = ['make_scanner'] 13 | 14 | NUMBER_RE = re.compile( 15 | r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?', 16 | (re.VERBOSE | re.MULTILINE | re.DOTALL)) 17 | 18 | def py_make_scanner(context): 19 | parse_object = context.parse_object 20 | parse_array = context.parse_array 21 | parse_string = context.parse_string 22 | match_number = NUMBER_RE.match 23 | encoding = context.encoding 24 | strict = context.strict 25 | parse_float = context.parse_float 26 | parse_int = context.parse_int 27 | parse_constant = context.parse_constant 28 | object_hook = context.object_hook 29 | object_pairs_hook = context.object_pairs_hook 30 | memo = context.memo 31 | 32 | def _scan_once(string, idx): 33 | try: 34 | nextchar = string[idx] 35 | except IndexError: 36 | raise StopIteration 37 | 38 | if nextchar == '"': 39 | return parse_string(string, idx + 1, encoding, strict) 40 | elif nextchar == '{': 41 | return parse_object((string, idx + 1), encoding, strict, 42 | _scan_once, object_hook, object_pairs_hook, memo) 43 | elif nextchar == '[': 44 | return parse_array((string, idx + 1), _scan_once) 45 | elif nextchar == 'n' and string[idx:idx + 4] == 'null': 46 | return None, idx + 4 47 | elif nextchar == 't' and string[idx:idx + 4] == 'true': 48 | return True, idx + 4 49 | elif nextchar == 'f' and string[idx:idx + 5] == 'false': 50 | return False, idx + 5 51 | 52 | m = match_number(string, idx) 53 | if m is not None: 54 | integer, frac, exp = m.groups() 55 | if frac or exp: 56 | res = parse_float(integer + (frac or '') + (exp or '')) 57 | else: 58 | res = parse_int(integer) 59 | return res, m.end() 60 | elif nextchar == 'N' and string[idx:idx + 3] == 'NaN': 61 | return parse_constant('NaN'), idx + 3 62 | elif nextchar == 'I' and string[idx:idx + 8] == 'Infinity': 63 | return parse_constant('Infinity'), idx + 8 64 | elif nextchar == '-' and string[idx:idx + 9] == '-Infinity': 65 | return parse_constant('-Infinity'), idx + 9 66 | else: 67 | raise StopIteration 68 | 69 | def scan_once(string, idx): 70 | try: 71 | return _scan_once(string, idx) 72 | finally: 73 | memo.clear() 74 | 75 | return scan_once 76 | 77 | make_scanner = c_make_scanner or py_make_scanner 78 | -------------------------------------------------------------------------------- /lib/simplejson/tests/__init__.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import doctest 3 | 4 | 5 | class OptionalExtensionTestSuite(unittest.TestSuite): 6 | def run(self, result): 7 | import simplejson 8 | run = unittest.TestSuite.run 9 | run(self, result) 10 | simplejson._toggle_speedups(False) 11 | run(self, result) 12 | simplejson._toggle_speedups(True) 13 | return result 14 | 15 | 16 | def additional_tests(suite=None): 17 | import simplejson 18 | import simplejson.encoder 19 | import simplejson.decoder 20 | if suite is None: 21 | suite = unittest.TestSuite() 22 | for mod in (simplejson, simplejson.encoder, simplejson.decoder): 23 | suite.addTest(doctest.DocTestSuite(mod)) 24 | suite.addTest(doctest.DocFileSuite('../../index.rst')) 25 | return suite 26 | 27 | 28 | def all_tests_suite(): 29 | suite = unittest.TestLoader().loadTestsFromNames([ 30 | 'simplejson.tests.test_check_circular', 31 | 'simplejson.tests.test_decode', 32 | 'simplejson.tests.test_default', 33 | 'simplejson.tests.test_dump', 34 | 'simplejson.tests.test_encode_basestring_ascii', 35 | 'simplejson.tests.test_encode_for_html', 36 | 'simplejson.tests.test_fail', 37 | 'simplejson.tests.test_float', 38 | 'simplejson.tests.test_indent', 39 | 'simplejson.tests.test_pass1', 40 | 'simplejson.tests.test_pass2', 41 | 'simplejson.tests.test_pass3', 42 | 'simplejson.tests.test_recursion', 43 | 'simplejson.tests.test_scanstring', 44 | 'simplejson.tests.test_separators', 45 | 'simplejson.tests.test_speedups', 46 | 'simplejson.tests.test_unicode', 47 | 'simplejson.tests.test_decimal', 48 | ]) 49 | suite = additional_tests(suite) 50 | return OptionalExtensionTestSuite([suite]) 51 | 52 | 53 | def main(): 54 | runner = unittest.TextTestRunner() 55 | suite = all_tests_suite() 56 | runner.run(suite) 57 | 58 | 59 | if __name__ == '__main__': 60 | import os 61 | import sys 62 | sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 63 | main() 64 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_check_circular.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | import simplejson as json 3 | 4 | def default_iterable(obj): 5 | return list(obj) 6 | 7 | class TestCheckCircular(TestCase): 8 | def test_circular_dict(self): 9 | dct = {} 10 | dct['a'] = dct 11 | self.assertRaises(ValueError, json.dumps, dct) 12 | 13 | def test_circular_list(self): 14 | lst = [] 15 | lst.append(lst) 16 | self.assertRaises(ValueError, json.dumps, lst) 17 | 18 | def test_circular_composite(self): 19 | dct2 = {} 20 | dct2['a'] = [] 21 | dct2['a'].append(dct2) 22 | self.assertRaises(ValueError, json.dumps, dct2) 23 | 24 | def test_circular_default(self): 25 | json.dumps([set()], default=default_iterable) 26 | self.assertRaises(TypeError, json.dumps, [set()]) 27 | 28 | def test_circular_off_default(self): 29 | json.dumps([set()], default=default_iterable, check_circular=False) 30 | self.assertRaises(TypeError, json.dumps, [set()], check_circular=False) 31 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_decimal.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from unittest import TestCase 3 | 4 | import simplejson as json 5 | 6 | class TestDecimal(TestCase): 7 | NUMS = "1.0", "10.00", "1.1", "1234567890.1234567890", "500" 8 | def test_decimal_encode(self): 9 | for d in map(Decimal, self.NUMS): 10 | self.assertEquals(json.dumps(d, use_decimal=True), str(d)) 11 | 12 | def test_decimal_decode(self): 13 | for s in self.NUMS: 14 | self.assertEquals(json.loads(s, parse_float=Decimal), Decimal(s)) 15 | 16 | def test_decimal_roundtrip(self): 17 | for d in map(Decimal, self.NUMS): 18 | # The type might not be the same (int and Decimal) but they 19 | # should still compare equal. 20 | self.assertEquals( 21 | json.loads( 22 | json.dumps(d, use_decimal=True), parse_float=Decimal), 23 | d) 24 | self.assertEquals( 25 | json.loads( 26 | json.dumps([d], use_decimal=True), parse_float=Decimal), 27 | [d]) 28 | 29 | def test_decimal_defaults(self): 30 | d = Decimal(1) 31 | # use_decimal=False is the default 32 | self.assertRaises(TypeError, json.dumps, d, use_decimal=False) 33 | self.assertRaises(TypeError, json.dumps, d) -------------------------------------------------------------------------------- /lib/simplejson/tests/test_decode.py: -------------------------------------------------------------------------------- 1 | import decimal 2 | from unittest import TestCase 3 | from StringIO import StringIO 4 | 5 | import simplejson as json 6 | from simplejson import OrderedDict 7 | 8 | class TestDecode(TestCase): 9 | if not hasattr(TestCase, 'assertIs'): 10 | def assertIs(self, a, b): 11 | self.assertTrue(a is b, '%r is %r' % (a, b)) 12 | 13 | def test_decimal(self): 14 | rval = json.loads('1.1', parse_float=decimal.Decimal) 15 | self.assertTrue(isinstance(rval, decimal.Decimal)) 16 | self.assertEquals(rval, decimal.Decimal('1.1')) 17 | 18 | def test_float(self): 19 | rval = json.loads('1', parse_int=float) 20 | self.assertTrue(isinstance(rval, float)) 21 | self.assertEquals(rval, 1.0) 22 | 23 | def test_decoder_optimizations(self): 24 | # Several optimizations were made that skip over calls to 25 | # the whitespace regex, so this test is designed to try and 26 | # exercise the uncommon cases. The array cases are already covered. 27 | rval = json.loads('{ "key" : "value" , "k":"v" }') 28 | self.assertEquals(rval, {"key":"value", "k":"v"}) 29 | 30 | def test_empty_objects(self): 31 | s = '{}' 32 | self.assertEqual(json.loads(s), eval(s)) 33 | s = '[]' 34 | self.assertEqual(json.loads(s), eval(s)) 35 | s = '""' 36 | self.assertEqual(json.loads(s), eval(s)) 37 | 38 | def test_object_pairs_hook(self): 39 | s = '{"xkd":1, "kcw":2, "art":3, "hxm":4, "qrt":5, "pad":6, "hoy":7}' 40 | p = [("xkd", 1), ("kcw", 2), ("art", 3), ("hxm", 4), 41 | ("qrt", 5), ("pad", 6), ("hoy", 7)] 42 | self.assertEqual(json.loads(s), eval(s)) 43 | self.assertEqual(json.loads(s, object_pairs_hook=lambda x: x), p) 44 | self.assertEqual(json.load(StringIO(s), 45 | object_pairs_hook=lambda x: x), p) 46 | od = json.loads(s, object_pairs_hook=OrderedDict) 47 | self.assertEqual(od, OrderedDict(p)) 48 | self.assertEqual(type(od), OrderedDict) 49 | # the object_pairs_hook takes priority over the object_hook 50 | self.assertEqual(json.loads(s, 51 | object_pairs_hook=OrderedDict, 52 | object_hook=lambda x: None), 53 | OrderedDict(p)) 54 | 55 | def check_keys_reuse(self, source, loads): 56 | rval = loads(source) 57 | (a, b), (c, d) = sorted(rval[0]), sorted(rval[1]) 58 | self.assertIs(a, c) 59 | self.assertIs(b, d) 60 | 61 | def test_keys_reuse_str(self): 62 | s = u'[{"a_key": 1, "b_\xe9": 2}, {"a_key": 3, "b_\xe9": 4}]'.encode('utf8') 63 | self.check_keys_reuse(s, json.loads) 64 | 65 | def test_keys_reuse_unicode(self): 66 | s = u'[{"a_key": 1, "b_\xe9": 2}, {"a_key": 3, "b_\xe9": 4}]' 67 | self.check_keys_reuse(s, json.loads) 68 | 69 | def test_empty_strings(self): 70 | self.assertEqual(json.loads('""'), "") 71 | self.assertEqual(json.loads(u'""'), u"") 72 | self.assertEqual(json.loads('[""]'), [""]) 73 | self.assertEqual(json.loads(u'[""]'), [u""]) 74 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_default.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import simplejson as json 4 | 5 | class TestDefault(TestCase): 6 | def test_default(self): 7 | self.assertEquals( 8 | json.dumps(type, default=repr), 9 | json.dumps(repr(type))) 10 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_dump.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from cStringIO import StringIO 3 | 4 | import simplejson as json 5 | 6 | class TestDump(TestCase): 7 | def test_dump(self): 8 | sio = StringIO() 9 | json.dump({}, sio) 10 | self.assertEquals(sio.getvalue(), '{}') 11 | 12 | def test_dumps(self): 13 | self.assertEquals(json.dumps({}), '{}') 14 | 15 | def test_encode_truefalse(self): 16 | self.assertEquals(json.dumps( 17 | {True: False, False: True}, sort_keys=True), 18 | '{"false": true, "true": false}') 19 | self.assertEquals(json.dumps( 20 | {2: 3.0, 4.0: 5L, False: 1, 6L: True, "7": 0}, sort_keys=True), 21 | '{"false": 1, "2": 3.0, "4.0": 5, "6": true, "7": 0}') 22 | 23 | def test_ordered_dict(self): 24 | # http://bugs.python.org/issue6105 25 | items = [('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)] 26 | s = json.dumps(json.OrderedDict(items)) 27 | self.assertEqual(s, '{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5}') -------------------------------------------------------------------------------- /lib/simplejson/tests/test_encode_basestring_ascii.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import simplejson.encoder 4 | 5 | CASES = [ 6 | (u'/\\"\ucafe\ubabe\uab98\ufcde\ubcda\uef4a\x08\x0c\n\r\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?', '"/\\\\\\"\\ucafe\\ubabe\\uab98\\ufcde\\ubcda\\uef4a\\b\\f\\n\\r\\t`1~!@#$%^&*()_+-=[]{}|;:\',./<>?"'), 7 | (u'\u0123\u4567\u89ab\ucdef\uabcd\uef4a', '"\\u0123\\u4567\\u89ab\\ucdef\\uabcd\\uef4a"'), 8 | (u'controls', '"controls"'), 9 | (u'\x08\x0c\n\r\t', '"\\b\\f\\n\\r\\t"'), 10 | (u'{"object with 1 member":["array with 1 element"]}', '"{\\"object with 1 member\\":[\\"array with 1 element\\"]}"'), 11 | (u' s p a c e d ', '" s p a c e d "'), 12 | (u'\U0001d120', '"\\ud834\\udd20"'), 13 | (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), 14 | ('\xce\xb1\xce\xa9', '"\\u03b1\\u03a9"'), 15 | (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), 16 | ('\xce\xb1\xce\xa9', '"\\u03b1\\u03a9"'), 17 | (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), 18 | (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), 19 | (u"`1~!@#$%^&*()_+-={':[,]}|;.?", '"`1~!@#$%^&*()_+-={\':[,]}|;.?"'), 20 | (u'\x08\x0c\n\r\t', '"\\b\\f\\n\\r\\t"'), 21 | (u'\u0123\u4567\u89ab\ucdef\uabcd\uef4a', '"\\u0123\\u4567\\u89ab\\ucdef\\uabcd\\uef4a"'), 22 | ] 23 | 24 | class TestEncodeBaseStringAscii(TestCase): 25 | def test_py_encode_basestring_ascii(self): 26 | self._test_encode_basestring_ascii(simplejson.encoder.py_encode_basestring_ascii) 27 | 28 | def test_c_encode_basestring_ascii(self): 29 | if not simplejson.encoder.c_encode_basestring_ascii: 30 | return 31 | self._test_encode_basestring_ascii(simplejson.encoder.c_encode_basestring_ascii) 32 | 33 | def _test_encode_basestring_ascii(self, encode_basestring_ascii): 34 | fname = encode_basestring_ascii.__name__ 35 | for input_string, expect in CASES: 36 | result = encode_basestring_ascii(input_string) 37 | #self.assertEquals(result, expect, 38 | # '{0!r} != {1!r} for {2}({3!r})'.format( 39 | # result, expect, fname, input_string)) 40 | self.assertEquals(result, expect, 41 | '%r != %r for %s(%r)' % (result, expect, fname, input_string)) 42 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_encode_for_html.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import simplejson.decoder 4 | import simplejson.encoder 5 | 6 | 7 | class TestEncodeForHTML(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.decoder = simplejson.decoder.JSONDecoder() 11 | self.encoder = simplejson.encoder.JSONEncoderForHTML() 12 | 13 | def test_basic_encode(self): 14 | self.assertEqual(r'"\u0026"', self.encoder.encode('&')) 15 | self.assertEqual(r'"\u003c"', self.encoder.encode('<')) 16 | self.assertEqual(r'"\u003e"', self.encoder.encode('>')) 17 | 18 | def test_basic_roundtrip(self): 19 | for char in '&<>': 20 | self.assertEqual( 21 | char, self.decoder.decode( 22 | self.encoder.encode(char))) 23 | 24 | def test_prevent_script_breakout(self): 25 | bad_string = '' 26 | self.assertEqual( 27 | r'"\u003c/script\u003e\u003cscript\u003e' 28 | r'alert(\"gotcha\")\u003c/script\u003e"', 29 | self.encoder.encode(bad_string)) 30 | self.assertEqual( 31 | bad_string, self.decoder.decode( 32 | self.encoder.encode(bad_string))) 33 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_fail.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import simplejson as json 4 | 5 | # Fri Dec 30 18:57:26 2005 6 | JSONDOCS = [ 7 | # http://json.org/JSON_checker/test/fail1.json 8 | '"A JSON payload should be an object or array, not a string."', 9 | # http://json.org/JSON_checker/test/fail2.json 10 | '["Unclosed array"', 11 | # http://json.org/JSON_checker/test/fail3.json 12 | '{unquoted_key: "keys must be quoted}', 13 | # http://json.org/JSON_checker/test/fail4.json 14 | '["extra comma",]', 15 | # http://json.org/JSON_checker/test/fail5.json 16 | '["double extra comma",,]', 17 | # http://json.org/JSON_checker/test/fail6.json 18 | '[ , "<-- missing value"]', 19 | # http://json.org/JSON_checker/test/fail7.json 20 | '["Comma after the close"],', 21 | # http://json.org/JSON_checker/test/fail8.json 22 | '["Extra close"]]', 23 | # http://json.org/JSON_checker/test/fail9.json 24 | '{"Extra comma": true,}', 25 | # http://json.org/JSON_checker/test/fail10.json 26 | '{"Extra value after close": true} "misplaced quoted value"', 27 | # http://json.org/JSON_checker/test/fail11.json 28 | '{"Illegal expression": 1 + 2}', 29 | # http://json.org/JSON_checker/test/fail12.json 30 | '{"Illegal invocation": alert()}', 31 | # http://json.org/JSON_checker/test/fail13.json 32 | '{"Numbers cannot have leading zeroes": 013}', 33 | # http://json.org/JSON_checker/test/fail14.json 34 | '{"Numbers cannot be hex": 0x14}', 35 | # http://json.org/JSON_checker/test/fail15.json 36 | '["Illegal backslash escape: \\x15"]', 37 | # http://json.org/JSON_checker/test/fail16.json 38 | '["Illegal backslash escape: \\\'"]', 39 | # http://json.org/JSON_checker/test/fail17.json 40 | '["Illegal backslash escape: \\017"]', 41 | # http://json.org/JSON_checker/test/fail18.json 42 | '[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]', 43 | # http://json.org/JSON_checker/test/fail19.json 44 | '{"Missing colon" null}', 45 | # http://json.org/JSON_checker/test/fail20.json 46 | '{"Double colon":: null}', 47 | # http://json.org/JSON_checker/test/fail21.json 48 | '{"Comma instead of colon", null}', 49 | # http://json.org/JSON_checker/test/fail22.json 50 | '["Colon instead of comma": false]', 51 | # http://json.org/JSON_checker/test/fail23.json 52 | '["Bad value", truth]', 53 | # http://json.org/JSON_checker/test/fail24.json 54 | "['single quote']", 55 | # http://code.google.com/p/simplejson/issues/detail?id=3 56 | u'["A\u001FZ control characters in string"]', 57 | ] 58 | 59 | SKIPS = { 60 | 1: "why not have a string payload?", 61 | 18: "spec doesn't specify any nesting limitations", 62 | } 63 | 64 | class TestFail(TestCase): 65 | def test_failures(self): 66 | for idx, doc in enumerate(JSONDOCS): 67 | idx = idx + 1 68 | if idx in SKIPS: 69 | json.loads(doc) 70 | continue 71 | try: 72 | json.loads(doc) 73 | except json.JSONDecodeError: 74 | pass 75 | else: 76 | #self.fail("Expected failure for fail{0}.json: {1!r}".format(idx, doc)) 77 | self.fail("Expected failure for fail%d.json: %r" % (idx, doc)) 78 | 79 | def test_array_decoder_issue46(self): 80 | # http://code.google.com/p/simplejson/issues/detail?id=46 81 | for doc in [u'[,]', '[,]']: 82 | try: 83 | json.loads(doc) 84 | except json.JSONDecodeError, e: 85 | self.assertEquals(e.pos, 1) 86 | self.assertEquals(e.lineno, 1) 87 | self.assertEquals(e.colno, 1) 88 | except Exception, e: 89 | self.fail("Unexpected exception raised %r %s" % (e, e)) 90 | else: 91 | self.fail("Unexpected success parsing '[,]'") -------------------------------------------------------------------------------- /lib/simplejson/tests/test_float.py: -------------------------------------------------------------------------------- 1 | import math 2 | from unittest import TestCase 3 | 4 | import simplejson as json 5 | 6 | class TestFloat(TestCase): 7 | def test_floats(self): 8 | for num in [1617161771.7650001, math.pi, math.pi**100, 9 | math.pi**-100, 3.1]: 10 | self.assertEquals(float(json.dumps(num)), num) 11 | self.assertEquals(json.loads(json.dumps(num)), num) 12 | self.assertEquals(json.loads(unicode(json.dumps(num))), num) 13 | 14 | def test_ints(self): 15 | for num in [1, 1L, 1<<32, 1<<64]: 16 | self.assertEquals(json.dumps(num), str(num)) 17 | self.assertEquals(int(json.dumps(num)), num) 18 | self.assertEquals(json.loads(json.dumps(num)), num) 19 | self.assertEquals(json.loads(unicode(json.dumps(num))), num) 20 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_indent.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import simplejson as json 4 | import textwrap 5 | 6 | class TestIndent(TestCase): 7 | def test_indent(self): 8 | h = [['blorpie'], ['whoops'], [], 'd-shtaeou', 'd-nthiouh', 9 | 'i-vhbjkhnth', 10 | {'nifty': 87}, {'field': 'yes', 'morefield': False} ] 11 | 12 | expect = textwrap.dedent("""\ 13 | [ 14 | \t[ 15 | \t\t"blorpie" 16 | \t], 17 | \t[ 18 | \t\t"whoops" 19 | \t], 20 | \t[], 21 | \t"d-shtaeou", 22 | \t"d-nthiouh", 23 | \t"i-vhbjkhnth", 24 | \t{ 25 | \t\t"nifty": 87 26 | \t}, 27 | \t{ 28 | \t\t"field": "yes", 29 | \t\t"morefield": false 30 | \t} 31 | ]""") 32 | 33 | 34 | d1 = json.dumps(h) 35 | d2 = json.dumps(h, indent='\t', sort_keys=True, separators=(',', ': ')) 36 | d3 = json.dumps(h, indent=' ', sort_keys=True, separators=(',', ': ')) 37 | d4 = json.dumps(h, indent=2, sort_keys=True, separators=(',', ': ')) 38 | 39 | h1 = json.loads(d1) 40 | h2 = json.loads(d2) 41 | h3 = json.loads(d3) 42 | h4 = json.loads(d4) 43 | 44 | self.assertEquals(h1, h) 45 | self.assertEquals(h2, h) 46 | self.assertEquals(h3, h) 47 | self.assertEquals(h4, h) 48 | self.assertEquals(d3, expect.replace('\t', ' ')) 49 | self.assertEquals(d4, expect.replace('\t', ' ')) 50 | # NOTE: Python 2.4 textwrap.dedent converts tabs to spaces, 51 | # so the following is expected to fail. Python 2.4 is not a 52 | # supported platform in simplejson 2.1.0+. 53 | self.assertEquals(d2, expect) 54 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_pass1.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import simplejson as json 4 | 5 | # from http://json.org/JSON_checker/test/pass1.json 6 | JSON = r''' 7 | [ 8 | "JSON Test Pattern pass1", 9 | {"object with 1 member":["array with 1 element"]}, 10 | {}, 11 | [], 12 | -42, 13 | true, 14 | false, 15 | null, 16 | { 17 | "integer": 1234567890, 18 | "real": -9876.543210, 19 | "e": 0.123456789e-12, 20 | "E": 1.234567890E+34, 21 | "": 23456789012E666, 22 | "zero": 0, 23 | "one": 1, 24 | "space": " ", 25 | "quote": "\"", 26 | "backslash": "\\", 27 | "controls": "\b\f\n\r\t", 28 | "slash": "/ & \/", 29 | "alpha": "abcdefghijklmnopqrstuvwyz", 30 | "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", 31 | "digit": "0123456789", 32 | "special": "`1~!@#$%^&*()_+-={':[,]}|;.?", 33 | "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", 34 | "true": true, 35 | "false": false, 36 | "null": null, 37 | "array":[ ], 38 | "object":{ }, 39 | "address": "50 St. James Street", 40 | "url": "http://www.JSON.org/", 41 | "comment": "// /* */": " ", 43 | " s p a c e d " :[1,2 , 3 44 | 45 | , 46 | 47 | 4 , 5 , 6 ,7 ], 48 | "compact": [1,2,3,4,5,6,7], 49 | "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", 50 | "quotes": "" \u0022 %22 0x22 034 "", 51 | "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" 52 | : "A key can be any string" 53 | }, 54 | 0.5 ,98.6 55 | , 56 | 99.44 57 | , 58 | 59 | 1066 60 | 61 | 62 | ,"rosebud"] 63 | ''' 64 | 65 | class TestPass1(TestCase): 66 | def test_parse(self): 67 | # test in/out equivalence and parsing 68 | res = json.loads(JSON) 69 | out = json.dumps(res) 70 | self.assertEquals(res, json.loads(out)) 71 | try: 72 | json.dumps(res, allow_nan=False) 73 | except ValueError: 74 | pass 75 | else: 76 | self.fail("23456789012E666 should be out of range") 77 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_pass2.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | import simplejson as json 3 | 4 | # from http://json.org/JSON_checker/test/pass2.json 5 | JSON = r''' 6 | [[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] 7 | ''' 8 | 9 | class TestPass2(TestCase): 10 | def test_parse(self): 11 | # test in/out equivalence and parsing 12 | res = json.loads(JSON) 13 | out = json.dumps(res) 14 | self.assertEquals(res, json.loads(out)) 15 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_pass3.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import simplejson as json 4 | 5 | # from http://json.org/JSON_checker/test/pass3.json 6 | JSON = r''' 7 | { 8 | "JSON Test Pattern pass3": { 9 | "The outermost value": "must be an object or array.", 10 | "In this test": "It is an object." 11 | } 12 | } 13 | ''' 14 | 15 | class TestPass3(TestCase): 16 | def test_parse(self): 17 | # test in/out equivalence and parsing 18 | res = json.loads(JSON) 19 | out = json.dumps(res) 20 | self.assertEquals(res, json.loads(out)) 21 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_recursion.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import simplejson as json 4 | 5 | class JSONTestObject: 6 | pass 7 | 8 | 9 | class RecursiveJSONEncoder(json.JSONEncoder): 10 | recurse = False 11 | def default(self, o): 12 | if o is JSONTestObject: 13 | if self.recurse: 14 | return [JSONTestObject] 15 | else: 16 | return 'JSONTestObject' 17 | return json.JSONEncoder.default(o) 18 | 19 | 20 | class TestRecursion(TestCase): 21 | def test_listrecursion(self): 22 | x = [] 23 | x.append(x) 24 | try: 25 | json.dumps(x) 26 | except ValueError: 27 | pass 28 | else: 29 | self.fail("didn't raise ValueError on list recursion") 30 | x = [] 31 | y = [x] 32 | x.append(y) 33 | try: 34 | json.dumps(x) 35 | except ValueError: 36 | pass 37 | else: 38 | self.fail("didn't raise ValueError on alternating list recursion") 39 | y = [] 40 | x = [y, y] 41 | # ensure that the marker is cleared 42 | json.dumps(x) 43 | 44 | def test_dictrecursion(self): 45 | x = {} 46 | x["test"] = x 47 | try: 48 | json.dumps(x) 49 | except ValueError: 50 | pass 51 | else: 52 | self.fail("didn't raise ValueError on dict recursion") 53 | x = {} 54 | y = {"a": x, "b": x} 55 | # ensure that the marker is cleared 56 | json.dumps(x) 57 | 58 | def test_defaultrecursion(self): 59 | enc = RecursiveJSONEncoder() 60 | self.assertEquals(enc.encode(JSONTestObject), '"JSONTestObject"') 61 | enc.recurse = True 62 | try: 63 | enc.encode(JSONTestObject) 64 | except ValueError: 65 | pass 66 | else: 67 | self.fail("didn't raise ValueError on default recursion") 68 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_scanstring.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from unittest import TestCase 3 | 4 | import simplejson as json 5 | import simplejson.decoder 6 | 7 | class TestScanString(TestCase): 8 | def test_py_scanstring(self): 9 | self._test_scanstring(simplejson.decoder.py_scanstring) 10 | 11 | def test_c_scanstring(self): 12 | if not simplejson.decoder.c_scanstring: 13 | return 14 | self._test_scanstring(simplejson.decoder.c_scanstring) 15 | 16 | def _test_scanstring(self, scanstring): 17 | self.assertEquals( 18 | scanstring('"z\\ud834\\udd20x"', 1, None, True), 19 | (u'z\U0001d120x', 16)) 20 | 21 | if sys.maxunicode == 65535: 22 | self.assertEquals( 23 | scanstring(u'"z\U0001d120x"', 1, None, True), 24 | (u'z\U0001d120x', 6)) 25 | else: 26 | self.assertEquals( 27 | scanstring(u'"z\U0001d120x"', 1, None, True), 28 | (u'z\U0001d120x', 5)) 29 | 30 | self.assertEquals( 31 | scanstring('"\\u007b"', 1, None, True), 32 | (u'{', 8)) 33 | 34 | self.assertEquals( 35 | scanstring('"A JSON payload should be an object or array, not a string."', 1, None, True), 36 | (u'A JSON payload should be an object or array, not a string.', 60)) 37 | 38 | self.assertEquals( 39 | scanstring('["Unclosed array"', 2, None, True), 40 | (u'Unclosed array', 17)) 41 | 42 | self.assertEquals( 43 | scanstring('["extra comma",]', 2, None, True), 44 | (u'extra comma', 14)) 45 | 46 | self.assertEquals( 47 | scanstring('["double extra comma",,]', 2, None, True), 48 | (u'double extra comma', 21)) 49 | 50 | self.assertEquals( 51 | scanstring('["Comma after the close"],', 2, None, True), 52 | (u'Comma after the close', 24)) 53 | 54 | self.assertEquals( 55 | scanstring('["Extra close"]]', 2, None, True), 56 | (u'Extra close', 14)) 57 | 58 | self.assertEquals( 59 | scanstring('{"Extra comma": true,}', 2, None, True), 60 | (u'Extra comma', 14)) 61 | 62 | self.assertEquals( 63 | scanstring('{"Extra value after close": true} "misplaced quoted value"', 2, None, True), 64 | (u'Extra value after close', 26)) 65 | 66 | self.assertEquals( 67 | scanstring('{"Illegal expression": 1 + 2}', 2, None, True), 68 | (u'Illegal expression', 21)) 69 | 70 | self.assertEquals( 71 | scanstring('{"Illegal invocation": alert()}', 2, None, True), 72 | (u'Illegal invocation', 21)) 73 | 74 | self.assertEquals( 75 | scanstring('{"Numbers cannot have leading zeroes": 013}', 2, None, True), 76 | (u'Numbers cannot have leading zeroes', 37)) 77 | 78 | self.assertEquals( 79 | scanstring('{"Numbers cannot be hex": 0x14}', 2, None, True), 80 | (u'Numbers cannot be hex', 24)) 81 | 82 | self.assertEquals( 83 | scanstring('[[[[[[[[[[[[[[[[[[[["Too deep"]]]]]]]]]]]]]]]]]]]]', 21, None, True), 84 | (u'Too deep', 30)) 85 | 86 | self.assertEquals( 87 | scanstring('{"Missing colon" null}', 2, None, True), 88 | (u'Missing colon', 16)) 89 | 90 | self.assertEquals( 91 | scanstring('{"Double colon":: null}', 2, None, True), 92 | (u'Double colon', 15)) 93 | 94 | self.assertEquals( 95 | scanstring('{"Comma instead of colon", null}', 2, None, True), 96 | (u'Comma instead of colon', 25)) 97 | 98 | self.assertEquals( 99 | scanstring('["Colon instead of comma": false]', 2, None, True), 100 | (u'Colon instead of comma', 25)) 101 | 102 | self.assertEquals( 103 | scanstring('["Bad value", truth]', 2, None, True), 104 | (u'Bad value', 12)) 105 | 106 | def test_issue3623(self): 107 | self.assertRaises(ValueError, json.decoder.scanstring, "xxx", 1, 108 | "xxx") 109 | self.assertRaises(UnicodeDecodeError, 110 | json.encoder.encode_basestring_ascii, "xx\xff") 111 | 112 | def test_overflow(self): 113 | # Python 2.5 does not have maxsize 114 | maxsize = getattr(sys, 'maxsize', sys.maxint) 115 | self.assertRaises(OverflowError, json.decoder.scanstring, "xxx", 116 | maxsize + 1) 117 | 118 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_separators.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | from unittest import TestCase 3 | 4 | import simplejson as json 5 | 6 | 7 | class TestSeparators(TestCase): 8 | def test_separators(self): 9 | h = [['blorpie'], ['whoops'], [], 'd-shtaeou', 'd-nthiouh', 'i-vhbjkhnth', 10 | {'nifty': 87}, {'field': 'yes', 'morefield': False} ] 11 | 12 | expect = textwrap.dedent("""\ 13 | [ 14 | [ 15 | "blorpie" 16 | ] , 17 | [ 18 | "whoops" 19 | ] , 20 | [] , 21 | "d-shtaeou" , 22 | "d-nthiouh" , 23 | "i-vhbjkhnth" , 24 | { 25 | "nifty" : 87 26 | } , 27 | { 28 | "field" : "yes" , 29 | "morefield" : false 30 | } 31 | ]""") 32 | 33 | 34 | d1 = json.dumps(h) 35 | d2 = json.dumps(h, indent=' ', sort_keys=True, separators=(' ,', ' : ')) 36 | 37 | h1 = json.loads(d1) 38 | h2 = json.loads(d2) 39 | 40 | self.assertEquals(h1, h) 41 | self.assertEquals(h2, h) 42 | self.assertEquals(d2, expect) 43 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_speedups.py: -------------------------------------------------------------------------------- 1 | import decimal 2 | from unittest import TestCase 3 | 4 | from simplejson import decoder, encoder, scanner 5 | 6 | def has_speedups(): 7 | return encoder.c_make_encoder is not None 8 | 9 | class TestDecode(TestCase): 10 | def test_make_scanner(self): 11 | if not has_speedups(): 12 | return 13 | self.assertRaises(AttributeError, scanner.c_make_scanner, 1) 14 | 15 | def test_make_encoder(self): 16 | if not has_speedups(): 17 | return 18 | self.assertRaises(TypeError, encoder.c_make_encoder, 19 | None, 20 | "\xCD\x7D\x3D\x4E\x12\x4C\xF9\x79\xD7\x52\xBA\x82\xF2\x27\x4A\x7D\xA0\xCA\x75", 21 | None) 22 | -------------------------------------------------------------------------------- /lib/simplejson/tests/test_unicode.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | 3 | import simplejson as json 4 | 5 | class TestUnicode(TestCase): 6 | def test_encoding1(self): 7 | encoder = json.JSONEncoder(encoding='utf-8') 8 | u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' 9 | s = u.encode('utf-8') 10 | ju = encoder.encode(u) 11 | js = encoder.encode(s) 12 | self.assertEquals(ju, js) 13 | 14 | def test_encoding2(self): 15 | u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' 16 | s = u.encode('utf-8') 17 | ju = json.dumps(u, encoding='utf-8') 18 | js = json.dumps(s, encoding='utf-8') 19 | self.assertEquals(ju, js) 20 | 21 | def test_encoding3(self): 22 | u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' 23 | j = json.dumps(u) 24 | self.assertEquals(j, '"\\u03b1\\u03a9"') 25 | 26 | def test_encoding4(self): 27 | u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' 28 | j = json.dumps([u]) 29 | self.assertEquals(j, '["\\u03b1\\u03a9"]') 30 | 31 | def test_encoding5(self): 32 | u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' 33 | j = json.dumps(u, ensure_ascii=False) 34 | self.assertEquals(j, u'"' + u + u'"') 35 | 36 | def test_encoding6(self): 37 | u = u'\N{GREEK SMALL LETTER ALPHA}\N{GREEK CAPITAL LETTER OMEGA}' 38 | j = json.dumps([u], ensure_ascii=False) 39 | self.assertEquals(j, u'["' + u + u'"]') 40 | 41 | def test_big_unicode_encode(self): 42 | u = u'\U0001d120' 43 | self.assertEquals(json.dumps(u), '"\\ud834\\udd20"') 44 | self.assertEquals(json.dumps(u, ensure_ascii=False), u'"\U0001d120"') 45 | 46 | def test_big_unicode_decode(self): 47 | u = u'z\U0001d120x' 48 | self.assertEquals(json.loads('"' + u + '"'), u) 49 | self.assertEquals(json.loads('"z\\ud834\\udd20x"'), u) 50 | 51 | def test_unicode_decode(self): 52 | for i in range(0, 0xd7ff): 53 | u = unichr(i) 54 | #s = '"\\u{0:04x}"'.format(i) 55 | s = '"\\u%04x"' % (i,) 56 | self.assertEquals(json.loads(s), u) 57 | 58 | def test_object_pairs_hook_with_unicode(self): 59 | s = u'{"xkd":1, "kcw":2, "art":3, "hxm":4, "qrt":5, "pad":6, "hoy":7}' 60 | p = [(u"xkd", 1), (u"kcw", 2), (u"art", 3), (u"hxm", 4), 61 | (u"qrt", 5), (u"pad", 6), (u"hoy", 7)] 62 | self.assertEqual(json.loads(s), eval(s)) 63 | self.assertEqual(json.loads(s, object_pairs_hook=lambda x: x), p) 64 | od = json.loads(s, object_pairs_hook=json.OrderedDict) 65 | self.assertEqual(od, json.OrderedDict(p)) 66 | self.assertEqual(type(od), json.OrderedDict) 67 | # the object_pairs_hook takes priority over the object_hook 68 | self.assertEqual(json.loads(s, 69 | object_pairs_hook=json.OrderedDict, 70 | object_hook=lambda x: None), 71 | json.OrderedDict(p)) 72 | 73 | 74 | def test_default_encoding(self): 75 | self.assertEquals(json.loads(u'{"a": "\xe9"}'.encode('utf-8')), 76 | {'a': u'\xe9'}) 77 | 78 | def test_unicode_preservation(self): 79 | self.assertEquals(type(json.loads(u'""')), unicode) 80 | self.assertEquals(type(json.loads(u'"a"')), unicode) 81 | self.assertEquals(type(json.loads(u'["a"]')[0]), unicode) 82 | 83 | def test_ensure_ascii_false_returns_unicode(self): 84 | # http://code.google.com/p/simplejson/issues/detail?id=48 85 | self.assertEquals(type(json.dumps([], ensure_ascii=False)), unicode) 86 | self.assertEquals(type(json.dumps(0, ensure_ascii=False)), unicode) 87 | self.assertEquals(type(json.dumps({}, ensure_ascii=False)), unicode) 88 | self.assertEquals(type(json.dumps("", ensure_ascii=False)), unicode) 89 | 90 | def test_ensure_ascii_false_bytestring_encoding(self): 91 | # http://code.google.com/p/simplejson/issues/detail?id=48 92 | doc1 = {u'quux': 'Arr\xc3\xaat sur images'} 93 | doc2 = {u'quux': u'Arr\xeat sur images'} 94 | doc_ascii = '{"quux": "Arr\\u00eat sur images"}' 95 | doc_unicode = u'{"quux": "Arr\xeat sur images"}' 96 | self.assertEquals(json.dumps(doc1), doc_ascii) 97 | self.assertEquals(json.dumps(doc2), doc_ascii) 98 | self.assertEquals(json.dumps(doc1, ensure_ascii=False), doc_unicode) 99 | self.assertEquals(json.dumps(doc2, ensure_ascii=False), doc_unicode) 100 | -------------------------------------------------------------------------------- /lib/simplejson/tool.py: -------------------------------------------------------------------------------- 1 | r"""Command-line tool to validate and pretty-print JSON 2 | 3 | Usage:: 4 | 5 | $ echo '{"json":"obj"}' | python -m simplejson.tool 6 | { 7 | "json": "obj" 8 | } 9 | $ echo '{ 1.2:3.4}' | python -m simplejson.tool 10 | Expecting property name: line 1 column 2 (char 2) 11 | 12 | """ 13 | import sys 14 | import simplejson as json 15 | 16 | def main(): 17 | if len(sys.argv) == 1: 18 | infile = sys.stdin 19 | outfile = sys.stdout 20 | elif len(sys.argv) == 2: 21 | infile = open(sys.argv[1], 'rb') 22 | outfile = sys.stdout 23 | elif len(sys.argv) == 3: 24 | infile = open(sys.argv[1], 'rb') 25 | outfile = open(sys.argv[2], 'wb') 26 | else: 27 | raise SystemExit(sys.argv[0] + " [infile [outfile]]") 28 | try: 29 | obj = json.load(infile, 30 | object_pairs_hook=json.OrderedDict, 31 | use_decimal=True) 32 | except ValueError, e: 33 | raise SystemExit(e) 34 | json.dump(obj, outfile, sort_keys=True, indent=' ', use_decimal=True) 35 | outfile.write('\n') 36 | 37 | 38 | if __name__ == '__main__': 39 | main() 40 | -------------------------------------------------------------------------------- /lib/werkzeug/contrib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.contrib 4 | ~~~~~~~~~~~~~~~~ 5 | 6 | Contains user-submitted code that other users may find useful, but which 7 | is not part of the Werkzeug core. Anyone can write code for inclusion in 8 | the `contrib` package. All modules in this package are distributed as an 9 | add-on library and thus are not part of Werkzeug itself. 10 | 11 | This file itself is mostly for informational purposes and to tell the 12 | Python interpreter that `contrib` is a package. 13 | 14 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 15 | :license: BSD, see LICENSE for more details. 16 | """ 17 | -------------------------------------------------------------------------------- /lib/werkzeug/contrib/limiter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.contrib.limiter 4 | ~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | A middleware that limits incoming data. This works around problems with 7 | Trac_ or Django_ because those directly stream into the memory. 8 | 9 | .. _Trac: http://trac.edgewall.org/ 10 | .. _Django: http://www.djangoproject.com/ 11 | 12 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 13 | :license: BSD, see LICENSE for more details. 14 | """ 15 | from warnings import warn 16 | 17 | from werkzeug import LimitedStream 18 | 19 | 20 | class StreamLimitMiddleware(object): 21 | """Limits the input stream to a given number of bytes. This is useful if 22 | you have a WSGI application that reads form data into memory (django for 23 | example) and you don't want users to harm the server by uploading tons of 24 | data. 25 | 26 | Default is 10MB 27 | """ 28 | 29 | def __init__(self, app, maximum_size=1024 * 1024 * 10): 30 | self.app = app 31 | self.maximum_size = maximum_size 32 | 33 | def __call__(self, environ, start_response): 34 | limit = min(limit, int(environ.get('CONTENT_LENGTH') or 0)) 35 | environ['wsgi.input'] = LimitedStream(environ['wsgi.input'], limit) 36 | return self.app(environ, start_response) 37 | -------------------------------------------------------------------------------- /lib/werkzeug/contrib/profiler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.contrib.profiler 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | This module provides a simple WSGI profiler middleware for finding 7 | bottlenecks in web application. It uses the :mod:`profile` or 8 | :mod:`cProfile` module to do the profiling and writes the stats to the 9 | stream provided (defaults to stderr). 10 | 11 | Example usage:: 12 | 13 | from werkzeug.contrib.profiler import ProfilerMiddleware 14 | app = ProfilerMiddleware(app) 15 | 16 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 17 | :license: BSD, see LICENSE for more details. 18 | """ 19 | import sys 20 | try: 21 | try: 22 | from cProfile import Profile 23 | except ImportError: 24 | from profile import Profile 25 | from pstats import Stats 26 | available = True 27 | except ImportError: 28 | available = False 29 | 30 | 31 | class MergeStream(object): 32 | """An object that redirects `write` calls to multiple streams. 33 | Use this to log to both `sys.stdout` and a file:: 34 | 35 | f = open('profiler.log', 'w') 36 | stream = MergeStream(sys.stdout, f) 37 | profiler = ProfilerMiddleware(app, stream) 38 | """ 39 | 40 | def __init__(self, *streams): 41 | if not streams: 42 | raise TypeError('at least one stream must be given') 43 | self.streams = streams 44 | 45 | def write(self, data): 46 | for stream in self.streams: 47 | stream.write(data) 48 | 49 | 50 | class ProfilerMiddleware(object): 51 | """Simple profiler middleware. Wraps a WSGI application and profiles 52 | a request. This intentionally buffers the response so that timings are 53 | more exact. 54 | 55 | For the exact meaning of `sort_by` and `restrictions` consult the 56 | :mod:`profile` documentation. 57 | 58 | :param app: the WSGI application to profile. 59 | :param stream: the stream for the profiled stats. defaults to stderr. 60 | :param sort_by: a tuple of columns to sort the result by. 61 | :param restrictions: a tuple of profiling strictions. 62 | """ 63 | 64 | def __init__(self, app, stream=None, 65 | sort_by=('time', 'calls'), restrictions=()): 66 | if not available: 67 | raise RuntimeError('the profiler is not available because ' 68 | 'profile or pstat is not installed.') 69 | self._app = app 70 | self._stream = stream or sys.stdout 71 | self._sort_by = sort_by 72 | self._restrictions = restrictions 73 | 74 | def __call__(self, environ, start_response): 75 | response_body = [] 76 | 77 | def catching_start_response(status, headers, exc_info=None): 78 | start_response(status, headers, exc_info) 79 | return response_body.append 80 | 81 | def runapp(): 82 | appiter = self._app(environ, catching_start_response) 83 | response_body.extend(appiter) 84 | if hasattr(appiter, 'close'): 85 | appiter.close() 86 | 87 | p = Profile() 88 | p.runcall(runapp) 89 | body = ''.join(response_body) 90 | stats = Stats(p, stream=self._stream) 91 | stats.sort_stats(*self._sort_by) 92 | 93 | self._stream.write('-' * 80) 94 | self._stream.write('\nPATH: %r\n' % environ.get('PATH_INFO')) 95 | stats.print_stats(*self._restrictions) 96 | self._stream.write('-' * 80 + '\n\n') 97 | 98 | return [body] 99 | 100 | 101 | def make_action(app_factory, hostname='localhost', port=5000, 102 | threaded=False, processes=1, stream=None, 103 | sort_by=('time', 'calls'), restrictions=()): 104 | """Return a new callback for :mod:`werkzeug.script` that starts a local 105 | server with the profiler enabled:: 106 | 107 | from werkzeug.contrib import profiler 108 | action_profile = profiler.make_action(make_app) 109 | """ 110 | def action(hostname=('h', hostname), port=('p', port), 111 | threaded=threaded, processes=processes): 112 | """Start a new development server.""" 113 | from werkzeug.serving import run_simple 114 | app = ProfilerMiddleware(app_factory(), stream, sort_by, restrictions) 115 | run_simple(hostname, port, app, False, None, threaded, processes) 116 | return action 117 | -------------------------------------------------------------------------------- /lib/werkzeug/contrib/testtools.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.contrib.testtools 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | This module implements extended wrappers for simplified testing. 7 | 8 | `TestResponse` 9 | A response wrapper which adds various cached attributes for 10 | simplified assertions on various content types. 11 | 12 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 13 | :license: BSD, see LICENSE for more details. 14 | """ 15 | from werkzeug import Response, cached_property, import_string 16 | 17 | 18 | class ContentAccessors(object): 19 | """ 20 | A mixin class for response objects that provides a couple of useful 21 | accessors for unittesting. 22 | """ 23 | 24 | def xml(self): 25 | """Get an etree if possible.""" 26 | if 'xml' not in self.mimetype: 27 | raise AttributeError( 28 | 'Not a XML response (Content-Type: %s)' 29 | % self.mimetype) 30 | for module in ['xml.etree.ElementTree', 'ElementTree', 31 | 'elementtree.ElementTree']: 32 | etree = import_string(module, silent=True) 33 | if etree is not None: 34 | return etree.XML(self.body) 35 | raise RuntimeError('You must have ElementTree installed ' 36 | 'to use TestResponse.xml') 37 | xml = cached_property(xml) 38 | 39 | def lxml(self): 40 | """Get an lxml etree if possible.""" 41 | if ('html' not in self.mimetype and 'xml' not in self.mimetype): 42 | raise AttributeError('Not an HTML/XML response') 43 | from lxml import etree 44 | try: 45 | from lxml.html import fromstring 46 | except ImportError: 47 | fromstring = etree.HTML 48 | if self.mimetype=='text/html': 49 | return fromstring(self.data) 50 | return etree.XML(self.data) 51 | lxml = cached_property(lxml) 52 | 53 | def json(self): 54 | """Get the result of simplejson.loads if possible.""" 55 | if 'json' not in self.mimetype: 56 | raise AttributeError('Not a JSON response') 57 | try: 58 | from simplejson import loads 59 | except: 60 | from json import loads 61 | return loads(self.data) 62 | json = cached_property(json) 63 | 64 | 65 | class TestResponse(Response, ContentAccessors): 66 | """Pass this to `werkzeug.test.Client` for easier unittesting.""" 67 | -------------------------------------------------------------------------------- /lib/werkzeug/debug/render.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.debug.render 4 | ~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Render the traceback debugging page. 7 | 8 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import pprint 12 | from os.path import dirname, join 13 | 14 | from werkzeug.templates import Template 15 | 16 | 17 | def get_template(name): 18 | return Template.from_file(join(dirname(__file__), 'shared', name), 19 | unicode_mode=False, errors='ignore') 20 | 21 | 22 | def load_resource(res): 23 | try: 24 | f = file(join(dirname(__file__), 'shared', res)) 25 | except IOError: 26 | return '' 27 | try: 28 | return f.read() 29 | finally: 30 | f.close() 31 | 32 | 33 | t_body = get_template('body.tmpl') 34 | t_codetable = get_template('codetable.tmpl') 35 | t_vartable = get_template('vartable.tmpl') 36 | 37 | 38 | def code_table(frame): 39 | from werkzeug.debug.util import Namespace 40 | lines = [] 41 | lineno = frame['context_lineno'] 42 | if lineno is not None: 43 | lineno += 1 44 | for l in frame['pre_context']: 45 | lines.append(Namespace(mode='pre', lineno=lineno, code=l)) 46 | lineno += 1 47 | lines.append(Namespace(mode='cur', lineno=lineno, 48 | code=frame['context_line'])) 49 | lineno += 1 50 | for l in frame['post_context']: 51 | lines.append(Namespace(mode='post', lineno=lineno, code=l)) 52 | lineno += 1 53 | else: 54 | lines.append(Namespace(mode='cur', lineno=1, 55 | code='Sourcecode not available')) 56 | 57 | return t_codetable.render(lines=lines) 58 | 59 | 60 | def var_table(var): 61 | def safe_pformat(x): 62 | try: 63 | lines = pprint.pformat(x).splitlines() 64 | except: 65 | return '?' 66 | tmp = [] 67 | for line in lines: 68 | if len(line) > 79: 69 | line = line[:79] + '...' 70 | tmp.append(line) 71 | return '\n'.join(tmp) 72 | 73 | # dicts 74 | if isinstance(var, dict) or hasattr(var, 'items'): 75 | value = var.items() 76 | if not value: 77 | typ = 'empty' 78 | else: 79 | typ = 'dict' 80 | value.sort() 81 | value = [(repr(key), safe_pformat(val)) for key, val in value] 82 | 83 | # lists 84 | elif isinstance(var, list): 85 | if not var: 86 | typ = 'empty' 87 | else: 88 | typ = 'list' 89 | value = [safe_pformat(item) for item in var] 90 | 91 | # others 92 | else: 93 | typ = 'simple' 94 | value = repr(var) 95 | 96 | return t_vartable.render(type=typ, value=value) 97 | 98 | 99 | def debug_page(context): 100 | tc = context.to_dict() 101 | tc['var_table'] = var_table 102 | tc['code_table'] = code_table 103 | return t_body.render(tc) 104 | -------------------------------------------------------------------------------- /lib/werkzeug/debug/shared/body.tmpl: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | $escape(exception_type) in $escape(last_frame['basename']) (Werkzeug Debugger) 6 | 7 | 8 | 9 | 10 | 11 |
12 |

$escape(exception_type)

13 |

$escape(exception_value)

14 | 15 |

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

20 | 21 |

Traceback (toggle raw view)

22 |
23 |

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

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

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

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

Here is the plain Python traceback for copy and paste:

48 |
$escape(plaintb)
49 |

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

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

Request Data

57 |

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

59 | <% for num, (key, info) in enumerate(req_vars) %> 60 |
61 |
$escape(key)
62 |
$var_table(info)
63 |
64 | <% endfor %> 65 | <% endif %> 66 |
67 | 68 | 72 | 73 | 74 | 75 | 82 | -------------------------------------------------------------------------------- /lib/werkzeug/debug/shared/codetable.tmpl: -------------------------------------------------------------------------------- 1 | 2 | <% for line in lines %> 3 | 4 | 5 | 6 | 7 | <% endfor %> 8 |
$line.lineno$line.code
9 | -------------------------------------------------------------------------------- /lib/werkzeug/debug/shared/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/lib/werkzeug/debug/shared/console.png -------------------------------------------------------------------------------- /lib/werkzeug/debug/shared/less.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/lib/werkzeug/debug/shared/less.png -------------------------------------------------------------------------------- /lib/werkzeug/debug/shared/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/lib/werkzeug/debug/shared/more.png -------------------------------------------------------------------------------- /lib/werkzeug/debug/shared/source.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/lib/werkzeug/debug/shared/source.png -------------------------------------------------------------------------------- /lib/werkzeug/debug/shared/style.css: -------------------------------------------------------------------------------- 1 | body, input { font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 2 | 'Verdana', sans-serif; background-color: #AFC1C4; color: #000; 3 | text-align: center; margin: 1em; padding: 0; font-size: 15px; } 4 | input { background-color: #fff; margin: 0; text-align: left; } 5 | a { color: #11557C; } 6 | a:hover { color: #177199; } 7 | pre, code, table.source, 8 | textarea { font-family: 'Consolas', 'Deja Vu Sans Mono', 9 | 'Bitstream Vera Sans Mono', monospace; font-size: 13px; } 10 | 11 | div.debugger { text-align: left; padding: 12px; margin: auto; 12 | border: 1px solid #aaa; background-color: white; } 13 | h1 { color: #11557C; font-size: 30px; margin: 0 0 0.3em 0; } 14 | div.detail p { margin: 0 0 8px 13px; font-size: 14px; } 15 | div.explanation { margin: 13px; font-size: 11px; } 16 | div.footer { background-color: #E3EFF1; font-size: 0.8em; text-align: right; 17 | padding: 6px 8px 6px 0; margin: 30px -12px -12px -12px; 18 | color: #86989B; } 19 | 20 | h2 { font-size: 16px; margin: 1.3em 0 0.0 0; padding: 5px; 21 | background-color: #11557C; color: white; } 22 | h2 em { font-style: normal; color: #A5D6D9; font-weight: normal; } 23 | 24 | div.traceback, div.plain { background-color: #eee!important; border: 1px solid #ccc; 25 | margin: 0 0 1em 0; padding: 10px; } 26 | div.plain p { margin: 0; } 27 | div.plain textarea, 28 | div.plain pre { margin: 10px 0 0 0; padding: 4px; background-color: #333; 29 | border: 1px solid #111; color: white; } 30 | div.plain textarea { width: 99%; height: 300px; } 31 | div.traceback h3 { font-size: 1em; margin: 0 0 0.8em 0; } 32 | div.traceback ul { list-style: none; margin: 0; padding: 0 0 0 1em; } 33 | div.traceback h4 { font-size: 13px; font-weight: normal; margin: 0.7em 0 0.1em 0; } 34 | div.traceback pre { margin: 0; padding: 5px 0 3px 15px; 35 | background-color: #ccc; border-top: 1px solid #aaa; 36 | border-left: 1px solid #aaa; border-right: 1px solid #fafafa; 37 | border-bottom: 1px solid #fafafa; } 38 | div.traceback pre, 39 | div.box table.source { white-space: pre-wrap; /* css-3 should we be so lucky... */ 40 | white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ 41 | white-space: -pre-wrap; /* Opera 4-6 ?? */ 42 | white-space: -o-pre-wrap; /* Opera 7 ?? */ 43 | word-wrap: break-word; /* Internet Explorer 5.5+ */ 44 | _white-space: pre; /* IE only hack to re-specify in 45 | addition to word-wrap */ } 46 | div.traceback pre:hover { background-color: #fafafa; color: black; cursor: pointer; } 47 | div.traceback blockquote { margin: 1em 0 0 0; padding: 0; } 48 | div.traceback img { float: right; padding: 2px; margin: -3px 2px 0 0; display: none; } 49 | div.traceback img:hover { background-color: #ddd; cursor: pointer; } 50 | div.traceback pre:hover img { display: block; } 51 | 52 | pre.console { background-color: #fafafa!important; color: black; padding: 5px!important; 53 | margin: 3px 0 0 0!important; cursor: default!important; 54 | max-height: 400px; overflow: auto; } 55 | pre.console form { color: #555; } 56 | pre.console input { background-color: #fafafa; color: #555; 57 | width: 90%; font-family: 'Consolas', 'Deja Vu Sans Mono', 58 | 'Bitstream Vera Sans Mono', monospace; font-size: 13px; 59 | border: none!important; } 60 | 61 | span.string { color: #30799B; } 62 | span.number { color: #9C1A1C; } 63 | span.object { color: #485F6E; } 64 | span.extended { opacity: 0.5; } 65 | span.extended:hover { opacity: 1; } 66 | a.toggle { text-decoration: none; background-repeat: no-repeat; 67 | background-position: center center; 68 | background-image: url(./__debugger__?cmd=resource&f=more.png); } 69 | a.toggle:hover { background-color: #444; } 70 | a.open { background-image: url(./__debugger__?cmd=resource&f=less.png); } 71 | 72 | pre.console div.traceback { margin: 5px 0 5px 25px; white-space: normal; } 73 | pre.console div.traceback h3 { background-color: #555; color: white; 74 | margin: -10px -10px 5px -10px; padding: 5px; } 75 | pre.console div.traceback pre:hover { background-color: #ccc; cursor: default; } 76 | 77 | pre.console div.box { margin: 5px 0 5px 25px; white-space: normal; 78 | border: 1px solid #ddd; } 79 | pre.console div.box h3 { background-color: #555; color: white; 80 | margin: 0; padding: 5px; } 81 | pre.console div.box div.repr { padding: 8px; background-color: white; } 82 | pre.console div.box table { margin-top: 6px; } 83 | pre.console div.box pre.help { background-color: white; font-size: 12px; } 84 | pre.console div.box pre.help:hover { cursor: default; } 85 | pre.console table tr { vertical-align: top; } 86 | div.box table.source { background-color: #fafafa; font-size: 12px; 87 | border-collapse: collapse; width: 100%; } 88 | div.box table.source td { border-top: 1px solid #eee; padding: 4px 0 4px 10px; } 89 | div.box table.source td.lineno { color: #999; padding-right: 10px; width: 1px; } 90 | div.box table.source tr.in-frame { background-color: #D6EEFF; } 91 | div.box table.source tr.current { background-color: white; } 92 | div.sourceview { max-height: 400px; overflow: auto; border: 1px solid #ccc; } 93 | div.console { border: 1px solid #ccc; padding: 4px; background-color: #fafafa; } 94 | -------------------------------------------------------------------------------- /lib/werkzeug/debug/shared/vartable.tmpl: -------------------------------------------------------------------------------- 1 | 2 | <% if type == 'empty' %> 3 | 4 | <% elif type == 'simple' %> 5 | 6 | <% elif type == 'dict' %> 7 | 8 | <% for key, item in value %> 9 | 10 | <% endfor %> 11 | <% elif type == 'list' %> 12 | <% for item in value %> 13 | 14 | <% endfor %> 15 | <% endif %> 16 |
no data given
$escape(value)
NameValue
$escape(key)$escape(item)
$escape(item)
17 | -------------------------------------------------------------------------------- /lib/werkzeug/debug/templates/console.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Console // Werkzeug Debugger 6 | 7 | 8 | 9 | 13 | 14 | 15 |
16 |

Interactive Console

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

$escape(title)

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

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

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

$title

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

Help

8 |

Type help(object) for help about object.

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

$escape(traceback.exception_type)

18 |
19 |

$escape(traceback.exception)

20 |
21 |

Traceback (most recent call last)

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

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

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

Syntax Error

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

Traceback (most recent call last):

15 | <% endif %> 16 |
    17 | <% for frame in traceback.frames %> 18 | title="$escape(frame.info, True)"<% endif %>>$frame.render() 19 | <% endfor %> 20 |
21 |
$escape(traceback.exception)
22 | <% endif %> 23 |
24 | -------------------------------------------------------------------------------- /lib/werkzeug/debug/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.debug.utils 4 | ~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Various other utilities. 7 | 8 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 9 | :license: BSD. 10 | """ 11 | from os.path import join, dirname 12 | from werkzeug.templates import Template 13 | 14 | 15 | def get_template(filename): 16 | return Template.from_file(join(dirname(__file__), 'templates', filename)) 17 | 18 | 19 | def render_template(template_filename, **context): 20 | return get_template(template_filename).render(**context) 21 | -------------------------------------------------------------------------------- /lib/werkzeug/posixemulation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | r""" 3 | werkzeug.posixemulation 4 | ~~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Provides a POSIX emulation for some features that are relevant to 7 | web applications. The main purpose is to simplify support for 8 | systems such as Windows NT that are not 100% POSIX compatible. 9 | 10 | Currently this only implements a :func:`rename` function that 11 | follows POSIX semantics. Eg: if the target file already exists it 12 | will be replaced without asking. 13 | 14 | This module was introduced in 0.6.1 and is not a public interface. 15 | It might become one in later versions of Werkzeug. 16 | 17 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 18 | :license: BSD, see LICENSE for more details. 19 | """ 20 | import os 21 | import errno 22 | import time 23 | import random 24 | 25 | 26 | can_rename_open_file = False 27 | if os.name == 'nt': # pragma: no cover 28 | _rename = lambda src, dst: False 29 | _rename_atomic = lambda src, dst: False 30 | 31 | try: 32 | import ctypes 33 | 34 | _MOVEFILE_REPLACE_EXISTING = 0x1 35 | _MOVEFILE_WRITE_THROUGH = 0x8 36 | _MoveFileEx = ctypes.windll.kernel32.MoveFileExW 37 | 38 | def _rename(src, dst): 39 | if not isinstance(src, unicode): 40 | src = unicode(src, sys.getfilesystemencoding()) 41 | if not isinstance(dst, unicode): 42 | dst = unicode(dst, sys.getfilesystemencoding()) 43 | if _rename_atomic(src, dst): 44 | return True 45 | retry = 0 46 | rv = False 47 | while not rv and retry < 100: 48 | rv = _MoveFileEx(src, dst, _MOVEFILE_REPLACE_EXISTING | 49 | _MOVEFILE_WRITE_THROUGH) 50 | if not rv: 51 | time.sleep(0.001) 52 | retry += 1 53 | return rv 54 | 55 | # new in Vista and Windows Server 2008 56 | _CreateTransaction = ctypes.windll.ktmw32.CreateTransaction 57 | _CommitTransaction = ctypes.windll.ktmw32.CommitTransaction 58 | _MoveFileTransacted = ctypes.windll.kernel32.MoveFileTransactedW 59 | _CloseHandle = ctypes.windll.kernel32.CloseHandle 60 | can_rename_open_file = True 61 | 62 | def _rename_atomic(src, dst): 63 | ta = _CreateTransaction(None, 0, 0, 0, 0, 1000, 'Werkzeug rename') 64 | if ta == -1: 65 | return False 66 | try: 67 | retry = 0 68 | rv = False 69 | while not rv and retry < 100: 70 | rv = _MoveFileTransacted(src, dst, None, None, 71 | _MOVEFILE_REPLACE_EXISTING | 72 | _MOVEFILE_WRITE_THROUGH, ta) 73 | if rv: 74 | rv = _CommitTransaction(ta) 75 | break 76 | else: 77 | time.sleep(0.001) 78 | retry += 1 79 | return rv 80 | finally: 81 | _CloseHandle(ta) 82 | except Exception: 83 | pass 84 | 85 | def rename(src, dst): 86 | # Try atomic or pseudo-atomic rename 87 | if _rename(src, dst): 88 | return 89 | # Fall back to "move away and replace" 90 | try: 91 | os.rename(src, dst) 92 | except OSError, e: 93 | if e.errno != errno.EEXIST: 94 | raise 95 | old = "%s-%08x" % (dst, random.randint(0, sys.maxint)) 96 | os.rename(dst, old) 97 | os.rename(src, dst) 98 | try: 99 | os.unlink(old) 100 | except Exception: 101 | pass 102 | else: 103 | rename = os.rename 104 | can_rename_open_file = True 105 | -------------------------------------------------------------------------------- /lib/werkzeug/security.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | werkzeug.security 4 | ~~~~~~~~~~~~~~~~~ 5 | 6 | Security related helpers such as secure password hashing tools. 7 | 8 | :copyright: (c) 2010 by the Werkzeug Team, see AUTHORS for more details. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import hmac 12 | import string 13 | from random import SystemRandom 14 | 15 | # because the API of hmac changed with the introduction of the 16 | # new hashlib module, we have to support both. This sets up a 17 | # mapping to the digest factory functions and the digest modules 18 | # (or factory functions with changed API) 19 | try: 20 | from hashlib import sha1, md5 21 | _hash_funcs = _hash_mods = {'sha1': sha1, 'md5': md5} 22 | _sha1_mod = sha1 23 | _md5_mod = md5 24 | except ImportError: 25 | import sha as _sha1_mod, md5 as _md5_mod 26 | _hash_mods = {'sha1': _sha1_mod, 'md5': _md5_mod} 27 | _hash_funcs = {'sha1': _sha1_mod.new, 'md5': _md5_mod.new} 28 | 29 | 30 | SALT_CHARS = string.letters + string.digits 31 | 32 | 33 | _sys_rng = SystemRandom() 34 | 35 | 36 | def gen_salt(length): 37 | """Generate a random string of SALT_CHARS with specified ``length``.""" 38 | if length <= 0: 39 | raise ValueError('requested salt of length <= 0') 40 | return ''.join(_sys_rng.choice(SALT_CHARS) for _ in xrange(length)) 41 | 42 | 43 | def _hash_internal(method, salt, password): 44 | """Internal password hash helper. Supports plaintext without salt, 45 | unsalted and salted passwords. In case salted passwords are used 46 | hmac is used. 47 | """ 48 | if method == 'plain': 49 | return password 50 | if salt: 51 | if method not in _hash_mods: 52 | return None 53 | if isinstance(salt, unicode): 54 | salt = salt.encode('utf-8') 55 | h = hmac.new(salt, None, _hash_mods[method]) 56 | else: 57 | if method not in _hash_funcs: 58 | return None 59 | h = _hash_funcs[method]() 60 | if isinstance(password, unicode): 61 | password = password.encode('utf-8') 62 | h.update(password) 63 | return h.hexdigest() 64 | 65 | 66 | def generate_password_hash(password, method='sha1', salt_length=8): 67 | """Hash a password with the given method and salt with with a string of 68 | the given length. The format of the string returned includes the method 69 | that was used so that :func:`check_password_hash` can check the hash. 70 | 71 | The format for the hashed string looks like this:: 72 | 73 | method$salt$hash 74 | 75 | This method can **not** generate unsalted passwords but it is possible 76 | to set the method to plain to enforce plaintext passwords. If a salt 77 | is used, hmac is used internally to salt the password. 78 | 79 | :param password: the password to hash 80 | :param method: the hash method to use (``'md5'`` or ``'sha1'``) 81 | :param salt_length: the lengt of the salt in letters 82 | """ 83 | salt = method != 'plain' and gen_salt(salt_length) or '' 84 | h = _hash_internal(method, salt, password) 85 | if h is None: 86 | raise TypeError('invalid method %r' % method) 87 | return '%s$%s$%s' % (method, salt, h) 88 | 89 | 90 | def check_password_hash(pwhash, password): 91 | """check a password against a given salted and hashed password value. 92 | In order to support unsalted legacy passwords this method supports 93 | plain text passwords, md5 and sha1 hashes (both salted and unsalted). 94 | 95 | Returns `True` if the password matched, `False` otherwise. 96 | 97 | :param pwhash: a hashed string like returned by 98 | :func:`generate_password_hash` 99 | :param password: the plaintext password to compare against the hash 100 | """ 101 | if pwhash.count('$') < 2: 102 | return False 103 | method, salt, hashval = pwhash.split('$', 2) 104 | return _hash_internal(method, salt, password) == hashval 105 | -------------------------------------------------------------------------------- /lib/wtforms/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | WTForms 3 | ======= 4 | 5 | WTForms is a flexible forms validation and rendering library for python web 6 | development. 7 | 8 | :copyright: Copyright (c) 2010 by Thomas Johansson, James Crasta and others. 9 | :license: BSD, see LICENSE.txt for details. 10 | """ 11 | from wtforms import validators, widgets 12 | from wtforms.fields import * 13 | from wtforms.form import Form 14 | from wtforms.validators import ValidationError 15 | 16 | __version__ = '0.6.1' 17 | -------------------------------------------------------------------------------- /lib/wtforms/ext/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/lib/wtforms/ext/__init__.py -------------------------------------------------------------------------------- /lib/wtforms/ext/appengine/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lib/wtforms/ext/appengine/fields.py: -------------------------------------------------------------------------------- 1 | from wtforms import fields, widgets 2 | 3 | class ReferencePropertyField(fields.SelectFieldBase): 4 | """ 5 | A field for ``db.ReferenceProperty``. The list items are rendered in a 6 | select. 7 | """ 8 | widget = widgets.Select() 9 | 10 | def __init__(self, label=u'', validators=None, reference_class=None, 11 | label_attr=None, allow_blank=False, blank_text=u'', **kwargs): 12 | super(ReferencePropertyField, self).__init__(label, validators, 13 | **kwargs) 14 | self.label_attr = label_attr 15 | self.allow_blank = allow_blank 16 | self.blank_text = blank_text 17 | self._set_data(None) 18 | if reference_class is None: 19 | raise TypeError('Missing reference_class attribute in ' 20 | 'ReferencePropertyField') 21 | 22 | self.query = reference_class.all() 23 | 24 | def _get_data(self): 25 | if self._formdata is not None: 26 | for obj in self.query: 27 | key = str(obj.key()) 28 | if key == self._formdata: 29 | self._set_data(key) 30 | break 31 | return self._data 32 | 33 | def _set_data(self, data): 34 | self._data = data 35 | self._formdata = None 36 | 37 | data = property(_get_data, _set_data) 38 | 39 | def iter_choices(self): 40 | if self.allow_blank: 41 | yield (u'__None', self.blank_text, self.data is None) 42 | 43 | for obj in self.query: 44 | key = str(obj.key()) 45 | label = self.label_attr and getattr(obj, self.label_attr) or key 46 | yield (key, label, key == self.data) 47 | 48 | def process_formdata(self, valuelist): 49 | if valuelist: 50 | if valuelist[0] == '__None': 51 | self.data = None 52 | else: 53 | self._data = None 54 | self._formdata = valuelist[0] 55 | 56 | def pre_validate(self, form): 57 | if not self.allow_blank or self.data is not None: 58 | for obj in self.query: 59 | if self.data == str(obj.key()): 60 | break 61 | else: 62 | raise ValueError(self.gettext(u'Not a valid choice')) 63 | 64 | 65 | class StringListPropertyField(fields.TextAreaField): 66 | """ 67 | A field for ``db.StringListProperty``. The list items are rendered in a 68 | textarea. 69 | """ 70 | def process_data(self, value): 71 | if isinstance(value, list): 72 | value = '\n'.join(value) 73 | 74 | self.data = value 75 | 76 | def populate_obj(self, obj, name): 77 | if isinstance(self.data, basestring): 78 | value = self.data.splitlines() 79 | else: 80 | value = [] 81 | 82 | setattr(obj, name, value) 83 | 84 | 85 | class GeoPtPropertyField(fields.TextField): 86 | """For now, no processing or prevalidation is done.""" 87 | -------------------------------------------------------------------------------- /lib/wtforms/ext/dateutil/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/lib/wtforms/ext/dateutil/__init__.py -------------------------------------------------------------------------------- /lib/wtforms/ext/dateutil/fields.py: -------------------------------------------------------------------------------- 1 | """ 2 | A DateTimeField and DateField that use the `dateutil` package for parsing. 3 | """ 4 | from dateutil import parser 5 | 6 | from wtforms.fields import Field 7 | from wtforms.validators import ValidationError 8 | from wtforms.widgets import TextInput 9 | 10 | 11 | __all__ = ( 12 | 'DateTimeField', 'DateField', 13 | ) 14 | 15 | 16 | class DateTimeField(Field): 17 | """ 18 | DateTimeField represented by a text input, accepts all input text formats 19 | that `dateutil.parser.parse` will. 20 | 21 | :param parse_kwargs: 22 | A dictionary of keyword args to pass to the dateutil parse() function. 23 | See dateutil docs for available keywords. 24 | :param display_format: 25 | A format string to pass to strftime() to format dates for display. 26 | """ 27 | widget = TextInput() 28 | 29 | def __init__(self, label=u'', validators=None, parse_kwargs=None, 30 | display_format='%Y-%m-%d %H:%M', **kwargs): 31 | super(DateTimeField, self).__init__(label, validators, **kwargs) 32 | if parse_kwargs is None: 33 | parse_kwargs = {} 34 | self.parse_kwargs = parse_kwargs 35 | self.display_format = display_format 36 | 37 | def _value(self): 38 | if self.raw_data: 39 | return u' '.join(self.raw_data) 40 | else: 41 | return self.data and self.data.strftime(self.display_format) or u'' 42 | 43 | def process_formdata(self, valuelist): 44 | if valuelist: 45 | date_str = u' '.join(valuelist) 46 | if not date_str: 47 | self.data = None 48 | raise ValidationError(self.gettext(u'Please input a date/time value')) 49 | 50 | parse_kwargs = self.parse_kwargs.copy() 51 | if 'default' not in parse_kwargs: 52 | try: 53 | parse_kwargs['default'] = self.default() 54 | except TypeError: 55 | parse_kwargs['default'] = self.default 56 | try: 57 | self.data = parser.parse(date_str, **parse_kwargs) 58 | except ValueError: 59 | self.data = None 60 | raise ValidationError(self.gettext(u'Invalid date/time input')) 61 | 62 | 63 | class DateField(DateTimeField): 64 | """ 65 | Same as the DateTimeField, but stores only the date portion. 66 | """ 67 | def __init__(self, label=u'', validators=None, parse_kwargs=None, 68 | display_format='%Y-%m-%d', **kwargs): 69 | super(DateField, self).__init__(label, validators, parse_kwargs=parse_kwargs, display_format=display_format, **kwargs) 70 | 71 | def process_formdata(self, valuelist): 72 | super(DateField, self).process_formdata(valuelist) 73 | if self.data is not None and hasattr(self.data, 'date'): 74 | self.data = self.data.date() 75 | -------------------------------------------------------------------------------- /lib/wtforms/ext/django/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/lib/wtforms/ext/django/__init__.py -------------------------------------------------------------------------------- /lib/wtforms/ext/django/fields.py: -------------------------------------------------------------------------------- 1 | """ 2 | Useful form fields for use with the Django ORM. 3 | """ 4 | from wtforms import widgets 5 | from wtforms.fields import SelectFieldBase 6 | from wtforms.validators import ValidationError 7 | 8 | 9 | __all__ = ( 10 | 'ModelSelectField', 'QuerySetSelectField', 11 | ) 12 | 13 | 14 | class QuerySetSelectField(SelectFieldBase): 15 | """ 16 | Given a QuerySet either at initialization or inside a view, will display a 17 | select drop-down field of choices. The `data` property actually will 18 | store/keep an ORM model instance, not the ID. Submitting a choice which is 19 | not in the queryset will result in a validation error. 20 | 21 | Specifying `label_attr` in the constructor will use that property of the 22 | model instance for display in the list, else the model object's `__str__` 23 | or `__unicode__` will be used. 24 | 25 | If `allow_blank` is set to `True`, then a blank choice will be added to the 26 | top of the list. Selecting this choice will result in the `data` property 27 | being `None`. The label for the blank choice can be set by specifying the 28 | `blank_text` parameter. 29 | """ 30 | widget = widgets.Select() 31 | 32 | def __init__(self, label=u'', validators=None, queryset=None, label_attr='', allow_blank=False, blank_text=u'', **kwargs): 33 | super(QuerySetSelectField, self).__init__(label, validators, **kwargs) 34 | self.label_attr = label_attr 35 | self.allow_blank = allow_blank 36 | self.blank_text = blank_text 37 | self._set_data(None) 38 | if queryset is not None: 39 | self.queryset = queryset.all() # Make sure the queryset is fresh 40 | 41 | def _get_data(self): 42 | if self._formdata is not None: 43 | for obj in self.queryset: 44 | if obj.pk == self._formdata: 45 | self._set_data(obj) 46 | break 47 | return self._data 48 | 49 | def _set_data(self, data): 50 | self._data = data 51 | self._formdata = None 52 | 53 | data = property(_get_data, _set_data) 54 | 55 | def iter_choices(self): 56 | if self.allow_blank: 57 | yield (u'__None', self.blank_text, self.data is None) 58 | 59 | for obj in self.queryset: 60 | label = self.label_attr and getattr(obj, self.label_attr) or obj 61 | yield (obj.pk, label, obj == self.data) 62 | 63 | def process_formdata(self, valuelist): 64 | if valuelist: 65 | if valuelist[0] == '__None': 66 | self.data = None 67 | else: 68 | self._data = None 69 | self._formdata = int(valuelist[0]) 70 | 71 | def pre_validate(self, form): 72 | if not self.allow_blank or self.data is not None: 73 | for obj in self.queryset: 74 | if self.data == obj: 75 | break 76 | else: 77 | raise ValidationError('Not a valid choice') 78 | 79 | 80 | class ModelSelectField(QuerySetSelectField): 81 | """ 82 | Like a QuerySetSelectField, except takes a model class instead of a 83 | queryset and lists everything in it. 84 | """ 85 | def __init__(self, label=u'', validators=None, model=None, **kwargs): 86 | super(ModelSelectField, self).__init__(label, validators, queryset=model._default_manager.all(), **kwargs) 87 | -------------------------------------------------------------------------------- /lib/wtforms/ext/django/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/lib/wtforms/ext/django/templatetags/__init__.py -------------------------------------------------------------------------------- /lib/wtforms/ext/django/templatetags/wtforms.py: -------------------------------------------------------------------------------- 1 | """ 2 | Template tags for easy WTForms access in Django templates. 3 | """ 4 | import re 5 | 6 | from django import template 7 | from django.conf import settings 8 | from django.template import Variable 9 | 10 | 11 | register = template.Library() 12 | 13 | 14 | class FormFieldNode(template.Node): 15 | def __init__(self, field_var, html_attrs): 16 | self.field_var = field_var 17 | self.html_attrs = html_attrs 18 | 19 | def render(self, context): 20 | try: 21 | if '.' in self.field_var: 22 | base, field_name = self.field_var.rsplit('.', 1) 23 | field = getattr(Variable(base).resolve(context), field_name) 24 | else: 25 | field = context[self.field_var] 26 | except (template.VariableDoesNotExist, KeyError, AttributeError): 27 | return settings.TEMPLATE_STRING_IF_INVALID 28 | 29 | h_attrs = {} 30 | for k, v in self.html_attrs.iteritems(): 31 | try: 32 | h_attrs[k] = v.resolve(context) 33 | except template.VariableDoesNotExist: 34 | h_attrs[k] = settings.TEMPLATE_STRING_IF_INVALID 35 | 36 | return field(**h_attrs) 37 | 38 | 39 | @register.tag(name='form_field') 40 | def do_form_field(parser, token): 41 | """ 42 | Render a WTForms form field allowing optional HTML attributes. 43 | Invocation looks like this: 44 | {% form_field form.username class="big_text" onclick="alert('hello')" %} 45 | where form.username is the path to the field value we want. Any number 46 | of key="value" arguments are supported. Unquoted values are resolved as 47 | template variables. 48 | """ 49 | parts = token.contents.split(' ', 2) 50 | if len(parts) < 2: 51 | raise template.TemplateSyntaxError('%r tag must have the form field name as the first value, followed by optional key="value" attributes.' % parts[0]) 52 | 53 | html_attrs = {} 54 | if len(parts) == 3: 55 | raw_args = list(args_split(parts[2])) 56 | if (len(raw_args) % 2) != 0: 57 | raise template.TemplateSyntaxError('%r tag received the incorrect number of key=value arguments.' % parts[0]) 58 | for x in range(0, len(raw_args), 2): 59 | html_attrs[str(raw_args[x])] = Variable(raw_args[x+1]) 60 | 61 | return FormFieldNode(parts[1], html_attrs) 62 | 63 | 64 | args_split_re = re.compile(ur'''("(?:[^"\\]*(?:\\.[^"\\]*)*)"|'(?:[^'\\]*(?:\\.[^'\\]*)*)'|[^\s=]+)''') 65 | 66 | def args_split(text): 67 | """ Split space-separated key=value arguments. Keeps quoted strings intact. """ 68 | for bit in args_split_re.finditer(text): 69 | bit = bit.group(0) 70 | if bit[0] == '"' and bit[-1] == '"': 71 | yield '"' + bit[1:-1].replace('\\"', '"').replace('\\\\', '\\') + '"' 72 | elif bit[0] == "'" and bit[-1] == "'": 73 | yield "'" + bit[1:-1].replace("\\'", "'").replace("\\\\", "\\") + "'" 74 | else: 75 | yield bit 76 | -------------------------------------------------------------------------------- /lib/wtforms/ext/sqlalchemy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/lib/wtforms/ext/sqlalchemy/__init__.py -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Blog rLopes Main 4 | ---------------- 5 | """ 6 | 7 | import os 8 | import sys 9 | 10 | # add lib in PYTHONPATH 11 | sys.path.append(os.path.join(os.path.dirname(__file__), 'lib')) 12 | 13 | from google.appengine.ext.webapp.util import run_wsgi_app 14 | from blog import app 15 | 16 | 17 | run_wsgi_app(app) 18 | -------------------------------------------------------------------------------- /static/css/template.css: -------------------------------------------------------------------------------- 1 | body{ background:#ececec;} 2 | 3 | form.formular { 4 | font-family: tahoma, verdana, "sans-serif"; 5 | font-size: 12px; 6 | padding: 20px; 7 | border: 1px solid #A5A8B8; 8 | 9 | width:300px; 10 | margin-left:300px; 11 | } 12 | 13 | .formular fieldset { 14 | margin-top: 20px; 15 | padding : 15px; 16 | border: 1px solid #B5B8C8; 17 | 18 | } 19 | 20 | .formular legend { 21 | font-size: 12px; 22 | color: #15428B; 23 | font-weight: 900; 24 | } 25 | 26 | .formular fieldset label { 27 | float: none; 28 | text-align: inherit; 29 | width: auto; 30 | } 31 | 32 | .formular label span { 33 | color: #000; 34 | } 35 | 36 | .formular input, .formular select, .formular textarea { 37 | display : block; 38 | margin-bottom: 5px; 39 | } 40 | 41 | .formular .text-input { 42 | width: 250px; 43 | color: #555; 44 | padding: 4px; 45 | border: 1px solid #B5B8C8; 46 | font-size: 14px; 47 | margin-top: 4px; 48 | background: #FFF url('/img/form/text-bg.gif') repeat-x; 49 | 50 | } 51 | .formular textarea { 52 | width: 250px; 53 | height:70px; 54 | color: #555; 55 | padding: 4px; 56 | border: 1px solid #B5B8C8; 57 | font-size: 14px; 58 | margin-top: 4px; 59 | background: #FFF url('/img/form/text-bg.gif') repeat-x; 60 | 61 | } 62 | .formular .infos { 63 | background: #FFF; 64 | color: #333; 65 | font-size: 12px; 66 | padding: 10px; 67 | margin-bottom: 10px; 68 | } 69 | 70 | .formular span.checkbox, .formular .checkbox { 71 | display: inline; 72 | } 73 | 74 | .formular .submit { 75 | background: url('/img/form/button-bg.png') repeat-x; 76 | border: 1px solid #AAA; 77 | padding: 4px; 78 | margin-top: 20px; 79 | float: right; 80 | text-decoration: none; 81 | cursor:pointer; 82 | } 83 | 84 | .formular hr { 85 | clear: both; 86 | visibility: hidden; 87 | } 88 | 89 | .formular .fc-error { 90 | width: 350px; 91 | color: 555; 92 | padding: 4px; 93 | border: 1px solid #B5B8C8; 94 | font-size: 12px; 95 | margin-bottom: 15px; 96 | background: #FFEAEA; 97 | } -------------------------------------------------------------------------------- /static/css/validationEngine.jquery.css: -------------------------------------------------------------------------------- 1 | 2 | .inputContainer{position:relative; float:left;} 3 | .formError { 4 | position:absolute; 5 | top:300px; left:300px; 6 | display:block; 7 | z-index:5000; 8 | cursor:pointer; 9 | } 10 | #debugMode{ 11 | background:#000; 12 | position:fixed; 13 | width:100%; height:200px; 14 | top:0; left:0; 15 | overflow:scroll; 16 | opacity:0.8; 17 | display:block; 18 | padding:10px; 19 | color:#fff; 20 | font-size:14px; 21 | z-index:100000; 22 | } 23 | 24 | .ajaxSubmit{ padding:20px; background:#55ea55;border:1px solid #999;display:none} 25 | .formError .formErrorContent { 26 | width:100%; 27 | background:#ee0101; 28 | color:#fff; 29 | width:150px; 30 | font-family:tahoma; 31 | font-size:11px; 32 | border:2px solid #ddd; 33 | box-shadow: 0px 0px 6px #000; 34 | -moz-box-shadow: 0px 0px 6px #000; 35 | -webkit-box-shadow: 0px 0px 6px #000; 36 | padding:4px 10px 4px 10px; 37 | border-radius: 6px; 38 | -moz-border-radius: 6px; 39 | -webkit-border-radius: 6px; 40 | } 41 | .greenPopup .formErrorContent {background:#33be40;} 42 | 43 | .blackPopup .formErrorContent {background:#393939;color:#FFF;} 44 | 45 | .formError .formErrorArrow{ 46 | width:15px; 47 | margin:-2px 0 0 13px; 48 | z-index:5001; 49 | } 50 | .formError .formErrorArrowBottom{top:0;margin:-6px;} 51 | 52 | .formError .formErrorArrow div{ 53 | border-left:2px solid #ddd; 54 | border-right:2px solid #ddd; 55 | box-shadow: 0px 2px 3px #444; 56 | -moz-box-shadow: 0px 2px 3px #444; 57 | -webkit-box-shadow: 0px 2px 3px #444; 58 | font-size:0px; height:1px; background:#ee0101;margin:0 auto;line-height:0px; font-size:0px; display:block; 59 | } 60 | .formError .formErrorArrowBottom div{ 61 | box-shadow: none; 62 | -moz-box-shadow: none; 63 | -webkit-box-shadow: none; 64 | } 65 | 66 | .greenPopup .formErrorArrow div{background:#33be40;} 67 | .blackPopup .formErrorArrow div{background:#393939;color:#FFF;} 68 | 69 | .formError .formErrorArrow .line10{width:15px;border:none;} 70 | .formError .formErrorArrow .line9{width:13px;border:none;} 71 | .formError .formErrorArrow .line8{width:11px;} 72 | .formError .formErrorArrow .line7{width:9px;} 73 | .formError .formErrorArrow .line6{width:7px;} 74 | .formError .formErrorArrow .line5{width:5px;} 75 | .formError .formErrorArrow .line4{width:3px;} 76 | .formError .formErrorArrow .line3{width:1px; 77 | border-left:2px solid #ddd; 78 | border-right:2px solid #ddd; 79 | border-bottom:0px solid #ddd;} 80 | .formError .formErrorArrow .line2{width:3px;border:none;background:#ddd;} 81 | .formError .formErrorArrow .line1{width:1px;border:none;background:#ddd;} -------------------------------------------------------------------------------- /static/image/delicious_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/static/image/delicious_32.png -------------------------------------------------------------------------------- /static/image/facebook_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/static/image/facebook_32.png -------------------------------------------------------------------------------- /static/image/flickr_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/static/image/flickr_32.png -------------------------------------------------------------------------------- /static/image/ln.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/static/image/ln.jpg -------------------------------------------------------------------------------- /static/image/orkut_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/static/image/orkut_32.png -------------------------------------------------------------------------------- /static/image/page_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/static/image/page_bg.jpg -------------------------------------------------------------------------------- /static/image/rss_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/static/image/rss_32.png -------------------------------------------------------------------------------- /static/image/tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/static/image/tag.png -------------------------------------------------------------------------------- /static/image/twitter_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/riquellopes/micro-blog/06cafa3d4145a3fe0a3e31875a38cb16fb766e76/static/image/twitter_32.png -------------------------------------------------------------------------------- /static/js/jquery.validationEngine-extend.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | $.extend($.validationEngine, { 3 | submitForm:function(caller){ 4 | if($.validationEngine.settings.ajaxSubmit){ 5 | if($.validationEngine.settings.ajaxSubmitExtraData){ 6 | extraData = $.validationEngine.settings.ajaxSubmitExtraData; 7 | }else{ 8 | extraData = ""; 9 | } 10 | $.ajax({ 11 | type: "POST", 12 | url: $.validationEngine.settings.ajaxSubmitFile, 13 | async: true, 14 | data: $(caller).serialize()+"&"+extraData, 15 | error: function(data,transport){ $.validationEngine.debug("error in the ajax: "+data.status+" "+transport) }, 16 | success: function(data){ 17 | if(data == "true"){// the form clean and show message 18 | var form = $(caller); 19 | $.each($(':text, textarea', form),function(){ 20 | $(this).val( $(this).attr('default') ); 21 | }); 22 | 23 | $.validationEngine.buildPrompt("#send-contact", $.validationEngine.settings.ajaxSubmitMessage, true); 24 | $(".send-contactformError").addClass("blackPopup") 25 | .fadeOut(850, function(){ $(this).remove(); }); 26 | 27 | 28 | if ($.validationEngine.settings.success) 29 | { // AJAX SUCCESS, STOP THE LOCATION UPDATE 30 | $.validationEngine.settings.success && $.validationEngine.settings.success(); 31 | return false; 32 | } 33 | 34 | }else{ // HOUSTON WE GOT A PROBLEM (SOMETING IS NOT VALIDATING) 35 | data = eval( "("+data+")"); 36 | if(!data){ 37 | $.validationEngine.debug("you are not going into the success fonction and jsonValidateReturn return nothing"); 38 | } 39 | errorNumber = data.length 40 | for(index=0; indexadd code'); 31 | }, my_editor, true); 32 | 33 | }, my_editor, true); 34 | 35 | my_editor.render(); 36 | 37 | YAHOO.util.Event.on('form-salvar', 'click',function(){ 38 | my_editor.saveHTML(); 39 | }); 40 | } 41 | }); 42 | 43 | // Load the files using the insert() method. 44 | loader.insert(); 45 | 46 | })(); 47 | -------------------------------------------------------------------------------- /static/js/rlopes.js: -------------------------------------------------------------------------------- 1 | var rlopes = { 2 | init:function() 3 | { 4 | $("#form-contact").validationEngine({ 5 | validationEventTriggers:"keyup blur", 6 | ajaxSubmit: true, 7 | ajaxSubmitFile:'/contact', 8 | ajaxSubmitMessage: "Obrigado, mensagem enviada!", 9 | success : false, 10 | failure : function() {} 11 | }); 12 | 13 | $("#form-contact :text, #form-contact textarea").focus(rlopes.form_focus); 14 | $("#tags").keydown(rlopes.tags); 15 | }, 16 | form_focus:function() 17 | { 18 | $(this).val(""); 19 | }, 20 | tags:function(e) 21 | { 22 | var virgula = function(text){ 23 | if( /\s\w/i.test( text ) ) 24 | return text.replace(/\s/i, ","); 25 | return text; 26 | } 27 | 28 | $(this).val( virgula( $(this).val() ) ); 29 | } 30 | }; 31 | 32 | $(rlopes.init); 33 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | 5 | # add lib in PYTHONPATH 6 | 7 | path_lib = os.getcwd().replace('/tests', '/lib') 8 | sys.path.append(path_lib) 9 | 10 | -------------------------------------------------------------------------------- /tests/test_context_processor.py: -------------------------------------------------------------------------------- 1 | # usr/lib/python 2 | 3 | import unittest 4 | from flask import url_for 5 | from blog.context_processor import admin_logged, rss_url, admin_logout_url 6 | 7 | 8 | class TestContextProcessor(unittest.TestCase): 9 | 10 | def test_verifica_se_admin_esta_deslogado(self): 11 | self.assertEquals(admin_logged(), dict(admin_loggend=False)) 12 | 13 | def test_verifica_se_url_para_logout_existe(self): 14 | pass 15 | 16 | def test_url_para_rss(self): 17 | self.assertEquals(rss_url(), dict(rss_url="http://twitter.com/statuses/user_timeline/riquellopes.atom")) 18 | -------------------------------------------------------------------------------- /tests/test_hooks.py: -------------------------------------------------------------------------------- 1 | # -*- encoding:utf-8 -*- 2 | """ 3 | Blog rLopes TestHooks 4 | --------------------- 5 | """ 6 | import unittest 7 | from blog.hooks import page_not_found, internal_server_error 8 | 9 | class TestPageNotFound(unittest.TestCase): 10 | pass 11 | 12 | class TestInternalServerError(unittest.TestCase): 13 | pass 14 | 15 | if __name__ == '__main__': 16 | unittest.main() 17 | 18 | -------------------------------------------------------------------------------- /tests/test_model.py: -------------------------------------------------------------------------------- 1 | # -*- encoding:utf-8 -*- 2 | 3 | import unittest 4 | from blog.model import Post, TagCategory 5 | from nose.tools import assert_equals, assert_true, assert_raises 6 | 7 | class TestPost(unittest.TestCase): 8 | 9 | def setUp(self): 10 | tag = TagCategory() 11 | self.post = Post(title='Primeiro post', 12 | text='Nose test e fd', 13 | tags=tag.string_to_category('python, nose, tdd')) 14 | 15 | self.post.put() 16 | 17 | def test_verifica_se_post_foi_salvo_corretamente(self): 18 | assert_equals(self.post.url, 'primeiro-post') 19 | 20 | def test_verifica_se_texto_foi_salvo_corretamente(self): 21 | assert_equals(self.post.text, u'Nose test e fd') 22 | 23 | def test_verifica_se_tags_property_criado_corretamente(self): 24 | tag = TagCategory() 25 | categories = tag.string_to_category('python, nose, tdd') 26 | assert_equals(self.post.tags, categories) 27 | 28 | def test_verifica_se_title_foi_salvo_corretamente(self): 29 | assert_equals(self.post.title, 'Primeiro post') 30 | 31 | def test_apos_salvar_post_put_deve_retornar_a_chave_da_entidade(self): 32 | tag = TagCategory() 33 | post = Post(title='Primeiro post', 34 | text='Nose test e fd', 35 | tags=tag.string_to_category('python, nose, tdd')) 36 | 37 | assert_true(type(post.put().id()).__name__ == 'int') 38 | post.delete() 39 | 40 | def tearDown(self): 41 | self.post.delete() 42 | 43 | class TestTagCategory(unittest.TestCase): 44 | 45 | def test_retorno_string_to_category(self): 46 | tag = TagCategory() 47 | assert_equals([u'nose', u'python', u'tdd'], 48 | tag.string_to_category('python, nose, tdd')) 49 | 50 | def test_se_nao_for_passada_string_para_to_category_deve_levantar_uma_exception(self): 51 | tag = TagCategory() 52 | assert_raises(ValueError, tag.string_to_category, 123) 53 | -------------------------------------------------------------------------------- /tests/test_short_url.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | #! usr/bin/python 3 | 4 | import unittest 5 | from blog.util import short_url 6 | 7 | class TestShortUrl(unittest.TestCase): 8 | 9 | def test_verifica_se_url_um_retorna_short_url_desejada(self): 10 | self.assertEquals(short_url('http://code.google.com/p/gaeunit/wiki/Readme'), 'http://tinyurl.com/2egns5f') 11 | 12 | if __name__ == '__main__': 13 | unittest.main() 14 | -------------------------------------------------------------------------------- /tests/test_to_url.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | #! usr/bin/python 3 | 4 | import unittest 5 | from blog.util import slug 6 | 7 | class TestToUrl(unittest.TestCase): 8 | 9 | def test_verifica_string_um_retorna_url_desejada(self): 10 | self.assertEquals(slug('Henrique Lopes'), 'henrique-lopes') 11 | 12 | def test_verifica_string_dois_retorna_url_desejada(self): 13 | self.assertEquals(slug('Carol Marques'), 'carol-marques') 14 | 15 | def test_verifica_string_tres_retorna_url_desejada(self): 16 | self.assertEquals(slug('Henrique'), 'henrique') 17 | 18 | def test_verifica_string_quatro_retorna_url_desejada(self): 19 | self.assertEquals(slug('Caçador de tecnologia'), 'cacador-de-tecnologia') 20 | 21 | def test_verifica_string_cinco_retorna_url_desejada(self): 22 | self.assertEquals(slug('Primeiro post sobre python.'), 'primeiro-post-sobre-python') 23 | 24 | def test_verifica_string_seis_retorna_url_desejada(self): 25 | self.assertEquals(slug('Ser ágil eis a questão. Você é ?'), 'ser-agil-eis-a-questao-voce-e') 26 | 27 | if __name__ == '__main__': 28 | unittest.main() 29 | -------------------------------------------------------------------------------- /tests/test_view.py: -------------------------------------------------------------------------------- 1 | # /usr/bin/env python 2 | import unittest 3 | from nose.tools import assert_true, assert_equals 4 | import mocker 5 | from lxml import html 6 | from blog import app 7 | from blog.model import Post, TagCategory 8 | from blog.util import slug 9 | 10 | class TestView(unittest.TestCase): 11 | 12 | def setUp(self): 13 | self.app = app.test_client() 14 | tag = TagCategory() 15 | self.post = Post(title='Primeiro post', 16 | text='Nose test e fd', 17 | tags=tag.string_to_category('python, nose, tdd')) 18 | 19 | self.post.put() 20 | self.mocker = mocker.Mocker() 21 | 22 | def tearDown(self): 23 | self.post.delete() 24 | self.mocker.restore() 25 | 26 | def test_index(self): 27 | response = self.app.get('/') 28 | assert_true(' rlopes | Henrique Lopes' in str(response.data) ) 29 | 30 | def test_read_post(self): 31 | response = self.app.get("/%s" % self.post.url) 32 | title = '

%s

' % (self.post.url, self.post.title) 33 | assert_true(title in str(response.data)) 34 | 35 | def test_read_post_that_not_exist(self): 36 | response = self.app.get("/post/meu-primeiro-post") 37 | assert_true(' Ops! Error 404 | Henrique Lopes' in str(response.data)) 38 | 39 | def admin_loggend(self, times=1): 40 | loggend = self.mocker.replace('google.appengine.api.users.is_current_user_admin') 41 | loggend() 42 | self.mocker.result(True) 43 | self.mocker.count(times) 44 | self.mocker.replay() 45 | 46 | def test_form_insert(self): 47 | self.admin_loggend(10) 48 | response = self.app.get("/post/form") 49 | assert_true(' Criar post | Henrique Lopes' in str(response.data)) 50 | 51 | def test_form_update(self): 52 | self.admin_loggend(5) 53 | url = "/%i/form" % self.post.key().id() 54 | response = self.app.get(url) 55 | title = ' Update - %s | Henrique Lopes' % self.post.title 56 | assert_true(title in str(response.data)) 57 | 58 | def test_form_update_error(self): 59 | self.admin_loggend(5) 60 | url = "/%i/form" % 1 61 | response = self.app.get(url) 62 | title = ' Ops! Error 404 | Henrique Lopes' 63 | assert_true(title in str(response.data)) 64 | 65 | def test_create_post(self): 66 | self.admin_loggend(10) 67 | data = { 68 | 'title':"My first post. bla bla bla", 69 | 'tags':"python,tdd", 70 | 'text':"bla bla bla bla bla bla bla bla" 71 | } 72 | response = self.app.post("/create", data=data, follow_redirects=True) 73 | post = Post.all().filter('url', slug(data['title'])).get() 74 | assert_equals(post.title, data['title']) 75 | post.delete() 76 | 77 | def test_create_post_validate(self): 78 | self.admin_loggend(10) 79 | data={} 80 | response = self.app.post("/create", data=data, follow_redirects=True) 81 | assert_true("O Titulo deve ter entre 5 a 60 caracteres." in str(response.data)) 82 | 83 | def test_update_post(self): 84 | self.admin_loggend(10) 85 | data={ 86 | 'title':"My uppdates.", 87 | 'tags':"python, nose, tdd, dojo", 88 | 'text':"Nose test e fd" 89 | } 90 | url = "%i/update" % self.post.key().id() 91 | response = self.app.post(url, data=data, follow_redirects=True) 92 | post = Post.get_by_id( self.post.key().id() ) 93 | assert_equals(post.title, data['title'] ) 94 | 95 | def test_update_post_validate(self): 96 | self.admin_loggend(10) 97 | data={ 98 | 'title':"My uppdates.", 99 | 'tags':"python, nose, tdd, dojo", 100 | 'text':"" 101 | } 102 | url = "%i/update" % self.post.key().id() 103 | response = self.app.post(url, data=data, follow_redirects=True) 104 | assert_true("O Texto deve ter no minimo 5 caracteres." in str(response.data)) 105 | 106 | def tags_to_string(self, tags): 107 | tstr = "" 108 | for tag in tags: 109 | tstr += "%s," % tag 110 | return tstr[:-1] 111 | 112 | def test_form_populate(self): 113 | self.admin_loggend(10) 114 | url = "%i/form" % self.post.key().id() 115 | response = self.app.get(url) 116 | dom = html.fromstring(response.data) 117 | input_title = dom.xpath('//input[@type="text" and @name="title"]')[0] 118 | input_tags = dom.xpath('//input[@type="text" and @name="tags"]')[0] 119 | textare_text = dom.xpath('//textarea[@name="text"]')[0] 120 | assert_equals(input_title.value, self.post.title) 121 | assert_equals(input_tags.value, self.tags_to_string(self.post.tags)) 122 | assert_equals(textare_text.value, self.post.text) 123 | 124 | def test_delete_post(self): 125 | self.admin_loggend(5) 126 | tag = TagCategory() 127 | post = Post(title='Primeiro post', 128 | text='Nose test e fd', 129 | tags=tag.string_to_category('python, nose, tdd')) 130 | post.put() 131 | url="%i/delete" % post.key().id() 132 | self.app.get(url) 133 | post = Post.get_by_id( post.key().id() ) 134 | assert_true(post is None) 135 | 136 | def test_send_email(self): 137 | data = { 138 | 'nome':"Henrique", 139 | 'email':"riquellopes@gmail.com", 140 | 'mensagem':"O blog esta ficando bom." 141 | } 142 | response = self.app.post('/contact', data=data, follow_redirects=True) 143 | assert_true("True", str(response.data)) 144 | 145 | def test_send_email_error(self): 146 | data={ 147 | 'nome':"", 148 | 'email':"riquellopes@gmail.com", 149 | 'text':"" 150 | } 151 | response= self.app.post('/contact', data=data, follow_redirects=True) 152 | assert_true("[['#mensagem', 'false', 'Digite sua mensagem'],\ 153 | ['#nome', 'false', 'Digite seu nome']]", str(response.data)) 154 | --------------------------------------------------------------------------------