-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
357 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
*.pyc | ||
*.swp | ||
build | ||
dist | ||
*.egg-info | ||
.tox/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
sudo: false | ||
language: python | ||
python: | ||
- "2.7" | ||
- "3.6" | ||
install: | ||
- pip install tox-travis | ||
- pip install coveralls | ||
script: tox | ||
after_script: coveralls | ||
notifications: | ||
email: | ||
recipients: | ||
- romary@me.com | ||
on_success: change | ||
on_failure: always | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2017 Romary Dupuis, https://github.com/romaryd | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
include LICENSE README.md | ||
recursive-include tests *.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Awesome decorators | ||
|
||
[![Build Status](https://travis-ci.org/romaryd/python-awesome-decorators.svg?branch=master)](https://travis-ci.org/romaryd/python-awesome-decorators) | ||
[![Coverage Status](https://coveralls.io/repos/github/romaryd/python-awesome-decorators/badge.svg?branch=master)](https://coveralls.io/github/romaryd/python-awesome-decorators?branch=master) | ||
[![Maintainability](https://api.codeclimate.com/v1/badges/b03f759c2a1d62011a6d/maintainability)](https://codeclimate.com/github/romaryd/python-awesome-decorators/maintainability) | ||
[![Code Health](https://landscape.io/github/romaryd/python-awesome-decorators/master/landscape.svg?style=flat)](https://landscape.io/github/romaryd/python-awesome-decorators/master) | ||
|
||
List of awesome decorators I have found so far. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# -*- coding: utf8 -*- | ||
from .memoized import memoized | ||
from .timez import timeit, timeout, TimeoutError | ||
from .timer import Timer | ||
|
||
__version__ = '0.1.0' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# -*- coding: utf8 -*- | ||
""" | ||
Memoized | ||
Credits: Benjamin Bengfort <benjamin@bengfort.com> | ||
""" | ||
from functools import wraps | ||
|
||
|
||
def memoized(fget): | ||
""" | ||
Return a property attribute for new-style classes that only calls its | ||
getter on the first access. The result is stored and on subsequent | ||
accesses is returned, preventing the need to call the getter any more. | ||
https://github.com/estebistec/python-memoized-property | ||
""" | ||
attr_name = '_{0}'.format(fget.__name__) | ||
|
||
@wraps(fget) | ||
def fget_memoized(self): | ||
if not hasattr(self, attr_name): | ||
setattr(self, attr_name, fget(self)) | ||
return getattr(self, attr_name) | ||
|
||
return property(fget_memoized) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# -*- coding: utf8 -*- | ||
""" | ||
Timer | ||
Credit: Benjamin Bengfort <benjamin@bengfort.com> | ||
""" | ||
import time | ||
|
||
|
||
class Timer(object): | ||
""" | ||
A context object timer. Usage: | ||
>>> with Timer() as timer: | ||
... do_something() | ||
>>> print timer.elapsed | ||
""" | ||
|
||
def __init__(self, wall_clock=True): | ||
""" | ||
If wall_clock is True then use time.time() to get the number of | ||
actually elapsed seconds. If wall_clock is False, use time.clock to | ||
get the process time instead. | ||
""" | ||
self.wall_clock = wall_clock | ||
self.time = time.time if wall_clock else time.clock | ||
|
||
# Stubs for serializing an empty timer. | ||
self.started = None | ||
self.finished = None | ||
self.elapsed = 0.0 | ||
|
||
def __enter__(self): | ||
self.started = self.time() | ||
return self | ||
|
||
def __exit__(self, typ, value, tb): | ||
self.finished = self.time() | ||
self.elapsed = self.finished - self.started | ||
|
||
def __str__(self): | ||
return '{} second(s)'.format(self.elapsed) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# -*- coding: utf8 -*- | ||
""" | ||
Timeit and timeout are about tracking and controlling performance issues. | ||
Credits: Benjamin Bengfort <benjamin@bengfort.com> | ||
""" | ||
from functools import wraps | ||
import signal | ||
from .timer import Timer | ||
|
||
|
||
class TimeoutError(Exception): | ||
""" | ||
An operation timed out | ||
""" | ||
pass | ||
|
||
|
||
def timeit(func): | ||
""" | ||
Returns the number of seconds that a function took along with the result | ||
""" | ||
|
||
@wraps(func) | ||
def timer_wrapper(*args, **kwargs): | ||
""" | ||
Inner function that uses the Timer context object | ||
""" | ||
with Timer() as timer: | ||
result = func(*args, **kwargs) | ||
|
||
return result, timer | ||
|
||
return timer_wrapper | ||
|
||
|
||
def timeout(seconds): | ||
""" | ||
Raises a TimeoutError if a function does not terminate within | ||
specified seconds. | ||
""" | ||
def _timeout_error(signal, frame): | ||
raise TimeoutError("Operation did not finish within \ | ||
{} seconds".format(seconds)) | ||
|
||
def timeout_decorator(func): | ||
|
||
@wraps(func) | ||
def timeout_wrapper(*args, **kwargs): | ||
signal.signal(signal.SIGALRM, _timeout_error) | ||
signal.alarm(seconds) | ||
try: | ||
return func(*args, **kwargs) | ||
finally: | ||
signal.alarm(0) | ||
|
||
return timeout_wrapper | ||
|
||
return timeout_decorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
#!/usr/bin/env python | ||
|
||
import os | ||
import re | ||
try: | ||
from setuptools import setup | ||
from setuptools import find_packages | ||
except ImportError: | ||
raise ImportError("Could not import \"setuptools\"." | ||
"Please install the setuptools package.") | ||
|
||
|
||
def text_of(relpath): | ||
""" | ||
Return string containing the contents of the file at *relpath* relative to | ||
this file. | ||
""" | ||
thisdir = os.path.dirname(__file__) | ||
file_path = os.path.join(thisdir, os.path.normpath(relpath)) | ||
with open(file_path) as f: | ||
text = f.read() | ||
return text | ||
|
||
|
||
# Read the version without importing the package | ||
# (and thus attempting to import packages it depends on that may not be | ||
# installed yet) | ||
version = re.search( | ||
"__version__ = '([^']+)'", text_of('awesomedecorators/__init__.py') | ||
).group(1) | ||
|
||
|
||
NAME = 'python-awesome-decorators' | ||
VERSION = version | ||
DESCRIPTION = 'List of nice decorators in Python.' | ||
KEYWORDS = 'decorators' | ||
AUTHOR = 'Romary Dupuis' | ||
AUTHOR_EMAIL = 'romary@me.com' | ||
URL = 'https://github.com/romaryd/python-awesome-decorators' | ||
LICENSE = text_of('LICENSE') | ||
PACKAGES = find_packages(exclude=['tests', 'tests.*']) | ||
|
||
INSTALL_REQUIRES = ['python-dateutil'] | ||
TEST_SUITE = 'tests' | ||
TESTS_REQUIRE = ['pytest'] | ||
|
||
CLASSIFIERS = [ | ||
'Development Status :: 1 - Alpha', | ||
'Intended Audience :: Developers', | ||
'License :: OSI Approved :: MIT License', | ||
'Operating System :: OS Independent', | ||
'Programming Language :: Python', | ||
'Programming Language :: Python :: 2.7', | ||
'Programming Language :: Python :: 3.6', | ||
'Topic :: Software Development', | ||
'Topic :: Software Development :: Libraries :: Python Modules', | ||
'Topic :: Utilities', | ||
] | ||
|
||
LONG_DESCRIPTION = text_of('README.md') | ||
|
||
|
||
params = { | ||
'name': NAME, | ||
'version': VERSION, | ||
'description': DESCRIPTION, | ||
'keywords': KEYWORDS, | ||
'long_description': LONG_DESCRIPTION, | ||
'author': AUTHOR, | ||
'author_email': AUTHOR_EMAIL, | ||
'url': URL, | ||
'license': LICENSE, | ||
'packages': PACKAGES, | ||
'install_requires': INSTALL_REQUIRES, | ||
'tests_require': TESTS_REQUIRE, | ||
'test_suite': TEST_SUITE, | ||
'classifiers': CLASSIFIERS, | ||
} | ||
|
||
if __name__ == '__main__': | ||
setup(**params) |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
""" | ||
Testing the decorators utility package. | ||
""" | ||
|
||
import unittest | ||
import time | ||
from awesomedecorators import memoized, timeit, timeout, Timer, TimeoutError | ||
|
||
|
||
class DecoratorsTests(unittest.TestCase): | ||
""" | ||
Basic decorators utility tests. | ||
""" | ||
|
||
def test_memoized(self): | ||
""" | ||
Test the memoized property | ||
""" | ||
|
||
class Thing(object): | ||
|
||
@memoized | ||
def attr(self): | ||
return 42 | ||
|
||
thing = Thing() | ||
self.assertFalse(hasattr(thing, '_attr')) | ||
self.assertEqual(thing.attr, 42) | ||
self.assertTrue(hasattr(thing, '_attr')) | ||
|
||
def test_timeit(self): | ||
""" | ||
Test the timeit decorator | ||
""" | ||
|
||
@timeit | ||
def myfunc(): | ||
return 42 | ||
|
||
output = myfunc() | ||
self.assertEqual(len(output), 2) | ||
result, timer = output | ||
self.assertEqual(result, 42) | ||
self.assertTrue(isinstance(timer, Timer)) | ||
|
||
def test_timeout(self): | ||
""" | ||
Test the timeout decorator | ||
""" | ||
|
||
@timeout(1) | ||
def myfunc(): | ||
# Some function that should take more than 1 second | ||
time.sleep(2) | ||
|
||
with self.assertRaises(TimeoutError) as context: | ||
myfunc() | ||
self.assertTrue('Operation did not finish within' | ||
in str(context.exception)) | ||
|
||
def test_timeout_elapsed(self): | ||
""" | ||
Test the timeout decorator: elapsed value | ||
""" | ||
|
||
@timeout(2) | ||
def myfunc(): | ||
# Some function that should take more than 1 second | ||
time.sleep(1) | ||
|
||
with Timer() as timer: | ||
myfunc() | ||
self.assertGreater(timer.elapsed, 1.0) | ||
print(timer) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[flake8] | ||
exclude = dist,docs,*.egg-info,.git,ref,_scratch,.tox | ||
max-line-length = 80 | ||
ignore=F401 | ||
|
||
[tox] | ||
envlist = py27, py36, flake8 | ||
|
||
[testenv] | ||
deps= | ||
nose | ||
coverage | ||
commands=nosetests -v --with-coverage --cover-package=awesomedecorators --cover-inclusive --cover-erase tests | ||
|
||
[testenv:flake8] | ||
basepython = python2.7 | ||
deps = flake8 | ||
commands = flake8 awesomedecorators ./tests |