[Python-modules-commits] [python-aiomeasures] 01/03: import python-aiomeasures_0.5.14.orig.tar.gz
Ondrej Novy
onovy at debian.org
Sun Feb 4 09:20:31 UTC 2018
This is an automated email from the git hooks/post-receive script.
onovy pushed a commit to branch master
in repository python-aiomeasures.
commit 0b9588e529aa3e53bb603ea5551c99427a451315
Author: Ondřej Nový <onovy at debian.org>
Date: Sun Feb 4 10:14:34 2018 +0100
import python-aiomeasures_0.5.14.orig.tar.gz
---
.coveragerc | 14 +
.gitattributes | 1 +
.gitignore | 62 +
.gitlab-ci.yml | 33 +
LICENSE | 20 +
MANIFEST.in | 2 +
README.rst | 25 +
aiomeasures/__init__.py | 19 +
aiomeasures/_version.py | 460 +++++
aiomeasures/checks.py | 24 +
aiomeasures/clients/__init__.py | 4 +
aiomeasures/clients/bases.py | 101 +
aiomeasures/clients/datadog/__init__.py | 1 +
aiomeasures/clients/datadog/client.py | 48 +
aiomeasures/clients/datadog/formatting.py | 156 ++
aiomeasures/clients/statsd/__init__.py | 1 +
aiomeasures/clients/statsd/client.py | 48 +
aiomeasures/clients/statsd/formatting.py | 156 ++
aiomeasures/collectors.py | 28 +
aiomeasures/events.py | 57 +
aiomeasures/metrics.py | 86 +
aiomeasures/reporters/__init__.py | 3 +
aiomeasures/reporters/statsd_reporter.py | 89 +
aiomeasures/util.py | 61 +
docs/Makefile | 192 ++
docs/conf.py | 296 +++
docs/index.rst | 13 +
docs/make.bat | 263 +++
docs/requirements.txt | 3 +
requirements-test.txt | 4 +
runtests.py | 3123 +++++++++++++++++++++++++++++
setup.cfg | 18 +
setup.py | 55 +
tests/conftest.py | 16 +
tests/test_datadog.py | 172 ++
tests/test_statsd.py | 172 ++
versioneer.py | 1699 ++++++++++++++++
37 files changed, 7525 insertions(+)
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..8aae4f8
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,14 @@
+[run]
+omit =
+ aiomeasures/_version.py
+
+[report]
+exclude_lines =
+ pragma: no cover
+ def __repr__
+ if self.debug:
+ if settings.DEBUG
+ raise AssertionError
+ raise NotImplementedError
+ if 0:
+ if __name__ == .__main__.:
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..0d40700
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+aiomeasures/_version.py export-subst
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..794c8b2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,62 @@
+# Created by https://www.gitignore.io/api/python
+
+### Python ###
+# 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
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+.env
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..d055eda
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,33 @@
+before_script:
+ - python -m pip install -e .
+ - python -m pip install -r requirements-test.txt
+
+python3.3 tests:
+ script:
+ - py.test --cov aiomeasures --cov-report term-missing tests/
+ tags:
+ - python3.3
+
+python3.4 tests:
+ script:
+ - py.test --cov aiomeasures --cov-report term-missing tests/
+ tags:
+ - python3.4
+
+python3.5 tests:
+ script:
+ - py.test --cov aiomeasures --cov-report term-missing tests/
+ tags:
+ - python3.5
+
+publish to pypi:
+ type: deploy
+ script:
+ - python -m pip install twine wheel
+ - python setup.py sdist bdist_wheel
+ - twine upload -u $PYPI_USER -p $PYPI_PASSWORD dist/*
+ tags:
+ - python3.4
+ only:
+ - /^v[\d\.]+.*$/
+ allow_failure: true
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a0f0f20
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright © 2015,
+
+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.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..8fc97a0
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+include versioneer.py
+include aiomeasures/_version.py
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..a4d883a
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,25 @@
+AIO Measures
+============
+
+This library allows you to send metrics to your Datadog or Statsd server.
+This works on Python >= 3.3 and relies on asyncio.
+
+
+Installation::
+
+ python -m pip install aiomeasures
+
+
+Usage::
+
+ from aiomeasures import Datadog
+
+ client = Datadog('udp://127.0.0.1:6789')
+ client.incr('foo')
+ client.decr('bar', tags={'one': 'two'})
+ with client.timer('baz'):
+ # long process
+ pass
+
+
+The client will send metrics to agent as possible.
\ No newline at end of file
diff --git a/aiomeasures/__init__.py b/aiomeasures/__init__.py
new file mode 100644
index 0000000..8676a75
--- /dev/null
+++ b/aiomeasures/__init__.py
@@ -0,0 +1,19 @@
+"""
+ AIO Measures
+ ~~~~~~~~~~~~
+
+"""
+
+from ._version import get_versions
+from .checks import *
+from .clients import *
+from .events import *
+from .metrics import *
+
+__all__ = (checks.__all__
+ + clients.__all__
+ + events.__all__
+ + metrics.__all__)
+
+__version__ = get_versions()['version']
+del get_versions
diff --git a/aiomeasures/_version.py b/aiomeasures/_version.py
new file mode 100644
index 0000000..ae7ae86
--- /dev/null
+++ b/aiomeasures/_version.py
@@ -0,0 +1,460 @@
+
+# This file helps to compute a version number in source trees obtained from
+# git-archive tarball (such as those provided by githubs download-from-tag
+# feature). Distribution tarballs (built by setup.py sdist) and build
+# directories (produced by setup.py build) will contain a much shorter file
+# that just contains the computed version number.
+
+# This file is released into the public domain. Generated by
+# versioneer-0.15 (https://github.com/warner/python-versioneer)
+
+import errno
+import os
+import re
+import subprocess
+import sys
+
+
+def get_keywords():
+ # these strings will be replaced by git during git-archive.
+ # setup.py/versioneer.py will grep for the variable names, so they must
+ # each be defined on a line of their own. _version.py will just call
+ # get_keywords().
+ git_refnames = " (HEAD -> master, tag: v0.5.14)"
+ git_full = "5bde8a08fb0ef6346e2e67c8203b84011a124315"
+ keywords = {"refnames": git_refnames, "full": git_full}
+ return keywords
+
+
+class VersioneerConfig:
+ pass
+
+
+def get_config():
+ # these strings are filled in when 'setup.py versioneer' creates
+ # _version.py
+ cfg = VersioneerConfig()
+ cfg.VCS = "git"
+ cfg.style = "pep440"
+ cfg.tag_prefix = "v"
+ cfg.parentdir_prefix = "None"
+ cfg.versionfile_source = "/_version.py"
+ cfg.verbose = False
+ return cfg
+
+
+class NotThisMethod(Exception):
+ pass
+
+
+LONG_VERSION_PY = {}
+HANDLERS = {}
+
+
+def register_vcs_handler(vcs, method): # decorator
+ def decorate(f):
+ if vcs not in HANDLERS:
+ HANDLERS[vcs] = {}
+ HANDLERS[vcs][method] = f
+ return f
+ return decorate
+
+
+def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False):
+ assert isinstance(commands, list)
+ p = None
+ for c in commands:
+ try:
+ dispcmd = str([c] + args)
+ # remember shell=False, so use git.cmd on windows, not just git
+ p = subprocess.Popen([c] + args, cwd=cwd, stdout=subprocess.PIPE,
+ stderr=(subprocess.PIPE if hide_stderr
+ else None))
+ break
+ except EnvironmentError:
+ e = sys.exc_info()[1]
+ if e.errno == errno.ENOENT:
+ continue
+ if verbose:
+ print("unable to run %s" % dispcmd)
+ print(e)
+ return None
+ else:
+ if verbose:
+ print("unable to find command, tried %s" % (commands,))
+ return None
+ stdout = p.communicate()[0].strip()
+ if sys.version_info[0] >= 3:
+ stdout = stdout.decode()
+ if p.returncode != 0:
+ if verbose:
+ print("unable to run %s (error)" % dispcmd)
+ return None
+ return stdout
+
+
+def versions_from_parentdir(parentdir_prefix, root, verbose):
+ # Source tarballs conventionally unpack into a directory that includes
+ # both the project name and a version string.
+ dirname = os.path.basename(root)
+ if not dirname.startswith(parentdir_prefix):
+ if verbose:
+ print("guessing rootdir is '%s', but '%s' doesn't start with "
+ "prefix '%s'" % (root, dirname, parentdir_prefix))
+ raise NotThisMethod("rootdir doesn't start with parentdir_prefix")
+ return {"version": dirname[len(parentdir_prefix):],
+ "full-revisionid": None,
+ "dirty": False, "error": None}
+
+
+ at register_vcs_handler("git", "get_keywords")
+def git_get_keywords(versionfile_abs):
+ # the code embedded in _version.py can just fetch the value of these
+ # keywords. When used from setup.py, we don't want to import _version.py,
+ # so we do it with a regexp instead. This function is not used from
+ # _version.py.
+ keywords = {}
+ try:
+ f = open(versionfile_abs, "r")
+ for line in f.readlines():
+ if line.strip().startswith("git_refnames ="):
+ mo = re.search(r'=\s*"(.*)"', line)
+ if mo:
+ keywords["refnames"] = mo.group(1)
+ if line.strip().startswith("git_full ="):
+ mo = re.search(r'=\s*"(.*)"', line)
+ if mo:
+ keywords["full"] = mo.group(1)
+ f.close()
+ except EnvironmentError:
+ pass
+ return keywords
+
+
+ at register_vcs_handler("git", "keywords")
+def git_versions_from_keywords(keywords, tag_prefix, verbose):
+ if not keywords:
+ raise NotThisMethod("no keywords at all, weird")
+ refnames = keywords["refnames"].strip()
+ if refnames.startswith("$Format"):
+ if verbose:
+ print("keywords are unexpanded, not using")
+ raise NotThisMethod("unexpanded keywords, not a git-archive tarball")
+ refs = set([r.strip() for r in refnames.strip("()").split(",")])
+ # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of
+ # just "foo-1.0". If we see a "tag: " prefix, prefer those.
+ TAG = "tag: "
+ tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)])
+ if not tags:
+ # Either we're using git < 1.8.3, or there really are no tags. We use
+ # a heuristic: assume all version tags have a digit. The old git %d
+ # expansion behaves like git log --decorate=short and strips out the
+ # refs/heads/ and refs/tags/ prefixes that would let us distinguish
+ # between branches and tags. By ignoring refnames without digits, we
+ # filter out many common branch names like "release" and
+ # "stabilization", as well as "HEAD" and "master".
+ tags = set([r for r in refs if re.search(r'\d', r)])
+ if verbose:
+ print("discarding '%s', no digits" % ",".join(refs-tags))
+ if verbose:
+ print("likely tags: %s" % ",".join(sorted(tags)))
+ for ref in sorted(tags):
+ # sorting will prefer e.g. "2.0" over "2.0rc1"
+ if ref.startswith(tag_prefix):
+ r = ref[len(tag_prefix):]
+ if verbose:
+ print("picking %s" % r)
+ return {"version": r,
+ "full-revisionid": keywords["full"].strip(),
+ "dirty": False, "error": None
+ }
+ # no suitable tags, so version is "0+unknown", but full hex is still there
+ if verbose:
+ print("no suitable tags, using unknown + full revision id")
+ return {"version": "0+unknown",
+ "full-revisionid": keywords["full"].strip(),
+ "dirty": False, "error": "no suitable tags"}
+
+
+ at register_vcs_handler("git", "pieces_from_vcs")
+def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command):
+ # this runs 'git' from the root of the source tree. This only gets called
+ # if the git-archive 'subst' keywords were *not* expanded, and
+ # _version.py hasn't already been rewritten with a short version string,
+ # meaning we're inside a checked out source tree.
+
+ if not os.path.exists(os.path.join(root, ".git")):
+ if verbose:
+ print("no .git in %s" % root)
+ raise NotThisMethod("no .git directory")
+
+ GITS = ["git"]
+ if sys.platform == "win32":
+ GITS = ["git.cmd", "git.exe"]
+ # if there is a tag, this yields TAG-NUM-gHEX[-dirty]
+ # if there are no tags, this yields HEX[-dirty] (no NUM)
+ describe_out = run_command(GITS, ["describe", "--tags", "--dirty",
+ "--always", "--long"],
+ cwd=root)
+ # --long was added in git-1.5.5
+ if describe_out is None:
+ raise NotThisMethod("'git describe' failed")
+ describe_out = describe_out.strip()
+ full_out = run_command(GITS, ["rev-parse", "HEAD"], cwd=root)
+ if full_out is None:
+ raise NotThisMethod("'git rev-parse' failed")
+ full_out = full_out.strip()
+
+ pieces = {}
+ pieces["long"] = full_out
+ pieces["short"] = full_out[:7] # maybe improved later
+ pieces["error"] = None
+
+ # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty]
+ # TAG might have hyphens.
+ git_describe = describe_out
+
+ # look for -dirty suffix
+ dirty = git_describe.endswith("-dirty")
+ pieces["dirty"] = dirty
+ if dirty:
+ git_describe = git_describe[:git_describe.rindex("-dirty")]
+
+ # now we have TAG-NUM-gHEX or HEX
+
+ if "-" in git_describe:
+ # TAG-NUM-gHEX
+ mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe)
+ if not mo:
+ # unparseable. Maybe git-describe is misbehaving?
+ pieces["error"] = ("unable to parse git-describe output: '%s'"
+ % describe_out)
+ return pieces
+
+ # tag
+ full_tag = mo.group(1)
+ if not full_tag.startswith(tag_prefix):
+ if verbose:
+ fmt = "tag '%s' doesn't start with prefix '%s'"
+ print(fmt % (full_tag, tag_prefix))
+ pieces["error"] = ("tag '%s' doesn't start with prefix '%s'"
+ % (full_tag, tag_prefix))
+ return pieces
+ pieces["closest-tag"] = full_tag[len(tag_prefix):]
+
+ # distance: number of commits since tag
+ pieces["distance"] = int(mo.group(2))
+
+ # commit: short hex revision ID
+ pieces["short"] = mo.group(3)
+
+ else:
+ # HEX: no tags
+ pieces["closest-tag"] = None
+ count_out = run_command(GITS, ["rev-list", "HEAD", "--count"],
+ cwd=root)
+ pieces["distance"] = int(count_out) # total number of commits
+
+ return pieces
+
+
+def plus_or_dot(pieces):
+ if "+" in pieces.get("closest-tag", ""):
+ return "."
+ return "+"
+
+
+def render_pep440(pieces):
+ # now build up version string, with post-release "local version
+ # identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you
+ # get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty
+
+ # exceptions:
+ # 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty]
+
+ if pieces["closest-tag"]:
+ rendered = pieces["closest-tag"]
+ if pieces["distance"] or pieces["dirty"]:
+ rendered += plus_or_dot(pieces)
+ rendered += "%d.g%s" % (pieces["distance"], pieces["short"])
+ if pieces["dirty"]:
+ rendered += ".dirty"
+ else:
+ # exception #1
+ rendered = "0+untagged.%d.g%s" % (pieces["distance"],
+ pieces["short"])
+ if pieces["dirty"]:
+ rendered += ".dirty"
+ return rendered
+
+
+def render_pep440_pre(pieces):
+ # TAG[.post.devDISTANCE] . No -dirty
+
+ # exceptions:
+ # 1: no tags. 0.post.devDISTANCE
+
+ if pieces["closest-tag"]:
+ rendered = pieces["closest-tag"]
+ if pieces["distance"]:
+ rendered += ".post.dev%d" % pieces["distance"]
+ else:
+ # exception #1
+ rendered = "0.post.dev%d" % pieces["distance"]
+ return rendered
+
+
+def render_pep440_post(pieces):
+ # TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that
+ # .dev0 sorts backwards (a dirty tree will appear "older" than the
+ # corresponding clean one), but you shouldn't be releasing software with
+ # -dirty anyways.
+
+ # exceptions:
+ # 1: no tags. 0.postDISTANCE[.dev0]
+
+ if pieces["closest-tag"]:
+ rendered = pieces["closest-tag"]
+ if pieces["distance"] or pieces["dirty"]:
+ rendered += ".post%d" % pieces["distance"]
+ if pieces["dirty"]:
+ rendered += ".dev0"
+ rendered += plus_or_dot(pieces)
+ rendered += "g%s" % pieces["short"]
+ else:
+ # exception #1
+ rendered = "0.post%d" % pieces["distance"]
+ if pieces["dirty"]:
+ rendered += ".dev0"
+ rendered += "+g%s" % pieces["short"]
+ return rendered
+
+
+def render_pep440_old(pieces):
+ # TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty.
+
+ # exceptions:
+ # 1: no tags. 0.postDISTANCE[.dev0]
+
+ if pieces["closest-tag"]:
+ rendered = pieces["closest-tag"]
+ if pieces["distance"] or pieces["dirty"]:
+ rendered += ".post%d" % pieces["distance"]
+ if pieces["dirty"]:
+ rendered += ".dev0"
+ else:
+ # exception #1
+ rendered = "0.post%d" % pieces["distance"]
+ if pieces["dirty"]:
+ rendered += ".dev0"
+ return rendered
+
+
+def render_git_describe(pieces):
+ # TAG[-DISTANCE-gHEX][-dirty], like 'git describe --tags --dirty
+ # --always'
+
+ # exceptions:
+ # 1: no tags. HEX[-dirty] (note: no 'g' prefix)
+
+ if pieces["closest-tag"]:
+ rendered = pieces["closest-tag"]
+ if pieces["distance"]:
+ rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
+ else:
+ # exception #1
+ rendered = pieces["short"]
+ if pieces["dirty"]:
+ rendered += "-dirty"
+ return rendered
+
+
+def render_git_describe_long(pieces):
+ # TAG-DISTANCE-gHEX[-dirty], like 'git describe --tags --dirty
+ # --always -long'. The distance/hash is unconditional.
+
+ # exceptions:
+ # 1: no tags. HEX[-dirty] (note: no 'g' prefix)
+
+ if pieces["closest-tag"]:
+ rendered = pieces["closest-tag"]
+ rendered += "-%d-g%s" % (pieces["distance"], pieces["short"])
+ else:
+ # exception #1
+ rendered = pieces["short"]
+ if pieces["dirty"]:
+ rendered += "-dirty"
+ return rendered
+
+
+def render(pieces, style):
+ if pieces["error"]:
+ return {"version": "unknown",
+ "full-revisionid": pieces.get("long"),
+ "dirty": None,
+ "error": pieces["error"]}
+
+ if not style or style == "default":
+ style = "pep440" # the default
+
+ if style == "pep440":
+ rendered = render_pep440(pieces)
+ elif style == "pep440-pre":
+ rendered = render_pep440_pre(pieces)
+ elif style == "pep440-post":
+ rendered = render_pep440_post(pieces)
+ elif style == "pep440-old":
+ rendered = render_pep440_old(pieces)
+ elif style == "git-describe":
+ rendered = render_git_describe(pieces)
+ elif style == "git-describe-long":
+ rendered = render_git_describe_long(pieces)
+ else:
+ raise ValueError("unknown style '%s'" % style)
+
+ return {"version": rendered, "full-revisionid": pieces["long"],
+ "dirty": pieces["dirty"], "error": None}
+
+
+def get_versions():
+ # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have
+ # __file__, we can work backwards from there to the root. Some
+ # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which
+ # case we can only use expanded keywords.
+
+ cfg = get_config()
+ verbose = cfg.verbose
+
+ try:
+ return git_versions_from_keywords(get_keywords(), cfg.tag_prefix,
+ verbose)
+ except NotThisMethod:
+ pass
+
+ try:
+ root = os.path.realpath(__file__)
+ # versionfile_source is the relative path from the top of the source
+ # tree (where the .git directory might live) to this file. Invert
+ # this to find the root from __file__.
+ for i in cfg.versionfile_source.split('/'):
+ root = os.path.dirname(root)
+ except NameError:
+ return {"version": "0+unknown", "full-revisionid": None,
+ "dirty": None,
+ "error": "unable to find root of source tree"}
+
+ try:
+ pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose)
+ return render(pieces, cfg.style)
+ except NotThisMethod:
+ pass
+
+ try:
+ if cfg.parentdir_prefix:
+ return versions_from_parentdir(cfg.parentdir_prefix, root, verbose)
+ except NotThisMethod:
+ pass
+
+ return {"version": "0+unknown", "full-revisionid": None,
+ "dirty": None,
+ "error": "unable to compute version"}
diff --git a/aiomeasures/checks.py b/aiomeasures/checks.py
new file mode 100644
index 0000000..c9e14af
--- /dev/null
+++ b/aiomeasures/checks.py
@@ -0,0 +1,24 @@
+__all__ = ['Check']
+
+
+class Check:
+
+ __slots__ = ('name', 'status', 'timestamp', 'hostname', 'tags', 'message')
+
+ def __init__(self, name, status, timestamp=None,
+ hostname=None, tags=None, message=None):
+ self.name = name
+ self.status = status
+ self.timestamp = timestamp
+ self.hostname = hostname
+ self.tags = tags
+ self.message = message
+
+ def __repr__(self):
+ args = ['%s=%s' for attr in ()]
+ attrs = ('name', 'status', 'tags')
+ for attr in attrs:
+ value = getattr(self, attr, None)
+ if value is not None:
+ args.append('%s=%r' % (attr, value))
+ return '<%s(%s)>' % (self.__class__.__name__, ', '.join(args))
diff --git a/aiomeasures/clients/__init__.py b/aiomeasures/clients/__init__.py
new file mode 100644
index 0000000..2514e63
--- /dev/null
+++ b/aiomeasures/clients/__init__.py
@@ -0,0 +1,4 @@
+from .datadog import Datadog
+from .statsd import StatsD
+
+__all__ = ['Datadog', 'StatsD']
diff --git a/aiomeasures/clients/bases.py b/aiomeasures/clients/bases.py
new file mode 100644
index 0000000..9ddbcd8
--- /dev/null
+++ b/aiomeasures/clients/bases.py
@@ -0,0 +1,101 @@
+import asyncio
+from abc import ABCMeta, abstractmethod
+from aiomeasures.checks import Check
+from aiomeasures.events import Event
+from aiomeasures.metrics import CountingMetric, GaugeMetric
+from aiomeasures.metrics import HistogramMetric, SetMetric, TimingMetric
+from time import perf_counter
+
+
+class Client(metaclass=ABCMeta):
+
+ def incr(self, name, value=None, rate=None, tags=None):
+ value = abs(value or 1)
+ metric = CountingMetric(name, value, rate=rate, tags=tags)
+ return self.register(metric)
+
+ def decr(self, name, value=None, rate=None, tags=None):
+ value = -abs(value or 1)
+ metric = CountingMetric(name, value, rate=rate, tags=tags)
+ return self.register(metric)
+
+ def counter(self, name, value, rate=None, tags=None):
+ metric = CountingMetric(name, value, rate=rate, tags=tags)
+ return self.register(metric)
+
+ def timing(self, name, value=None, rate=None, tags=None):
+ metric = TimingMetric(name, value, rate=rate, tags=tags)
+ return self.register(metric)
+
+ def timer(self, name, rate=None, tags=None):
+ return Timer(client=self, name=name, rate=rate, tags=tags)
+
+ def gauge(self, name, value, rate=None, delta=False):
+ metric = GaugeMetric(name, value, rate=rate, delta=delta)
+ return self.register(metric)
+
+ def histogram(self, name, value, rate=None, delta=False):
+ metric = HistogramMetric(name, value, rate=rate, delta=delta)
+ return self.register(metric)
+
+ def set(self, name, value, rate=None, tags=None):
+ metric = SetMetric(name, value, rate=rate, tags=tags)
+ return self.register(metric)
+
+ def event(self, title, text, **kwargs):
+ event = Event(title, text, **kwargs)
+ return self.register(event)
+
+ def check(self, name, status, **kwargs):
+ check = Check(name, status, **kwargs)
+ return self.register(check)
+
+ @abstractmethod
+ def format(self, metric, prefix=None):
+ raise NotImplementedError()
+
+ @asyncio.coroutine
+ @abstractmethod
+ def send(self):
+ """Sends key/value pairs via UDP or TCP.
+ """
+ raise NotImplementedError()
+
+ @abstractmethod
+ def register(self, metric):
+ raise NotImplementedError()
+
+ @abstractmethod
+ def close(self):
+ raise NotImplementedError()
+
+
+class Timer:
+
+ def __init__(self, client, name, rate=None, tags=None):
+ self.client = client
+ self.name = name
+ self.rate = rate
+ self.tags = tags
+
+ def __call__(self, func):
+ def wrapper(*args, **kwargs):
+ try:
+ self.start()
+ return func(*args, **kwargs)
+ finally:
+ self.stop()
+ return wrapper
+
+ def __enter__(self):
+ self.start()
+
+ def __exit__(self, type, value, tb):
+ self.stop()
+
+ def start(self):
+ self._started = perf_counter()
+
+ def stop(self):
+ value = int((perf_counter() - self._started) * 1000)
+ self.client.timing(self.name, value, rate=self.rate, tags=self.tags)
diff --git a/aiomeasures/clients/datadog/__init__.py b/aiomeasures/clients/datadog/__init__.py
new file mode 100644
index 0000000..421945b
--- /dev/null
+++ b/aiomeasures/clients/datadog/__init__.py
@@ -0,0 +1 @@
+from .client import *
diff --git a/aiomeasures/clients/datadog/client.py b/aiomeasures/clients/datadog/client.py
new file mode 100644
index 0000000..c0dab2c
--- /dev/null
+++ b/aiomeasures/clients/datadog/client.py
@@ -0,0 +1,48 @@
+import asyncio
+import logging
+from . import formatting
+from aiomeasures.clients.bases import Client
+from aiomeasures.collectors import Collector
+from aiomeasures.reporters import StatsDReporter
+from random import random
+
+
+class Datadog(Client):
+
+ def __init__(self, addr, *, prefix=None, tags=None, loop=None):
+ """Sends statistics to the stats daemon over UDP
+
+ Parameters:
+ addr (str): the address in the form udp://host:port
+ loop (EventLoop): the event loop
+ prefix (str): prefix for all keys
+ tags (dict): default tags for everything
+ """
+ self.loop = loop or asyncio.get_event_loop()
+ self.log = logging.getLogger(__name__)
+ self.prefix = prefix
+ self.tags = tags
+ self.collector = Collector([], 5000)
+ self.reporter = StatsDReporter(addr, loop=self.loop)
+
+ def register(self, metric):
+ self.collector.append(metric)
+ asyncio.Task(self.send(), loop=self.loop)
+ # self.loop.async(self.send())
+ return metric
+
+ def format(self, obj):
+ return formatting.format(obj, self.prefix, self.tags)
+
+ @asyncio.coroutine
+ def send(self):
+ """Sends key/value pairs to Datadog agent.
+ """
+ yield from self.reporter.connect()
+ rate = random()
+ formatter = self.format
+ metrics = self.collector.flush(rate=rate, formatter=formatter)
+ yield from self.reporter.send(metrics)
+
+ def close(self):
+ self.reporter.close()
diff --git a/aiomeasures/clients/datadog/formatting.py b/aiomeasures/clients/datadog/formatting.py
new file mode 100644
index 0000000..089149b
--- /dev/null
+++ b/aiomeasures/clients/datadog/formatting.py
@@ -0,0 +1,156 @@
+from aiomeasures.checks import Check
+from aiomeasures.events import Event
+from aiomeasures.metrics import CountingMetric, GaugeMetric
+from aiomeasures.metrics import HistogramMetric, SetMetric, TimingMetric
+from collections.abc import Mapping
+from datetime import datetime, timedelta
+from decimal import Decimal
+try:
+ from functools import singledispatch
+except:
+ from singledispatch import singledispatch
+
+
+ at singledispatch
+def format(obj, prefix=None, tags=None):
+ raise ValueError('Cannot consume %r' % obj)
+
+
+ at format.register(Check)
+def format_check(check, prefix=None, tags=None):
+ response = '_sc|%s' % check.name
+ if check.status in (0, 'ok', 'OK'):
+ response += '|0'
+ if check.status in (1, 'warn', 'warning', 'WARNING'):
+ response += '|1'
+ if check.status in (2, 'crit', 'critical', 'CRITICAL'):
+ response += '|2'
+ if check.status in (3, 'unknown', 'UNKNOWN'):
+ response += '|3'
+
+ if check.timestamp:
+ response += '|d:%s' % format_timestamp(check.timestamp)
+ if check.hostname:
+ response += '|h:%s' % check.hostname
+ if check.tags or tags:
+ tags = format_tags(check.tags, tags)
+ response += '|#%s' % ','.join(tags)
+ if check.message:
+ response += '|m:%s' % check.message
+ return response
+
+
+ at format.register(Event)
+def format_event(event, prefix=None, tags=None):
+ a, b = len(event.title), len(event.text)
+ response = '_e{%s,%s}%s|%s' % (a, b, event.title, event.text)
+ if event.date_happened:
+ response += '|d:%s' % format_timestamp(event.date_happened)
+ if event.hostname:
... 6795 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-aiomeasures.git
More information about the Python-modules-commits
mailing list