commit 3bb6be0ffc182d7d1ca9c5eda086cfd2bfe4b189 Author: Dave Shawley Date: Mon Jan 18 15:32:49 2016 -0500 SYN diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf9640f --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +coverage.xml +nosetests.xml +*.egg +*.egg-info +*.pyc +*.pyo +/.coverage +/build/ +/dist/ +/downloads/ +/env/ +/htmlcov/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..c0454c8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: python +python: +- 2.7 +- 3.4 +- 3.5 +- pypy +before_install: +- pip install nose coverage codecov +- pip install -r requires/testing.txt +install: +- pip install -e . +script: nosetests +after_success: +- codecov +sudo: false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1391310 --- /dev/null +++ b/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2016 AWeber Communications +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * 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. + * Neither the name of Sprockets 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 HOLDER 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. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..e697e00 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include LICENSE +include tests.py +graft docs +graft requires diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..37f2c6a --- /dev/null +++ b/README.rst @@ -0,0 +1,39 @@ +sprockets.mixins.metrics +======================== +Adjust counter and timer metrics in InfluxDB or Graphite using the same API. + +.. code-block:: python + + from sprockets.mixins import mediatype, metrics + from tornado import gen, web + import queries + + class MyHandler(metrics.StatsdMixin, mediatype.ContentMixin, + web.RequestHandler): + + def initialize(self): + super(MyHandler, self).initialize() + self.db = queries.TornadoSession(os.environ['MY_PGSQL_DSN']) + + @gen.coroutine + def get(self, obj_id): + with self.execution_timer('dbquery.get'): + result = yield self.db.query('SELECT * FROM foo WHERE id=%s', + obj_id) + self.send_response(result) + +This simple handler will emit a timer metric that identifies each call to the +``get`` method as well as a separate metric for the database query. Switching +from using `statsd`_ to `InfluxDB`_ is simply a matter of switch from the +``metrics.StatsdMixin`` to the ``metrics.InfluxDBMixin``. + +Development Quickstart +---------------------- +.. code-block:: bash + + $ python3.4 -mvenv env + $ . ./env/bin/activate + (env)$ pip install -r requires/development.txt + +.. _statsd: https://github.com/etsy/statsd +.. _InfluxDB: https://influxdata.com diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..b434c2e --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +import alabaster +from sprockets.mixins import metrics + +project = 'sprockets.mixins.metrics' +copyright = 'AWeber Communications, Inc.' +version = metrics.__version__ +release = '.'.join(str(v) for v in metrics.version_info[0:2]) + +needs_sphinx = '1.0' +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.intersphinx', + 'sphinx.ext.viewcode', +] + +templates_path = [] +source_suffix = '.rst' +source_encoding = 'utf-8-sig' +master_doc = 'index' +exclude_patterns = [] +pygments_style = 'sphinx' +html_style = 'custom.css' +html_static_path = ['.'] +html_theme = 'alabaster' +html_theme_path = [alabaster.get_path()] +html_sidebars = { + '**': ['about.html', 'navigation.html'], +} +html_theme_options = { + 'github_user': 'sprockets', + 'github_repo': 'sprockets.mixins.metrics', + 'description': 'Application metrics tracker', + 'github_banner': True, + 'travis_button': True, +} + +intersphinx_mapping = { + 'python': ('http://docs.python.org/3/', None), + 'tornado': ('http://tornadoweb.org/en/latest/', None), +} diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 0000000..8b58b97 --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1,112 @@ +How to Contribute +================= +Do you want to contribute fixes or improvements? + + **AWesome!** *Thank you very much, and let's get started.* + +Set up a development environment +-------------------------------- +The first thing that you need is a development environment so that you can +run the test suite, update the documentation, and everything else that is +involved in contributing. The easiest way to do that is to create a virtual +environment for your endevours:: + + $ virtualenv -p python2.7 env + +Don't worry about writing code against previous versions of Python unless +you you don't have a choice. That is why we run our tests through `tox`_. +If you don't have a choice, then install `virtualenv`_ to create the +environment instead. The next step is to install the development tools +that this project uses. These are listed in *requires/development.txt*:: + + $ env/bin/pip install -qr requires/development.txt + +At this point, you will have everything that you need to develop at your +disposal. *setup.py* is the swiss-army knife in your development tool +chest. It provides the following commands: + +**./setup.py nosetests** + Run the test suite using `nose`_ and generate a nice coverage report. + +**./setup.py build_sphinx** + Generate the documentation using `sphinx`_. + +**./setup.py flake8** + Run `flake8`_ over the code and report style violations. + +If any of the preceding commands give you problems, then you will have to +fix them **before** your pull request will be accepted. + +Running Tests +------------- +The easiest (and quickest) way to run the test suite is to use the +*nosetests* command. It will run the test suite against the currently +installed python version and report not only the test result but the +test coverage as well:: + + $ ./setup.py nosetests + + running nosetests + running egg_info + writing dependency_links to sprockets.http.egg-info/dependency_links.txt + writing top-level names to sprockets.http.egg-info/top_level.txt + writing sprockets.http.egg-info/PKG-INFO + reading manifest file 'sprockets.http.egg-info/SOURCES.txt' + reading manifest template 'MANIFEST.in' + warning: no previously-included files matching '__pycache__'... + warning: no previously-included files matching '*.swp' found ... + writing manifest file 'sprockets.http.egg-info/SOURCES.txt' + ... + + Name Stmts Miss Branch BrMiss Cover Missing + ---------------------------------------------------------------------- + ... + ---------------------------------------------------------------------- + TOTAL 95 2 59 2 97% + ---------------------------------------------------------------------- + Ran 44 tests in 0.054s + + OK + +That's the quick way to run tests. The slightly longer way is to run +the `detox`_ utility. It will run the test suite against all of the +supported python versions in parallel. This is essentially what Travis-CI +will do when you issue a pull request anyway:: + + $ env/bin/detox + py27 recreate: /.../sprockets.http/build/tox/py27 + GLOB sdist-make: /.../sprockets.http/setup.py + py34 recreate: /.../sprockets.http/build/tox/py34 + py27 installdeps: -rtest-requirements.txt, mock + py34 installdeps: -rtest-requirements.txt + py27 inst: /.../sprockets.http/build/tox/dist/sprockets.http-0.0.0.zip + py27 runtests: PYTHONHASHSEED='2156646470' + py27 runtests: commands[0] | /../sprockets.http/build/tox/py27/bin/nosetests + py34 inst: /../sprockets.http/.build/tox/dist/sprockets.http-0.0.0.zip + py34 runtests: PYTHONHASHSEED='2156646470' + py34 runtests: commands[0] | /.../sprockets.http/build/tox/py34/bin/nosetests + _________________________________ summary _________________________________ + py27: commands succeeded + py34: commands succeeded + congratulations :) + +This is what you want to see. Now you can make your modifications and keep +the tests passing. + +Submitting a Pull Request +------------------------- +Once you have made your modifications, gotten all of the tests to pass, +and added any necessary documentation, it is time to contribute back for +posterity. You've probably already cloned this repository and created a +new branch. If you haven't, then checkout what you have as a branch and +roll back *master* to where you found it. Then push your repository up +to github and issue a pull request. Describe your changes in the request, +if Travis isn't too annoyed someone will review it, and eventually merge +it back. + +.. _flake8: http://flake8.readthedocs.org/ +.. _nose: http://nose.readthedocs.org/ +.. _sphinx: http://sphinx-doc.org/ +.. _detox: http://testrun.org/tox/ +.. _tox: http://testrun.org/tox/ +.. _virtualenv: http://virtualenv.pypa.io/ diff --git a/docs/custom.css b/docs/custom.css new file mode 100644 index 0000000..9e9b139 --- /dev/null +++ b/docs/custom.css @@ -0,0 +1,4 @@ +@import url("alabaster.css"); +h1.logo { + font-size: 12pt; +} diff --git a/docs/history.rst b/docs/history.rst new file mode 100644 index 0000000..9b445f3 --- /dev/null +++ b/docs/history.rst @@ -0,0 +1,9 @@ +.. :changelog: + +Release History +=============== + +`Next Release`_ +--------------- + +.. _Next Release: https://github.com/sprockets/sprockets.mixins.metrics/compare/0.0.0...master diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..13af388 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,11 @@ +.. include:: ../README.rst + +License +------- +.. include:: ../LICENSE + +.. toctree:: + :hidden: + + contributing + history diff --git a/requires/development.txt b/requires/development.txt new file mode 100644 index 0000000..2806c16 --- /dev/null +++ b/requires/development.txt @@ -0,0 +1 @@ +Sphinx diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..261bee8 --- /dev/null +++ b/setup.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# + +import os.path + +import setuptools + +from sprockets.mixins import metrics + + +def read_requirements(name): + requirements = [] + try: + with open(os.path.join('requires', name)) as req_file: + for line in req_file: + if '#' in line: + line = line[:line.index('#')] + line = line.strip() + if line.startswith('-r'): + requirements.extend(read_requirements(line[2:].strip())) + elif line and not line.startswith('-'): + requirements.append(line) + except IOError: + pass + return requirements + + +setuptools.setup( + name='sprockets.mixins.metrics', + version=metrics.__version__, + description='Record performance metrics about your application', + long_description='\n'+open('README.rst').read(), + author='AWeber Communications', + author_email='api@aweber.com', + license='BSD', + url='https://github.com/sprockets/sprockets.mixins.metrics', + install_requires=read_requirements('installation.txt'), + tests_require=read_requirements('testing.txt'), + packages=setuptools.find_packages(), + namespace_packages=['sprockets', 'sprockets.mixins'], + classifiers=[ + 'Development Status :: 1 - Planning', + 'Environment :: No Input/Output (Daemon)', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Natural Language :: English', + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Software Development :: Libraries', + 'Topic :: Software Development :: Libraries :: Python Modules'], + test_suite='nose.collector', + zip_safe=True, +) diff --git a/sprockets/__init__.py b/sprockets/__init__.py new file mode 100644 index 0000000..de40ea7 --- /dev/null +++ b/sprockets/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/sprockets/mixins/__init__.py b/sprockets/mixins/__init__.py new file mode 100644 index 0000000..de40ea7 --- /dev/null +++ b/sprockets/mixins/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/sprockets/mixins/metrics/__init__.py b/sprockets/mixins/metrics/__init__.py new file mode 100644 index 0000000..2e4dd40 --- /dev/null +++ b/sprockets/mixins/metrics/__init__.py @@ -0,0 +1,2 @@ +version_info = (0, 0, 0) +__version__ = '.'.join(str(v) for v in version_info) diff --git a/tests.py b/tests.py new file mode 100644 index 0000000..e69de29