{
"info": {
"author": "Agustin Barto",
"author_email": "abarto@gmail.com",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 4 - Beta",
"Environment :: Web Environment",
"Framework :: Django",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Topic :: Utilities"
],
"description": "django\\_uncertainty\n===================\n\nIntroduction\n------------\n\n``django_uncertainty`` is a `Django `_ middleware that allows the\ndeveloper to introduce controlled uncertainty into his or her site. The main purpose is providing a\ntool to reproduce less-than-ideal conditions in a local development environment to evaluate\nexternal actors might react when a Django site starts misbehaving.\n\nIt requires `Django 1.10 `_ or later as it\nuses the new middleware framework.\n\nInstallation\n------------\n\nYou can get ``django_uncertainty`` using pip:\n\n$ pip install django\\_uncertainty\n\nIf you want to install it from source, grab the git repository from\nGitHub and run setup.py:\n\n::\n\n $ git clone git://github.com/abarto/django_uncertainty.git\n $ cd django_uncertainty\n $ python setup.py install\n\nOnce the package has been installed, you need to add the middleware to\nyour Django settings file:\n\n::\n\n MIDDLEWARE = [\n 'django.middleware.security.SecurityMiddleware',\n ...\n 'uncertainty.UncertaintyMiddleware'\n ]\n\nUsage\n-----\n\nThe middleware behaviour is controlled by the ``DJANGO_UNCERTAINTY`` Django setting. For example:\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.cond(\n u.path_matches('^/api'), u.random_choice([\n (u.delay(u.default(), 5), 0.3), (u.server_error(), 0.2)]))\n\nThis tells the middleware that if the request path starts with \"/api\", 30% of the time the request\nis going to be delayed by 5 seconds, 20% of the time the site is going to respond with a status 500\n(Server Error), and the rest of the time the site is going to function normally.\n\nThe next section describes all the available behaviours and conditions.\n\nBehaviours\n----------\n\nAll behaviours are implemented as sub-classes of the ``Behaviour``\nclass:\n\n::\n\n class Behaviour:\n \"\"\"Base of all behaviours. It is also the default implementation which just just returns the\n result of calling get_response.\"\"\"\n def __call__(self, get_response, request):\n \"\"\"Returns the result of calling get_response (as given by the UncertaintyMiddleware\n middleware with request as argument. It returns the same response that would have been\n created by the Django stack without the introduction of UncertaintyMiddleware.\n :param get_response: The get_response method provided by the Django stack\n :param request: The request that triggered the middleware\n :return: The result of calling get_response with the request parameter\n \"\"\"\n response = get_response(request)\n return response\n\nBehaviours work like functions that take the same parameters given the\nthe Django middleware.\n\ndefault\n~~~~~~~\n\nAs the name implies, this is the default behaviour. It just makes the requests continue as usual\nthrough the Django stack. Using ``default`` is the same as omitting the ``DJANGO_UNCERTAINTY``\nsetting altogether.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.default()\n\nhtml\n~~~~\n\nOverrides the site's response with an arbitrary HTTP response. Without any arguments it returns a\nresponse with status code 200 (Ok). ``html`` takes the same arguments as Django's\n`HttpResponse `_.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.html('Hello World!
')\n\nok\n~~\n\nAn alias for ``html``.\n\nbad\\_request\n~~~~~~~~~~~~\n\nOverrides the site's response with an HTTP response with status code 400 (Bad Request).\n``bad_request`` takes the same arguments as Django's\n`HttpResponseBadRequest `_.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.bad_request('Oops!')\n\nforbidden\n~~~~~~~~~\n\nOverrides the site's response with an HTTP response with status code 403 (Forbidden). ``forbidden``\ntakes the same arguments as Django's\n`HttpResponseForbidden `_.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.forbidden('NOPE')\n\nnot\\_allowed\n~~~~~~~~~~~~\n\nOverrides the site's response with an HTTP response with status code 405 (Not Allowed).\n``not_allowed`` takes the same arguments as Django's\n`HttpResponseNotAllowed `_.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.not_allowed(permitted_methods=['PUT'], content='NOPE')\n\nnot\\_found\n~~~~~~~~~~\n\nOverrides the site's response with an HTTP response with status code 404 (Not Found).\n``not_found`` takes the same arguments as Django's\n`HttpResponse `_.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.not_found(permitted_methods=['PUT'], content='Who?')\n\nserver\\_error\n~~~~~~~~~~~~~\n\nOverrides the site's response with an HTTP response with status code 500 (Internal Server Error).\n``server_error`` takes the same arguments as Django's\n`HttpResponseServerError `_.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.server_error('BOOM')\n\nstatus\n~~~~~~\n\nOverrides the site's response with an HTTP response with a given status code.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.status(201, content='Created
')\n\njson\n~~~~\n\nOverrides the site's response with an arbitrary HTTP response with content type\n``application/json``. Without any arguments it returns a response with status code 200 (Ok).\n``json`` takes the same arguments as Django's\n`JsonResponse `_.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.json({'foo': 1, 'bar': True})\n\ndelay\n~~~~~\n\nIntroduces a delay after invoking another behaviour. For example, this specifies a delay of half a\nsecond into the actual site responses:\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.delay(u.default(), 0.5)\n\nYou can replace the first argument with any other valid behaviour.\n\ndelay\\_request\n~~~~~~~~~~~~~~\n\nIt is similar to ``delay``, but the delay is introduced *before* the specified behaviour is invoked.\n\nrandom\\_choice\n~~~~~~~~~~~~~~\n\nThis is the work horse of ``django_uncertainty``. ``random_choice`` allows you to specify different\nbehaviours that are going to be chosen at random (following the give proportions) when a request is\nreceived. It takes a list of behaviours or tuples of behaviours and proportions,\n\nFor example, let's say we want 30% of the request to be responded with an Internal Server Error\nresponse, 20% with a Bad Request response, and the rest with the actual response but with a 1\nsecond delay. This can be specified as follows:\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.random_choice([(u.server_error(), 0.3), (u.bad_request(), 0.2), u.delay(u.default(), 1)])\n\nIf proportions are specified, the total sum of them must be less than 1. If no proportions are\nspecified, the behaviours are chosen with an even chance between them:\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.random_choice([u.server_error(), u.default()])\n\nThis specifies that approximetly half the request are going to be responded with an Internal Server\nError, and half will work normally.\n\nconditional\n~~~~~~~~~~~\n\nIt allows you to specify that a certain behaviour should be invoked only if a certain condition is\nmet. If the condition is not met, the alternative behvaiour (which is ``default`` by default) is\nexecuted.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.conditional(u.is_post, u.server_error())\n\nThe specification above states that if the request uses the POST method, the site should respond\nwith an Internal Server Error. If you want to specify an alternative behaviour other than the\ndefault, use the ``alternative_behaviour`` argument:\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.conditional(u.is_post, u.server_error(), alternative_behaviour=u.delay(u.default(), 0.3)\n\nConditions can be combined using boolean operators. For instance,\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.conditional(u.is_authenticated | -u.is_get, u.bad_request())\n\nspecifies that if the request is authenticated or if it uses the GET method, a Bad Request response\nshould be used.\n\nIn the next section, all the predefined conditions are presented.\n\ncond\n~~~~\n\nAn alias for ``conditional``.\n\nmulti\\_conditional\n~~~~~~~~~~~~~~~~~~\n\n``multi_conditional`` takes a list of condition/behaviour pairs, and when a request is received, it\niterates over the conditions until one is met, and the corresponding behaviour is invoked. If no\ncondition is met, the default behaviour is invoked.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.multi_conditional([(u.is_get, u.delay(u.default(), 0.5), (u.is_post, u.server_error())])\n\nThe specification above states that if the request uses the GET method, it should be delayed by\nhalf a second, if it uses POST, it should respond with an Internal Server Error, and if neither of\nthose conditions are met, the request should go through as usual.\n\nThe default behaviour to be used when no conditions are met can be specified with the ``default_behaviour`` argument:\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.multi_conditional(\n [\n (u.is_get, u.delay(u.default(), 0.5),\n (u.is_post, u.server_error())\n ], default_behaviour=u.not_found())\n\nmulti\\_cond\n~~~~~~~~~~~\n\nAn alias for ``multi_conditional``.\n\ncase\n~~~~\n\nAn alias for ``multi_conditional``.\n\nslowdown\n~~~~~~~~\n\n``slowdown`` slows down a streaming response by introducing a set delay (in seconds) in between the\nchunks. If the response is not a streaming one, ``slowdown`` does nothing.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.slowdown(u.default(), 0.5) # 0.5 seconds delay between each chunk\n\nrandom_stop\n~~~~~~~~~~~\n\n``random_stop`` stops a streaming response randomly with a given probability. If the response is\nnot a streaming one, ``random_stop`` does nothing.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.random_stop(u.default(), 0.2) # 0.2 chance of stoping the stream\n\nCustom behaviours\n~~~~~~~~~~~~~~~~~\n\nWe've done our best to implement behaviours that make sense in the context of introducing\nuncertainty into a Django site, however, if you need to implement your own behaviours, all you need\nto do is derive the ``Behaviour`` class. Let's say you want a Behaviour that adds a header to the\nresponse generated by another behaviour. Here's one possible implementation of such behaviour:\n\n::\n\n class AddHeaderBehaviour(Behaviour):\n def __init__(self, behaviour, header_name, header_value):\n self._behaviour = behaviour\n self._header_name = header_name\n self._header_value = header_value\n\n def __call__(self, get_response, request):\n response = self._behaviour(get_response, request)\n response[self._header_name] = self._header_value\n\n return response\n\nFor streaming responses, create a new subclass of ``StreamBehaviour`` overriding the\n``wrap_streaming_content`` method. For example, if you need to drop every other chunk from the\nresponse stream, here's what you can do:\n\n::\n\n class DropEvenStreamBehaviour(StreamBehaviour):\n def wrap_streaming_content(self, streaming_content):\n \"\"\"Drops every other chunk of streaming_content.\n :param streaming_content: The streaming_content field of the response.\n \"\"\"\n for i, chunk in enumerate(streaming_content):\n if i % 2 == 0:\n yield chunk\n\n\nIf you think that there's a use case that we haven't covered that might be useful for other users,\nfeel free to create an issue on `GitHub `__.\n\nConditions\n----------\n\nConditions are subclasses of the ``Predicate`` class:\n\n::\n\n class Predicate:\n \"\"\"Represents a condition that a Django request must meet. It is used in conjunction with\n ConditionalBehaviour to control if behaviours are invoked depending on the result of the\n Predicate invocation. Multiple predicates can be combined with or and and.\n \"\"\"\n def __call__(self, get_response, request):\n \"\"\"Returns True for all calls.\n :param get_response: The get_response method provided by the Django stack\n :param request: The request that triggered the middleware\n :return: True for all calls.\n \"\"\"\n return True\n\nWhenever a conditional behaviour is used, the predicate is invoked with the same parameters that\nwould be given the the behaviour.\n\nis\\_method\n~~~~~~~~~~\n\nThe condition is met if the request uses the specified method.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.cond(u.is_method('PATCH'), u.not_allowed())\n\nis\\_get\n~~~~~~~\n\nThe condition is met if the request uses the GET HTTP method.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.cond(u.is_get, u.not_allowed())\n\nis\\_delete\n~~~~~~~~~~\n\nThe condition is met if the request uses the DELETE HTTP method.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.cond(u.is_delete, u.not_allowed())\n\nis\\_post\n~~~~~~~~\n\nThe condition is met if the request uses the POST HTTP method.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.cond(u.is_post, u.not_allowed())\n\nis\\_put\n~~~~~~~\n\nThe condition is met if the request uses the PUT HTTP method.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.cond(u.is_put, u.not_allowed())\n\nhas\\_parameter\n~~~~~~~~~~~~~~\n\nThe condition is met if the request has the given parameter.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.cond(u.has_parameter('q'), u.server_error())\n\nhas\\_param\n~~~~~~~~~~\n\nAn alias for ``has_parameter``\n\npath\\_matches\n~~~~~~~~\n\nThe condition is met if the request path matches the given regular expression.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.cond(u.path_matches('^/api'), u.delay(u.default(), 0.2))\n\nis\\_authenticated\n~~~~~~~~~~~~~~~~~\n\nThe condition is met if the user has authenticated itself.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.cond(u.is_authenticated, u.not_found())\n\nuser\\_is\n~~~~~~~~\n\nThe condition is met if the authenticated user has the given username.\n\n::\n\n import uncertainty as u\n DJANGO_UNCERTAINTY = u.cond(u.user_is('admin', u.forbidden())\n\nCustom conditions\n~~~~~~~~~~~~~~~~~\n\nAs with behaviours, custom conditions are creating deriving the ``Predicate`` class. Let's say you\nwant a condition that checks the presence of a header in the request. Here's one possible\nimplementation of such condition:\n\n::\n\n class HasHeaderPredicate(Predicate):\n def __index__(self, header_name):\n self._header_name = header_name\n\n def __call__(self, get_response, request):\n return self._header_name in request\n\nFeedback\n--------\n\nAll feedback is appreciated, so if you found problems or have ides for new features, just create an\nissue on `GitHub `_.",
"description_content_type": null,
"docs_url": null,
"download_url": "UNKNOWN",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://github.com/abarto/django_uncertainty",
"keywords": null,
"license": "BSD",
"maintainer": null,
"maintainer_email": null,
"name": "django_uncertainty",
"package_url": "https://pypi.org/project/django_uncertainty/",
"platform": "UNKNOWN",
"project_url": "https://pypi.org/project/django_uncertainty/",
"project_urls": {
"Download": "UNKNOWN",
"Homepage": "https://github.com/abarto/django_uncertainty"
},
"release_url": "https://pypi.org/project/django_uncertainty/1.7/",
"requires_dist": null,
"requires_python": null,
"summary": "A Django middeware to generate predictable errors on sites",
"version": "1.7"
},
"last_serial": 2637563,
"releases": {
"1.1": [
{
"comment_text": "",
"digests": {
"md5": "a21fa7b39a14cfa4a6cd77031fd6f0ac",
"sha256": "79b202559f6cbaae2eba9977521b4eaa0d9253994ad964c95a667121f11389b0"
},
"downloads": -1,
"filename": "django_uncertainty-1.1.tar.gz",
"has_sig": false,
"md5_digest": "a21fa7b39a14cfa4a6cd77031fd6f0ac",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 13730,
"upload_time": "2016-08-06T02:54:58",
"url": "https://files.pythonhosted.org/packages/0c/8f/b81e595fbda980624e87a8bbe4824cf10ed3c1f41b9432f75bd5388644e5/django_uncertainty-1.1.tar.gz"
}
],
"1.2": [
{
"comment_text": "",
"digests": {
"md5": "4d701d5c5119159e8128a6ee31be4952",
"sha256": "18d38d984316be5c5303aadd65a108c1be8aa69ad4919e78461d70f31d2806a0"
},
"downloads": -1,
"filename": "django_uncertainty-1.2.tar.gz",
"has_sig": false,
"md5_digest": "4d701d5c5119159e8128a6ee31be4952",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 12975,
"upload_time": "2016-08-08T20:02:56",
"url": "https://files.pythonhosted.org/packages/4a/46/18863ca7645cd93afd0067f61a29a0858284f20336020f9d9d327e51a180/django_uncertainty-1.2.tar.gz"
}
],
"1.3": [
{
"comment_text": "",
"digests": {
"md5": "0a2643441501b8b1b719452927a513a3",
"sha256": "14ca87ee7a8d8bef15a6880cef30cb8c57afa05e37d6313aba4bb26138ccff12"
},
"downloads": -1,
"filename": "django_uncertainty-1.3.tar.gz",
"has_sig": false,
"md5_digest": "0a2643441501b8b1b719452927a513a3",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 13033,
"upload_time": "2016-08-09T15:59:18",
"url": "https://files.pythonhosted.org/packages/02/58/ffaaa63460cf100d487cd8028bf1b21d862d729fcfb232051f3da4232825/django_uncertainty-1.3.tar.gz"
}
],
"1.4": [
{
"comment_text": "",
"digests": {
"md5": "0ac55f25649b1f855ecbd55dde2d1506",
"sha256": "63313ed1c85e13e432695c3393f5838d00bd31b16adf6c2253af651c9c30ea33"
},
"downloads": -1,
"filename": "django_uncertainty-1.4.tar.gz",
"has_sig": false,
"md5_digest": "0ac55f25649b1f855ecbd55dde2d1506",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 13070,
"upload_time": "2016-08-09T19:10:05",
"url": "https://files.pythonhosted.org/packages/e9/58/839a46cc963950ca8b39716c2484bd9c36e7ff816d9dc12561ad725382ee/django_uncertainty-1.4.tar.gz"
}
],
"1.5": [
{
"comment_text": "",
"digests": {
"md5": "118dcf644b5a854e154acafc35e4635b",
"sha256": "fb20921afd128a4417804f39e98f2bc019bf1380e008eea37b529cd9c32001f3"
},
"downloads": -1,
"filename": "django_uncertainty-1.5.tar.gz",
"has_sig": false,
"md5_digest": "118dcf644b5a854e154acafc35e4635b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 13076,
"upload_time": "2016-08-23T16:51:37",
"url": "https://files.pythonhosted.org/packages/4e/1a/49b52e18d8d7d03e9067881d726e68be9b0960f089697f4752929f0b6823/django_uncertainty-1.5.tar.gz"
}
],
"1.6": [
{
"comment_text": "",
"digests": {
"md5": "b456d867f3dff881e5080c298734a432",
"sha256": "cdac282451b6b8962d569e8bcf8e734dde89e16e0c21a3378635e27f9f710d7a"
},
"downloads": -1,
"filename": "django_uncertainty-1.6.tar.gz",
"has_sig": false,
"md5_digest": "b456d867f3dff881e5080c298734a432",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 13630,
"upload_time": "2017-02-11T22:28:48",
"url": "https://files.pythonhosted.org/packages/85/19/729afa7d12b39c971793e7e5d7aad9d0898dca55e4bfa5a46efc6c620431/django_uncertainty-1.6.tar.gz"
}
],
"1.7": [
{
"comment_text": "",
"digests": {
"md5": "2b98cef8da0e6d971b751577d4113fbb",
"sha256": "1169846930a8872c62f81394f51186d8a76061590399e0f41ba882d417fda2e4"
},
"downloads": -1,
"filename": "django_uncertainty-1.7.tar.gz",
"has_sig": false,
"md5_digest": "2b98cef8da0e6d971b751577d4113fbb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 14244,
"upload_time": "2017-02-13T01:07:15",
"url": "https://files.pythonhosted.org/packages/93/ee/133c2a5cc343321c88859177d17f16f16703e17c9591e5e6678797c8dc8f/django_uncertainty-1.7.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "2b98cef8da0e6d971b751577d4113fbb",
"sha256": "1169846930a8872c62f81394f51186d8a76061590399e0f41ba882d417fda2e4"
},
"downloads": -1,
"filename": "django_uncertainty-1.7.tar.gz",
"has_sig": false,
"md5_digest": "2b98cef8da0e6d971b751577d4113fbb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 14244,
"upload_time": "2017-02-13T01:07:15",
"url": "https://files.pythonhosted.org/packages/93/ee/133c2a5cc343321c88859177d17f16f16703e17c9591e5e6678797c8dc8f/django_uncertainty-1.7.tar.gz"
}
]
}