Skip to content

Commit

Permalink
Merge branch 'developer' into 'master'
Browse files Browse the repository at this point in the history
0.1.8

Closes #28, #30, #31, and #32

See merge request polemarch/ce!11
  • Loading branch information
onegreyonewhite committed May 29, 2018
2 parents 6fd29bc + 82a2acd commit 216cc26
Show file tree
Hide file tree
Showing 357 changed files with 2,282 additions and 86,589 deletions.
4 changes: 2 additions & 2 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ ignore=CVS,migrations,unittests,tests,settings.py,settings_production.py
load-plugins=pylint_django

# Use multiple processes to speed up Pylint.
jobs=2
jobs=4

# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
Expand Down Expand Up @@ -277,7 +277,7 @@ ignore-mixin-members=yes
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
ignored-modules=vstutils.urls,vstutils.api.base,vstutils.api.views,vstutils.gui.views,vstutils.api.permissions,vstutils.api.filters,vstutils.middleware,vstutils.environment,vstutils.utils,vstutils.exceptions,vstutils.models

# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
Expand Down
4 changes: 3 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ include polemarch/main/tests/facts_stdout
include polemarch/main/tests/test_repo.tar
include requirements.txt
include requirements-git.txt
include requirements-test.txt
include requirements-doc.txt
recursive-include polemarch/*.c *
recursive-include polemarch/doc/html *
recursive-include polemarch/*.so *
recursive-include polemarch/static *
recursive-include polemarch/*/templates *
recursive-include polemarch/*/templates *
recursive-include polemarch/templates *
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ TESTS =
NAME = polemarch
USER = $(NAME)
VER = $(shell $(PY) -c 'import polemarch; print(polemarch.__version__)')
PIPARGS = --index-url=http://pipc.vst.lan:8001/simple/ --trusted-host pipc.vst.lan
VSTUTILS = $(shell cat requirements.txt | grep vstutils)
PIPARGS =
ARCHIVE = $(NAME)-$(VER).tar.gz
LICENSE = AGPL-3+
define DESCRIPTION
Expand Down Expand Up @@ -55,19 +56,22 @@ pylint:

build: build-clean
-rm -rf dist
$(PY) -m pip install $(VSTUTILS)
$(PY) setup.py sdist -v

compile: build-clean
-rm -rf dist
find ./polemarch -name "*.c" -print0 | xargs -0 rm -rf
-rm -rf polemarch/doc/*
$(PY) -m pip install $(VSTUTILS)
$(PY) setup.py compile -v

prebuild:
# Create virtualenv
$(PY) -m virtualenv --no-site-packages $(PREBUILD_DIR)
# Install required packages
$(PREBUILD_BINDIR)/pip install -U pip
$(PREBUILD_BINDIR)/pip install -U $(VSTUTILS)
$(PREBUILD_BINDIR)/pip install -U dist/$(NAME)-$(VER).tar.gz $(REQUIREMENTS)
$(PREBUILD_BINDIR)/pip install -U -r requirements-git.txt
# RECORD files are used by wheels for checksum. They contain path names which
Expand Down
6 changes: 3 additions & 3 deletions initbin/polemarchweb.service
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
ExecStart=/opt/polemarch/bin/uwsgi /opt/polemarch/lib/python2.7/site-packages/polemarch/web.ini
ExecReload=/opt/polemarch/bin/uwsgi --reload /var/run/polemarch/web.pid
ExecStop=/opt/polemarch/bin/uwsgi --stop /var/run/polemarch/web.pid
ExecStart=/opt/polemarch/bin/polemarchctl web
ExecReload=/opt/polemarch/bin/polemarchctl web reload=/var/run/polemarch/web.pid
ExecStop=/opt/polemarch/bin/polemarchctl web stop=/var/run/polemarch/web.pid
PIDFile=/var/run/polemarch/web.pid
User=polemarch
Group=polemarch
Expand Down
33 changes: 18 additions & 15 deletions polemarch/__init__.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
from .environment import prepare_environment
from vstutils.environment import prepare_environment, cmd_execution, os

__version__ = "0.1.7"
default_settings = {
# ansible specific environment variables
"ANSIBLE_HOST_KEY_CHECKING": 'False',
"ANSIBLE_FORCE_COLOR": "true",
# celery specific
"C_FORCE_ROOT": "true",
# django settings module
"DJANGO_SETTINGS_MODULE": os.getenv(
"DJANGO_SETTINGS_MODULE", 'polemarch.main.settings'
),
# VSTUTILS settings
"VST_PROJECT": os.getenv("VST_PROJECT", 'polemarch'),
"VST_ROOT_URLCONF": os.getenv("VST_ROOT_URLCONF", 'polemarch.main.urls'),
"VST_WSGI": os.getenv("VST_WSGI", 'polemarch.main.wsgi')
}

def _main(**kwargs):
# pylint: disable=unused-variable
import sys
from django.core.management import execute_from_command_line
prepare_environment(**kwargs)
execute_from_command_line(sys.argv)
__version__ = "0.1.8"

def get_app(**kwargs):
from celery import Celery
prepare_environment(**kwargs)
celery_app = Celery('polemarch')
celery_app.config_from_object('django.conf:settings', namespace='CELERY')
celery_app.autodiscover_tasks()
return celery_app
prepare_environment(**default_settings)
5 changes: 3 additions & 2 deletions polemarch/__main__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from polemarch import _main
from vstutils.environment import cmd_execution, sys
sys.path.append('./')

_main()
cmd_execution()
164 changes: 5 additions & 159 deletions polemarch/api/base.py
Original file line number Diff line number Diff line change
@@ -1,176 +1,22 @@
from collections import namedtuple
import six
from django.db.models.query import QuerySet
from rest_framework import viewsets, views as rest_views
from rest_framework.decorators import detail_route, list_route
from rest_framework.response import Response as RestResponse
from rest_framework.reverse import reverse


_ResponseClass = namedtuple("ResponseData", [
"data", "status"
])


class Response(_ResponseClass):

def _asdict(self):
data = super(Response, self)._asdict()
data["status"] = data.get("status", 200)
if isinstance(data["data"], (six.string_types, six.text_type)):
data["data"] = dict(detail=self.data)
return data

@property
def resp(self):
return RestResponse(**self._asdict())

@property
def resp_dict(self): # nocv
return self._asdict()


class QuerySetMixin(rest_views.APIView):
queryset = None
model = None

def _base_get_queryset(self):
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)

queryset = self.queryset
if isinstance(queryset, QuerySet):
# Ensure queryset is re-evaluated on each request.
queryset = queryset.all()
return queryset

def get_extra_queryset(self):
return self.queryset

def get_queryset(self):
if self.queryset is None:
assert self.model is not None, (
"'%s' should either include a `queryset` or `model` attribute,"
" or override the `get_queryset()` method."
% self.__class__.__name__
)
qs = self.model.objects.all()
self.queryset = getattr(qs, 'cleared', qs.all)()
if self.kwargs.get("pk", None) is None:
self.queryset = self.get_extra_queryset()
return self._base_get_queryset()


class GenericViewSet(QuerySetMixin, viewsets.GenericViewSet):
serializer_class_one = None
model = None

def get_serializer_class(self):
if self.kwargs.get("pk", False) or self.action in ["create"] or \
int(self.request.query_params.get("detail", u"0")):
if self.serializer_class_one is not None:
return self.serializer_class_one
return super(GenericViewSet, self).get_serializer_class()

def filter_route_queryset(self, queryset, filter_classes=None):
if filter_classes is not None:
if not isinstance(filter_classes, (list, tuple)):
filter_classes = [filter_classes]
for filter_class in list(filter_classes):
queryset = filter_class(self.request.query_params,
queryset=queryset,
request=self.request).qs
return queryset

def get_paginated_route_response(self, queryset, serializer_class,
filter_classes=None, **kwargs):
queryset = self.filter_route_queryset(queryset, filter_classes)

page = self.paginate_queryset(queryset)
if page is not None:
serializer = serializer_class(page, many=True, **kwargs)
return self.get_paginated_response(serializer.data)

serializer = serializer_class(queryset, many=True, **kwargs)
return RestResponse(serializer.data)

@list_route(methods=["post"])
def filter(self, request):
queryset = self.filter_queryset(self.get_queryset())
queryset = queryset.filter(**request.data.get("filter", {}))
queryset = queryset.exclude(**request.data.get("exclude", {}))

return self.get_paginated_route_response(
queryset=queryset,
serializer_class=self.get_serializer_class(),
context=self.get_serializer_context()
)
from vstutils.api.base import action


class LimitedPermissionMixin(object):
POST_WHITE_LIST = []

def get_extra_queryset(self):
return self.queryset.user_filter(self.request.user)


class PermissionMixin(LimitedPermissionMixin): # nocv
@detail_route(methods=["put", "get"])
@action(methods=["put", "get"], detail=True)
def owner(self, request, pk=None):
# pylint: disable=unused-argument
serializer = self.get_serializer(self.get_object())
return serializer.owner(request).resp

@detail_route(methods=["post", "put", "delete", "get"])
@action(methods=["post", "put", "delete", "get"], detail=True)
def permissions(self, request, pk=None):
# pylint: disable=unused-argument
serializer = self.get_serializer(self.get_object())
return serializer.permissions(request).resp


class ReadOnlyModelViewSet(GenericViewSet,
viewsets.ReadOnlyModelViewSet):
pass


class HistoryModelViewSet(GenericViewSet,
viewsets.ReadOnlyModelViewSet,
viewsets.mixins.DestroyModelMixin):
pass


class ModelViewSetSet(GenericViewSet, viewsets.ModelViewSet):
POST_WHITE_LIST = []


class NonModelsViewSet(GenericViewSet):
base_name = None

def get_queryset(self):
return QuerySet() # nocv


class ListNonModelViewSet(NonModelsViewSet,
viewsets.mixins.ListModelMixin):
# pylint: disable=abstract-method
schema = None

@property
def methods(self):
this_class_dict = ListNonModelViewSet.__dict__
obj_class_dict = self.__class__.__dict__
new_methods = list()
for name, attr in obj_class_dict.items():
detail = getattr(attr, 'detail', True)
if name not in this_class_dict and not detail:
new_methods.append(name.replace('_', "-"))
return new_methods

def list(self, request, *args, **kwargs):
routes = {
method: reverse("{}-{}".format(self.base_name, method),
request=request)
for method in self.methods
}
return Response(routes, 200).resp
57 changes: 0 additions & 57 deletions polemarch/api/handlers.py
Original file line number Diff line number Diff line change
@@ -1,57 +0,0 @@
import logging
import sys
import traceback
from django.core.exceptions import PermissionDenied

from django.core import exceptions as djexcs
from django.http.response import Http404
from rest_framework import exceptions, status, views
from rest_framework.response import Response

from ..main import exceptions as mexcs

logger = logging.getLogger("polemarch")


def polemarch_exception_handler(exc, context):
# pylint: disable=too-many-return-statements
logger.info(traceback.format_exc())
default_exc = (exceptions.APIException, djexcs.PermissionDenied)

if isinstance(exc, PermissionDenied): # pragma: no cover
return Response({"detail": str(exc)},
status=status.HTTP_403_FORBIDDEN)

elif isinstance(exc, mexcs.DataNotReady):
return Response({"detail": exc.msg},
status=status.HTTP_424_FAILED_DEPENDENCY)

elif isinstance(exc, (mexcs.NotApplicable, Http404)):
return Response({"detail": getattr(exc, 'msg', str(exc))},
status=status.HTTP_404_NOT_FOUND)

elif isinstance(exc, djexcs.ValidationError):
if hasattr(exc, 'error_dict'):
errors = dict(exc)
elif hasattr(exc, 'error_list'): # nocv
errors = {'other_errors': list(exc)}
else:
errors = {'other_errors': str(exc)} # nocv
return Response({"detail": errors},
status=status.HTTP_400_BAD_REQUEST)

elif isinstance(exc, mexcs.UnknownTypeException):
return Response({"detail": exc.msg},
status=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE)

elif not isinstance(exc, default_exc) and isinstance(exc, Exception):
return Response({'detail': str(sys.exc_info()[1]),
'error_type': sys.exc_info()[0].__name__},
status=status.HTTP_400_BAD_REQUEST)

default_response = views.exception_handler(exc, context)

if isinstance(exc, exceptions.NotAuthenticated):
default_response["X-Anonymous"] = "true"

return default_response
13 changes: 0 additions & 13 deletions polemarch/api/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,3 @@ def has_object_permission(self, request, view, obj):
if view.action in view.POST_WHITE_LIST: # nocv
return obj.acl_handler.viewable_by(request.user) # nocv
return obj.acl_handler.editable_by(request.user) # noce


class SuperUserPermission(ModelPermission):
def has_object_permission(self, request, view, obj):
if request.user.is_superuser:
return True
elif request.user == obj:
return True
return False


class StaffPermission(permissions.IsAdminUser):
pass
Loading

0 comments on commit 216cc26

Please sign in to comment.