summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndi Albrecht <albrecht.andi@gmail.com>2011-08-09 16:04:40 +0200
committerAndi Albrecht <albrecht.andi@gmail.com>2011-08-09 16:04:40 +0200
commit5d015084e5bf640f41c8d93c89d2ff70c0967c2b (patch)
treecfa3b58e11e49a6266a2a05ab9e477c5d62c5024
parenta8b46d3b96ead22263de782b36090f2f1bc7c15d (diff)
parent530da46905753a1e950bcc7e736d72a7309033c6 (diff)
downloadsqlparse-5d015084e5bf640f41c8d93c89d2ff70c0967c2b.tar.gz
merge
-rw-r--r--.hgignore2
-rw-r--r--.hgtags1
-rw-r--r--CHANGES9
-rw-r--r--extras/appengine/Makefile45
-rw-r--r--extras/appengine/README25
-rw-r--r--extras/appengine/__init__.py0
-rw-r--r--extras/appengine/app.yaml13
-rw-r--r--extras/appengine/appengine_config.py6
-rwxr-xr-xextras/appengine/bootstrap.py82
-rw-r--r--extras/appengine/config.py7
-rw-r--r--extras/appengine/cron.yaml4
-rw-r--r--extras/appengine/index.yaml7
-rw-r--r--extras/appengine/main.py175
-rw-r--r--extras/appengine/settings.py38
-rw-r--r--extras/appengine/sqlformat/__init__.py24
-rw-r--r--extras/appengine/sqlformat/legacy.py158
-rw-r--r--extras/appengine/sqlformat/templates/about.html (renamed from extras/appengine/templates/about.html)0
-rw-r--r--extras/appengine/sqlformat/templates/api.html (renamed from extras/appengine/templates/api.html)0
-rw-r--r--extras/appengine/sqlformat/templates/index.html (renamed from extras/appengine/templates/index.html)51
-rw-r--r--extras/appengine/sqlformat/templates/master.html (renamed from extras/appengine/templates/master.html)11
-rw-r--r--extras/appengine/sqlformat/templates/python-client-example.html (renamed from extras/appengine/templates/python-client-example.html)0
-rw-r--r--extras/appengine/sqlformat/templates/source.html (renamed from extras/appengine/templates/source.html)0
-rw-r--r--extras/appengine/sqlformat/urls.py11
-rw-r--r--extras/appengine/sqlformat/views.py225
-rw-r--r--extras/appengine/static/blank.gifbin64 -> 0 bytes
-rw-r--r--extras/appengine/static/canvas.html114
-rw-r--r--extras/appengine/static/favicon.icobin0 -> 4162 bytes
-rw-r--r--extras/appengine/static/img_loading.gifbin1348 -> 0 bytes
-rw-r--r--extras/appengine/static/jquery.textarearesizer.compressed.js1
-rw-r--r--extras/appengine/static/resize-grip.pngbin167 -> 0 bytes
-rw-r--r--extras/appengine/static/rpc_relay.html1
-rw-r--r--extras/appengine/static/script.js7
-rw-r--r--extras/appengine/static/sqlformat_client_example.py1
-rw-r--r--extras/appengine/templates/404.html12
-rw-r--r--extras/appengine/templates/500.html19
-rw-r--r--extras/py3k/Makefile9
-rw-r--r--sqlparse/__init__.py2
-rw-r--r--sqlparse/lexer.py3
-rw-r--r--tests/test_parse.py7
-rw-r--r--tests/utils.py3
40 files changed, 400 insertions, 673 deletions
diff --git a/.hgignore b/.hgignore
index a28d991..7444a39 100644
--- a/.hgignore
+++ b/.hgignore
@@ -5,7 +5,7 @@ dist
MANIFEST
.coverage
extras/appengine/sqlparse
-extras/appengine/pygments
+extras/appengine/lib/
extras/py3k/sqlparse
extras/py3k/tests
extras/py3k/sqlparse.diff
diff --git a/.hgtags b/.hgtags
index fea6495..1cdbba6 100644
--- a/.hgtags
+++ b/.hgtags
@@ -1,3 +1,4 @@
49f461d2899eb3175ba7d935337e5938403372d4 0.1.0
294617d5c43131aacd11ddccc9ccb238a41bf563 0.1.1
079b282ad3ee0d620128ee64e2bba5791e733b62 0.1.2
+ac9ea581527a4540d46d766c07b23a88dd04704c 0.1.3
diff --git a/CHANGES b/CHANGES
index 203d98c..2b3d88a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,5 @@
-In Development
---------------
+Release 0.1.3 (Jul 29, 2011)
+----------------------------
Bug Fixes
* Improve parsing of floats (thanks to Kris).
@@ -16,6 +16,11 @@ Bug Fixes
* Improve parsing of stand-alone comments (issue26).
* Detection of placeholders in paramterized queries (issue22,
reported by Glyph Lefkowitz).
+ * Add parsing of MS Access column names with braces (issue27,
+ reported by frankz...@gmail.com).
+
+Other
+ * Replace Django by Flask in App Engine frontend (issue11).
Release 0.1.2 (Nov 23, 2010)
diff --git a/extras/appengine/Makefile b/extras/appengine/Makefile
deleted file mode 100644
index 31393c1..0000000
--- a/extras/appengine/Makefile
+++ /dev/null
@@ -1,45 +0,0 @@
-# Makefile to simplify some common AppEngine actions.
-# Use 'make help' for a list of commands.
-
-PYTHON=`which python2.5`
-DEV_APPSERVER=$(PYTHON) `which dev_appserver.py`
-APPCFG=$(PYTHON) `which appcfg.py`
-PORT=8080
-
-
-default: help
-
-help:
- @echo "Available commands:"
- @sed -n '/^[a-zA-Z0-9_.]*:/s/:.*//p' <Makefile | sort
-
-serve:
- $(DEV_APPSERVER) --port=$(PORT) .
-
-serve_remote:
- $(DEV_APPSERVER) --port=$(PORT) --address 0.0.0.0 .
-
-serve_email:
- $(DEV_APPSERVER) --port=$(PORT) --enable_sendmail .
-
-serve_remote_email:
- $(DEV_APPSERVER) --port=$(PORT) --enable_sendmail --address 0.0.0.0 .
-
-update:
- $(APPCFG) update .
-
-upload: update
-
-update_indexes:
- $(APPCFG) update_indexes .
-
-vacuum_indexes:
- $(APPCFG) vacuum_indexes .
-
-all: pygments sqlparse
-
-pygments:
- ln -s `python -c "import pygments,os; print os.path.dirname(pygments.__file__)"` .
-
-sqlparse:
- ln -s ../../sqlparse .
diff --git a/extras/appengine/README b/extras/appengine/README
index 4762faa..04d32b2 100644
--- a/extras/appengine/README
+++ b/extras/appengine/README
@@ -1,22 +1,3 @@
-gae-sqlformat - An SQL formatting tool runnging on App Engine
-=============================================================
-
-
-To set up this application run
-
- make all
-
-This command fetches Django from Subversion and symlinks Pygments
-and sqlparse. Note: You'll need Pygments installed somewhere in your
-PYTHONPATH.
-
-For a manual setup have a look at the Makefile ;-)
-
-
-To run the development server run
-
- make serve
-
-
-Homepage: http://sqlformat.appspot.com
-
+- 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/__init__.py b/extras/appengine/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/extras/appengine/__init__.py
+++ /dev/null
diff --git a/extras/appengine/app.yaml b/extras/appengine/app.yaml
index 14aab5e..3afa891 100644
--- a/extras/appengine/app.yaml
+++ b/extras/appengine/app.yaml
@@ -1,5 +1,5 @@
application: sqlformat
-version: 2
+version: dev
runtime: python
api_version: 1
@@ -23,8 +23,15 @@ handlers:
- url: /static
static_dir: static
+- url: /_ereporter.*
+ script: $PYTHON_LIB/google/appengine/ext/ereporter/report_generator.py
+ login: admin
+
- url: .*
script: main.py
-derived_file_type:
-- python_precompiled \ No newline at end of file
+builtins:
+- appstats: on
+
+inbound_services:
+- warmup
diff --git a/extras/appengine/appengine_config.py b/extras/appengine/appengine_config.py
new file mode 100644
index 0000000..8ed3ee5
--- /dev/null
+++ b/extras/appengine/appengine_config.py
@@ -0,0 +1,6 @@
+
+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
new file mode 100755
index 0000000..debc2bf
--- /dev/null
+++ b/extras/appengine/bootstrap.py
@@ -0,0 +1,82 @@
+#!/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
new file mode 100644
index 0000000..1599c00
--- /dev/null
+++ b/extras/appengine/config.py
@@ -0,0 +1,7 @@
+# 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
new file mode 100644
index 0000000..a7fefce
--- /dev/null
+++ b/extras/appengine/cron.yaml
@@ -0,0 +1,4 @@
+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/index.yaml b/extras/appengine/index.yaml
index e69de29..7071349 100644
--- a/extras/appengine/index.yaml
+++ b/extras/appengine/index.yaml
@@ -0,0 +1,7 @@
+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
index 5014cb9..4cabba8 100644
--- a/extras/appengine/main.py
+++ b/extras/appengine/main.py
@@ -1,140 +1,41 @@
-# Copyright 2008 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Main program for Rietveld.
-
-This is also a template for running a Django app under Google App
-Engine, especially when using a newer version of Django than provided
-in the App Engine standard library.
-
-The site-specific code is all in other files: urls.py, models.py,
-views.py, settings.py.
-"""
-
-# Standard Python imports.
+# SQLFormat's main script, dead simple :)
+
import os
import sys
+
+from google.appengine.ext.webapp.util import run_wsgi_app
+
+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
-import traceback
-
-
-# Log a message each time this module get loaded.
-logging.info('Loading %s, app version = %s',
- __name__, os.getenv('CURRENT_VERSION_ID'))
-
-os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
-
-from google.appengine.dist import use_library
-use_library('django', '1.2')
-
-# Fail early if we can't import Django. Log identifying information.
-import django
-logging.info('django.__file__ = %r, django.VERSION = %r',
- django.__file__, django.VERSION)
-assert django.VERSION[0] >= 1, "This Django version is too old"
-
-# AppEngine imports.
-from google.appengine.ext.webapp import util
-from google.appengine.api import mail
-
-
-# Helper to enter the debugger. This passes in __stdin__ and
-# __stdout__, because stdin and stdout are connected to the request
-# and response streams. You must import this from __main__ to use it.
-# (I tried to make it universally available via __builtin__, but that
-# doesn't seem to work for some reason.)
-def BREAKPOINT():
- import pdb
- p = pdb.Pdb(None, sys.__stdin__, sys.__stdout__)
- p.set_trace()
-
-
-# Custom Django configuration.
-from django.conf import settings
-settings._target = None
-
-# Import various parts of Django.
-import django.core.handlers.wsgi
-import django.core.signals
-import django.db
-import django.dispatch.dispatcher
-import django.forms
-
-# Work-around to avoid warning about django.newforms in djangoforms.
-django.newforms = django.forms
-
-
-def log_exception(*args, **kwds):
- """Django signal handler to log an exception."""
- excinfo = sys.exc_info()
- cls, err = excinfo[:2]
- subject = 'Exception in request: %s: %s' % (cls.__name__, err)
- logging.exception(subject)
- try:
- repr_request = repr(kwds.get('request', 'Request not available.'))
- except:
- repr_request = 'Request repr() not available.'
- msg = ('Application: %s\nVersion: %s\n\n%s\n\n%s'
- % (os.getenv('APPLICATION_ID'), os.getenv('CURRENT_VERSION_ID'),
- '\n'.join(traceback.format_exception(*excinfo)),
- repr_request))
- mail.send_mail_to_admins('albrecht.andi@googlemail.com',
- '[%s] %s' % (os.getenv('APPLICATION_ID'), subject),
- msg)
-
-
-# Log all exceptions detected by Django.
-django.core.signals.got_request_exception.connect(log_exception)
-
-# Unregister Django's default rollback event handler.
-#django.core.signals.got_request_exception.disconnect(
-# django.db._rollback_on_exception)
-
-
-def real_main():
- """Main program."""
- # Create a Django application for WSGI.
- application = django.core.handlers.wsgi.WSGIHandler()
- # Run the WSGI CGI handler with that application.
- util.run_wsgi_app(application)
-
-
-def profile_main():
- """Main program for profiling."""
- import cProfile
- import pstats
- import StringIO
-
- prof = cProfile.Profile()
- prof = prof.runctx('real_main()', globals(), locals())
- stream = StringIO.StringIO()
- stats = pstats.Stats(prof, stream=stream)
- # stats.strip_dirs() # Don't; too many modules are named __init__.py.
- stats.sort_stats('time') # 'time', 'cumulative' or 'calls'
- stats.print_stats() # Optional arg: how many to print
- # The rest is optional.
- # stats.print_callees()
- # stats.print_callers()
- print '\n<hr>'
- print '<h1>Profile</h1>'
- print '<pre>'
- print stream.getvalue()[:1000000]
- print '</pre>'
-
-# Set this to profile_main to enable profiling.
-main = real_main
-
-
-if __name__ == '__main__':
- main()
+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)
+
+run_wsgi_app(app)
diff --git a/extras/appengine/settings.py b/extras/appengine/settings.py
deleted file mode 100644
index 74af42c..0000000
--- a/extras/appengine/settings.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright 2008 Google Inc.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Minimal Django settings."""
-
-import os
-
-APPEND_SLASH = False
-DEBUG = os.environ['SERVER_SOFTWARE'].startswith('Dev')
-DEBUG=False
-INSTALLED_APPS = (
- 'sqlformat',
-)
-MIDDLEWARE_CLASSES = (
- 'django.middleware.common.CommonMiddleware',
- 'django.middleware.http.ConditionalGetMiddleware',
-# 'codereview.middleware.AddUserToRequestMiddleware',
-)
-ROOT_URLCONF = 'sqlformat.urls'
-TEMPLATE_CONTEXT_PROCESSORS = ()
-TEMPLATE_DEBUG = DEBUG
-TEMPLATE_DIRS = (
- os.path.join(os.path.dirname(__file__), 'templates'),
- )
-TEMPLATE_LOADERS = (
- 'django.template.loaders.filesystem.load_template_source',
- )
diff --git a/extras/appengine/sqlformat/__init__.py b/extras/appengine/sqlformat/__init__.py
index e69de29..db9c132 100644
--- a/extras/appengine/sqlformat/__init__.py
+++ b/extras/appengine/sqlformat/__init__.py
@@ -0,0 +1,24 @@
+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')
+
+@app.route('/fail')
+def fail():
+ # test URL for failure handling
+ raise AssertionError('You shouldn\'t be here!')
+
+
+# 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
new file mode 100644
index 0000000..6d3aaf8
--- /dev/null
+++ b/extras/appengine/sqlformat/legacy.py
@@ -0,0 +1,158 @@
+"""Legacy URLs."""
+
+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'])
+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 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/templates/about.html b/extras/appengine/sqlformat/templates/about.html
index 2d4e03e..2d4e03e 100644
--- a/extras/appengine/templates/about.html
+++ b/extras/appengine/sqlformat/templates/about.html
diff --git a/extras/appengine/templates/api.html b/extras/appengine/sqlformat/templates/api.html
index 79bf118..79bf118 100644
--- a/extras/appengine/templates/api.html
+++ b/extras/appengine/sqlformat/templates/api.html
diff --git a/extras/appengine/templates/index.html b/extras/appengine/sqlformat/templates/index.html
index 3b4ea6f..22d6fdb 100644
--- a/extras/appengine/templates/index.html
+++ b/extras/appengine/sqlformat/templates/index.html
@@ -8,15 +8,13 @@
<form method="post" action="" id="form_options" enctype="multipart/form-data">
<div id="input">
- {% if form.non_field_errors %}{{form.non_field_errors}}{% endif %}
<div>
<strong>Type your SQL here:</strong><br />
- {{form.data}}
- {% if form.data.errors %}{{form.data.errors}}{% endif %}
+ <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>
- {{form.datafile}}
+ <input type="file" name="datafile" id="id_datafile" />
</div>
<div id="examples" style="margin-top: .5em;"></div>
<div id="actions" style="margin-top: .5em;">
@@ -50,51 +48,60 @@
<h1 class="skip">Options</h1>
<fieldset><legend id="general"><strong>General Options</strong></legend>
<div id="general_content" class="content">
- {{form.remove_comments}}
- <label for="id_remove_comments">{{form.remove_comments.label}}</label>
+ <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 />
- {{form.highlight}}
- <label for="id_highlight">{{form.highlight.label}}</label>
- {% if form.highlight.errors %}
- <ul class="errors">{{form.highlight.errors</ul>
- {% endif %}
+ <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 &amp; Identifiers</strong></legend>
- <div>
- {{form.keyword_case.label}}: {{form.keyword_case}}
+ <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>
- {{form.identifier_case.label}}: {{form.identifier_case}}
+ 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 &amp; Margins</strong>
</legend>
<div id="indent_content" class="content">
<label for="id_n_indents">Indentation: </label>
- {{form.n_indents}} {{form.n_indents.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>
-<!--
- <label for="id_right_margin">Right margin: </label>
- {{form.right_margin}} {{form.right_margin.label}}
- <div class="help">Empty field means don't mind right margin.</div>
--->
</div>
</fieldset>
<fieldset><legend id="output"><strong>Output Format</strong></legend>
<label for="id_output_format">Language: </label>
- {{form.output_format}}
+ <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>
diff --git a/extras/appengine/templates/master.html b/extras/appengine/sqlformat/templates/master.html
index ea1c662..88a9d36 100644
--- a/extras/appengine/templates/master.html
+++ b/extras/appengine/sqlformat/templates/master.html
@@ -1,8 +1,8 @@
<html>
<head>
- <title>{% block title %}SQLFormat - Online SQL Formatting Service{% endblock %}</title>
- <meta name="keywords" content="SQL, format, parse, python, beautify, pretty, online, formatting" />
- <meta name="description" content="Easy to use web service to format SQL statements online." />
+ <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>
@@ -10,8 +10,6 @@
google.load("jquery", "1.2.6");
</script>
<script src="/static/hotkeys.js"></script>
- <script type="text/javascript"
- src="/static/jquery.textarearesizer.compressed.js"></script>
<script src="/static/script.js"></script>
</head>
<body>
@@ -66,7 +64,7 @@
<div id="footer">
<div id="footer-inner">
<div style="float: left; font-size: .85em;">
- <div>&copy; 2009 Andi Albrecht&nbsp;&nbsp;
+ <div>&copy; 2011 Andi Albrecht&nbsp;&nbsp;
<code>&lt;albrecht dot andi gmail&gt;</code>
</div>
<div>
@@ -99,6 +97,7 @@
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/templates/python-client-example.html b/extras/appengine/sqlformat/templates/python-client-example.html
index 68bf820..68bf820 100644
--- a/extras/appengine/templates/python-client-example.html
+++ b/extras/appengine/sqlformat/templates/python-client-example.html
diff --git a/extras/appengine/templates/source.html b/extras/appengine/sqlformat/templates/source.html
index a0ed89d..a0ed89d 100644
--- a/extras/appengine/templates/source.html
+++ b/extras/appengine/sqlformat/templates/source.html
diff --git a/extras/appengine/sqlformat/urls.py b/extras/appengine/sqlformat/urls.py
deleted file mode 100644
index c83290e..0000000
--- a/extras/appengine/sqlformat/urls.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from django.conf.urls.defaults import *
-
-urlpatterns = patterns(
- 'sqlformat.views',
- (r'^$', 'index'),
- (r'^source/$', 'source'),
- (r'^about/$', 'about'),
- (r'^api/$', 'api'),
- (r'^format/$', 'format'),
- (r'^load_example', 'load_example'),
-)
diff --git a/extras/appengine/sqlformat/views.py b/extras/appengine/sqlformat/views.py
deleted file mode 100644
index 806496f..0000000
--- a/extras/appengine/sqlformat/views.py
+++ /dev/null
@@ -1,225 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import logging
-import md5
-import os
-import sys
-import time
-
-from django import forms
-from django.http import HttpResponse
-from django.template import loader
-from django.utils import simplejson as json
-
-from google.appengine.api import users
-
-from pygments import highlight
-from pygments.formatters import HtmlFormatter
-from pygments.lexers import SqlLexer, PythonLexer, PhpLexer
-
-import sqlparse
-
-
-INITIAL_SQL = "select * from foo join bar on val1 = val2 where id = 123;"
-EXAMPLES_DIR = os.path.join(os.path.dirname(__file__), '../examples')
-
-
-# Custom render_to_response() function to avoid loading django.shortcuts
-# since django.shortcuts depends on a lot of Django modules we don't need
-# here, e.g. lots of modules from django.db.
-def render_to_response(template, params=None):
- if params is None:
- params = {}
- return HttpResponse(loader.render_to_string(template, params))
-
-
-def _get_user_image(user):
- if user is None:
- return None
- digest = md5.new(user.email().lower()).hexdigest()
- if os.environ['SERVER_SOFTWARE'].startswith('Dev'):
- host = 'localhost%3A8080'
- else:
- host = 'sqlformat.appspot.com'
- default = 'http%3A%2F%2F'+host+'%2Fstatic%2Fblank.gif'
- return 'http://gravatar.com/avatar/%s?s=32&d=%s' % (digest, default)
-
-def _get_examples():
- fnames = os.listdir(EXAMPLES_DIR)
- fnames.sort()
- return fnames
-
-
-class FormOptions(forms.Form):
- data = forms.CharField(widget=forms.Textarea({'class': 'resizable'}),
- initial=INITIAL_SQL, required=False)
- datafile = forms.FileField(required=False)
- highlight = forms.BooleanField(initial=True, required=False,
- widget=forms.CheckboxInput(),
- label='Enable syntax highlighting')
- remove_comments = forms.BooleanField(initial=False, required=False,
- widget=forms.CheckboxInput(),
- label='Remove comments')
- keyword_case = forms.CharField(
- widget=forms.Select(choices=(('', 'Unchanged'),
- ('lower', 'Lower case'),
- ('upper', 'Upper case'),
- ('capitalize', 'Capitalize'))),
- required=False, initial='upper', label='Keywords')
- identifier_case = forms.CharField(
- widget=forms.Select(choices=(('', 'Unchanged'),
- ('lower', 'Lower case'),
- ('upper', 'Upper case'),
- ('capitalize', 'Capitalize'))),
- required=False, initial='', label='Identifiers')
- n_indents = forms.IntegerField(min_value=1, max_value=30,
- initial=2, required=False,
- label='spaces',
- widget=forms.TextInput({'size': 2,
- 'maxlength': 2}))
-# right_margin = forms.IntegerField(min_value=10, max_value=500,
-# initial=60, required=False,
-# label='characters',
-# widget=forms.TextInput({'size': 3,
-# 'maxlength': 3}))
- output_format = forms.CharField(
- widget=forms.Select(choices=(('sql', 'SQL'),
- ('python', 'Python'),
- ('php', 'PHP'),
- )),
- required=False, initial='sql', label='Language')
-
- def clean(self):
- super(FormOptions, self).clean()
- data = self.cleaned_data.get('data')
- logging.info(self.files)
- if 'datafile' in self.files:
- self._datafile = self.files['datafile'].read()
- else:
- self._datafile = None
- if not data and not self._datafile:
- raise forms.ValidationError('Whoops, I need a file or text!')
- elif data and self._datafile:
- raise forms.ValidationError('Whoops, I need a file OR text!')
- return self.cleaned_data
-
- def clean_output_format(self):
- frmt = self.cleaned_data.get('output_format')
- if not frmt:
- frmt = 'sql'
- return frmt.lower()
-
- def get_data(self):
- data = self.cleaned_data.get('data')
- if self._datafile:
- return self._datafile
- else:
- return data
-
-
-def format_sql(form, format='html'):
- data = form.cleaned_data
- popts = {}
- sql = form.get_data()
- if data.get('remove_comments'):
- popts['strip_comments'] = True
- if data.get('keyword_case'):
- popts['keyword_case'] = data.get('keyword_case')
- if data.get('identifier_case'):
- popts['identifier_case'] = data.get('identifier_case')
- if data.get('n_indents', None) is not None:
- popts['reindent'] = True
- popts['indent_width'] = data.get('n_indents')
- if data.get('right_margin', None) is not None:
- popts['right_margin'] = data.get('right_margin')
- if data.get('output_format', None) is not None:
- popts['output_format'] = data.get('output_format')
- 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
-
-
-def index(request):
- output = None
- data = {}
- proc_time = None
- if request.method == 'POST':
- logging.debug(request.POST)
- form = FormOptions(request.POST, request.FILES)
- if form.is_valid():
- start = time.time()
- output = format_sql(form,
- format=request.POST.get('format', 'html'))
- proc_time = time.time()-start
- else:
- form = FormOptions()
- if request.POST.get('format', None) == 'json':
- logging.warning(form.errors)
- data['errors'] = str(form.errors)
- data['output'] = output
- logging.info('%r', proc_time)
- data['proc_time'] = '%.3f' % (proc_time or 0.0)
- data = json.dumps(data)
- return HttpResponse(data, content_type='text/x-json')
- elif request.POST.get('format', None) == 'text':
- if not form.is_valid():
- data = str(form.errors) # XXX convert to plain text
- else:
- data = output
- return HttpResponse(data, content_type='text/plain')
- return render_to_response('index.html',
- {'form': form, 'output': output,
- 'proc_time': proc_time and '%.3f' % proc_time or None,
- 'user': users.get_current_user(),
- 'login_url': users.create_login_url('/'),
- 'logout_url': users.create_logout_url('/'),
- 'userimg': _get_user_image(users.get_current_user()),
- 'examples': _get_examples()})
-
-
-def format(request):
- if request.method == 'POST':
- form = FormOptions(request.POST)
- if form.is_valid():
- try:
- response = format_sql(form, format='text')
- except:
- err = sys.exc_info()[1]
- response = 'ERROR: Parsing failed. %s' % str(err)
- else:
- response = 'ERROR: %s' % str(form.errors)
- else:
- response = 'POST request required'
- return HttpResponse(response, content_type='text/plain')
-
-def source(request):
- return render_to_response('source.html')
-
-def about(request):
- return render_to_response('about.html')
-
-def api(request):
- return render_to_response('api.html')
-
-def load_example(request):
- fname = request.POST.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 HttpResponse(data, content_type='text/x-json')
diff --git a/extras/appengine/static/blank.gif b/extras/appengine/static/blank.gif
deleted file mode 100644
index 3be2119..0000000
--- a/extras/appengine/static/blank.gif
+++ /dev/null
Binary files differ
diff --git a/extras/appengine/static/canvas.html b/extras/appengine/static/canvas.html
deleted file mode 100644
index ab642d0..0000000
--- a/extras/appengine/static/canvas.html
+++ /dev/null
@@ -1,114 +0,0 @@
-<html>
-
-<head>
-<style type="text/css">
- /*
- These styles are customizable.
- Provide .canvas-gadget (the div that holds the canvas mode gadget)
- at least 500px width so that the gadget has sufficient screen real estate
-*/
-body {
- margin: 0;
- font-family:arial, sans-serif;
- text-align:center;
-}
-.container {
- width:652px;
- margin:0 auto;
- text-align:left;
-}
-.fc-sign-in-header {
- text-align:left;
- font-size: 13px;
- padding:3px 10px;
- border-bottom:1px solid #000000;
-}
-.signin {
- text-align:left;
- float:right;
- font-size: 13px;
- height: 32px;
-}
-.go-back {
- text-align:left;
- margin:5px auto 15px auto;
-}
-.go-back a, .go-back a:visited {
- font-weight:bold;
-}
-.canvas-gadget {
- text-align:left;
- width:650px; /* ALLOW AT LEAST 500px WIDTH*/
- margin:10px auto 10px auto;
- border:1px solid #cccccc;
-}
-.site-header {
- margin-top: 10px;
-}
-.section-title {
- font-size: 2em;
-}
-.clear {
- clear:both;
- font-size:1px;
- height:1px;
- line-height:0;
- margin:0;
- padding:0;
-}
-</style>
-<script type="text/javascript" src="http://www.google.com/friendconnect/script/friendconnect.js"></script>
-</head>
-<body>
-<div class="container">
- <div class="fc-sign-in-header">
- <!--REQUIRED SO VISITORS CAN SIGN IN-->
- <div class="signin" id="gadget-signin"></div>
- <script type="text/javascript">
- var skin = {};
- skin['BORDER_COLOR'] = '#cccccc';
- skin['ENDCAP_BG_COLOR'] = '#e0ecff';
- skin['ENDCAP_TEXT_COLOR'] = '#333333';
- skin['ENDCAP_LINK_COLOR'] = '#0000cc';
- skin['ALTERNATE_BG_COLOR'] = '#ffffff';
- skin['CONTENT_BG_COLOR'] = '#ffffff';
- skin['CONTENT_LINK_COLOR'] = '#0000cc';
- skin['CONTENT_TEXT_COLOR'] = '#333333';
- skin['CONTENT_SECONDARY_LINK_COLOR'] = '#7777cc';
- skin['CONTENT_SECONDARY_TEXT_COLOR'] = '#666666';
- skin['CONTENT_HEADLINE_COLOR'] = '#333333';
- skin['ALIGNMENT'] = 'right';
- google.friendconnect.container.renderCanvasSignInGadget({'id': 'gadget-signin'}, skin);
- </script>
- <!--END REQUIRED-->
- <div class="clear"></div>
- </div>
-
- <div class="site-header"><span class="section-title">Site Name</span></div>
- <div class="go-back">
- <!--REQUIRED SO VISITORS CAN RETURN TO REFERRING PAGE-->
- <a href="javascript:google.friendconnect.container.goBackToSite();">
- &lsaquo;&lsaquo; Return home</a>
- <!--END REQUIRED-->
- </div>
- <!-- REQUIRED - THIS IS WHERE THE GADGET IS PRESENTED. ALLOW AT LEAST 500px WIDTH -->
- <div id="gadget-canvas" class="canvas-gadget"></div>
- <script type="text/javascript">
- var skin = {};
- skin['BORDER_COLOR'] = '#cccccc';
- skin['ENDCAP_BG_COLOR'] = '#e0ecff';
- skin['ENDCAP_TEXT_COLOR'] = '#333333';
- skin['ENDCAP_LINK_COLOR'] = '#0000cc';
- skin['ALTERNATE_BG_COLOR'] = '#ffffff';
- skin['CONTENT_BG_COLOR'] = '#ffffff';
- skin['CONTENT_LINK_COLOR'] = '#0000cc';
- skin['CONTENT_TEXT_COLOR'] = '#333333';
- skin['CONTENT_SECONDARY_LINK_COLOR'] = '#7777cc';
- skin['CONTENT_SECONDARY_TEXT_COLOR'] = '#666666';
- skin['CONTENT_HEADLINE_COLOR'] = '#333333';
- google.friendconnect.container.renderUrlCanvasGadget({'id': 'gadget-canvas'}, skin);
- </script>
- <!--END REQUIRED-->
-</div>
-</body>
-</html> \ No newline at end of file
diff --git a/extras/appengine/static/favicon.ico b/extras/appengine/static/favicon.ico
new file mode 100644
index 0000000..1372520
--- /dev/null
+++ b/extras/appengine/static/favicon.ico
Binary files differ
diff --git a/extras/appengine/static/img_loading.gif b/extras/appengine/static/img_loading.gif
deleted file mode 100644
index 6465823..0000000
--- a/extras/appengine/static/img_loading.gif
+++ /dev/null
Binary files differ
diff --git a/extras/appengine/static/jquery.textarearesizer.compressed.js b/extras/appengine/static/jquery.textarearesizer.compressed.js
deleted file mode 100644
index 5464ae6..0000000
--- a/extras/appengine/static/jquery.textarearesizer.compressed.js
+++ /dev/null
@@ -1 +0,0 @@
-(function($){var textarea,staticOffset;var iLastMousePos=0;var iMin=32;var grip;$.fn.TextAreaResizer=function(){return this.each(function(){textarea=$(this).addClass('processed'),staticOffset=null;$(this).wrap('<div class="resizable-textarea"><span></span></div>').parent().append($('<div class="grippie"></div>').bind("mousedown",{el:this},startDrag));var grippie=$('div.grippie',$(this).parent())[0];grippie.style.marginRight=(grippie.offsetWidth-$(this)[0].offsetWidth)+'px'})};function startDrag(e){textarea=$(e.data.el);textarea.blur();iLastMousePos=mousePosition(e).y;staticOffset=textarea.height()-iLastMousePos;textarea.css('opacity',0.25);$(document).mousemove(performDrag).mouseup(endDrag);return false}function performDrag(e){var iThisMousePos=mousePosition(e).y;var iMousePos=staticOffset+iThisMousePos;if(iLastMousePos>=(iThisMousePos)){iMousePos-=5}iLastMousePos=iThisMousePos;iMousePos=Math.max(iMin,iMousePos);textarea.height(iMousePos+'px');if(iMousePos<iMin){endDrag(e)}return false}function endDrag(e){$(document).unbind('mousemove',performDrag).unbind('mouseup',endDrag);textarea.css('opacity',1);textarea.focus();textarea=null;staticOffset=null;iLastMousePos=0}function mousePosition(e){return{x:e.clientX+document.documentElement.scrollLeft,y:e.clientY+document.documentElement.scrollTop}}})(jQuery); \ No newline at end of file
diff --git a/extras/appengine/static/resize-grip.png b/extras/appengine/static/resize-grip.png
deleted file mode 100644
index cae2a4e..0000000
--- a/extras/appengine/static/resize-grip.png
+++ /dev/null
Binary files differ
diff --git a/extras/appengine/static/rpc_relay.html b/extras/appengine/static/rpc_relay.html
deleted file mode 100644
index c602043..0000000
--- a/extras/appengine/static/rpc_relay.html
+++ /dev/null
@@ -1 +0,0 @@
-<html><head><script type="text/javascript" src="http://www.google.com/friendconnect/script/rpc_relay.js"></script></head></html> \ No newline at end of file
diff --git a/extras/appengine/static/script.js b/extras/appengine/static/script.js
index 71bbabb..8bdf271 100644
--- a/extras/appengine/static/script.js
+++ b/extras/appengine/static/script.js
@@ -11,7 +11,6 @@ function update_output() {
data.keyword_case = $('#id_keyword_case').val();
data.identifier_case = $('#id_identifier_case').val();
data.n_indents = $('#id_n_indents').val();
- data.right_margin = $('#id_right_margin').val();
data.output_format = $('#id_output_format').val();
form = document.getElementById('form_options');
$(form.elements).attr('disabled', 'disabled');
@@ -72,7 +71,7 @@ function load_example() {
fname = $('#sel_example').val();
data = {fname: fname};
$.post('/load_example', data,
- function(data) {
+ function(data) {
$('#id_data').val(data.answer);
}, 'json');
}
@@ -96,8 +95,4 @@ function init() {
$(document).bind('keydown', {combi: 't', disableInInput: true},
textarea_grab_focus);
initialized = true;
- /* jQuery textarea resizer plugin usage */
- $(document).ready(function() {
- $('textarea.resizable:not(.processed)').TextAreaResizer();
- });
} \ No newline at end of file
diff --git a/extras/appengine/static/sqlformat_client_example.py b/extras/appengine/static/sqlformat_client_example.py
index eec17b9..8b2a9e9 100644
--- a/extras/appengine/static/sqlformat_client_example.py
+++ b/extras/appengine/static/sqlformat_client_example.py
@@ -13,6 +13,7 @@ payload = (
('n_indents', 2),
)
+
response = urllib2.urlopen(REMOTE_API,
urllib.urlencode(payload))
print response.read()
diff --git a/extras/appengine/templates/404.html b/extras/appengine/templates/404.html
deleted file mode 100644
index 1eb60e8..0000000
--- a/extras/appengine/templates/404.html
+++ /dev/null
@@ -1,12 +0,0 @@
-<html>
- <head><title>404 Not Found</title></head>
- <body>
- <h1>404 - Not Found.</h1>
- <p>
- If you think this is a bug please file an issue on the tracker:
- <a href="http://code.google.com/p/python-sqlparse/issues/list">
- http://code.google.com/p/python-sqlparse/issues/list
- </a>.
- </p>
- </body>
-</html>
diff --git a/extras/appengine/templates/500.html b/extras/appengine/templates/500.html
deleted file mode 100644
index 8fa8f56..0000000
--- a/extras/appengine/templates/500.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<html>
- <head><title>500 Internal Server Error</title></head>
- <body>
- <h1>uups... 500 Internal Server Error.</h1>
- <p>
- Looks like you've hit a bug! Please file an issue on the tracker:
- <a href="http://code.google.com/p/python-sqlparse/issues/list">
- http://code.google.com/p/python-sqlparse/issues/list
- </a>.
- </p>
- <p>
- Please add a short description what happened and if possible add the SQL
- statement you've tried to format.
- </p>
- <p>
- Thanks!
- </p>
- </body>
-</html>
diff --git a/extras/py3k/Makefile b/extras/py3k/Makefile
index 499f2bf..57fcaeb 100644
--- a/extras/py3k/Makefile
+++ b/extras/py3k/Makefile
@@ -1,17 +1,16 @@
-2TO3=2to3-3.1
+2TO3=2to3
+2TO3OPTS=--no-diffs -w -n
all: sqlparse tests
sqlparse:
cp -r ../../sqlparse .
- $(2TO3) sqlparse > sqlparse.diff
- patch -p0 < sqlparse.diff
+ $(2TO3) $(2TO3OPTS) sqlparse
patch -p0 < fixes.diff
tests:
cp -r ../../tests .
- $(2TO3) tests > tests.diff
- patch -p0 < tests.diff
+ $(2TO3) $(2TO3OPTS) tests
clean:
rm -rf sqlparse
diff --git a/sqlparse/__init__.py b/sqlparse/__init__.py
index 903fdd1..7698e46 100644
--- a/sqlparse/__init__.py
+++ b/sqlparse/__init__.py
@@ -6,7 +6,7 @@
"""Parse SQL statements."""
-__version__ = '0.1.2'
+__version__ = '0.1.3'
class SQLParseError(Exception):
diff --git a/sqlparse/lexer.py b/sqlparse/lexer.py
index 950ef1b..8929e3e 100644
--- a/sqlparse/lexer.py
+++ b/sqlparse/lexer.py
@@ -150,7 +150,7 @@ class LexerMeta(type):
return type.__call__(cls, *args, **kwds)
-class Lexer:
+class Lexer(object):
__metaclass__ = LexerMeta
@@ -189,6 +189,7 @@ class Lexer:
(r"(''|'.*?[^\\]')", tokens.String.Single),
# not a real string literal in ANSI SQL:
(r'(""|".*?[^\\]")', tokens.String.Symbol),
+ (r'(\[.*[^\]]\])', tokens.Name),
(r'(LEFT |RIGHT )?(INNER |OUTER )?JOIN\b', tokens.Keyword),
(r'END( IF| LOOP)?\b', tokens.Keyword),
(r'NOT NULL\b', tokens.Keyword),
diff --git a/tests/test_parse.py b/tests/test_parse.py
index 6ff8dd8..5f9bb2d 100644
--- a/tests/test_parse.py
+++ b/tests/test_parse.py
@@ -95,3 +95,10 @@ class SQLParseTest(TestCaseBase):
self.assert_(t[-1].ttype is sqlparse.tokens.Name.Placeholder)
self.assertEqual(t[-1].value, '$a')
+ def test_access_symbol(self): # see issue27
+ t = sqlparse.parse('select a.[foo bar] as foo')[0].tokens
+ self.assert_(isinstance(t[-1], sqlparse.sql.Identifier))
+ self.assertEqual(t[-1].get_name(), 'foo')
+ self.assertEqual(t[-1].get_real_name(), '[foo bar]')
+ self.assertEqual(t[-1].get_parent_name(), 'a')
+
diff --git a/tests/utils.py b/tests/utils.py
index 6135dc3..e2c01a3 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -33,6 +33,7 @@ class TestCaseBase(unittest.TestCase):
ssecond = unicode(second)
diff = difflib.ndiff(sfirst.splitlines(), ssecond.splitlines())
fp = StringIO()
- print >> fp, NL, NL.join(diff)
+ fp.write(NL)
+ fp.write(NL.join(diff))
print fp.getvalue()
raise self.failureException, fp.getvalue()