Browse Source

Initial version of module

master
Brett Langdon 9 years ago
commit
71a312b30a
No known key found for this signature in database GPG Key ID: A2ECAB73CE12147F
10 changed files with 415 additions and 0 deletions
  1. +89
    -0
      .gitignore
  2. +17
    -0
      .travis.yml
  3. +10
    -0
      CHANGELOG
  4. +21
    -0
      LICENSE
  5. +1
    -0
      MANIFEST.in
  6. +59
    -0
      README.rst
  7. +26
    -0
      example.py
  8. +37
    -0
      flask_defer.py
  9. +36
    -0
      setup.py
  10. +119
    -0
      test_flask_defer.py

+ 89
- 0
.gitignore View File

@ -0,0 +1,89 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# IPython Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# dotenv
.env
# virtualenv
venv/
ENV/
# Spyder project settings
.spyderproject
# Rope project settings
.ropeproject

+ 17
- 0
.travis.yml View File

@ -0,0 +1,17 @@
language: python
python:
- "2.6"
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "pypy"
sudo: false
install:
- pip install flake8
script:
- flake8 --max-line-length=120 flask_defer.py && python test_flask_defer.py

+ 10
- 0
CHANGELOG View File

@ -0,0 +1,10 @@
Flask-Defer Changelog
===================
Version 1.0.0
-------------
:Released: 11-28-2016
:Changes:
Initial release of package

+ 21
- 0
LICENSE View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Brett Langdon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 1
- 0
MANIFEST.in View File

@ -0,0 +1 @@
include CHANGELOG LICENSE README.rst flask_defer.py test_flask_defer.py

+ 59
- 0
README.rst View File

@ -0,0 +1,59 @@
Flask-Defer
=========
.. image:: https://badge.fury.io/py/flask-defer.svg
:target: https://badge.fury.io/py/flask-defer
.. image:: https://travis-ci.org/brettlangdon/flask-defer.svg?branch=master
:target: https://travis-ci.org/brettlangdon/flask-defer
Easily register a function to execute at the end of the current request.
Installation
~~~~~~~~~~~~
.. code:: bash
pip install Flask-Defer
Usage
~~~~~
.. code:: python
from flask import Flask
from flask_defer import FlaskDefer, after_request
app = Flask(__name__)
FlaskDefer(app)
def defer_me(name, say_hello=False):
if say_hello:
print 'Saying hello to, {name}'.format(name=name)
@app.route('/')
def index():
print 'Start of request method'
# Defer `defer_me` until after the current request has finished
after_request(defer_me, 'name', say_hello=True)
print 'Ending request method'
return 'Thanks!'
if __name__ == '__main__':
app.run()
.. code:: bash
$ python example.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Start of request method
Ending request method
Saying hello to, name
127.0.0.1 - - [28/Nov/2016 15:41:39] "GET / HTTP/1.1" 200 -

+ 26
- 0
example.py View File

@ -0,0 +1,26 @@
from flask import Flask
from flask_defer import FlaskDefer, after_request
app = Flask(__name__)
FlaskDefer(app)
def defer_me(name, say_hello=False):
if say_hello:
print 'Saying hello to, {name}'.format(name=name)
@app.route('/')
def index():
print 'Start of request method'
# Defer `defer_me` until after the current request has finished
after_request(defer_me, 'name', say_hello=True)
print 'Ending request method'
return 'Thanks!'
if __name__ == '__main__':
app.run()

+ 37
- 0
flask_defer.py View File

@ -0,0 +1,37 @@
try:
from flask import _app_ctx_stack as stack
except ImportError:
from flask import _request_ctx_stack as stack
__all__ = ['after_request', 'defer', 'FlaskDefer']
def defer(func, *args, **kwargs):
params = dict(func=func, args=args, kwargs=kwargs)
ctx = stack.top
if not hasattr(ctx, 'deferred_tasks'):
setattr(ctx, 'deferred_tasks', [])
ctx.deferred_tasks.append(params)
# Alias `defer` as `after_request`
after_request = defer
class FlaskDefer(object):
def __init__(self, app=None):
if app is not None:
self.init_app(app)
def init_app(self, app):
if hasattr(app, 'teardown_appcontext'):
app.teardown_appcontext(self._execute_deferred)
else:
app.teardown_request(self._execute_deferred)
def _execute_deferred(self, exception):
ctx = stack.top
if hasattr(ctx, 'deferred_tasks'):
for params in ctx.deferred_tasks:
# DEV: Do not try/except, let these function calls fail
params['func'](*params['args'], **params['kwargs'])

+ 36
- 0
setup.py View File

@ -0,0 +1,36 @@
"""
Flask-Defer
"""
from setuptools import setup
def get_long_description():
with open('README.rst') as f:
rv = f.read()
return rv
setup(
name='Flask-Defer',
version='1.0.0',
url='https://github.com/brettlangdon/flask-defer.git',
license='MIT',
author='Brett Langdon',
author_email='me@brett.is',
description='Flask extension to defer task execution under after request teardown',
long_description=get_long_description(),
py_modules=['flask_defer'],
zip_safe=False,
include_package_data=True,
platforms='any',
install_requires=['Flask'],
classifiers=[
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Software Development :: Libraries :: Python Modules'
],
)

+ 119
- 0
test_flask_defer.py View File

@ -0,0 +1,119 @@
import unittest
from flask import Flask
from flask_defer import FlaskDefer, defer, stack
def deferred_task(name, with_keyword=False):
pass
class TestFlaskDefer(unittest.TestCase):
def setUp(self):
self.app = Flask(__name__)
self.defer = FlaskDefer(app=self.app)
@self.app.route('/')
def test_endpoint():
defer(deferred_task, 'name', with_keyword=True)
return 'test_endpoint'
@self.app.route('/multiple')
def test_multiple():
defer(deferred_task, 'first', with_keyword=True)
defer(deferred_task, 'second', with_keyword=False)
defer(deferred_task, 'third', with_keyword=True)
return 'test_multiple'
@self.app.route('/extra')
def test_extra_params():
defer(deferred_task, 'name', 'extra', with_keyword=True, extra_param='param')
return 'test_extra_params'
@self.app.route('/no-defer')
def test_no_defer():
return 'test_no_defer'
def test_deferring_task(self):
with self.app.test_client() as client:
# Make the request so we register the task
client.get('/')
ctx = stack.top
self.assertTrue(hasattr(ctx, 'deferred_tasks'))
self.assertEqual(len(ctx.deferred_tasks), 1)
task = ctx.deferred_tasks[0]
self.assertDictEqual(task, dict(
args=('name', ),
func=deferred_task,
kwargs=dict(with_keyword=True),
))
# Assert that the deferred tasks aren't shared between requests
with self.app.test_client() as client:
client.get('/no-defer')
ctx = stack.top
self.assertFalse(hasattr(ctx, 'deferred_tasks'))
def test_deferring_task_multiple(self):
with self.app.test_client() as client:
# Make the request so we register the task
client.get('/multiple')
ctx = stack.top
self.assertTrue(hasattr(ctx, 'deferred_tasks'))
self.assertEqual(len(ctx.deferred_tasks), 3)
task = ctx.deferred_tasks[0]
self.assertDictEqual(task, dict(
args=('first', ),
func=deferred_task,
kwargs=dict(with_keyword=True),
))
task = ctx.deferred_tasks[1]
self.assertDictEqual(task, dict(
args=('second', ),
func=deferred_task,
kwargs=dict(with_keyword=False),
))
task = ctx.deferred_tasks[2]
self.assertDictEqual(task, dict(
args=('third', ),
func=deferred_task,
kwargs=dict(with_keyword=True),
))
def test_deferring_task_no_defer(self):
with self.app.test_client() as client:
# Make the request
client.get('/no-defer')
ctx = stack.top
self.assertFalse(hasattr(ctx, 'deferred_tasks'))
def test_deferring_task_extra(self):
# We get a TypeError from the invalid function call
# TODO: There has to be a better/more concise way to test this
with self.assertRaises(TypeError):
with self.app.test_client() as client:
# Make the request so we register the task
client.get('/extra')
ctx = stack.top
self.assertTrue(hasattr(ctx, 'deferred_tasks'))
self.assertEqual(len(ctx.deferred_tasks), 1)
task = ctx.deferred_tasks[0]
self.assertDictEqual(task, dict(
args=('name', 'extra'),
func=deferred_task,
kwargs=dict(with_keyword=True, extra_param='param'),
))
if __name__ == '__main__':
unittest.main()

Loading…
Cancel
Save