[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