diff options
63 files changed, 247 insertions, 2105 deletions
diff --git a/.travis.yml b/.travis.yml index 098eb14..56f8d9a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,8 @@ language: python -python: 2.7 -env: - - TOX_ENV=py26 - - TOX_ENV=py27 - - TOX_ENV=py32 - - TOX_ENV=py33 - - TOX_ENV=py34 - - TOX_ENV=pypy -before_install: - - sudo apt-get install pypy -install: - - pip install tox -script: - - tox -e $TOX_ENV +python: + - "2.7" + - "3.3" + - "3.4" + - "pypy" +install: "pip install pytest" +script: "py.test" @@ -16,6 +16,10 @@ coverage: py.test --cov=sqlparse --cov-report=html --cov-report=term clean: - $(PYTHON) setup.py clean - find . -name '*.pyc' -delete - find . -name '*~' -delete + @$(PYTHON) setup.py clean + @find . -name '*.pyc' -delete + @find . -name '*~' -delete + @rm -rf *.egg-info + @rm -rf dist + @rm -rf htmlcov + @find . -name '__pycache__' -delete @@ -1,3 +1,21 @@ +Goals for 0.2.0 +--------------- + +X Drop support for Python <= 2.6 +X Remove SQLParserError from top level module. +X Don't use 2to3 anymore +~ Remove unused code +~ Possible deprecation: + - RightMarginFilter + Seems to be broken and not used. However, a replacement would be good. +~ Update documentation +~ Cleanup docs on PyPI/github +~ Write release notes + + +Misc +---- + * See https://groups.google.com/d/msg/sqlparse/huz9lKXt0Lc/11ybIKPJWbUJ for some interesting hints and suggestions. diff --git a/extras/appengine/README b/extras/appengine/README deleted file mode 100644 index 04d32b2..0000000 --- a/extras/appengine/README +++ /dev/null @@ -1,3 +0,0 @@ -- Run "./bootstrap.py" to download all required Python modules. -- Run "dev_appserver.py ." for a local server. -- Have a look at config.py :) diff --git a/extras/appengine/app.yaml b/extras/appengine/app.yaml deleted file mode 100644 index b7cf550..0000000 --- a/extras/appengine/app.yaml +++ /dev/null @@ -1,54 +0,0 @@ -application: sqlformat-hrd -version: dev27 -runtime: python27 -api_version: 1 -threadsafe: true - -default_expiration: 7d # This is good for images, which never change - -handlers: - -- url: /(robots.txt|favicon.ico|sitemap.xml) - static_files: static/\1 - upload: static/(robots.txt|favicon.ico|sitemap.xml) - -- url: /google7a062e78b56854c0.html - static_files: static/robots.txt - upload: static/robots.txt - -- url: /static/(script.js|styles.css) - static_files: static/\1 - upload: static/(script.js|styles.css) - -- url: /static - static_dir: static - -- url: .* - script: main.app - -builtins: -- appstats: on - -skip_files: -- ^(.*/)?app\.yaml -- ^(.*/)?app\.yml -- ^(.*/)?index\.yaml -- ^(.*/)?index\.yml -- ^(.*/)?#.*# -- ^(.*/)?.*~ -- ^(.*/)?.*\.py[co] -- ^(.*/)?.*/RCS/.* -- ^(.*/)?\..* -- ^(.*/)?jinja2* - -inbound_services: -- warmup - -libraries: -- name: jinja2 - version: latest -- name: markupsafe - version: latest -- name: setuptools - version: latest - diff --git a/extras/appengine/appengine_config.py b/extras/appengine/appengine_config.py deleted file mode 100644 index 8ed3ee5..0000000 --- a/extras/appengine/appengine_config.py +++ /dev/null @@ -1,6 +0,0 @@ - -def webapp_add_wsgi_middleware(app): - from google.appengine.ext.appstats import recording - app = recording.appstats_wsgi_middleware(app) - return app - diff --git a/extras/appengine/bootstrap.py b/extras/appengine/bootstrap.py deleted file mode 100755 index debc2bf..0000000 --- a/extras/appengine/bootstrap.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python - -"""Downloads required third-party modules.""" - -import os -import urllib2 -import gzip -import tarfile -import tempfile -import shutil -import sys -from StringIO import StringIO - -HERE = os.path.abspath(os.path.dirname(__file__)) -LIB_DIR = os.path.join(HERE, 'lib') - -PACKAGES = { - 'http://pypi.python.org/packages/source/F/Flask/Flask-0.7.2.tar.gz': - [('Flask-0.7.2/flask', 'flask')], - 'http://pypi.python.org/packages/source/W/Werkzeug/Werkzeug-0.6.2.tar.gz': - [('Werkzeug-0.6.2/werkzeug', 'werkzeug')], - 'http://pypi.python.org/packages/source/J/Jinja2/Jinja2-2.5.5.tar.gz': - [('Jinja2-2.5.5/jinja2/', 'jinja2')], - 'http://pypi.python.org/packages/source/s/simplejson/simplejson-2.1.6.tar.gz': - [('simplejson-2.1.6/simplejson', 'simplejson')], - 'http://pypi.python.org/packages/source/P/Pygments/Pygments-1.4.tar.gz': - [('Pygments-1.4/pygments', 'pygments')], -} - - -def fetch_all(): - if not os.path.isdir(LIB_DIR): - os.makedirs(LIB_DIR) - for url, targets in PACKAGES.iteritems(): - if not _missing_targets(targets): - continue - sys.stdout.write(url) - sys.stdout.flush() - fetch(url, targets) - sys.stdout.write(' done\n') - sys.stdout.flush() - - -def fetch(url, targets): - blob = urllib2.urlopen(url).read() - gz = gzip.GzipFile(fileobj=StringIO(blob)) - tar = tarfile.TarFile(fileobj=gz) - tmpdir = tempfile.mkdtemp() - try: - tar.extractall(tmpdir) - for src, dest in targets: - dest = os.path.join(LIB_DIR, dest) - if os.path.isdir(dest): - shutil.rmtree(dest) - shutil.copytree(os.path.join(tmpdir, src), dest) - finally: - shutil.rmtree(tmpdir) - - -def _missing_targets(targets): - for _, dest in targets: - dest = os.path.join(LIB_DIR, dest) - if not os.path.isdir(dest): - return True - return False - - -def link_sqlparse(): - if os.path.islink('sqlparse'): - return - elif os.path.exists('sqlparse'): - shutil.rmtree('sqlparse') - if hasattr(os, 'symlink'): - os.symlink('../../sqlparse', 'sqlparse') - else: - shutil.copytree(os.path.join(HERE, '../../sqlparse'), - 'sqlparse') - - -if __name__ == '__main__': - fetch_all() - link_sqlparse() diff --git a/extras/appengine/config.py b/extras/appengine/config.py deleted file mode 100644 index 1599c00..0000000 --- a/extras/appengine/config.py +++ /dev/null @@ -1,7 +0,0 @@ -# SQLFormat configuration - -# Debug flag -DEBUG = True - -# Secret key, please change this -SECRET_KEY = 'notsosecret' diff --git a/extras/appengine/cron.yaml b/extras/appengine/cron.yaml deleted file mode 100644 index a7fefce..0000000 --- a/extras/appengine/cron.yaml +++ /dev/null @@ -1,4 +0,0 @@ -cron: -- description: Daily exception report - url: /_ereporter?sender=albrecht.andi@googlemail.com&versions=all&delete=false - schedule: every day 00:00
\ No newline at end of file diff --git a/extras/appengine/examples/customers.sql b/extras/appengine/examples/customers.sql deleted file mode 100644 index 8b73850..0000000 --- a/extras/appengine/examples/customers.sql +++ /dev/null @@ -1 +0,0 @@ -USE mydatabase;SELECT orders.customer, orders.day_of_order, orders.product, orders.quantity as number_ordered, inventory.quantity as number_instock, inventory.price FROM orders JOIN inventory ON orders.product = inventory.product;
\ No newline at end of file diff --git a/extras/appengine/examples/multiple_inserts.sql b/extras/appengine/examples/multiple_inserts.sql deleted file mode 100644 index cf49d5d..0000000 --- a/extras/appengine/examples/multiple_inserts.sql +++ /dev/null @@ -1 +0,0 @@ -insert into customer (id, name) values (1, 'John');insert into customer (id, name) values (2, 'Jack');insert into customer (id, name) values (3, 'Jane');insert into customer (id, name) values (4, 'Jim');insert into customer (id, name) values (5, 'Jerry');insert into customer (id, name) values (1, 'Joe');
\ No newline at end of file diff --git a/extras/appengine/examples/pg_view.sql b/extras/appengine/examples/pg_view.sql deleted file mode 100644 index edf9f06..0000000 --- a/extras/appengine/examples/pg_view.sql +++ /dev/null @@ -1 +0,0 @@ -SELECT DISTINCT (current_database())::information_schema.sql_identifier AS view_catalog, (nv.nspname)::information_schema.sql_identifier AS view_schema, (v.relname)::information_schema.sql_identifier AS view_name, (current_database())::information_schema.sql_identifier AS table_catalog, (nt.nspname)::information_schema.sql_identifier AS table_schema, (t.relname)::information_schema.sql_identifier AS table_name FROM pg_namespace nv, pg_class v, pg_depend dv, pg_depend dt, pg_class t, pg_namespace nt WHERE ((((((((((((((nv.oid = v.relnamespace) AND (v.relkind = 'v'::"char")) AND (v.oid = dv.refobjid)) AND (dv.refclassid = ('pg_class'::regclass)::oid)) AND (dv.classid = ('pg_rewrite'::regclass)::oid)) AND (dv.deptype = 'i'::"char")) AND (dv.objid = dt.objid)) AND (dv.refobjid <> dt.refobjid)) AND (dt.classid = ('pg_rewrite'::regclass)::oid)) AND (dt.refclassid = ('pg_class'::regclass)::oid)) AND (dt.refobjid = t.oid)) AND (t.relnamespace = nt.oid)) AND (t.relkind = ANY (ARRAY['r'::"char", 'v'::"char"]))) AND pg_has_role(t.relowner, 'USAGE'::text)) ORDER BY (current_database())::information_schema.sql_identifier, (nv.nspname)::information_schema.sql_identifier, (v.relname)::information_schema.sql_identifier, (current_database())::information_schema.sql_identifier, (nt.nspname)::information_schema.sql_identifier, (t.relname)::information_schema.sql_identifier; diff --git a/extras/appengine/examples/subquery.sql b/extras/appengine/examples/subquery.sql deleted file mode 100644 index dd4bbc1..0000000 --- a/extras/appengine/examples/subquery.sql +++ /dev/null @@ -1 +0,0 @@ -select sum(a1.Sales) from Store_Information a1 where a1.Store_name in (select store_name from Geography a2 where a2.store_name = a1.store_name);
\ No newline at end of file diff --git a/extras/appengine/examples/subquery2.sql b/extras/appengine/examples/subquery2.sql deleted file mode 100644 index 6c00a87..0000000 --- a/extras/appengine/examples/subquery2.sql +++ /dev/null @@ -1 +0,0 @@ -select user_id, count(*) as how_many from bboard where not exists (select 1 from bboard_authorized_maintainers bam where bam.user_id = bboard.user_id) and posting_time + 60 > sysdate group by user_id order by how_many desc;
\ No newline at end of file diff --git a/extras/appengine/index.yaml b/extras/appengine/index.yaml deleted file mode 100644 index 7071349..0000000 --- a/extras/appengine/index.yaml +++ /dev/null @@ -1,7 +0,0 @@ -indexes: -- kind: __google_ExceptionRecord - properties: - - name: date - - name: major_version - - name: minor_version - direction: desc diff --git a/extras/appengine/main.py b/extras/appengine/main.py deleted file mode 100644 index ae4d251..0000000 --- a/extras/appengine/main.py +++ /dev/null @@ -1,37 +0,0 @@ -# SQLFormat's main script, dead simple :) - -import os -import sys - -LIB_DIR = os.path.join(os.path.dirname(__file__), 'lib') - -if LIB_DIR not in sys.path: - sys.path.insert(0, LIB_DIR) - -from sqlformat import app - -import config - -import logging -from google.appengine.ext import ereporter - -ereporter.register_logger() - - -class EreporterMiddleware(object): - - def __init__(self, app): - self.app = app - - def __call__(self, environ, start_response): - try: - return self.app(environ, start_response) - except: - logging.exception('Exception in request:') - logging.debug(environ) - raise - - -app.config.from_object(config) - -app = EreporterMiddleware(app) diff --git a/extras/appengine/sqlformat/__init__.py b/extras/appengine/sqlformat/__init__.py deleted file mode 100644 index 11f4d9d..0000000 --- a/extras/appengine/sqlformat/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -from flask import Flask, make_response - -from sqlformat.legacy import legacy - - -app = Flask('sqlformat') - - -@app.route('/ping') -def ping(): - return make_response('pong') - -@app.route('/_ah/warmup') -def warmup(): - return make_response('polishing chrome') - - -# Register legacy URLs last so that newer URLs replace them. -app.register_blueprint(legacy) diff --git a/extras/appengine/sqlformat/legacy.py b/extras/appengine/sqlformat/legacy.py deleted file mode 100644 index 7f6c161..0000000 --- a/extras/appengine/sqlformat/legacy.py +++ /dev/null @@ -1,166 +0,0 @@ -"""Legacy URLs.""" - -# This module reflects the URLs and behavior of the former Django -# application. - -import logging -import os -import time - -from google.appengine.api import memcache - -from flask import Blueprint, make_response, render_template, Response, request - -from pygments import highlight -from pygments.formatters import HtmlFormatter -from pygments.lexers import SqlLexer, PythonLexer, PhpLexer - -import simplejson as json - -import sqlparse - - -legacy = Blueprint('', 'legacy') - - -EXAMPLES_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '../examples')) - - -@legacy.route('/', methods=['POST', 'GET']) -def index(): - data = {'examples': _get_examples()} - extra = {'highlight': True, 'comments': False, - 'keywords': 'upper', 'idcase': '', - 'n_indents': '2', - 'lang': 'sql'} - sql_orig = 'select * from foo join bar on val1 = val2 where id = 123;' - if request.method == 'POST': - oformat = request.form.get('format', 'html') - extra['highlight'] = 'highlight' in request.form - extra['comments'] = 'remove_comments' in request.form - extra['keywords'] = request.form.get('keyword_case', '') - extra['idcase'] = request.form.get('identifier_case', '') - extra['n_indents'] = request.form.get('n_indents', '2') - extra['lang'] = request.form.get('output_format', 'sql') - sql = _get_sql(request.form, request.files) - sql_orig = sql - start = time.time() - data['output'] = _format_sql(sql, request.form, format=oformat) - data['proc_time'] = '%.3f' % (time.time()-start) - if oformat == 'json': - data['errors'] = '' - return make_response(Response(json.dumps(data), - content_type='text/x-json')) - elif oformat == 'text': - return make_response(Response(data['output'], content_type='text/plain')) - data['sql_orig'] = sql_orig - data['extra'] = extra - return render_template('index.html', **data) - - -@legacy.route('/source/') -def source(): - return render_template('source.html') - - -@legacy.route('/about/') -def about(): - return render_template('about.html') - -@legacy.route('/api/') -def api(): - return render_template('api.html') - - -@legacy.route('/format/', methods=['GET', 'POST']) -@legacy.route('/format', methods=['GET', 'POST']) -def format_(): - if request.method == 'POST': - sql = _get_sql(request.form, request.files) - data = request.form - else: - sql = _get_sql(request.args) - data = request.args - formatted = _format_sql(sql, data, format='text') - return make_response(Response(formatted, content_type='text/plain')) - - -@legacy.route('/load_example', methods=['GET', 'POST']) -def load_example(): - fname = request.form.get('fname') - if fname is None: - answer = 'Uups, I\'ve got no filename...' - elif fname not in _get_examples(): - answer = 'Hmm, I think you don\'t want to do that.' - else: - answer = open(os.path.join(EXAMPLES_DIR, fname)).read() - data = json.dumps({'answer': answer}) - return make_response(Response(data, content_type='text/x-json')) - - -def _get_examples(): - examples = memcache.get('legacy_examples') - if examples is None: - examples = os.listdir(EXAMPLES_DIR) - memcache.set('legacy_examples', examples) - return examples - - -def _get_sql(data, files=None): - sql = None - if files is not None and 'datafile' in files: - raw = files['datafile'].read() - try: - sql = raw.decode('utf-8') - except UnicodeDecodeError, err: - logging.error(err) - logging.debug(repr(raw)) - sql = (u'-- UnicodeDecodeError: %s\n' - u'-- Please make sure to upload UTF-8 encoded data for now.\n' - u'-- If you want to help improving this part of the application\n' - u'-- please file a bug with some demo data at:\n' - u'-- http://code.google.com/p/python-sqlparse/issues/entry\n' - u'-- Thanks!\n' % err) - if not sql: - sql = data.get('data') - return sql or '' - - -def _format_sql(sql, data, format='html'): - popts = {} - if data.get('remove_comments'): - popts['strip_comments'] = True - if data.get('keyword_case', 'undefined') not in ('undefined', ''): - popts['keyword_case'] = data.get('keyword_case') - if data.get('identifier_case', 'undefined') not in ('undefined', ''): - popts['identifier_case'] = data.get('identifier_case') - if data.get('n_indents', None) is not None: - val = data.get('n_indents') - try: - popts['indent_width'] = max(1, min(1000, int(val))) - popts['reindent'] = True - except (ValueError, TypeError): - pass - if (not 'indent_width' in popts and - data.get('reindent', '').lower() in ('1', 'true', 't')): - popts['indent_width'] = 2 - popts['reindent'] = True - if data.get('output_format', None) is not None: - popts['output_format'] = data.get('output_format') - logging.debug('Format: %s, POPTS: %r', format, popts) - logging.debug(sql) - sql = sqlparse.format(sql, **popts) - if format in ('html', 'json'): - if data.get('highlight', False): - if popts['output_format'] == 'python': - lexer = PythonLexer() - elif popts['output_format'] == 'php': - lexer = PhpLexer() - else: - lexer = SqlLexer() - sql = highlight(sql, lexer, HtmlFormatter()) - else: - sql = ('<textarea class="resizable" ' - 'style="height: 350px; margin-top: 1em;">%s</textarea>' - % sql) - return sql diff --git a/extras/appengine/sqlformat/templates/about.html b/extras/appengine/sqlformat/templates/about.html deleted file mode 100644 index 2d4e03e..0000000 --- a/extras/appengine/sqlformat/templates/about.html +++ /dev/null @@ -1,46 +0,0 @@ -{% extends "master.html" %} - -{% block title %}About{% endblock %} - -{% block main %} -<h1>About this Application</h1> -<p> - This application is a online SQL formatting tool. -</p> -<p> - Basically it's a playground for a Python module to parse and format - SQL statements. Sometimes it's easier to combine the available - options and to see the resulting output using a web front-end than - on the command line ;-) -</p> -<p> - To get started, enter a SQL statement in the text box on the top, - choose some options and click on "Format SQL" (Ctrl+F) - to see the result. -</p> -<p> - <em>Note:</em> The SQL formatter and parser is in an early stage - of development. If you're looking for a mature tool, try one of - <a href="http://www.google.com/search?q=online+sql+formatter">these</a>. -</p> -<h2>Using it from the Command Line</h2> -<p> - There are three ways to use this SQL formatter from the command line: -</p> -<ol> - <li>Grab the <a href="/source/">sources</a> and use the module in your - Python scripts.</li> - <li> - Write a little script in your favorite language that sends a POST - request to this application.<br/> - Read the <a href="/api/">API Documentation</a> for more information. - </li> - <li>Use - <a href="/static/lynx_screenshot.png" - alt="Lynx Screenshot" target="_blank" - title="Screenshot: sqlformat.appspot.com on Lynx">Lynx - </a> - </li> -</ol> - -{% endblock %} diff --git a/extras/appengine/sqlformat/templates/api.html b/extras/appengine/sqlformat/templates/api.html deleted file mode 100644 index 2b32cbb..0000000 --- a/extras/appengine/sqlformat/templates/api.html +++ /dev/null @@ -1,66 +0,0 @@ -{% extends "master.html" %} - -{% block title %}API{% endblock %} - -{% block main %} -<h1>API Documentation</h1> - -<p> - Using the API for this application is pretty simple. Just send a - request to -</p> -<p> - <code>http://sqlformat.appspot.com/format/</code> -</p> - -<h2>Options</h2> -<p> - The server accepts various options to control formatting. Only - the <em>data</em> option is required. All others are optional.<br /> - Either use <code>GET</code> and pack the options in the query string - or <code>POST</code> and submit your parameters as form data.<br /> - When using <code>POST</code> make sure your request includes a - <em>Content-Type: application/x-www-form-urlencoded</em> header. -</p> - -<dl> - <dt>data</dt> - <dd>The SQL statement to format.</dd> - <dt>remove_comments</dt> - <dd>Set to 1 to remove comments.</dd> - <dt>keyword_case</dt> - <dd>How to convert keywords. Allowed values are 'lower', 'upper', - 'capitalize'.</dd> - <dt>identifier_case</dt> - <dd>How to convert identifiers. Allowed values are 'lower', 'upper', - 'capitalize'.</dd> - <dt>n_indents</dt> - <dd>An integer indicating the indendation depth.</dd> - <dt>right_margin</dt> - <dd>An integer indicating the maximum line length.</dd> - <dt>output_format</dt> - <dd>Transfer the statement into another programming language. - Allowed values are 'python', 'php'</dd> -</dl> - -<h2>Example</h2> -<p> - Here's a example in Python: -</p> -{% include "python-client-example.html" %} -<p> - <a href="/static/sqlformat_client_example.py">Download sqlformat_example_client.py</a> -</p> - -<p> - And another example using curl and a <code>GET</code> request: -</p> -<div class="highlight example"><pre> -$ curl "http://sqlformat.appspot.com/format<b>?</b>keyword_case=upper<b>&</b>reindent=true<b>&</b>data=select%20*%20from%20foo;" -SELECT * -FROM foo; -$ -</pre></div> - - -{% endblock %} diff --git a/extras/appengine/sqlformat/templates/index.html b/extras/appengine/sqlformat/templates/index.html deleted file mode 100644 index 22d6fdb..0000000 --- a/extras/appengine/sqlformat/templates/index.html +++ /dev/null @@ -1,120 +0,0 @@ -{% extends "master.html" %} - -{% block main %} - -{% if output %} - <a href="#output" class="skip">Jump to formatted query</a> -{% endif %} - -<form method="post" action="" id="form_options" enctype="multipart/form-data"> - <div id="input"> - <div> - <strong>Type your SQL here:</strong><br /> - <textarea id="id_data" rows="10" cols="40" name="data" class="resizable">{{ sql_orig }}</textarea> - </div> - <div style="margin-top: .5em;"> - <strong>...or upload a file:</strong> - <input type="file" name="datafile" id="id_datafile" /> - </div> - <div id="examples" style="margin-top: .5em;"></div> - <div id="actions" style="margin-top: .5em;"> - <input type="submit" value="Format SQL" id="btn_format" /> - </div> - {% if output %}<a name="output"></a> - <div id="response">{{output|safe}}</div> - {% else %} - <div id="response"></div> - {% endif %} - <div id="proc_time"> - {% if proc_time %}Processed in {{proc_time}} seconds.{% endif %} - </div> - <div style="margin-top: 1em;"> - <script type="text/javascript"> - <!-- - google_ad_client = "pub-8870624642249726"; - /* 468x60, Erstellt 07.03.09 */ - google_ad_slot = "9840041509"; - google_ad_width = 468; - google_ad_height = 60; - //--> - </script> - <script type="text/javascript" - src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> - </script> - </div> - - </div> - <div id="options"> - <h1 class="skip">Options</h1> - <fieldset><legend id="general"><strong>General Options</strong></legend> - <div id="general_content" class="content"> - <input type="checkbox" id="id_remove_comments" name="remove_comments" value="1" {% if extra.comments %}checked="checked"{% endif %}/> - <label for="id_remove_comments">Remove comments</label> - <br /> - <input type="checkbox" id="id_highlight" name="highlight" value="1" {% if extra.highlight %}checked="checked"{% endif %} /> - <label for="id_highlight">Enable syntax highlighting</label> - </div> - </fieldset> - <fieldset><legend id="kwcase"> - <strong>Keywords & Identifiers</strong></legend> - <div> - Keywords: <select id="id_keyword_case" name="keyword_case"> - <option value="">Unchanged</option> - <option value="lower" {% if extra.keywords == 'lower' %}selected="selected"{% endif %}>Lower case</option> - <option value="upper" {% if extra.keywords == 'upper' %}selected="selected"{% endif %}>Upper case</option> - <option value="capitalize" {% if extra.keywords == 'capitalize' %}selected="selected"{% endif %}>Capitalize</option> - </select> - </div> - <div> - Identifiers: <select name="identifier_case" id="id_identifier_case"> - <option value="">Unchanged</option> - <option value="lower" {% if extra.idcase == 'lower' %}selected="selected"{% endif %}>Lower case</option> - <option value="upper" {% if extra.idcase == 'upper' %}selected="selected"{% endif %}>Upper case</option> - <option value="capitalize" {% if extra.idcase == 'capitalize' %}selected="selected"{% endif %}>Capitalize</option> - </select> - </div> - </fieldset> - <fieldset><legend id="indent"><strong>Indentation & Margins</strong> - </legend> - <div id="indent_content" class="content"> - <label for="id_n_indents">Indentation: </label> - <input name="n_indents" value="{{extra.n_indents}}" maxlength="2" type="text" id="id_n_indents" size="2" /> spaces - <div class="help">Empty field means leave indentation unchanged.</div> - </div> - </fieldset> - <fieldset><legend id="output"><strong>Output Format</strong></legend> - <label for="id_output_format">Language: </label> - <select name="output_format" id="id_output_format"> - <option value="sql" {% if extra.lang == 'sql' %}selected="selected"{% endif %}>SQL</option> - <option value="python" {% if extra.lang == 'python' %}selected="selected"{% endif %}>Python</option> - <option value="php" {% if extra.lang == 'php' %}selected="selected"{% endif %}>PHP</option> - </select> - </fieldset> - - <div class="dev">This software is in development.</div> - - <div> - <g:plusone size="medium"></g:plusone> - <a href="http://flattr.com/thing/350724/SQLFormat-Online-SQL-formatting-service" target="_blank"> - <img src="http://api.flattr.com/button/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0" /> - </a> - </div> - <div style="padding-top: 15px;"> - <a href="http://twitter.com/share" class="twitter-share-button" data-count="horizontal" data-via="andialbrecht">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script> - </div> - </div> - <div class="clearfix"></div> -</form> - -<script language="javascript"> -html = '<strong>...or select an example:</strong> '; -html = html + '<select onchange="load_example();" id="sel_example">'; -html = html + '<option value="">-- Choose Example --</option>'; -{% for ex in examples %} - html = html + '<option value="{{ex}}">{{ex}}</option>'; -{% endfor %} -html = html + '</select>'; -$('#examples').html(html); -</script> -{% endblock %} - diff --git a/extras/appengine/sqlformat/templates/master.html b/extras/appengine/sqlformat/templates/master.html deleted file mode 100644 index 88a9d36..0000000 --- a/extras/appengine/sqlformat/templates/master.html +++ /dev/null @@ -1,103 +0,0 @@ -<html> - <head> - <title>{% block title %}SQLFormat - Online SQL Formatter{% endblock %}</title> - <meta name="keywords" content="SQL, format, parse, python, beautify, pretty, online, formatting, formatter" /> - <meta name="description" content="Easy to use web service to format SQL statements." /> - <link rel="stylesheet" href="/static/pygments.css" /> - <link rel="stylesheet" href="/static/styles.css" /> - <script src="http://www.google.com/jsapi"></script> - <script> - google.load("jquery", "1.2.6"); - </script> - <script src="/static/hotkeys.js"></script> - <script src="/static/script.js"></script> - </head> - <body> - - <div id="help"> - <p>Keyboard Shortcuts</p> - <p> - <span class="shortcut">H</span> - Show / hide this help window<br/> - <span class="shortcut">Ctrl+F</span> - Format SQL and display result<br/> - <span class="shortcut">O</span> - Show / hide options<br/> - <span class="shortcut">T</span> - Set focus on SQL input<br/> - </p> - </div> - - <div id="header"> - <div id="header-inner"> - {% if user %}<img src="{{userimg}}" border="0" align="right" style="padding-top:.4em;"/>{% endif %} - <h1> - <a href="/"> - <span class="q">S<span class="q2">Q</span>L</span>Format - </a> - </h1> - <div id="slogan">Online SQL formatting service</div> - <div id="topmenu"> - <a href="/">Home</a> - | - <a href="/about/">About</a> - | - <a href="/source/">Source Code</a> - | - <a href="/api/">API</a> -<!-- - | - {% if user %} - <a href="{{logout_url}}">Sign out</a> - {% else %} - <a href="{{login_url}}">Sign in</a> - <span style="color: #babdb6;">with your Google account - to save preferred settings.</span> - {% endif %} ---> - </div> - </div> - </div> - - <div id="main"> - <div id="main-inner"> - {% block main %}MAIN CONTENT GOES HERE{% endblock %} - </div> - </div> - - <div id="footer"> - <div id="footer-inner"> - <div style="float: left; font-size: .85em;"> - <div>© 2011 Andi Albrecht - <code><albrecht dot andi gmail></code> - </div> - <div> - <a href="/">Home</a> - | - <a href="/about/">About</a> - | - <a href="/source/">Source Code</a> - | - <a href="/api/">API</a> - | - <a href="http://andialbrecht.wordpress.com/">Blog</a> - </div> - </div> - <div style="float: right;"> - <img src="http://code.google.com/appengine/images/appengine-silver-120x30.gif" - alt="Powered by Google App Engine" /> - </div> - <div class="clearfix"></div> - </div> - </div> - - <script type="text/javascript"> - var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); - document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); - </script> - <script type="text/javascript"> - try { - var pageTracker = _gat._getTracker("UA-3535525-2"); - pageTracker._trackPageview(); - } catch(err) {}</script> - <script>init();</script> - <script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script> - - </body> -</html> diff --git a/extras/appengine/sqlformat/templates/python-client-example.html b/extras/appengine/sqlformat/templates/python-client-example.html deleted file mode 100644 index 68bf820..0000000 --- a/extras/appengine/sqlformat/templates/python-client-example.html +++ /dev/null @@ -1,17 +0,0 @@ -<div class="highlight example"><pre><span class="c">#!/usr/bin/env python</span> - -<span class="k">import</span> <span class="nn">urllib</span> -<span class="k">import</span> <span class="nn">urllib2</span> - -<span class="n">payload</span> <span class="o">=</span> <span class="p">(</span> - <span class="p">(</span><span class="s">'data'</span><span class="p">,</span> <span class="s">'select * from foo join bar on val1 = val2 where id = 123;'</span><span class="p">),</span> - <span class="p">(</span><span class="s">'format'</span><span class="p">,</span> <span class="s">'text'</span><span class="p">),</span> - <span class="p">(</span><span class="s">'keyword_case'</span><span class="p">,</span> <span class="s">'upper'</span><span class="p">),</span> - <span class="p">(</span><span class="s">'reindent'</span><span class="p">,</span> <span class="bp">True</span><span class="p">),</span> - <span class="p">(</span><span class="s">'n_indents'</span><span class="p">,</span> <span class="mf">2</span><span class="p">),</span> - <span class="p">)</span> - -<span class="n">response</span> <span class="o">=</span> <span class="n">urllib2</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="s">'http://sqlformat.appspot.com/format/'</span><span class="p">,</span> - <span class="n">urllib</span><span class="o">.</span><span class="n">urlencode</span><span class="p">(</span><span class="n">payload</span><span class="p">))</span> -<span class="k">print</span> <span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">()</span> -</pre></div> diff --git a/extras/appengine/sqlformat/templates/source.html b/extras/appengine/sqlformat/templates/source.html deleted file mode 100644 index a0ed89d..0000000 --- a/extras/appengine/sqlformat/templates/source.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends "master.html" %} - -{% block title %}Source code{% endblock %} - -{% block main %} -<div id="response"> - <h1>Source Code</h1> - - <h2>Python Module</h2> - <p> - The sources for the SQL parser and formatter module are - hosted on Google Code. - To clone the repository run: - <p> - <code class="pre"> hg clone http://python-sqlparse.googlecode.com/hg/ python-sqlparse</code> - </p> - <p> - <a href="http://python-sqlparse.googlecode.com">Visit the project page</a> - | - <a href="http://code.google.com/p/python-sqlparse/source/browse/">Browse the sources online</a> - | - <a href="http://python-sqlparse.googlecode.com/svn/docs/api/index.html"> API Documentation</a> - </p> - <p> - Some relevant parts of the Python module contain code from the - <a href="http://pygments.org/">pygments</a> syntax highlighter. - The underlying Python module uses a non-validating SQL parser. - This approach makes it possible to parse even syntactically incorrect - SQL statements. - </p> - - <p> - Currently the parser module is used by - <a href="http://crunchyfrog.googlecode.com/">CrunchyFrog</a> - a - database front-end for Gnome. - </p> - - <p> - The <code>sqlparse</code> module is released under the terms of the - <a href="http://www.opensource.org/licenses/bsd-license.php">New BSD License</a>. - </p> - - <h2>App Engine Application</h2> - <p> - The source code for this App Engine application is available in the - <code>examples</code> directory of the Python module - (but it's really nothing special ;-). - </p> - - <h2>Contributing</h2> - <p> - Please file bug reports and feature requests on the project site at - <a href="http://code.google.com/p/python-sqlparse/issues/entry">http://code.google.com/p/python-sqlparse/issues/entry</a> - or if you have code to contribute upload it to - <a href="http://codereview.appspot.com">http://codereview.appspot.com</a> - and add albrecht.andi@googlemail.com as reviewer. - </p> - -</div> -{% endblock %} diff --git a/extras/appengine/static/bg_options.png b/extras/appengine/static/bg_options.png Binary files differdeleted file mode 100644 index bc1a6ed..0000000 --- a/extras/appengine/static/bg_options.png +++ /dev/null diff --git a/extras/appengine/static/bgfieldset.png b/extras/appengine/static/bgfieldset.png Binary files differdeleted file mode 100644 index 4d55f4a..0000000 --- a/extras/appengine/static/bgfieldset.png +++ /dev/null diff --git a/extras/appengine/static/bgfooter.png b/extras/appengine/static/bgfooter.png Binary files differdeleted file mode 100644 index 9ce5bdd..0000000 --- a/extras/appengine/static/bgfooter.png +++ /dev/null diff --git a/extras/appengine/static/bgtop.png b/extras/appengine/static/bgtop.png Binary files differdeleted file mode 100644 index a0d4709..0000000 --- a/extras/appengine/static/bgtop.png +++ /dev/null diff --git a/extras/appengine/static/favicon.ico b/extras/appengine/static/favicon.ico Binary files differdeleted file mode 100644 index 1372520..0000000 --- a/extras/appengine/static/favicon.ico +++ /dev/null diff --git a/extras/appengine/static/hotkeys.js b/extras/appengine/static/hotkeys.js deleted file mode 100644 index 0e62a92..0000000 --- a/extras/appengine/static/hotkeys.js +++ /dev/null @@ -1 +0,0 @@ -(function(B){B.fn.__bind__=B.fn.bind;B.fn.__unbind__=B.fn.unbind;B.fn.__find__=B.fn.find;var A={version:"0.7.8",override:/keydown|keypress|keyup/g,triggersMap:{},specialKeys:{27:"esc",9:"tab",32:"space",13:"return",8:"backspace",145:"scroll",20:"capslock",144:"numlock",19:"pause",45:"insert",36:"home",46:"del",35:"end",33:"pageup",34:"pagedown",37:"left",38:"up",39:"right",40:"down",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12"},shiftNums:{"`":"~","1":"!","2":"@","3":"#","4":"$","5":"%","6":"^","7":"&","8":"*","9":"(","0":")","-":"_","=":"+",";":":","'":'"',",":"<",".":">","/":"?","\\":"|"},newTrigger:function(E,D,F){var C={};C[E]={};C[E][D]={cb:F,disableInInput:false};return C}};if(B.browser.mozilla){A.specialKeys=B.extend(A.specialKeys,{96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9"})}B.fn.find=function(C){this.query=C;return B.fn.__find__.apply(this,arguments)};B.fn.unbind=function(H,E,G){if(B.isFunction(E)){G=E;E=null}if(E&&typeof E==="string"){var F=((this.prevObject&&this.prevObject.query)||(this[0].id&&this[0].id)||this[0]).toString();var D=H.split(" ");for(var C=0;C<D.length;C++){delete A.triggersMap[F][D[C]][E]}}return this.__unbind__(H,G)};B.fn.bind=function(J,F,K){var H=J.match(A.override);if(B.isFunction(F)||!H){return this.__bind__(J,F,K)}else{var N=null,I=B.trim(J.replace(A.override,""));if(I){N=this.__bind__(I,F,K)}if(typeof F==="string"){F={combi:F}}if(F.combi){for(var M=0;M<H.length;M++){var D=H[M];var G=F.combi.toLowerCase(),E=A.newTrigger(D,G,K),L=((this.prevObject&&this.prevObject.query)||(this[0].id&&this[0].id)||this[0]).toString();E[D][G].disableInInput=F.disableInInput;if(!A.triggersMap[L]){A.triggersMap[L]=E}else{if(!A.triggersMap[L][D]){A.triggersMap[L][D]=E[D]}}var C=A.triggersMap[L][D][G];if(!C){A.triggersMap[L][D][G]=[E[D][G]]}else{if(C.constructor!==Array){A.triggersMap[L][D][G]=[C]}else{A.triggersMap[L][D][G][C.length]=E[D][G]}}this.each(function(){var O=B(this);if(O.attr("hkId")&&O.attr("hkId")!==L){L=O.attr("hkId")+";"+L}O.attr("hkId",L)});N=this.__bind__(H.join(" "),F,A.handler)}}return N}};A.findElement=function(C){if(!B(C).attr("hkId")){if(B.browser.opera||B.browser.safari){while(!B(C).attr("hkId")&&C.parentNode){C=C.parentNode}}}return C};A.handler=function(E){var O=A.findElement(E.currentTarget),I=B(O),D=I.attr("hkId");if(D){D=D.split(";");var G=E.which,Q=E.type,P=A.specialKeys[G],N=!P&&String.fromCharCode(G).toLowerCase(),H=E.shiftKey,C=E.ctrlKey,M=E.altKey||E.originalEvent.altKey,F=null;for(var R=0;R<D.length;R++){if(A.triggersMap[D[R]][Q]){F=A.triggersMap[D[R]][Q];break}}if(F){var J;if(!H&&!C&&!M){J=F[P]||(N&&F[N])}else{var L="";if(M){L+="alt+"}if(C){L+="ctrl+"}if(H){L+="shift+"}J=F[L+P];if(!J){if(N){J=F[L+N]||F[L+A.shiftNums[N]]||(L==="shift+"&&F[A.shiftNums[N]])}}}if(J){var S=false;for(var R=0;R<J.length;R++){if(J[R].disableInInput){var K=B(E.target);if(I.is("input")||I.is("textarea")||K.is("input")||K.is("textarea")){return true}}S=S||J[R].cb.apply(this,[E])}return S}}}};window.hotkeys=A;return B})(jQuery);
\ No newline at end of file diff --git a/extras/appengine/static/loading.gif b/extras/appengine/static/loading.gif Binary files differdeleted file mode 100644 index a879bed..0000000 --- a/extras/appengine/static/loading.gif +++ /dev/null diff --git a/extras/appengine/static/lynx_screenshot.png b/extras/appengine/static/lynx_screenshot.png Binary files differdeleted file mode 100644 index d1592ac..0000000 --- a/extras/appengine/static/lynx_screenshot.png +++ /dev/null diff --git a/extras/appengine/static/pygments.css b/extras/appengine/static/pygments.css deleted file mode 100644 index da02807..0000000 --- a/extras/appengine/static/pygments.css +++ /dev/null @@ -1,59 +0,0 @@ -.c { color: #408080; font-style: italic } /* Comment */ -.err { border: 1px solid #FF0000 } /* Error */ -.k { color: #008000; font-weight: bold } /* Keyword */ -.o { color: #666666 } /* Operator */ -.cm { color: #408080; font-style: italic } /* Comment.Multiline */ -.cp { color: #BC7A00 } /* Comment.Preproc */ -.c1 { color: #408080; font-style: italic } /* Comment.Single */ -.cs { color: #408080; font-style: italic } /* Comment.Special */ -.gd { color: #A00000 } /* Generic.Deleted */ -.ge { font-style: italic } /* Generic.Emph */ -.gr { color: #FF0000 } /* Generic.Error */ -.gh { color: #000080; font-weight: bold } /* Generic.Heading */ -.gi { color: #00A000 } /* Generic.Inserted */ -.go { color: #808080 } /* Generic.Output */ -.gp { color: #000080; font-weight: bold } /* Generic.Prompt */ -.gs { font-weight: bold } /* Generic.Strong */ -.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ -.gt { color: #0040D0 } /* Generic.Traceback */ -.kc { color: #008000; font-weight: bold } /* Keyword.Constant */ -.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ -.kp { color: #008000 } /* Keyword.Pseudo */ -.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ -.kt { color: #B00040 } /* Keyword.Type */ -.m { color: #666666 } /* Literal.Number */ -.s { color: #BA2121 } /* Literal.String */ -.na { color: #7D9029 } /* Name.Attribute */ -.nb { color: #008000 } /* Name.Builtin */ -.nc { color: #0000FF; font-weight: bold } /* Name.Class */ -.no { color: #880000 } /* Name.Constant */ -.nd { color: #AA22FF } /* Name.Decorator */ -.ni { color: #999999; font-weight: bold } /* Name.Entity */ -.ne { color: #D2413A; font-weight: bold } /* Name.Exception */ -.nf { color: #0000FF } /* Name.Function */ -.nl { color: #A0A000 } /* Name.Label */ -.nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ -.nt { color: #008000; font-weight: bold } /* Name.Tag */ -.nv { color: #19177C } /* Name.Variable */ -.ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ -.w { color: #bbbbbb } /* Text.Whitespace */ -.mf { color: #666666 } /* Literal.Number.Float */ -.mh { color: #666666 } /* Literal.Number.Hex */ -.mi { color: #666666 } /* Literal.Number.Integer */ -.mo { color: #666666 } /* Literal.Number.Oct */ -.sb { color: #BA2121 } /* Literal.String.Backtick */ -.sc { color: #BA2121 } /* Literal.String.Char */ -.sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ -.s2 { color: #BA2121 } /* Literal.String.Double */ -.se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ -.sh { color: #BA2121 } /* Literal.String.Heredoc */ -.si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ -.sx { color: #008000 } /* Literal.String.Other */ -.sr { color: #BB6688 } /* Literal.String.Regex */ -.s1 { color: #BA2121 } /* Literal.String.Single */ -.ss { color: #19177C } /* Literal.String.Symbol */ -.bp { color: #008000 } /* Name.Builtin.Pseudo */ -.vc { color: #19177C } /* Name.Variable.Class */ -.vg { color: #19177C } /* Name.Variable.Global */ -.vi { color: #19177C } /* Name.Variable.Instance */ -.il { color: #666666 } /* Literal.Number.Integer.Long */ diff --git a/extras/appengine/static/robots.txt b/extras/appengine/static/robots.txt deleted file mode 100644 index c033917..0000000 --- a/extras/appengine/static/robots.txt +++ /dev/null @@ -1,8 +0,0 @@ -# Directions for web crawlers. -# See http://www.robotstxt.org/wc/norobots.html. - -User-agent: HTTrack -User-agent: puf -User-agent: MSIECrawler -User-agent: Nutch -Disallow: / diff --git a/extras/appengine/static/script.js b/extras/appengine/static/script.js deleted file mode 100644 index 8bdf271..0000000 --- a/extras/appengine/static/script.js +++ /dev/null @@ -1,98 +0,0 @@ -var initialized = false; - -function update_output() { - data = {} - data.data = $('#id_data').val(); - data.format = 'json'; - if ( $('#id_remove_comments').attr('checked') ) { - data.remove_comments = 1 - } - if ( $('#id_highlight').attr('checked') ) { data.highlight = 1 } - data.keyword_case = $('#id_keyword_case').val(); - data.identifier_case = $('#id_identifier_case').val(); - data.n_indents = $('#id_n_indents').val(); - data.output_format = $('#id_output_format').val(); - form = document.getElementById('form_options'); - $(form.elements).attr('disabled', 'disabled'); - $('#response').addClass('loading'); - $.post('/', data, - function(data) { - if ( data.output ) { - $('#response').html(data.output); - proc_time = 'Processed in '+data.proc_time+' seconds.'; - } else { - $('#response').html('An error occured: '+data.errors); - proc_time = ''; - } - $('#proc_time').html(proc_time); - $(form.elements).each( function(idx) { - obj = $(this); - if ( ! obj.is('.keep-disabled') ) { - obj.removeAttr('disabled'); - } - }); - $('#response').removeClass('loading'); - }, 'json'); - return false; -} - -function toggle_fieldset(event) { - id = $(this).attr('id'); - $('#'+id+'_content').slideDown(); - $('legend').each(function(idx) { - obj = $('#'+this.id+'_content'); - if ( this.id != id ) { - obj.slideUp(); - } - }); -} - - -function textarea_grab_focus(evt) { - evt.stopPropagation(); - evt.preventDefault(); - $('#id_data').focus(); - return false; -} - - -function show_help() { - $('#help').toggle(); - return false; -} - - -function hide_help() { - $('#help').hide(); - return false; -} - -function load_example() { - fname = $('#sel_example').val(); - data = {fname: fname}; - $.post('/load_example', data, - function(data) { - $('#id_data').val(data.answer); - }, 'json'); -} - - -function init() { - if (initialized) { return } - //$('legend').bind('click', toggle_fieldset); - // $('legend').each(function(idx) { - // obj = $('#'+this.id+'_content'); - // if ( this.id != 'general' ) { - // obj.hide(); - // } - // }); - $(document).bind('keydown', {combi:'Ctrl+f'}, - update_output); - $('#btn_format').val('Format SQL [Ctrl+F]'); - $(document).bind('keydown', {combi: 'h', disableInInput: true}, - show_help); - $(document).bind('keydown', 'Esc', hide_help); - $(document).bind('keydown', {combi: 't', disableInInput: true}, - textarea_grab_focus); - initialized = true; -}
\ No newline at end of file diff --git a/extras/appengine/static/sitemap.xml b/extras/appengine/static/sitemap.xml deleted file mode 100644 index db3288e..0000000 --- a/extras/appengine/static/sitemap.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<urlset - xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 - http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"> -<!-- created with Free Online Sitemap Generator www.xml-sitemaps.com --> - -<url> - <loc>http://sqlformat.appspot.com/</loc> - <changefreq>monthly</changefreq> -</url> -<url> - <loc>http://sqlformat.appspot.com/about/</loc> - <changefreq>monthly</changefreq> -</url> -<url> - <loc>http://sqlformat.appspot.com/source/</loc> - <changefreq>monthly</changefreq> -</url> -<url> - <loc>http://sqlformat.appspot.com/api/</loc> - <changefreq>monthly</changefreq> -</url> -</urlset>
\ No newline at end of file diff --git a/extras/appengine/static/sqlformat_client_example.py b/extras/appengine/static/sqlformat_client_example.py deleted file mode 100644 index 8b2a9e9..0000000 --- a/extras/appengine/static/sqlformat_client_example.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python - -import urllib -import urllib2 - -REMOTE_API = 'http://sqlformat.appspot.com/format/' - -payload = ( - ('data', 'select * from foo join bar on val1 = val2 where id = 123;'), - ('format', 'text'), - ('keyword_case', 'upper'), - ('reindent', True), - ('n_indents', 2), - ) - - -response = urllib2.urlopen(REMOTE_API, - urllib.urlencode(payload)) -print response.read() - diff --git a/extras/appengine/static/styles.css b/extras/appengine/static/styles.css deleted file mode 100644 index 41a540a..0000000 --- a/extras/appengine/static/styles.css +++ /dev/null @@ -1,245 +0,0 @@ -body { - color: #000000; - background: #eeeeec; - font-family: "Free Sans", Arial, Verdana, sans; - font-size: 10pt; - margin: 0; - padding: 0; -} - -#header { - background: url(/static/bgtop.png) top left repeat-x; - border-bottom: 3px solid #2e3436; -} - -#header-inner, #main-inner, #footer-inner { - width: 70em; - margin-left: auto; - margin-right: auto; -} - - -#header-inner h1 { - margin: 0; - padding: 0; - margin-bottom: .2em; - font-weight: normal; - float: left; - font-size: 2em; - letter-spacing: .07em; -} - -#header-inner .q { - color: #f57900; - padding-right: 3px; -} - -#header-inner .q2 { - font-family: Georgia, "Times New Roman", serif; -} - -#header-inner h1 a { - text-decoration: none; - color: #eeeeec; -} - -#header-inner #slogan { - float: left; - color: #babdb6; - font-size: 1.4em; - margin-left: 1em; - letter-spacing: .18em; - padding-top: .2em; -} - - -#topmenu { - color: #729fcf; - clear: left; - padding-top: .5em; - padding-bottom: .5em; - font-size: 1.1em; -} - -#topmenu a { - color: #eeeeec; - text-decoration: none; -} - -#topmenu a:hover { - color: #ce5c00; -} - - -#main { - padding: 10px; - background: white; - line-height: 1.5em; - text-align: justify; -} - -#main form ul { - margin: 0; - padding: 0; - list-style-type: none; -} - -#main p, #main ol, #main .example, #main dl { - font-size: 12pt; - margin-left: 2em; -} - -#main dt { - font-weight: bold; -} - -#main li { - margin-bottom: .7em; -} - -#main a { - color: #f57900; -} - -#main h1, h2, h3, h4 { - color: #204a87; - font-weight: normal; - letter-spacing: .05em; -} - -#main pre, #main code.pre { - font-size: 10pt; - line-height: 1em; - padding: 4px; - background-color: #eeeeec; - border: 1px solid #babdb6; -} - -#input { - width: 50em; - float: right; - margin-left: 2em; -} - -#options { - width: 18em; - float: left; - color: #2e3436; - margin-top: .75em; - text-align: left; -} - -#options fieldset { - border: 1px solid #dddddd; - margin-bottom: .6em; - background: url(/static/bgfieldset.png) bottom left repeat-x; - -moz-border-radius: 3px; -} - - -#options input, select { - border: 1px solid #dddddd; -} - -#options .help { - font-size: .9em; - color: #888a85; - margin-bottom: .6em; -} - - -#footer { - background: url(/static/bgfooter.png) top left repeat-x; - padding: 10px; - min-height: 80px; - border-top: 4px solid #babdb6; -} - -#footer-inner { - width: 70em; - margin-left: auto; - margin-right: auto; - color: #888a85; -} - -#footer-inner a { - color: #888a85; -} - -#footer-inner a:hover { - color: #555753; -} - -.clearfix { - clear: both; -} - -.skip { - display: none; -} - -textarea { - border: 1px solid #cccccc; - border-bottom: none; - padding: 4px; - font-size: 12pt; - width: 100%; -} - -textarea:focus { - background-color: #eeeeec; -} - -div.grippie { - background: url(/static/resize-grip.png) bottom right no-repeat #eeeeec; - border-color: #cccccc; - border-style: solid; - border-width: 0pt 1px 1px; - cursor: se-resize; - height: 14px; - overflow: hidden; -} - -#help { - display: none; - position: fixed; - right: 10%; - left: 10%; - top: 0; - opacity: 0.85; - -moz-opacity: 0.85; - -khtml-opacity: 0.85; - filter: alpha(opacity=85); - -moz-border-radius: 0px 0px 10px 10px; - - background: #2e3436; - color: white; - font-weight: bold; - - padding: 1em; - z-index: 1; - overflow-x: hidden; - overflow-y: auto; -} - -#help .shortcut { - color: #f57900; - font-weight: bold; - width: 20px; - display: inline; -} - -.loading { - background: url(/static/loading.gif) top left no-repeat; -} - -.dev { - color: #cc0000; - font-size: .9em; - letter-spacing: 1; -} - -#proc_time { - color: #888a85; - font-size: .85em; -}
\ No newline at end of file diff --git a/extras/buildbot/googlecode_atom.py b/extras/buildbot/googlecode_atom.py deleted file mode 100644 index 0d4631f..0000000 --- a/extras/buildbot/googlecode_atom.py +++ /dev/null @@ -1,171 +0,0 @@ -# GoogleCode Atom Feed Poller -# Author: Srivats P. <pstavirs> -# Based on Mozilla's HgPoller -# http://bonsai.mozilla.org/cvsblame.cgi?file=/mozilla/tools/buildbot/buildbot/changes/Attic/hgpoller.py&revision=1.1.4.2 -# -# Description: -# Use this ChangeSource for projects hosted on http://code.google.com/ -# -# This ChangeSource uses the project's commit Atom feed. Depending upon the -# frequency of commits, you can tune the polling interval for the feed -# (default is 1 hour) -# -# Parameters: -# feedurl (MANDATORY): The Atom feed URL of the GoogleCode repo -# pollinterval (OPTIONAL): Polling frequency for the feed (in seconds) -# -# Example: -# To poll the Ostinato project's commit feed every 3 hours, use - -# from googlecode_atom import GoogleCodeAtomPoller -# poller = GoogleCodeAtomPoller( -# feedurl="http://code.google.com/feeds/p/ostinato/hgchanges/basic", -# pollinterval=10800) -# c['change_source'] = [ poller ] -# - -from time import strptime -from calendar import timegm -from xml.dom import minidom, Node - -from twisted.python import log, failure -from twisted.internet import defer, reactor -from twisted.internet.task import LoopingCall -from twisted.web.client import getPage - -from buildbot.changes import base, changes - -def googleCodePollerForProject(project, vcs, pollinterval=3600): - return GoogleCodeAtomPoller( - 'http://code.google.com/feeds/p/%s/%schanges/basic' % (project, vcs), - pollinterval=pollinterval) - - -class GoogleCodeAtomPoller(base.ChangeSource): - """This source will poll a GoogleCode Atom feed for changes and - submit them to the change master. Works for both Svn and Hg repos. - TODO: branch processing - """ - - compare_attrs = ['feedurl', 'pollinterval'] - parent = None - loop = None - volatile = ['loop'] - working = False - - def __init__(self, feedurl, pollinterval=3600): - """ - @type feedurl: string - @param feedurl: The Atom feed URL of the GoogleCode repo - (e.g. http://code.google.com/feeds/p/ostinato/hgchanges/basic) - - @type pollinterval: int - @param pollinterval: The time (in seconds) between queries for - changes (default is 1 hour) - """ - - self.feedurl = feedurl - self.branch = None - self.pollinterval = pollinterval - self.lastChange = None - self.loop = LoopingCall(self.poll) - - def startService(self): - log.msg("GoogleCodeAtomPoller starting") - base.ChangeSource.startService(self) - reactor.callLater(0, self.loop.start, self.pollinterval) - - def stopService(self): - log.msg("GoogleCodeAtomPoller stoppping") - self.loop.stop() - return base.ChangeSource.stopService(self) - - def describe(self): - return ("Getting changes from the GoogleCode repo changes feed %s" % - self._make_url()) - - def poll(self): - if self.working: - log.msg("Not polling because last poll is still working") - else: - self.working = True - d = self._get_changes() - d.addCallback(self._process_changes) - d.addCallbacks(self._finished_ok, self._finished_failure) - - def _finished_ok(self, res): - assert self.working - self.working = False - log.msg("GoogleCodeAtomPoller poll success") - - return res - - def _finished_failure(self, res): - log.msg("GoogleCodeAtomPoller poll failed: %s" % res) - assert self.working - self.working = False - return None - - def _make_url(self): - return "%s" % (self.feedurl) - - def _get_changes(self): - url = self._make_url() - log.msg("GoogleCodeAtomPoller polling %s" % url) - - return getPage(url, timeout=self.pollinterval) - - def _parse_changes(self, query): - dom = minidom.parseString(query) - entries = dom.getElementsByTagName("entry") - changes = [] - # Entries come in reverse chronological order - for i in entries: - d = {} - - # revision is the last part of the 'id' url - d["revision"] = i.getElementsByTagName( - "id")[0].firstChild.data.split('/')[-1] - if d["revision"] == self.lastChange: - break # no more new changes - - d["when"] = timegm(strptime( - i.getElementsByTagName("updated")[0].firstChild.data, - "%Y-%m-%dT%H:%M:%SZ")) - d["author"] = i.getElementsByTagName( - "author")[0].getElementsByTagName("name")[0].firstChild.data - # files and commit msg are separated by 2 consecutive <br/> - content = i.getElementsByTagName( - "content")[0].firstChild.data.split("<br/>\n <br/>") - # Remove the action keywords from the file list - fl = content[0].replace( - u' \xa0\xa0\xa0\xa0Add\xa0\xa0\xa0\xa0', '').replace( - u' \xa0\xa0\xa0\xa0Delete\xa0\xa0\xa0\xa0', '').replace( - u' \xa0\xa0\xa0\xa0Modify\xa0\xa0\xa0\xa0', '') - # Get individual files and remove the 'header' - d["files"] = fl.encode("ascii", "replace").split("<br/>")[1:] - d["files"] = [f.strip() for f in d["files"]] - try: - d["comments"] = content[1].encode("ascii", "replace") - except: - d["comments"] = "No commit message provided" - - changes.append(d) - - changes.reverse() # want them in chronological order - return changes - - def _process_changes(self, query): - change_list = self._parse_changes(query) - - # Skip calling addChange() if this is the first successful poll. - if self.lastChange is not None: - for change in change_list: - c = changes.Change(revision = change["revision"], - who = change["author"], - files = change["files"], - comments = change["comments"], - when = change["when"], - branch = self.branch) - self.parent.addChange(c) - if change_list: - self.lastChange = change_list[-1]["revision"] diff --git a/extras/buildbot/master.cfg b/extras/buildbot/master.cfg deleted file mode 100644 index 92403fe..0000000 --- a/extras/buildbot/master.cfg +++ /dev/null @@ -1,264 +0,0 @@ -# -*- python -*- -# ex: set syntax=python: - -# This is a sample buildmaster config file. It must be installed as -# 'master.cfg' in your buildmaster's base directory (although the filename -# can be changed with the --basedir option to 'mktap buildbot master'). - -# It has one job: define a dictionary named BuildmasterConfig. This -# dictionary has a variety of keys to control different aspects of the -# buildmaster. They are documented in docs/config.xhtml . - -PYTHON_VERSIONS = ('2.4', '2.5', '2.6', '2.7', '3.2') - - -# This is the dictionary that the buildmaster pays attention to. We also use -# a shorter alias to save typing. -c = BuildmasterConfig = {} - -####### DB URL - -# This specifies what database buildbot uses to store change and scheduler -# state -c['db_url'] = "sqlite:///state.sqlite" - -####### BUILDSLAVES - -# the 'slaves' list defines the set of allowable buildslaves. Each element is -# a BuildSlave object, which is created with bot-name, bot-password. These -# correspond to values given to the buildslave's mktap invocation. -from buildbot.buildslave import BuildSlave -c['slaves'] = [BuildSlave("bot1linux", "imtheslave")] - -# to limit to two concurrent builds on a slave, use -# c['slaves'] = [BuildSlave("bot1name", "bot1passwd", max_builds=2)] - - -# 'slavePortnum' defines the TCP port to listen on. This must match the value -# configured into the buildslaves (with their --master option) - -c['slavePortnum'] = 9989 - -####### CHANGESOURCES - -# the 'change_source' setting tells the buildmaster how it should find out -# about source code changes. Any class which implements IChangeSource can be -# put here: there are several in buildbot/changes/*.py to choose from. - -from buildbot.changes.pb import PBChangeSource -c['change_source'] = PBChangeSource() - -from googlecode_atom import GoogleCodeAtomPoller -poller = GoogleCodeAtomPoller( - feedurl="http://code.google.com/feeds/p/python-sqlparse/hgchanges/basic", - pollinterval=600) -c['change_source'] = [ poller ] - -# For example, if you had CVSToys installed on your repository, and your -# CVSROOT/freshcfg file had an entry like this: -#pb = ConfigurationSet([ -# (None, None, None, PBService(userpass=('foo', 'bar'), port=4519)), -# ]) - -# then you could use the following buildmaster Change Source to subscribe to -# the FreshCVS daemon and be notified on every commit: -# -#from buildbot.changes.freshcvs import FreshCVSSource -#fc_source = FreshCVSSource("cvs.example.com", 4519, "foo", "bar") -#c['change_source'] = fc_source - -# or, use a PBChangeSource, and then have your repository's commit script run -# 'buildbot sendchange', or use contrib/svn_buildbot.py, or -# contrib/arch_buildbot.py : -# -#from buildbot.changes.pb import PBChangeSource -#c['change_source'] = PBChangeSource() - -# If you wat to use SVNPoller, it might look something like -# # Where to get source code changes -# from buildbot.changes.svnpoller import SVNPoller -# source_code_svn_url='https://svn.myproject.org/bluejay/trunk' -# svn_poller = SVNPoller( -# svnurl=source_code_svn_url, -# pollinterval=60*60, # seconds -# histmax=10, -# svnbin='/usr/bin/svn', -## ) -# c['change_source'] = [ svn_poller ] - -####### SCHEDULERS - -## configure the Schedulers - -from buildbot.scheduler import Scheduler -c['schedulers'] = [] -for py_ver in PYTHON_VERSIONS: - c['schedulers'].append( - Scheduler(name="py%s" % py_ver, branch=None, - treeStableTimer=2*60, - builderNames=["builder-%s" % py_ver])) - - -####### BUILDERS - -# the 'builders' list defines the Builders. Each one is configured with a -# dictionary, using the following keys: -# name (required): the name used to describe this builder -# slavename or slavenames (required): which slave(s) to use (must appear in c['slaves']) -# factory (required): a BuildFactory to define how the build is run -# builddir (optional): which subdirectory to run the builder in - -# buildbot/process/factory.py provides several BuildFactory classes you can -# start with, which implement build processes for common targets (GNU -# autoconf projects, CPAN perl modules, etc). The factory.BuildFactory is the -# base class, and is configured with a series of BuildSteps. When the build -# is run, the appropriate buildslave is told to execute each Step in turn. - -# the first BuildStep is typically responsible for obtaining a copy of the -# sources. There are source-obtaining Steps in buildbot/steps/source.py for -# CVS, SVN, and others. - -cvsroot = ":pserver:anonymous@cvs.sourceforge.net:/cvsroot/buildbot" -cvsmodule = "buildbot" - -from buildbot.process import factory -from buildbot.steps.source import CVS, Mercurial -from buildbot.steps.shell import Compile, ShellCommand -from buildbot.steps.python_twisted import Trial -f1 = factory.BuildFactory() -f1.addStep(CVS(cvsroot=cvsroot, cvsmodule=cvsmodule, login="", mode="copy")) -f1.addStep(Compile(command=["python", "./setup.py", "build"])) -f1.addStep(Trial(testChanges=True, testpath=".")) - -from buildbot.config import BuilderConfig - -def _mk_factory(py_ver): - py_bin = "/home/build/python/python%(ver)s/bin/python%(ver)s" % {"ver": py_ver} - py2to3 = "/home/build/python/python%(ver)s/bin/2to3" % {"ver": py_ver} - site_pkgs = "/home/build/python/python%(ver)s/lib/site-packages/sqlparse" % {"ver": py_ver} - is_py3k = bool(py_ver.startswith("3")) - workdir = "build/" - f = factory.BuildFactory() - f.addStep(Mercurial(repourl="http://python-sqlparse.googlecode.com/hg/")) - f.addStep(ShellCommand(command=["rm", "-rf", site_pkgs], - description="removing installed package", - descriptionDone="site-pkgs clean")) - if is_py3k: - workdir = "build/extras/py3k/" - f.addStep(ShellCommand(command=["make", "clean"], - workdir=workdir, - description="cleaning up", - descriptionDone="cleaned up")) - f.addStep(ShellCommand(command=["make", "2TO3=%s" % py2to3], - workdir=workdir, - description="creating py3 version", - descriptionDone="py3 version created")) - f.addStep(Compile(command=[py_bin, "setup.py", "build"], - workdir=workdir)) - f.addStep(ShellCommand(command=[py_bin, "setup.py", "install"], - description="installing module", - descriptionDone="module installed", - workdir=workdir)) - f.addStep(ShellCommand(command=["mv", "sqlparse", "_sqlparse"], - description="moving local module", - descriptionDone="local module moved", - workdir=workdir)) - f.addStep(ShellCommand(command=[py_bin, "tests/run_tests.py"], - description="running tests", - descriptionDone="tests done", - workdir=workdir)) - f.addStep(ShellCommand(command=["mv", "_sqlparse", "sqlparse"], - description="restoring local module", - descriptionDone="local module restored", - workdir=workdir)) - return f - -def _mk_builder(py_ver): - return BuilderConfig( - name="builder-%s" % py_ver, - slavename="bot1linux", - builddir="full-%s" % py_ver, - factory=_mk_factory(py_ver)) - -c['builders'] = [] -for py_ver in PYTHON_VERSIONS: - c['builders'].append(_mk_builder(py_ver)) - - -####### STATUS TARGETS - -# 'status' is a list of Status Targets. The results of each build will be -# pushed to these targets. buildbot/status/*.py has a variety to choose from, -# including web pages, email senders, and IRC bots. - -c['status'] = [] - -from buildbot.status import html -from buildbot.status.web import auth, authz -authz_cfg=authz.Authz( - # change any of these to True to enable; see the manual for more - # options - gracefulShutdown = False, - forceBuild = True, - forceAllBuilds = True, - pingBuilder = True, - stopBuild = False, - stopAllBuilds = False, - cancelPendingBuild = True, -) -c['status'].append(html.WebStatus(http_port=8010, authz=authz_cfg)) - -from buildbot.status import mail -c['status'].append(mail.MailNotifier( - fromaddr="buildbot@andialbrecht.de", - extraRecipients=["albrecht.andi@gmail.com"], - sendToInterestedUsers=False, - mode="failing")) -# -# from buildbot.status import words -# c['status'].append(words.IRC(host="irc.example.com", nick="bb", -# channels=["#example"])) -# c['status'].append(words.IRC(host="irc.example.com", nick="bb", -# channels=["#example"], useSSL=True)) -# -# from buildbot.status import client -# c['status'].append(client.PBListener(9988)) - - -####### DEBUGGING OPTIONS - -# if you set 'debugPassword', then you can connect to the buildmaster with -# the diagnostic tool in contrib/debugclient.py . From this tool, you can -# manually force builds and inject changes, which may be useful for testing -# your buildmaster without actually committing changes to your repository (or -# before you have a functioning 'sources' set up). The debug tool uses the -# same port number as the slaves do: 'slavePortnum'. - -#c['debugPassword'] = "debugpassword" - -# if you set 'manhole', you can ssh into the buildmaster and get an -# interactive python shell, which may be useful for debugging buildbot -# internals. It is probably only useful for buildbot developers. You can also -# use an authorized_keys file, or plain telnet. -#from buildbot import manhole -#c['manhole'] = manhole.PasswordManhole("tcp:9999:interface=127.0.0.1", -# "admin", "password") - - -####### PROJECT IDENTITY - -# the 'projectName' string will be used to describe the project that this -# buildbot is working on. For example, it is used as the title of the -# waterfall HTML page. The 'projectURL' string will be used to provide a link -# from buildbot HTML pages to your project's home page. - -c['projectName'] = "python-sqlparse" -c['projectURL'] = "http://python-sqlparse.googlecode.com" - -# the 'buildbotURL' string should point to the location where the buildbot's -# internal web server (usually the html.WebStatus page) is visible. This -# typically uses the port number set in the Waterfall 'status' entry, but -# with an externally-visible host name which the buildbot cannot figure out -# without some help. - -c['buildbotURL'] = "http://buildbot.andialbrecht.de" diff --git a/extras/sqlformat.png b/extras/sqlformat.png Binary files differdeleted file mode 100644 index 4189bc4..0000000 --- a/extras/sqlformat.png +++ /dev/null diff --git a/extras/sqlformat.svg b/extras/sqlformat.svg deleted file mode 100644 index 59e1183..0000000 --- a/extras/sqlformat.svg +++ /dev/null @@ -1,115 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="120" - height="60" - id="svg2" - sodipodi:version="0.32" - inkscape:version="0.46" - version="1.0" - sodipodi:docname="sqlformat.svg" - inkscape:output_extension="org.inkscape.output.svg.inkscape"> - <defs - id="defs4"> - <linearGradient - id="linearGradient3163"> - <stop - id="stop3165" - offset="0" - style="stop-color:#888a85;stop-opacity:1;" /> - <stop - id="stop3167" - offset="1" - style="stop-color:#565854;stop-opacity:1;" /> - </linearGradient> - <inkscape:perspective - sodipodi:type="inkscape:persp3d" - inkscape:vp_x="0 : 526.18109 : 1" - inkscape:vp_y="0 : 1000 : 0" - inkscape:vp_z="744.09448 : 526.18109 : 1" - inkscape:persp3d-origin="372.04724 : 350.78739 : 1" - id="perspective10" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3163" - id="linearGradient3161" - x1="57.357143" - y1="1.6964277" - x2="57.464291" - y2="58.125" - gradientUnits="userSpaceOnUse" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="5.6" - inkscape:cx="58.539628" - inkscape:cy="24.488765" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="false" - inkscape:window-width="1436" - inkscape:window-height="720" - inkscape:window-x="209" - inkscape:window-y="171" /> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Ebene 1" - inkscape:groupmode="layer" - id="layer1"> - <rect - style="fill:url(#linearGradient3161);fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.5103448;fill-opacity:1" - id="rect2383" - width="118.21429" - height="58.035713" - x="1.0714285" - y="1.0714284" - ry="3.5714285" /> - <text - xml:space="preserve" - style="font-size:41.68727112px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#f57900;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:FreeSans;-inkscape-font-specification:FreeSans" - x="3.8212376" - y="38.731842" - id="text3169" - transform="scale(1.3303696,0.7516708)"><tspan - sodipodi:role="line" - id="tspan3171" - x="3.8212376" - y="38.731842">S<tspan - style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Georgia;-inkscape-font-specification:Georgia Italic" - id="tspan3173">Q</tspan>L</tspan></text> - <text - xml:space="preserve" - style="font-size:29.59413719px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:FreeSans;-inkscape-font-specification:FreeSans" - x="5.0176215" - y="60.359047" - id="text3177" - transform="scale(1.1410205,0.8764084)"><tspan - sodipodi:role="line" - id="tspan3179" - x="5.0176215" - y="60.359047">Format</tspan></text> - </g> -</svg> diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index a2cbd90..0000000 --- a/pytest.ini +++ /dev/null @@ -1,6 +0,0 @@ -[pytest] -pep8ignore = - extras/* ALL - examples/* ALL - docs/* ALL - * E125 E127 @@ -4,16 +4,10 @@ # the BSD License: http://www.opensource.org/licenses/bsd-license.php. import re -import sys -try: - from setuptools import setup, find_packages - packages = find_packages(exclude=('tests',)) -except ImportError: - if sys.version_info[0] == 3: - raise RuntimeError('distribute is required to install this package.') - from distutils.core import setup - packages = ['sqlparse', 'sqlparse.engine'] +from setuptools import setup, find_packages + +packages = find_packages(exclude=('tests',)) def get_version(): @@ -21,7 +15,7 @@ def get_version(): see http://stackoverflow.com/questions/458550/standard-way-to-embed-version-into-python-package """ - VERSIONFILE='sqlparse/__init__.py' + VERSIONFILE = 'sqlparse/__init__.py' verstrline = open(VERSIONFILE, "rt").read() VSRE = r'^__version__ = [\'"]([^\'"]*)[\'"]' mo = re.search(VSRE, verstrline, re.M) @@ -86,11 +80,6 @@ Parsing:: VERSION = get_version() -kwargs = {} -if sys.version_info[0] == 3: - kwargs['use_2to3'] = True - - setup( name='sqlparse', version=VERSION, @@ -108,16 +97,13 @@ setup( 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.4', - 'Programming Language :: Python :: 2.5', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4' 'Topic :: Database', 'Topic :: Software Development' ], scripts=['bin/sqlformat'], - **kwargs ) diff --git a/sqlparse/__init__.py b/sqlparse/__init__.py index 83bb684..d943956 100644 --- a/sqlparse/__init__.py +++ b/sqlparse/__init__.py @@ -8,15 +8,12 @@ __version__ = '0.1.17-dev' - # Setup namespace +from sqlparse import compat from sqlparse import engine from sqlparse import filters from sqlparse import formatter -# Deprecated in 0.1.5. Will be removed in 0.2.0 -from sqlparse.exceptions import SQLParseError - def parse(sql, encoding=None): """Parse sql and return a list of statements. @@ -67,7 +64,8 @@ def split(sql, encoding=None): """ stack = engine.FilterStack() stack.split_statements = True - return [unicode(stmt).strip() for stmt in stack.run(sql, encoding)] + return [compat.text_type(stmt).strip() + for stmt in stack.run(sql, encoding)] from sqlparse.engine.filter import StatementFilter diff --git a/sqlparse/compat.py b/sqlparse/compat.py new file mode 100644 index 0000000..1849c13 --- /dev/null +++ b/sqlparse/compat.py @@ -0,0 +1,39 @@ +"""Python 2/3 compatibility. + +This module only exists to avoid a dependency on six +for very trivial stuff. We only need to take care regarding +string types and buffers. +""" + +import sys + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +if PY3: + text_type = str + string_types = (str,) + from io import StringIO + + def u(s): + return s + +elif PY2: + text_type = unicode + string_types = (basestring,) + from StringIO import StringIO # flake8: noqa + + def u(s): + return unicode(s, 'unicode_escape') + + +# Directly copied from six: +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(meta): + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + return type.__new__(metaclass, 'temporary_class', (), {}) diff --git a/sqlparse/engine/grouping.py b/sqlparse/engine/grouping.py index a317044..12ae385 100644 --- a/sqlparse/engine/grouping.py +++ b/sqlparse/engine/grouping.py @@ -5,11 +5,6 @@ import itertools from sqlparse import sql from sqlparse import tokens as T -try: - next -except NameError: # Python < 2.6 - next = lambda i: i.next() - def _group_left_right(tlist, ttype, value, cls, check_right=lambda t: True, @@ -116,7 +111,7 @@ def group_as(tlist): def _right_valid(token): # Currently limited to DML/DDL. Maybe additional more non SQL reserved # keywords should appear here (see issue8). - return not token.ttype in (T.DML, T.DDL) + return token.ttype not in (T.DML, T.DDL) def _left_valid(token): if token.ttype is T.Keyword and token.value in ('NULL',): @@ -216,9 +211,10 @@ def group_identifier(tlist): if identifier_tokens and identifier_tokens[-1].ttype is T.Whitespace: identifier_tokens = identifier_tokens[:-1] if not (len(identifier_tokens) == 1 - and (isinstance(identifier_tokens[0], (sql.Function, sql.Parenthesis)) - or identifier_tokens[0].ttype in (T.Literal.Number.Integer, - T.Literal.Number.Float))): + and (isinstance(identifier_tokens[0], (sql.Function, + sql.Parenthesis)) + or identifier_tokens[0].ttype in ( + T.Literal.Number.Integer, T.Literal.Number.Float))): group = tlist.group_tokens(sql.Identifier, identifier_tokens) idx = tlist.token_index(group) + 1 else: @@ -451,6 +447,5 @@ def group(tlist): group_if, group_for, group_foreach, - group_begin, - ]: + group_begin]: func(tlist) diff --git a/sqlparse/filters.py b/sqlparse/filters.py index 676344f..b187907 100644 --- a/sqlparse/filters.py +++ b/sqlparse/filters.py @@ -4,9 +4,8 @@ import re from os.path import abspath, join -from sqlparse import sql, tokens as T +from sqlparse import compat, sql, tokens as T from sqlparse.engine import FilterStack -from sqlparse.lexer import tokenize from sqlparse.pipeline import Pipeline from sqlparse.tokens import (Comment, Comparison, Keyword, Name, Punctuation, String, Whitespace) @@ -25,7 +24,7 @@ class _CaseFilter: if case is None: case = 'upper' assert case in ['lower', 'upper', 'capitalize'] - self.convert = getattr(unicode, case) + self.convert = getattr(compat.text_type, case) def process(self, stack, stream): for ttype, value in stream: @@ -52,20 +51,20 @@ class TruncateStringFilter: def __init__(self, width, char): self.width = max(width, 1) - self.char = unicode(char) + self.char = compat.text_type(char) def process(self, stack, stream): for ttype, value in stream: if ttype is T.Literal.String.Single: if value[:2] == '\'\'': inner = value[2:-2] - quote = u'\'\'' + quote = compat.text_type('\'\'') else: inner = value[1:-1] - quote = u'\'' + quote = compat.text_type('\'') if len(inner) > self.width: - value = u''.join((quote, inner[:self.width], self.char, - quote)) + value = compat.text_type('').join( + (quote, inner[:self.width], self.char, quote)) yield ttype, value @@ -143,7 +142,6 @@ class IncludeStatement: # Found file path to include if token_type in String.Symbol: -# if token_type in tokens.String.Symbol: # Get path of file to include path = join(self.dirpath, value[1:-1]) @@ -154,13 +152,14 @@ class IncludeStatement: f.close() # There was a problem loading the include file - except IOError, err: + except IOError as err: # Raise the exception to the interpreter if self.raiseexceptions: raise # Put the exception as a comment on the SQL code - yield Comment, u'-- IOError: %s\n' % err + yield Comment, compat.text_type( + '-- IOError: %s\n' % err) else: # Create new FilterStack to parse readed file @@ -171,13 +170,14 @@ class IncludeStatement: self.raiseexceptions) # Max recursion limit reached - except ValueError, err: + except ValueError as err: # Raise the exception to the interpreter if self.raiseexceptions: raise # Put the exception as a comment on the SQL code - yield Comment, u'-- ValueError: %s\n' % err + yield Comment, compat.text_type( + '-- ValueError: %s\n' % err) stack = FilterStack() stack.preprocess.append(filtr) @@ -300,7 +300,7 @@ class ReindentFilter: raise StopIteration def _get_offset(self, token): - raw = ''.join(map(unicode, self._flatten_up_to_token(token))) + raw = ''.join(map(compat.text_type, self._flatten_up_to_token(token))) line = raw.splitlines()[-1] # Now take current offset into account and return relative offset. full_offset = len(line) - len(self.char * (self.width * self.indent)) @@ -340,7 +340,7 @@ class ReindentFilter: if prev and prev.is_whitespace() and prev not in added: tlist.tokens.pop(tlist.token_index(prev)) offset += 1 - uprev = unicode(prev) + uprev = compat.text_type(prev) if (prev and (uprev.endswith('\n') or uprev.endswith('\r'))): nl = tlist.token_next(token) else: @@ -462,7 +462,7 @@ class ReindentFilter: self._process(stmt) if isinstance(stmt, sql.Statement): if self._last_stmt is not None: - if unicode(self._last_stmt).endswith('\n'): + if compat.text_type(self._last_stmt).endswith('\n'): nl = '\n' else: nl = '\n\n' @@ -491,10 +491,10 @@ class RightMarginFilter: else: self.line = token.value.splitlines()[-1] elif (token.is_group() - and not token.__class__ in self.keep_together): + and token.__class__ not in self.keep_together): token.tokens = self._process(stack, token, token.tokens) else: - val = unicode(token) + val = compat.text_type(token) if len(self.line) + len(val) > self.width: match = re.search('^ +', self.line) if match is not None: @@ -568,7 +568,7 @@ class ColumnsSelect: class SerializerUnicode: def process(self, stack, stmt): - raw = unicode(stmt) + raw = compat.text_type(stmt) lines = split_unquoted_newlines(raw) res = '\n'.join(line.rstrip() for line in lines) return res @@ -578,7 +578,7 @@ def Tokens2Unicode(stream): result = "" for _, value in stream: - result += unicode(value) + result += compat.text_type(value) return result @@ -600,7 +600,7 @@ class OutputFilter: else: varname = self.varname - has_nl = len(unicode(stmt).strip().splitlines()) > 1 + has_nl = len(compat.text_type(stmt).strip().splitlines()) > 1 stmt.tokens = self._process(stmt.tokens, varname, has_nl) return stmt diff --git a/sqlparse/lexer.py b/sqlparse/lexer.py index fd29f5c..7ce6d36 100644 --- a/sqlparse/lexer.py +++ b/sqlparse/lexer.py @@ -15,31 +15,23 @@ import re import sys +from sqlparse import compat from sqlparse import tokens +from sqlparse.compat import StringIO from sqlparse.keywords import KEYWORDS, KEYWORDS_COMMON -from cStringIO import StringIO class include(str): pass -class combined(tuple): - """Indicates a state combined from multiple states.""" - - def __new__(cls, *args): - return tuple.__new__(cls, args) - - def __init__(self, *args): - # tuple.__init__ doesn't do anything - pass - - def is_keyword(value): test = value.upper() return KEYWORDS_COMMON.get(test, KEYWORDS.get(test, tokens.Name)), value +# TODO(andi): Can this be removed? If so, add_filter and Lexer.filters +# should be removed too. def apply_filters(stream, filters, lexer=None): """ Use this method to apply an iterable of filters to @@ -81,14 +73,14 @@ class LexerMeta(type): try: rex = re.compile(tdef[0], rflags).match - except Exception, err: + except Exception as err: raise ValueError(("uncompilable regex %r in state" " %r of %r: %s" % (tdef[0], state, cls, err))) assert type(tdef[1]) is tokens._TokenType or callable(tdef[1]), \ - ('token type must be simple type or callable, not %r' - % (tdef[1],)) + ('token type must be simple type or callable, not %r' + % (tdef[1],)) if len(tdef) == 2: new_state = None @@ -106,24 +98,12 @@ class LexerMeta(type): new_state = -int(tdef2[5:]) else: assert False, 'unknown new state %r' % tdef2 - elif isinstance(tdef2, combined): - # combine a new state from existing ones - new_state = '_tmp_%d' % cls._tmpname - cls._tmpname += 1 - itokens = [] - for istate in tdef2: - assert istate != state, \ - 'circular state ref %r' % istate - itokens.extend(cls._process_state(unprocessed, - processed, istate)) - processed[new_state] = itokens - new_state = (new_state,) elif isinstance(tdef2, tuple): # push more than one state for state in tdef2: assert (state in unprocessed or state in ('#pop', '#push')), \ - 'unknown new state ' + state + 'unknown new state ' + state new_state = tdef2 else: assert False, 'unknown new state def %r' % tdef2 @@ -134,7 +114,6 @@ class LexerMeta(type): cls._all_tokens = {} cls._tmpname = 0 processed = cls._all_tokens[cls.__name__] = {} - #tokendefs = tokendefs or cls.tokens[name] for state in cls.tokens.keys(): cls._process_state(cls.tokens, processed, state) return processed @@ -152,9 +131,7 @@ class LexerMeta(type): return type.__call__(cls, *args, **kwds) -class Lexer(object): - - __metaclass__ = LexerMeta +class Lexer(compat.with_metaclass(LexerMeta)): encoding = 'utf-8' stripall = False @@ -235,8 +212,8 @@ class Lexer(object): if self.encoding == 'guess': try: text = text.decode('utf-8') - if text.startswith(u'\ufeff'): - text = text[len(u'\ufeff'):] + if text.startswith(compat.text_type('\ufeff')): + text = text[len(compat.text_type('\ufeff')):] except UnicodeDecodeError: text = text.decode('latin1') else: @@ -258,13 +235,13 @@ class Lexer(object): Also preprocess the text, i.e. expand tabs and strip it if wanted and applies registered filters. """ - if isinstance(text, basestring): + if isinstance(text, compat.string_types): if self.stripall: text = text.strip() elif self.stripnl: text = text.strip('\n') - if sys.version_info[0] < 3 and isinstance(text, unicode): + if compat.PY2 and isinstance(text, compat.text_type): text = StringIO(text.encode('utf-8')) self.encoding = 'utf-8' else: @@ -342,7 +319,7 @@ class Lexer(object): pos += 1 statestack = ['root'] statetokens = tokendefs['root'] - yield pos, tokens.Text, u'\n' + yield pos, tokens.Text, compat.text_type('\n') continue yield pos, tokens.Error, text[pos] pos += 1 diff --git a/sqlparse/sql.py b/sqlparse/sql.py index 5ecfbdc..8601537 100644 --- a/sqlparse/sql.py +++ b/sqlparse/sql.py @@ -5,6 +5,7 @@ import re import sys +from sqlparse import compat from sqlparse import tokens as T @@ -32,7 +33,7 @@ class Token(object): if sys.version_info[0] == 3: return self.value else: - return unicode(self).encode('utf-8') + return compat.text_type(self).encode('utf-8') def __repr__(self): short = self._get_repr_value() @@ -51,15 +52,15 @@ class Token(object): .. deprecated:: 0.1.5 Use ``unicode(token)`` (for Python 3: ``str(token)``) instead. """ - return unicode(self) + return compat.text_type(self) def _get_repr_name(self): return str(self.ttype).split('.')[-1] def _get_repr_value(self): - raw = unicode(self) + raw = compat.text_type(self) if len(raw) > 7: - raw = raw[:6] + u'...' + raw = raw[:6] + compat.text_type('...') return re.sub('\s+', ' ', raw) def flatten(self): @@ -83,7 +84,7 @@ class Token(object): return type_matched if regex: - if isinstance(values, basestring): + if isinstance(values, compat.string_types): values = set([values]) if self.ttype is T.Keyword: @@ -96,7 +97,7 @@ class Token(object): return True return False - if isinstance(values, basestring): + if isinstance(values, compat.string_types): if self.is_keyword: return values.upper() == self.normalized return values == self.value @@ -172,7 +173,7 @@ class TokenList(Token): if sys.version_info[0] == 3: return ''.join(x.value for x in self.flatten()) else: - return ''.join(unicode(x) for x in self.flatten()) + return ''.join(compat.text_type(x) for x in self.flatten()) def _get_repr_name(self): return self.__class__.__name__ @@ -185,9 +186,9 @@ class TokenList(Token): pre = ' +-' else: pre = ' | ' - print '%s%s%d %s \'%s\'' % (indent, pre, idx, + print('%s%s%d %s \'%s\'' % (indent, pre, idx, token._get_repr_name(), - token._get_repr_value()) + token._get_repr_value())) if (token.is_group() and (max_depth is None or depth < max_depth)): token._pprint_tree(max_depth, depth + 1) @@ -220,18 +221,10 @@ class TokenList(Token): else: yield token -# def __iter__(self): -# return self -# -# def next(self): -# for token in self.tokens: -# yield token - def is_group(self): return True def get_sublists(self): -# return [x for x in self.tokens if isinstance(x, TokenList)] for x in self.tokens: if isinstance(x, TokenList): yield x @@ -285,7 +278,7 @@ class TokenList(Token): if not isinstance(idx, int): idx = self.token_index(idx) - for n in xrange(idx, len(self.tokens)): + for n in range(idx, len(self.tokens)): token = self.tokens[n] if token.match(ttype, value, regex): return token @@ -510,7 +503,7 @@ class Identifier(TokenList): next_ = self.token_next(self.token_index(marker), False) if next_ is None: return None - return unicode(next_) + return compat.text_type(next_) def get_ordering(self): """Returns the ordering or ``None`` as uppercase string.""" diff --git a/sqlparse/tokens.py b/sqlparse/tokens.py index 01a9b89..53c31ce 100644 --- a/sqlparse/tokens.py +++ b/sqlparse/tokens.py @@ -13,15 +13,6 @@ class _TokenType(tuple): parent = None - def split(self): - buf = [] - node = self - while node is not None: - buf.append(node) - node = node.parent - buf.reverse() - return buf - def __contains__(self, val): return val is not None and (self is val or val[:len(self)] == self) diff --git a/sqlparse/utils.py b/sqlparse/utils.py index 3a49ac2..7595e9d 100644 --- a/sqlparse/utils.py +++ b/sqlparse/utils.py @@ -5,65 +5,42 @@ Created on 17/05/2012 ''' import re +from collections import OrderedDict -try: - from collections import OrderedDict -except ImportError: - OrderedDict = None +class Cache(OrderedDict): + """Cache with LRU algorithm using an OrderedDict as basis.""" + def __init__(self, maxsize=100): + OrderedDict.__init__(self) -if OrderedDict: - class Cache(OrderedDict): - """Cache with LRU algorithm using an OrderedDict as basis - """ - def __init__(self, maxsize=100): - OrderedDict.__init__(self) + self._maxsize = maxsize - self._maxsize = maxsize + def __getitem__(self, key, *args, **kwargs): + # Get the key and remove it from the cache, or raise KeyError + value = OrderedDict.__getitem__(self, key) + del self[key] - def __getitem__(self, key, *args, **kwargs): - # Get the key and remove it from the cache, or raise KeyError - value = OrderedDict.__getitem__(self, key) - del self[key] - - # Insert the (key, value) pair on the front of the cache - OrderedDict.__setitem__(self, key, value) - - # Return the value from the cache - return value - - def __setitem__(self, key, value, *args, **kwargs): - # Key was inserted before, remove it so we put it at front later - if key in self: - del self[key] + # Insert the (key, value) pair on the front of the cache + OrderedDict.__setitem__(self, key, value) - # Too much items on the cache, remove the least recent used - elif len(self) >= self._maxsize: - self.popitem(False) + # Return the value from the cache + return value - # Insert the (key, value) pair on the front of the cache - OrderedDict.__setitem__(self, key, value, *args, **kwargs) - -else: - class Cache(dict): - """Cache that reset when gets full - """ - def __init__(self, maxsize=100): - dict.__init__(self) - - self._maxsize = maxsize + def __setitem__(self, key, value, *args, **kwargs): + # Key was inserted before, remove it so we put it at front later + if key in self: + del self[key] - def __setitem__(self, key, value, *args, **kwargs): - # Reset the cache if we have too much cached entries and start over - if len(self) >= self._maxsize: - self.clear() + # Too much items on the cache, remove the least recent used + elif len(self) >= self._maxsize: + self.popitem(False) - # Insert the (key, value) pair on the front of the cache - dict.__setitem__(self, key, value, *args, **kwargs) + # Insert the (key, value) pair on the front of the cache + OrderedDict.__setitem__(self, key, value, *args, **kwargs) def memoize_generator(func): - """Memoize decorator for generators + """Memoize decorator for generators. Store `func` results in a cache according to their arguments as 'memoize' does but instead this works on decorators instead of regular functions. @@ -73,7 +50,6 @@ def memoize_generator(func): cache = Cache() def wrapped_func(*args, **kwargs): -# params = (args, kwargs) params = (args, tuple(sorted(kwargs.items()))) # Look if cached @@ -120,6 +96,7 @@ SPLIT_REGEX = re.compile(r""" LINE_MATCH = re.compile(r'(\r\n|\r|\n)') + def split_unquoted_newlines(text): """Split a string on all unquoted newlines. @@ -134,4 +111,4 @@ def split_unquoted_newlines(text): outputlines.append('') else: outputlines[-1] += line - return outputlines
\ No newline at end of file + return outputlines diff --git a/tests/test_filters.py b/tests/test_filters.py index d827454..925b0b6 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -74,5 +74,4 @@ LIMIT 1""" if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] unittest.main() diff --git a/tests/test_format.py b/tests/test_format.py index a105b1c..4746358 100644 --- a/tests/test_format.py +++ b/tests/test_format.py @@ -92,10 +92,14 @@ class TestFormat(TestCaseBase): f = lambda x: sqlparse.format(x) # Because of the use of - self.ndiffAssertEqual(f(s1), "SELECT some_column LIKE 'value\r'") - self.ndiffAssertEqual(f(s2), "SELECT some_column LIKE 'value\r'\nWHERE id = 1\n") - self.ndiffAssertEqual(f(s3), "SELECT some_column LIKE 'value\\'\r' WHERE id = 1\n") - self.ndiffAssertEqual(f(s4), "SELECT some_column LIKE 'value\\\\\\'\r' WHERE id = 1\n") + self.ndiffAssertEqual( + f(s1), "SELECT some_column LIKE 'value\r'") + self.ndiffAssertEqual( + f(s2), "SELECT some_column LIKE 'value\r'\nWHERE id = 1\n") + self.ndiffAssertEqual( + f(s3), "SELECT some_column LIKE 'value\\'\r' WHERE id = 1\n") + self.ndiffAssertEqual( + f(s4), "SELECT some_column LIKE 'value\\\\\\'\r' WHERE id = 1\n") def test_outputformat(self): sql = 'select * from foo;' diff --git a/tests/test_functions.py b/tests/test_functions.py index 52e2ce7..425ab7f 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -160,5 +160,4 @@ class Test_IsType(Test_SQL): if __name__ == "__main__": - #import sys;sys.argv = ['', 'Test.testName'] main() diff --git a/tests/test_grouping.py b/tests/test_grouping.py index 5ade830..5b87a38 100644 --- a/tests/test_grouping.py +++ b/tests/test_grouping.py @@ -3,6 +3,7 @@ import pytest import sqlparse +from sqlparse import compat from sqlparse import sql from sqlparse import tokens as T @@ -26,7 +27,7 @@ class TestGrouping(TestCaseBase): def test_comments(self): s = '/*\n * foo\n */ \n bar' parsed = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(parsed)) + self.ndiffAssertEqual(s, compat.text_type(parsed)) self.assertEqual(len(parsed.tokens), 2) def test_assignment(self): @@ -42,18 +43,18 @@ class TestGrouping(TestCaseBase): def test_identifiers(self): s = 'select foo.bar from "myscheme"."table" where fail. order' parsed = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(parsed)) + self.ndiffAssertEqual(s, compat.text_type(parsed)) self.assert_(isinstance(parsed.tokens[2], sql.Identifier)) self.assert_(isinstance(parsed.tokens[6], sql.Identifier)) self.assert_(isinstance(parsed.tokens[8], sql.Where)) s = 'select * from foo where foo.id = 1' parsed = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(parsed)) + self.ndiffAssertEqual(s, compat.text_type(parsed)) self.assert_(isinstance(parsed.tokens[-1].tokens[-1].tokens[0], sql.Identifier)) s = 'select * from (select "foo"."id" from foo)' parsed = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(parsed)) + self.ndiffAssertEqual(s, compat.text_type(parsed)) self.assert_(isinstance(parsed.tokens[-1].tokens[3], sql.Identifier)) s = "INSERT INTO `test` VALUES('foo', 'bar');" @@ -69,7 +70,7 @@ class TestGrouping(TestCaseBase): self.assertEqual(len(parsed.tokens[2].tokens), 4) identifiers = list(parsed.tokens[2].get_identifiers()) self.assertEqual(len(identifiers), 2) - self.assertEquals(identifiers[0].get_alias(), u"col") + self.assertEquals(identifiers[0].get_alias(), 'col') def test_identifier_wildcard(self): p = sqlparse.parse('a.*, b.id')[0] @@ -141,44 +142,44 @@ class TestGrouping(TestCaseBase): def test_where(self): s = 'select * from foo where bar = 1 order by id desc' p = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(p)) + self.ndiffAssertEqual(s, compat.text_type(p)) self.assertTrue(len(p.tokens), 16) s = 'select x from (select y from foo where bar = 1) z' p = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(p)) + self.ndiffAssertEqual(s, compat.text_type(p)) self.assertTrue(isinstance(p.tokens[-1].tokens[0].tokens[-2], sql.Where)) def test_typecast(self): s = 'select foo::integer from bar' p = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(p)) + self.ndiffAssertEqual(s, compat.text_type(p)) self.assertEqual(p.tokens[2].get_typecast(), 'integer') self.assertEqual(p.tokens[2].get_name(), 'foo') s = 'select (current_database())::information_schema.sql_identifier' p = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(p)) + self.ndiffAssertEqual(s, compat.text_type(p)) self.assertEqual(p.tokens[2].get_typecast(), 'information_schema.sql_identifier') def test_alias(self): s = 'select foo as bar from mytable' p = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(p)) + self.ndiffAssertEqual(s, compat.text_type(p)) self.assertEqual(p.tokens[2].get_real_name(), 'foo') self.assertEqual(p.tokens[2].get_alias(), 'bar') s = 'select foo from mytable t1' p = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(p)) + self.ndiffAssertEqual(s, compat.text_type(p)) self.assertEqual(p.tokens[6].get_real_name(), 'mytable') self.assertEqual(p.tokens[6].get_alias(), 't1') s = 'select foo::integer as bar from mytable' p = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(p)) + self.ndiffAssertEqual(s, compat.text_type(p)) self.assertEqual(p.tokens[2].get_alias(), 'bar') s = ('SELECT DISTINCT ' '(current_database())::information_schema.sql_identifier AS view') p = sqlparse.parse(s)[0] - self.ndiffAssertEqual(s, unicode(p)) + self.ndiffAssertEqual(s, compat.text_type(p)) self.assertEqual(p.tokens[4].get_alias(), 'view') def test_alias_case(self): # see issue46 diff --git a/tests/test_parse.py b/tests/test_parse.py index 6c9d6a6..bbe60df 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -8,6 +8,7 @@ from tests.utils import TestCaseBase import sqlparse import sqlparse.sql +from sqlparse import compat from sqlparse import tokens as T @@ -30,18 +31,18 @@ class SQLParseTest(TestCaseBase): self.assertEqual(str(stmts[1]), sql2) def test_newlines(self): - sql = u'select\n*from foo;' + sql = 'select\n*from foo;' p = sqlparse.parse(sql)[0] - self.assertEqual(unicode(p), sql) - sql = u'select\r\n*from foo' + self.assertEqual(compat.text_type(p), sql) + sql = 'select\r\n*from foo' p = sqlparse.parse(sql)[0] - self.assertEqual(unicode(p), sql) - sql = u'select\r*from foo' + self.assertEqual(compat.text_type(p), sql) + sql = 'select\r*from foo' p = sqlparse.parse(sql)[0] - self.assertEqual(unicode(p), sql) - sql = u'select\r\n*from foo\n' + self.assertEqual(compat.text_type(p), sql) + sql = 'select\r\n*from foo\n' p = sqlparse.parse(sql)[0] - self.assertEqual(unicode(p), sql) + self.assertEqual(compat.text_type(p), sql) def test_within(self): sql = 'foo(col1, col2)' diff --git a/tests/test_pipeline.py b/tests/test_pipeline.py index 3442a5b..0d0f360 100644 --- a/tests/test_pipeline.py +++ b/tests/test_pipeline.py @@ -30,7 +30,7 @@ class Test(unittest.TestCase): FROM links WHERE parent_dir == :parent_dir AND name == :name LIMIT 1""" - self.assertEqual([u'child_entry', u'inode', u'creation'], + self.assertEqual(['child_entry', 'inode', 'creation'], self.pipe(sql)) def test_3(self): @@ -64,7 +64,7 @@ WHERE dir_entries.inode == :inode GROUP BY dir_entries.inode LIMIT 1""" - self.assertEqual([u'st_dev', u'st_uid', u'st_gid', u'st_mode', - u'st_ino', u'st_nlink', u'st_ctime', - u'st_atime', u'st_mtime', u'st_size', u'size'], + self.assertEqual(['st_dev', 'st_uid', 'st_gid', 'st_mode', + 'st_ino', 'st_nlink', 'st_ctime', + 'st_atime', 'st_mtime', 'st_size', 'size'], self.pipe(sql)) diff --git a/tests/test_regressions.py b/tests/test_regressions.py index ea8cd56..6bd198b 100644 --- a/tests/test_regressions.py +++ b/tests/test_regressions.py @@ -5,6 +5,7 @@ import sys from tests.utils import TestCaseBase, load_file import sqlparse +from sqlparse import compat from sqlparse import sql from sqlparse import tokens as T @@ -84,7 +85,7 @@ class RegressionTests(TestCaseBase): self.assertEqual(len(p.tokens), 7) self.assertEqual(p.tokens[2].__class__, sql.IdentifierList) self.assertEqual(p.tokens[-1].__class__, sql.Identifier) - self.assertEqual(p.tokens[-1].get_name(), u'foo') + self.assertEqual(p.tokens[-1].get_name(), 'foo') sp = p.tokens[-1].tokens[0] self.assertEqual(sp.tokens[3].__class__, sql.IdentifierList) # make sure that formatting works as expected @@ -164,7 +165,8 @@ ALTER TABLE..... ;""" def test_comment_encoding_when_reindent(): # There was an UnicodeEncodeError in the reindent filter that # casted every comment followed by a keyword to str. - sql = u'select foo -- Comment containing Ümläuts\nfrom bar' + sql = compat.text_type(compat.u( + 'select foo -- Comment containing Ümläuts\nfrom bar')) formatted = sqlparse.format(sql, reindent=True) assert formatted == sql @@ -194,7 +196,9 @@ def test_format_accepts_encoding(): # issue20 sql = load_file('test_cp1251.sql', 'cp1251') formatted = sqlparse.format(sql, reindent=True, encoding='cp1251') if sys.version_info < (3,): - tformatted = u'insert into foo\nvalues (1); -- Песня про надежду\n' + tformatted = compat.text_type( + 'insert into foo\nvalues (1); -- Песня про надежду\n', + encoding='utf-8') else: tformatted = 'insert into foo\nvalues (1); -- Песня про надежду\n' assert formatted == tformatted diff --git a/tests/test_split.py b/tests/test_split.py index 54e8d04..8b22aad 100644 --- a/tests/test_split.py +++ b/tests/test_split.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- # Tests splitting functions. - -import unittest +import types from tests.utils import load_file, TestCaseBase import sqlparse +from sqlparse import compat class SQLSplitTest(TestCaseBase): @@ -19,8 +19,8 @@ class SQLSplitTest(TestCaseBase): sql2 = 'select * from foo where bar = \'foo;bar\';' stmts = sqlparse.parse(''.join([self._sql1, sql2])) self.assertEqual(len(stmts), 2) - self.ndiffAssertEqual(unicode(stmts[0]), self._sql1) - self.ndiffAssertEqual(unicode(stmts[1]), sql2) + self.ndiffAssertEqual(compat.text_type(stmts[0]), self._sql1) + self.ndiffAssertEqual(compat.text_type(stmts[1]), sql2) def test_split_backslash(self): stmts = sqlparse.parse(r"select '\\'; select '\''; select '\\\'';") @@ -30,31 +30,31 @@ class SQLSplitTest(TestCaseBase): sql = load_file('function.sql') stmts = sqlparse.parse(sql) self.assertEqual(len(stmts), 1) - self.ndiffAssertEqual(unicode(stmts[0]), sql) + self.ndiffAssertEqual(compat.text_type(stmts[0]), sql) def test_create_function_psql(self): sql = load_file('function_psql.sql') stmts = sqlparse.parse(sql) self.assertEqual(len(stmts), 1) - self.ndiffAssertEqual(unicode(stmts[0]), sql) + self.ndiffAssertEqual(compat.text_type(stmts[0]), sql) def test_create_function_psql3(self): sql = load_file('function_psql3.sql') stmts = sqlparse.parse(sql) self.assertEqual(len(stmts), 1) - self.ndiffAssertEqual(unicode(stmts[0]), sql) + self.ndiffAssertEqual(compat.text_type(stmts[0]), sql) def test_create_function_psql2(self): sql = load_file('function_psql2.sql') stmts = sqlparse.parse(sql) self.assertEqual(len(stmts), 1) - self.ndiffAssertEqual(unicode(stmts[0]), sql) + self.ndiffAssertEqual(compat.text_type(stmts[0]), sql) def test_dashcomments(self): sql = load_file('dashcomment.sql') stmts = sqlparse.parse(sql) self.assertEqual(len(stmts), 3) - self.ndiffAssertEqual(''.join(unicode(q) for q in stmts), sql) + self.ndiffAssertEqual(''.join(compat.text_type(q) for q in stmts), sql) def test_dashcomments_eol(self): stmts = sqlparse.parse('select foo; -- comment\n') @@ -70,36 +70,38 @@ class SQLSplitTest(TestCaseBase): sql = load_file('begintag.sql') stmts = sqlparse.parse(sql) self.assertEqual(len(stmts), 3) - self.ndiffAssertEqual(''.join(unicode(q) for q in stmts), sql) + self.ndiffAssertEqual(''.join(compat.text_type(q) for q in stmts), sql) def test_begintag_2(self): sql = load_file('begintag_2.sql') stmts = sqlparse.parse(sql) self.assertEqual(len(stmts), 1) - self.ndiffAssertEqual(''.join(unicode(q) for q in stmts), sql) + self.ndiffAssertEqual(''.join(compat.text_type(q) for q in stmts), sql) def test_dropif(self): sql = 'DROP TABLE IF EXISTS FOO;\n\nSELECT * FROM BAR;' stmts = sqlparse.parse(sql) self.assertEqual(len(stmts), 2) - self.ndiffAssertEqual(''.join(unicode(q) for q in stmts), sql) + self.ndiffAssertEqual(''.join(compat.text_type(q) for q in stmts), sql) def test_comment_with_umlaut(self): - sql = (u'select * from foo;\n' - u'-- Testing an umlaut: ä\n' - u'select * from bar;') + sql = (compat.u( + 'select * from foo;\n' + '-- Testing an umlaut: ä\n' + 'select * from bar;')) stmts = sqlparse.parse(sql) self.assertEqual(len(stmts), 2) - self.ndiffAssertEqual(''.join(unicode(q) for q in stmts), sql) + self.ndiffAssertEqual(''.join(compat.text_type(q) for q in stmts), sql) def test_comment_end_of_line(self): sql = ('select * from foo; -- foo\n' 'select * from bar;') stmts = sqlparse.parse(sql) self.assertEqual(len(stmts), 2) - self.ndiffAssertEqual(''.join(unicode(q) for q in stmts), sql) + self.ndiffAssertEqual(''.join(compat.text_type(q) for q in stmts), sql) # make sure the comment belongs to first query - self.ndiffAssertEqual(unicode(stmts[0]), 'select * from foo; -- foo\n') + self.ndiffAssertEqual( + compat.text_type(stmts[0]), 'select * from foo; -- foo\n') def test_casewhen(self): sql = ('SELECT case when val = 1 then 2 else null end as foo;\n' @@ -122,19 +124,15 @@ class SQLSplitTest(TestCaseBase): self.assertEqual(len(stmts), 2) def test_split_stream(self): - import types - from cStringIO import StringIO - - stream = StringIO("SELECT 1; SELECT 2;") + stream = compat.StringIO("SELECT 1; SELECT 2;") stmts = sqlparse.parsestream(stream) self.assertEqual(type(stmts), types.GeneratorType) self.assertEqual(len(list(stmts)), 2) def test_encoding_parsestream(self): - from cStringIO import StringIO - stream = StringIO("SELECT 1; SELECT 2;") + stream = compat.StringIO("SELECT 1; SELECT 2;") stmts = list(sqlparse.parsestream(stream)) - self.assertEqual(type(stmts[0].tokens[0].value), unicode) + self.assertEqual(type(stmts[0].tokens[0].value), compat.text_type) def test_split_simple(): diff --git a/tests/test_tokenize.py b/tests/test_tokenize.py index 0b23fa8..a38e50f 100644 --- a/tests/test_tokenize.py +++ b/tests/test_tokenize.py @@ -1,15 +1,15 @@ # -*- coding: utf-8 -*- -import sys import types import unittest import pytest import sqlparse +from sqlparse import compat from sqlparse import lexer from sqlparse import sql -from sqlparse.tokens import * +from sqlparse.tokens import * # noqa class TestTokenize(unittest.TestCase): @@ -21,14 +21,14 @@ class TestTokenize(unittest.TestCase): tokens = list(stream) self.assertEqual(len(tokens), 8) self.assertEqual(len(tokens[0]), 2) - self.assertEqual(tokens[0], (Keyword.DML, u'select')) - self.assertEqual(tokens[-1], (Punctuation, u';')) + self.assertEqual(tokens[0], (Keyword.DML, 'select')) + self.assertEqual(tokens[-1], (Punctuation, ';')) def test_backticks(self): s = '`foo`.`bar`' tokens = list(lexer.tokenize(s)) self.assertEqual(len(tokens), 3) - self.assertEqual(tokens[0], (Name, u'`foo`')) + self.assertEqual(tokens[0], (Name, '`foo`')) def test_linebreaks(self): # issue1 s = 'foo\nbar\n' @@ -50,7 +50,7 @@ class TestTokenize(unittest.TestCase): self.assertEqual(len(tokens), 3) self.assertEqual(tokens[0][0], Keyword.DDL) self.assertEqual(tokens[2][0], Name) - self.assertEqual(tokens[2][1], u'created_foo') + self.assertEqual(tokens[2][1], 'created_foo') s = "enddate" tokens = list(lexer.tokenize(s)) self.assertEqual(len(tokens), 1) @@ -72,7 +72,7 @@ class TestTokenize(unittest.TestCase): self.assertEqual(tokens[2][0], Number.Integer) self.assertEqual(tokens[2][1], '-1') - # Somehow this test fails on Python 3.2 + # Somehow this test fails on Python 3 @pytest.mark.skipif('sys.version_info >= (3,0)') def test_tab_expansion(self): s = "\t" @@ -132,10 +132,9 @@ class TestTokenList(unittest.TestCase): class TestStream(unittest.TestCase): - def test_simple(self): - from cStringIO import StringIO - stream = StringIO("SELECT 1; SELECT 2;") + def test_simple(self): + stream = compat.StringIO("SELECT 1; SELECT 2;") lex = lexer.Lexer() tokens = lex.get_tokens(stream) @@ -152,9 +151,7 @@ class TestStream(unittest.TestCase): self.assertEqual(len(tokens), 9) def test_error(self): - from cStringIO import StringIO - - stream = StringIO("FOOBAR{") + stream = compat.StringIO("FOOBAR{") lex = lexer.Lexer() lex.bufsize = 4 diff --git a/tests/utils.py b/tests/utils.py index 9eb46bf..92d7a4b 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -6,8 +6,8 @@ import codecs import difflib import os import unittest -from StringIO import StringIO +from sqlparse import compat import sqlparse.utils NL = '\n' @@ -31,16 +31,16 @@ class TestCaseBase(unittest.TestCase): def ndiffAssertEqual(self, first, second): """Like failUnlessEqual except use ndiff for readable output.""" if first != second: - sfirst = unicode(first) - ssecond = unicode(second) + sfirst = compat.text_type(first) + ssecond = compat.text_type(second) # Using the built-in .splitlines() method here will cause incorrect # results when splitting statements that have quoted CR/CR+LF # characters. sfirst = sqlparse.utils.split_unquoted_newlines(sfirst) ssecond = sqlparse.utils.split_unquoted_newlines(ssecond) diff = difflib.ndiff(sfirst, ssecond) - fp = StringIO() + fp = compat.StringIO() fp.write(NL) fp.write(NL.join(diff)) - print fp.getvalue() - raise self.failureException, fp.getvalue() + print(fp.getvalue()) + raise self.failureException(fp.getvalue()) @@ -1,5 +1,8 @@ [tox] -envlist=py26,py27,py32,py33,py34,pypy +envlist=codechecks,py27,py32,py33,py34,pypy + +[flake8] +ignore = E129 [testenv] deps= @@ -9,29 +12,8 @@ commands= sqlformat --version # Sanity check. py.test --cov=sqlparse/ tests -[testenv:py32] -changedir={envdir} -commands= - sqlformat --version # Sanity check. - rm -rf tests/ - cp -r {toxinidir}/tests/ tests/ - 2to3 -w --no-diffs -n tests/ - py.test --cov={envdir}/lib/python3.2/site-packages/sqlparse/ tests - -[testenv:py33] -changedir={envdir} -commands= - sqlformat --version # Sanity check. - rm -rf tests/ - cp -r {toxinidir}/tests/ tests/ - 2to3 -w --no-diffs -n tests/ - py.test --cov={envdir}/lib/python3.3/site-packages/sqlparse/ tests - -[testenv:py34] -changedir={envdir} +[testenv:codechecks] +deps= + flake8 commands= - sqlformat --version # Sanity check. - rm -rf tests/ - cp -r {toxinidir}/tests/ tests/ - 2to3 -w --no-diffs -n tests/ - py.test --cov={envdir}/lib/python3.4/site-packages/sqlparse/ tests + flake8 sqlparse tests |