Skip to content

Commit

Permalink
namespace loggers inherit Flask logger config
Browse files Browse the repository at this point in the history
  • Loading branch information
SteadBytes committed Sep 1, 2019
1 parent 3a17d1f commit bdb8eed
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 28 deletions.
26 changes: 19 additions & 7 deletions flask_restplus/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,7 @@ def __init__(self, app=None, version='1.0', title=None, description=None,
self._refresolver = None
self.format_checker = format_checker
self.namespaces = []
self.default_namespace = self.namespace(default, default_label,
endpoint='{0}-declaration'.format(default),
validate=validate,
api=self,
path='/',
)

self.ns_paths = dict()

self.representations = OrderedDict(DEFAULT_REPRESENTATIONS)
Expand All @@ -146,7 +141,14 @@ def __init__(self, app=None, version='1.0', title=None, description=None,
self.resources = []
self.app = None
self.blueprint = None

# must come after self.app initialisation to prevent __getattr__ recursion
# in self._configure_namespace_logger
self.default_namespace = self.namespace(default, default_label,
endpoint='{0}-declaration'.format(default),
validate=validate,
api=self,
path='/',
)
if app is not None:
self.app = app
self.init_app(app)
Expand Down Expand Up @@ -205,6 +207,9 @@ def _init_app(self, app):
for resource, namespace, urls, kwargs in self.resources:
self._register_view(app, resource, namespace, *urls, **kwargs)

for ns in self.namespaces:
self._configure_namespace_logger(app, ns)

self._register_apidoc(app)
self._validate = self._validate if self._validate is not None else app.config.get('RESTPLUS_VALIDATE', False)
app.config.setdefault('RESTPLUS_MASK_HEADER', 'X-Fields')
Expand Down Expand Up @@ -266,6 +271,11 @@ def register_resource(self, namespace, resource, *urls, **kwargs):
self.resources.append((resource, namespace, urls, kwargs))
return endpoint

def _configure_namespace_logger(self, app, namespace):
for handler in app.logger.handlers:
namespace.logger.addHandler(handler)
namespace.logger.setLevel(app.logger.level)

def _register_view(self, app, resource, namespace, *urls, **kwargs):
endpoint = kwargs.pop('endpoint', None) or camel_to_dash(resource.__name__)
resource_class_args = kwargs.pop('resource_class_args', ())
Expand Down Expand Up @@ -427,6 +437,8 @@ def add_namespace(self, ns, path=None):
# Register models
for name, definition in six.iteritems(ns.models):
self.models[name] = definition
if not self.blueprint and self.app is not None:
self._configure_namespace_logger(self.app, ns)

def namespace(self, *args, **kwargs):
'''
Expand Down
12 changes: 12 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@ def api(request, app):
yield api


@pytest.fixture
def mock_app(mocker):
app = mocker.Mock(Flask)
# mock Flask app object doesn't have any real loggers -> mock logging
# set up on Api object
mocker.patch.object(restplus.Api, '_configure_namespace_logger')
app.view_functions = {}
app.extensions = {}
app.config = {}
return app


@pytest.fixture(autouse=True)
def _push_custom_request_context(request):
app = request.getfixturevalue('app')
Expand Down
31 changes: 10 additions & 21 deletions tests/legacy/test_api_legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import flask
import pytest
import six
import logging

from json import dumps, JSONEncoder

Expand Down Expand Up @@ -126,21 +127,17 @@ def test_media_types_q(self, app):
}):
assert api.mediatypes() == ['application/json', 'application/xml']

def test_decorator(self, mocker):
def test_decorator(self, mocker, mock_app):
def return_zero(func):
return 0

app = mocker.Mock(flask.Flask)
app.view_functions = {}
app.extensions = {}
app.config = {}
view = mocker.Mock()
api = restplus.Api(app)
api = restplus.Api(mock_app)
api.decorators.append(return_zero)
api.output = mocker.Mock()
api.add_resource(view, '/foo', endpoint='bar')

app.add_url_rule.assert_called_with('/foo', view_func=0)
mock_app.add_url_rule.assert_called_with('/foo', view_func=0)

def test_add_resource_endpoint(self, app, mocker):
view = mocker.Mock(**{'as_view.return_value.__name__': str('test_view')})
Expand Down Expand Up @@ -181,28 +178,20 @@ def get(self):
foo2 = client.get('/foo/toto')
assert foo2.data == b'"foo1"\n'

def test_add_resource(self, mocker):
app = mocker.Mock(flask.Flask)
app.view_functions = {}
app.extensions = {}
app.config = {}
api = restplus.Api(app)
def test_add_resource(self, mocker, mock_app):
api = restplus.Api(mock_app)
api.output = mocker.Mock()
api.add_resource(views.MethodView, '/foo')

app.add_url_rule.assert_called_with('/foo',
mock_app.add_url_rule.assert_called_with('/foo',
view_func=api.output())

def test_add_resource_kwargs(self, mocker):
app = mocker.Mock(flask.Flask)
app.view_functions = {}
app.extensions = {}
app.config = {}
api = restplus.Api(app)
def test_add_resource_kwargs(self, mocker, mock_app):
api = restplus.Api(mock_app)
api.output = mocker.Mock()
api.add_resource(views.MethodView, '/foo', defaults={"bar": "baz"})

app.add_url_rule.assert_called_with('/foo',
mock_app.add_url_rule.assert_called_with('/foo',
view_func=api.output(),
defaults={"bar": "baz"})

Expand Down

0 comments on commit bdb8eed

Please sign in to comment.