Skip to content

Commit

Permalink
Merge pull request #23 from samstav/master
Browse files Browse the repository at this point in the history
packaging and CI refactor
  • Loading branch information
phumpal committed Dec 9, 2015
2 parents 4c9f8bf + a708b26 commit 01bc275
Show file tree
Hide file tree
Showing 15 changed files with 259 additions and 107 deletions.
27 changes: 27 additions & 0 deletions airbrake/__about__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Airbrake-Python package attributes and metadata."""

__all__ = (
'__title__',
'__summary__',
'__author__',
'__email__',
'__version__',
'__keywords__',
'__license__',
'__url__',
'__notifier__',
)

__title__ = 'airbrake'
__summary__ = 'Python SDK for airbrake.io'
__author__ = 'BK Box, Sam Stavinoha'
__email__ = 'samuel.stavinoha@rackspace.com'
__version__ = '1.1.3'
__keywords__ = ['airbrake', 'exceptions', 'airbrake.io']
__license__ = 'Apache License, Version 2.0'
__url__ = 'https://github.com/airbrake/airbrake-python'
__notifier__ = {
'name': 'airbrake-python',
'version': __version__,
'url': __url__,
}
38 changes: 15 additions & 23 deletions airbrake/__init__.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@
"""
airbrake-python
~~~~~~~~~~~~~~~
"""airbrake-python.
Client for sending python exceptions to airbrake.io
Python SDK for airbrake.io
"""

__version__ = "1.1.3"
__url__ = "https://github.com/airbrake/airbrake-python"
_notifier = {
'name': 'airbrake-python',
'version': __version__,
'url': __url__
}
# pylint: disable=wildcard-import

import inspect
import logging
import os

from airbrake import utils
from airbrake.notifier import Airbrake
from airbrake.handler import AirbrakeHandler

logging.basicConfig()

from airbrake.__about__ import * # noqa
from airbrake import exc # noqa
from airbrake.notifier import Airbrake # noqa
from airbrake.handler import AirbrakeHandler # noqa

def getLogger(name=None, **kwargs):

def getLogger(name=None, **kwargs): # pylint: disable=invalid-name
"""Return a Logger with an AirbrakeHandler."""
if not name:
curframe = inspect.currentframe()
callingpath = inspect.getouterframes(curframe, 2)[1][1]
Expand All @@ -35,16 +26,17 @@ def getLogger(name=None, **kwargs):
logger = logging.getLogger(name)

if not has_airbrake_handler(logger):
ab = AirbrakeHandler(**kwargs)
logger.addHandler(ab)
abh = AirbrakeHandler(**kwargs)
logger.addHandler(abh)
if logger.getEffectiveLevel() == logging.NOTSET:
logger.setLevel(ab.level)
elif not logger.isEnabledFor(ab.level):
logger.setLevel(ab.level)
logger.setLevel(abh.level)
elif not logger.isEnabledFor(abh.level):
logger.setLevel(abh.level)

return logger


def has_airbrake_handler(logger):
"""Check a logger for an AirbrakeHandler."""
return any([isinstance(handler, AirbrakeHandler)
for handler in logger.handlers])
11 changes: 11 additions & 0 deletions airbrake/exc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Airbrake-Python custom exceptions."""


class AirbrakeException(Exception):

"""Base class for errors thrown by airbrake-python."""


class AirbrakeNotConfigured(AirbrakeException):

"""The client was not given id and key nor found in env."""
11 changes: 5 additions & 6 deletions airbrake/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,21 @@
"""
import logging

from airbrake import Airbrake
from airbrake.notifier import Airbrake

_FAKE_LOGRECORD = logging.LogRecord('', '', '', '', '', '', '', '')


class AirbrakeHandler(logging.Handler):

"""
A handler class which ships logs to airbrake.io
"""A handler class which ships logs to airbrake.io.
Requires one:
* `project_id` AND `api_key`
* an instance of airbrake.Airbrake
"""

def __init__(self, airbrake=None, level=logging.ERROR, project_id=None, api_key=None, environment=None):
def __init__(self, airbrake=None, level=logging.ERROR, project_id=None,
api_key=None, environment=None):
"""Initialize the Airbrake handler with a default logging level.
Default level of logs handled by this class are >= ERROR,
Expand Down Expand Up @@ -64,7 +63,7 @@ def emit(self, record):
self.airbrake.log(**airbrakeerror)
except (KeyboardInterrupt, SystemExit):
raise
except:
except: # pylint: disable=bare-except
self.handleError(record)


Expand Down
28 changes: 16 additions & 12 deletions airbrake/notifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Initialize this class to ship errors to airbrake.io
using the log() method.
"""

import json
import os
import platform
Expand All @@ -12,7 +13,8 @@

import requests

from airbrake import _notifier as airbrake_python_notifier
from airbrake.__about__ import __notifier__
from airbrake import exc as ab_exc
from airbrake import utils


Expand Down Expand Up @@ -41,12 +43,12 @@ class Airbrake(object):
"""

def __init__(self, project_id=None, api_key=None, environment=None):

"""Client constructor."""
# properties
self._api_url = None
self._context = None
self.deploy_url = "http://api.airbrake.io/deploys.txt"
self.notifier = airbrake_python_notifier
self.notifier = __notifier__

if not environment:
environment = (os.getenv('AIRBRAKE_ENVIRONMENT') or
Expand All @@ -61,21 +63,24 @@ def __init__(self, project_id=None, api_key=None, environment=None):
self.api_key = str(api_key)

if not all((self.project_id, self.api_key)):
raise TypeError("Airbrake API Key (api_key) and Project ID "
"(project_id) must be set. These values "
"may be set using the environment variables "
"AIRBRAKE_API_KEY and AIRBRAKE_PROJECT_ID or "
"by passing in the arguments explicitly.")
raise ab_exc.AirbrakeNotConfigured(
"Airbrake API Key (api_key) and Project ID "
"(project_id) must be set. These values "
"may be set using the environment variables "
"AIRBRAKE_API_KEY and AIRBRAKE_PROJECT_ID or "
"by passing in the arguments explicitly."
)

self._exc_queue = utils.CheckableQueue()

def __repr__(self):
"""Return value for the repr function."""
return ("Airbrake(project_id=%s, api_key=*****, environment=%s)"
% (self.project_id, self.environment))

@property
def context(self):
"""Contains the python, os, and app environment context."""
"""The python, os, and app environment context."""
if not self._context:
self._context = {}
# python
Expand Down Expand Up @@ -120,7 +125,6 @@ def log(self, exc_info=None, message=None, filename=None,
Exception info willl be read from sys.exc_info() if it is not
supplied. To prevent this behavior, pass exc_info=False.
"""

if not utils.is_exc_info_tuple(exc_info):
# compatibility, allows exc_info not to be a exc tuple
errmessage = None
Expand Down Expand Up @@ -202,7 +206,7 @@ def deploy(self, env=None):
return response


class Error(object):
class Error(object): # pylint: disable=too-few-public-methods
"""Format the exception according to airbrake.io v3 API.
The airbrake.io docs used to implements this class are here:
Expand All @@ -211,7 +215,7 @@ class Error(object):

def __init__(self, exc_info=None, message=None, filename=None,
line=None, function=None, errtype=None):

"""Error object constructor."""
self.exc_info = exc_info

# default datastructure
Expand Down
20 changes: 12 additions & 8 deletions airbrake/utils.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,39 @@
"""Util functions/classes for airbrake-python."""

try:
from queue import Queue, Full, Empty
except ImportError:
#Py2 legacy fix
# Py2 legacy fix
from Queue import Queue, Full, Empty

import traceback
import types
import sys

if sys.version_info < (3,):
#Py2 legacy fix
type_of_type = types.TypeType
else:
type_of_type = type
try:
TypeType = types.TypeType
except AttributeError:
# For >= Python 3
TypeType = type


class CheckableQueue(Queue):

"""Checkable FIFO Queue which makes room for new items."""

def __init__(self, maxsize=1000):
"""Queue constructor."""
Queue.__init__(self, maxsize=maxsize)

def __contains__(self, item):
"""Check the Queue for the existence of an item."""
try:
with self.mutex:
return item in self.queue
except AttributeError:
return item in self.queue

def put(self, item, block=False, timeout=1):
"""Add an item to the Queue."""
try:
Queue.put(self, item, block=block, timeout=timeout)
except Full:
Expand All @@ -50,7 +54,7 @@ def is_exc_info_tuple(exc_info):
errtype, value, tback = exc_info
if all([x is None for x in exc_info]):
return True
elif all((isinstance(errtype, type_of_type),
elif all((isinstance(errtype, TypeType),
isinstance(value, Exception),
isinstance(tback, types.TracebackType))):
return True
Expand Down
14 changes: 14 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
machine:
python:
version: '2.7.10'

dependencies:
override:
- pip -V
- pip install -U pip
- pip install -U tox tox-pyenv
- pyenv local 2.7.9 3.3.3 3.4.3 3.5.0

test:
override:
- tox -v --recreate
20 changes: 20 additions & 0 deletions pylintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[Messages Control]
disable=
bad-builtin,
locally-disabled,
fixme,

[BASIC]
# Don't require docstrings on tests or Python Special Methods
no-docstring-rgx=(__.*__|[tT]est.*|setUp|tearDown)
# Some names we are okay with
good-names=f,e,i,j,k,v,ex,_,x,y,z,kw
# No camel case globals, max length 30
const-rgx=(^([A-Za-z_](?!(([A-Z][a-z0-9]+)+))){2,30}$)
max-args = 7
max-attributes = 8

[REPORTS]
msg-template={msg_id}({symbol}): {path},{line}: {msg} | {obj}
reports=yes
output-format=colorized
3 changes: 1 addition & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
requests==2.3.0
wsgiref==0.1.2
requests==2.8.1
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[wheel]
universal = 1

0 comments on commit 01bc275

Please sign in to comment.