Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
playpauseandstop committed Apr 3, 2012
0 parents commit 8d150b8
Show file tree
Hide file tree
Showing 23 changed files with 650 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
*.pyc
*.pyo

testapp/env/
27 changes: 27 additions & 0 deletions LICENSE
@@ -0,0 +1,27 @@
Copyright (c) 2012, Igor Davydenko.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

3. Neither the name of tddspry nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3 changes: 3 additions & 0 deletions MANIFEST.in
@@ -0,0 +1,3 @@
include LICENSE
include MANIFEST.in
include README.rst
161 changes: 161 additions & 0 deletions README.rst
@@ -0,0 +1,161 @@
===============
Flask-LazyViews
===============

Registering url routes for your `Flask <http://flask.pocoo.org/>`_ app or
blueprint in lazy way :)

**Based on original snippet from Flask documentation!**

Requirements
============

* `Python <http://www.python.org/>`_ 2.6 or higher
* `Flask`_ 0.8 or higher

Installation
============

::

$ pip install Flask-LazyViews

License
=======

``Flask-LazyViews`` is licensed under the `BSD License
<https://github.com/playpauseandstop/Flask-LazyViews/blob/master/LICENSE>`_.

Usage
=====

For application
---------------

``project/app.py``

::

from flask import Flask
from flask.ext.lazyviews import LazyViews


app = Flask(__name__)
views = LazyViews(app)

views.add('/', 'views.home')
views.add('/page/<int:page>', 'views.page')

``project/views.py``

::

from flask import render_template


def home():
return render_template('home.html')


def page(page_id):
page = get_page(page_id)
return render_template('page.html', page=page)


For blueprint
-------------

.. note:: Blueprint name is ``test`` :)

``project/app.py``

::

...

from project.test import blueprint as test_blueprint

...

app.register_blueprint(test_blueprint, url_prefix='/test')


``project/test/__init__.py``

::

from flask import Blueprint
from flask.ext.lazyviews import LazyViews


blueprint = Blueprint('test', __name__)
views = LazyViews(blueprint, '.views')

views.add('/', 'test')
views.add('/advanced', 'advanced_test', methods=('GET', 'POST'))

``project/test/views.py``

::

from flask import render_template, request


def advanced_test():
context = generate_context(request.form)
return render_template('test/advanced.html', **context)


def test():
return render_template('test/test.html')

Explanations
============

The main point of ``Flask-LazyViews`` is simplifying process of adding views
to the app and blueprint using `lazy technique
<http://flask.pocoo.org/docs/patterns/lazyloading/>`_ from Flask
documentation.

Also the next goal is simplifying ``viewname`` definition. For most cases our
views functions placed in ``.views`` module of app or blueprint, so we don't
need to input full path to that module.

This especially useful for blueprints. Let see the example above, if we using
original snippet - we'll need to provide path to blueprint's views
module::

add_url(blueprint, '/', 'test.views.test')

but with ``Flask-LazyViews`` we could to ignore ``test``.

From other side if your view functions placed in some other location or you
need to provide full path to its - you still could do this.

Also you could setup ``import_prefix`` like done in Django's ``patterns``::

views = LazyViews(app, 'views')
views.add('/', 'home')
views.add('/page/<int:id>', 'page', methods=('GET', 'POST'))

Important
---------

Be careful with ``import_prefix`` value if you used ``__name__`` as Flask
application name or blueprint ``import_name``. Setting relative path could
cause server errors.

Bugs, feature requests?
=======================

If you found some bug in ``Flask-LazyViews`` library, please, add new issue to
the project's `GitHub issues
<https://github.com/playpauseandstop/Flask-LazyViews/issues>`_.

ChangeLog
=========

0.1
---

* Initial release.
1 change: 1 addition & 0 deletions flask_lazyviews/__init__.py
@@ -0,0 +1 @@
from .lazyviews import LazyViews
35 changes: 35 additions & 0 deletions flask_lazyviews/lazyviews.py
@@ -0,0 +1,35 @@
from .utils import LazyView


class LazyViews(object):
"""
"""
def __init__(self, mixed, import_prefix=None):
"""
"""
self._mixed = mixed

if import_prefix and import_prefix.startswith('.'):
import_name = \
mixed.import_name if mixed.import_name != '__main__' else ''

assert import_name, 'You should properly configure import name ' \
'for %r instance or edit import prefix to ' \
'not start with ".".' % \
mixed.__class__.__name__

import_prefix = import_name + import_prefix

self._import_prefix = import_prefix

def add(self, url_rule, import_name, **options):
"""
"""
view = LazyView(self.build_import_name(import_name))
self._mixed.add_url_rule(url_rule, view_func=view, **options)

def build_import_name(self, import_name):
"""
"""
not_empty = lambda data: filter(lambda item: item, data)
return '.'.join(not_empty((self._import_prefix or None, import_name)))
30 changes: 30 additions & 0 deletions flask_lazyviews/utils.py
@@ -0,0 +1,30 @@
from werkzeug.utils import cached_property, import_string


__all__ = ('LazyView', )


class LazyView(object):
"""
Import view function only when necessary.
"""
def __init__(self, name):
"""
Initialize ``LazyView`` instance for view that would be imported from
``name`` path.
"""
self.__module__, self.__name__ = name.rsplit('.', 1)
self.import_name = name

def __call__(self, *args, **kwargs):
"""
Make real call to the view.
"""
return self.view(*args, **kwargs)

@cached_property
def view(self):
"""
Import view and cache it to current cls.
"""
return import_string(self.import_name)
43 changes: 43 additions & 0 deletions setup.py
@@ -0,0 +1,43 @@
#!/usr/bin/env python

import os

from distutils.core import setup


DIRNAME = os.path.dirname(__file__)

readme = open(os.path.join(DIRNAME, 'README.rst'), 'r')
README = readme.read()
readme.close()


setup(
name='Flask-LazyViews',
version='0.1',
description='Registering url routes for Flask app and blueprints in ' \
'lazy way.',
long_description=README,
author='Igor Davydenko',
author_email='playpauseandstop@gmail.com',
url='https://github.com/playpauseandstop/Flask-LazyViews',
install_requires=[
'Flask',
],
packages=[
'flask_lazyviews',
],
platforms='any',
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Web Environment',
'Operating System :: OS Independent',
'Topic :: Utilities',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Software Development :: Libraries :: Python Modules',
'License :: OSI Approved :: BSD License',
],
keywords='flask lazy views',
license='BSD License',
)
21 changes: 21 additions & 0 deletions testapp/Makefile
@@ -0,0 +1,21 @@
.PHONY: bootstrap server test

# Project and environment settings
env = env
project = .
pip = $(env)/bin/pip
python = PYTHONPATH=.. $(env)/bin/python

# Server settings
IP ?= 0.0.0.0
PORT ?= 4354

bootstrap:
virtualenv --distribute $(env)
$(pip) install -r requirements.txt

server:
$(python) $(project)/app.py $(IP):$(PORT)

test:
$(python) $(project)/tests.py
Empty file added testapp/__init__.py
Empty file.
46 changes: 46 additions & 0 deletions testapp/app.py
@@ -0,0 +1,46 @@
#!/usr/bin/env python

import os
import sys

from flask import Flask
from flask.ext.lazyviews import LazyViews

from testapp.test import blueprint as test_blueprint


# Init Flask application
app = Flask(__name__)

# Add url routes to application
views = LazyViews(app)
views.add('/', 'views.home')
views.add('/page/<int:page_id>', 'views.page', endpoint='flatpage')

# Register test blueprint
app.register_blueprint(test_blueprint, url_prefix='/test')


if __name__ == '__main__':
host = '0.0.0.0'
port = 5000

if len(sys.argv) == 2:
mixed = sys.argv[1]

try:
host, port = mixed.split(':')
except ValueError:
port = mixed
elif len(sys.argv) == 3:
host, port = sys.argv[1:]

try:
port = int(port)
except (TypeError, ValueError):
print >> sys.stderr, 'Please, use proper digit value to the ' \
'``port`` argument.\nCannot convert %r to ' \
'integer.' % port

app.debug = bool(int(os.environ.get('DEBUG', 1)))
app.run(host=host, port=port)
2 changes: 2 additions & 0 deletions testapp/requirements.txt
@@ -0,0 +1,2 @@
Flask>=0.8
unittest2

0 comments on commit 8d150b8

Please sign in to comment.