Skip to content

Commit

Permalink
move all the setting back ahead
Browse files Browse the repository at this point in the history
  • Loading branch information
danpoland committed Mar 16, 2017
1 parent 3695671 commit 09e1ad0
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 6 deletions.
8 changes: 7 additions & 1 deletion pyramid_restful/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
__version__ = '0.6.8'
from .settings import reload_api_settings

__version__ = '0.7.0'

VERSION = __version__


def includeme(config):
reload_api_settings(config.registry.settings)
16 changes: 13 additions & 3 deletions pyramid_restful/generics.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from .views import APIView

from pyramid.httpexceptions import HTTPNotFound
from sqlalchemy import Column
from sqlalchemy.orm.exc import NoResultFound

from . import mixins
Expand All @@ -10,13 +9,25 @@
class GenericAPIView(APIView):
"""
Provide default functionality for working with RESTFul endpoints.
pagination_class can be overridden as a class attribute:
class MyView(GenericAPIView):
pagination_class = MyPager
"""

@property
def pagination_class(self):
if not hasattr(self, '_pagination_class'):
# make sure we have the most up to date settings
# Somebody please come up with something better
from pyramid_restful.settings import api_settings
self._pagination_class = api_settings.default_pagination_class

return self._pagination_class

model = None # SQLAlchemy model class
schema_class = None # marshmallow schema class
filter_classes = ()
lookup_column = 'id'
pagination_class = None # todo make default configurable

def get_query(self):
assert self.model is not None, (
Expand Down Expand Up @@ -57,7 +68,6 @@ def get_schema_class(self):
"""
Return the class to use for the serializer.
Defaults to using `self.serializer_class`.
You may want to override this if you need to provide different
serializations depending on the incoming request.
"""
Expand Down
14 changes: 12 additions & 2 deletions pyramid_restful/pagination/pagenumber.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,22 @@ class PageNumberPagination(BasePagination):
"""
A simple page number based style that supports page numbers as
query parameters. For example:
http://api.example.org/accounts/?page=4
http://api.example.org/accounts/?page=4&page_size=100
page_size can be overridden as class attribute:
class MyPager(PageNumberPagination):
page_size = 10
"""

page_size = 100 # todo make configurable
@property
def page_size(self):
if not hasattr(self, '_page_size'):
# make sure we have the most up to date settings
# Somebody please come up with something better
from pyramid_restful.settings import api_settings
self._page_size = api_settings.page_size

return self._page_size

paginator_class = Paginator

Expand Down
103 changes: 103 additions & 0 deletions pyramid_restful/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import six

from importlib import import_module


DEFAULTS = {
# Generic view behavior
'default_pagination_class': 'pyramid_restful.pagination.PageNumberPagination',
# Pagination
'page_size': None,
}

# List of settings that may be in string import notation.
IMPORT_STRINGS = (
'default_pagination_class',
)


def perform_import(val, setting_name):
"""
If the given setting is a string import notation,
then perform the necessary import or imports.
"""

if val is None:
return None
elif isinstance(val, six.string_types):
return import_from_string(val, setting_name)
elif isinstance(val, (list, tuple)):
return [import_from_string(item, setting_name) for item in val]

return val


def import_from_string(val, setting_name):
"""
Attempt to import a class from a string representation.
"""

try:
parts = val.split('.')
module_path, class_name = '.'.join(parts[:-1]), parts[-1]
module = import_module(module_path)
return getattr(module, class_name)
except (ImportError, AttributeError) as e:
msg = "Could not import '%s' for API setting '%s'. %s: %s." % (val, setting_name, e.__class__.__name__, e)
raise ImportError(msg)


class APISettings:
"""
A settings object, that allows API settings to accessed as properties
"""

def __init__(self, app_settings=None, defaults=None, import_strings=None):
if app_settings:
self._app_settings = app_settings

self.defaults = defaults or DEFAULTS
self.import_strings = import_strings or IMPORT_STRINGS

@property
def app_settings(self):
if not hasattr(self, '_app_settings'):
self._app_settings = {}
return self._app_settings

def __getattr__(self, attr):
if attr not in self.defaults:
raise AttributeError('Invalid API setting: `{}`'.format(attr))

try:
val = self.app_settings[attr]
except KeyError:
val = self.defaults[attr]

# Coerce import strings into classes
if attr in self.import_strings:
val = perform_import(val, attr)

# Cache the result
setattr(self, attr, val)

return val


api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)


def reload_api_settings(settings, prefix='restful'):
global api_settings
app_settings = {}

for key, val in settings.items():
try:
keyfix, setting_name = key.split('.')
except ValueError:
continue

if keyfix == prefix:
app_settings[setting_name] = val

api_settings = APISettings(app_settings, DEFAULTS, IMPORT_STRINGS)
40 changes: 40 additions & 0 deletions tests/test_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import pytest

from pyramid_restful.settings import APISettings, reload_api_settings, DEFAULTS
from pyramid_restful.pagination import PageNumberPagination


class TestPagination(PageNumberPagination):
pass


def test_default_settings():
settings = APISettings()
assert settings.default_pagination_class == PageNumberPagination


def test_import_error():
settings = APISettings({
'default_pagination_class': [
'tests.invalid_module.InvalidClassName'
]
})

with pytest.raises(ImportError):
settings.default_pagination_class


def test_reload_api_settings():
pager = TestPagination()

reload_api_settings(
{
'testrestful.default_pagination_class': 'pyramid_restful.pagination.LinkHeaderPagination',
'testrestful.page_size': 99,
'testrestful': 'junk'
},
'testrestful'
)

assert pager.page_size == 99
reload_api_settings(DEFAULTS) # reset global settings

0 comments on commit 09e1ad0

Please sign in to comment.