klein-17.2.0/0000775000175000017500000000000013056447076013555 5ustar moshezmoshez00000000000000klein-17.2.0/src/0000775000175000017500000000000013056447076014344 5ustar moshezmoshez00000000000000klein-17.2.0/src/klein.egg-info/0000775000175000017500000000000013056447076017140 5ustar moshezmoshez00000000000000klein-17.2.0/src/klein.egg-info/PKG-INFO0000664000175000017500000000676113056447076020247 0ustar moshezmoshez00000000000000Metadata-Version: 1.1 Name: klein Version: 17.2.0 Summary: werkzeug + twisted.web Home-page: https://github.com/twisted/klein Author: Amber Brown (HawkOwl) Author-email: hawkowl@twistedmatrix.com License: MIT Description: ============================ Klein, a Web Micro-Framework ============================ .. image:: https://travis-ci.org/twisted/klein.svg?branch=master :target: http://travis-ci.org/twisted/klein :alt: Build Status .. image:: https://codecov.io/github/twisted/klein/coverage.svg?branch=master :target: https://codecov.io/github/twisted/klein?branch=master :alt: Code Coverage .. image:: https://img.shields.io/pypi/pyversions/klein.svg :target: https://pypi.python.org/pypi/klein :alt: Python Version Compatibility Klein is a micro-framework for developing production-ready web services with Python. It is 'micro' in that it has an incredibly small API similar to `Bottle `_ and `Flask `_. It is not 'micro' in that it depends on things outside the standard library. This is primarily because it is built on widely used and well tested components like `Werkzeug `_ and `Twisted `_. A `Klein bottle `_ is an example of a non-orientable surface, and a glass Klein bottle looks like a twisted bottle or twisted flask. This, of course, made it too good of a pun to pass up. Klein's documentation can be found at `Read The Docs `_. Example ======= This is a sample Klein application that returns 'Hello, world!', running on port ``8080``. .. code-block:: python from klein import run, route @route('/') def home(request): return 'Hello, world!' run("localhost", 8080) Contribute ========== ``klein`` is hosted on `GitHub `_ and is an open source project that welcomes contributions of all kinds from the community, including: - code patches, - `documentation `_ improvements, - `bug reports `_, - reviews for `contributed patches `_. For more information about contributing, see `the contributor guidelines `_. Keywords: twisted flask werkzeug web Platform: UNKNOWN Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules klein-17.2.0/src/klein.egg-info/dependency_links.txt0000664000175000017500000000000113056447076023206 0ustar moshezmoshez00000000000000 klein-17.2.0/src/klein.egg-info/SOURCES.txt0000664000175000017500000000102713056447076021024 0ustar moshezmoshez00000000000000AUTHORS LICENSE MANIFEST.in NEWS.rst README.rst setup.cfg setup.py src/klein/__init__.py src/klein/_plating.py src/klein/_version.py src/klein/app.py src/klein/interfaces.py src/klein/resource.py src/klein.egg-info/PKG-INFO src/klein.egg-info/SOURCES.txt src/klein.egg-info/dependency_links.txt src/klein.egg-info/requires.txt src/klein.egg-info/top_level.txt src/klein/test/__init__.py src/klein/test/py3_test_resource.py src/klein/test/test_app.py src/klein/test/test_plating.py src/klein/test/test_resource.py src/klein/test/util.pyklein-17.2.0/src/klein.egg-info/requires.txt0000664000175000017500000000004713056447076021541 0ustar moshezmoshez00000000000000six Twisted>=13.2 werkzeug incremental klein-17.2.0/src/klein.egg-info/top_level.txt0000664000175000017500000000000613056447076021666 0ustar moshezmoshez00000000000000klein klein-17.2.0/src/klein/0000775000175000017500000000000013056447076015446 5ustar moshezmoshez00000000000000klein-17.2.0/src/klein/resource.py0000664000175000017500000002157413054444052017645 0ustar moshezmoshez00000000000000# -*- test-case-name: klein.test.test_resource -*- from __future__ import absolute_import, division from twisted.internet import defer from twisted.python import log, failure from twisted.python.compat import unicode, intToBytes from twisted.web import server from twisted.web.iweb import IRenderable from twisted.web.resource import Resource, IResource, getChildForRequest from twisted.web.server import NOT_DONE_YET from twisted.web.template import renderElement from werkzeug.exceptions import HTTPException from klein.interfaces import IKleinRequest __all__ = ["KleinResource", "ensure_utf8_bytes"] def ensure_utf8_bytes(v): """ Coerces a value which is either a C{unicode} or C{str} to a C{str}. If ``v`` is a C{unicode} object it is encoded as utf-8. """ if isinstance(v, unicode): v = v.encode("utf-8") return v class _StandInResource(object): """ A standin for a Resource. This is a sentinel value for L{KleinResource}, to say that we are rendering a L{Resource}, which may close the connection itself later. """ class _URLDecodeError(Exception): """ Raised if one or more string parts of the URL could not be decoded. """ __slots__ = ["errors"] def __init__(self, errors): """ @param errors: List of decoding errors. @type errors: L{list} of L{tuple} of L{str}, L{twisted.python.failure.Failure} """ self.errors = errors def __repr__(self): return "".format(self.errors) def _extractURLparts(request): """ Extracts and decodes URI parts from C{request}. All strings must be UTF8-decodable. @param request: A Twisted Web request. @type request: L{twisted.web.iweb.IRequest} @raise URLDecodeError: If one of the parts could not be decoded as UTF-8. @return: L{tuple} of the URL scheme, the server name, the server port, the path info and the script name. @rtype: L{tuple} of L{unicode}, L{unicode}, L{int}, L{unicode}, L{unicode} """ server_name = request.getRequestHostname() if hasattr(request.getHost(), 'port'): server_port = request.getHost().port else: server_port = 0 if (bool(request.isSecure()), server_port) not in [ (True, 443), (False, 80), (False, 0), (True, 0)]: server_name = server_name + b":" + intToBytes(server_port) script_name = b'' if request.prepath: script_name = b'/'.join(request.prepath) if not script_name.startswith(b'/'): script_name = b'/' + script_name path_info = b'' if request.postpath: path_info = b'/'.join(request.postpath) if not path_info.startswith(b'/'): path_info = b'/' + path_info url_scheme = u'https' if request.isSecure() else u'http' utf8Failures = [] try: server_name = server_name.decode("utf-8") except UnicodeDecodeError: utf8Failures.append(("SERVER_NAME", failure.Failure())) try: path_info = path_info.decode("utf-8") except UnicodeDecodeError: utf8Failures.append(("PATH_INFO", failure.Failure())) try: script_name = script_name.decode("utf-8") except UnicodeDecodeError: utf8Failures.append(("SCRIPT_NAME", failure.Failure())) if utf8Failures: raise _URLDecodeError(utf8Failures) return url_scheme, server_name, server_port, path_info, script_name class KleinResource(Resource): """ A ``Resource`` that can do URL routing. """ isLeaf = True def __init__(self, app): Resource.__init__(self) self._app = app def __eq__(self, other): if isinstance(other, KleinResource): return vars(self) == vars(other) return NotImplemented def __ne__(self, other): result = self.__eq__(other) if result is NotImplemented: return result return not result def render(self, request): # Stuff we need to know for the mapper. try: url_scheme, server_name, server_port, path_info, script_name = \ _extractURLparts(request) except _URLDecodeError as e: for what, fail in e.errors: log.err(fail, "Invalid encoding in {what}.".format(what=what)) request.setResponseCode(400) return b"Non-UTF-8 encoding in URL." # Bind our mapper. mapper = self._app.url_map.bind( server_name, script_name, path_info=path_info, default_method=request.method, url_scheme=url_scheme, ) # Make the mapper available to the view. kleinRequest = IKleinRequest(request) kleinRequest.mapper = mapper # Make sure we'll notice when the connection goes away unambiguously. request_finished = [False] def _finish(result): request_finished[0] = True def _execute(): # Actually doing the match right here. This can cause an exception # to percolate up. If that happens it will be handled below in # processing_failed, either by a user-registered error handler or # one of our defaults. (rule, kwargs) = mapper.match(return_rule=True) endpoint = rule.endpoint # Try pretty hard to fix up prepath and postpath. segment_count = self._app.endpoints[endpoint].segment_count request.prepath.extend(request.postpath[:segment_count]) request.postpath = request.postpath[segment_count:] request.notifyFinish().addBoth(_finish) # Standard Twisted Web stuff. Defer the method action, giving us # something renderable or printable. Return NOT_DONE_YET and set up # the incremental renderer. d = defer.maybeDeferred(self._app.execute_endpoint, endpoint, request, **kwargs) request.notifyFinish().addErrback(lambda _: d.cancel()) return d d = defer.maybeDeferred(_execute) def process(r): """ Recursively go through r and any child Resources until something returns an IRenderable, then render it and let the result of that bubble back up. """ if IResource.providedBy(r): request.render(getChildForRequest(r, request)) return _StandInResource if IRenderable.providedBy(r): renderElement(request, r) return _StandInResource return r d.addCallback(process) def processing_failed(failure, error_handlers): # The failure processor writes to the request. If the # request is already finished we should suppress failure # processing. We don't return failure here because there # is no way to surface this failure to the user if the # request is finished. if request_finished[0]: if not failure.check(defer.CancelledError): log.err(failure, "Unhandled Error Processing Request.") return # If there are no more registered handlers, apply some defaults if len(error_handlers) == 0: if failure.check(HTTPException): he = failure.value request.setResponseCode(he.code) resp = he.get_response({}) for header, value in resp.headers: request.setHeader(ensure_utf8_bytes(header), ensure_utf8_bytes(value)) return ensure_utf8_bytes(he.get_body({})) else: request.processingFailed(failure) return error_handler = error_handlers[0] # Each error handler is a tuple of (list_of_exception_types, handler_fn) if failure.check(*error_handler[0]): d = defer.maybeDeferred(self._app.execute_error_handler, error_handler[1], request, failure) return d.addErrback(processing_failed, error_handlers[1:]) return processing_failed(failure, error_handlers[1:]) d.addErrback(processing_failed, self._app._error_handlers) def write_response(r): if r is not _StandInResource: if isinstance(r, unicode): r = r.encode('utf-8') if (r is not None) and (r != NOT_DONE_YET): request.write(r) if not request_finished[0]: request.finish() d.addCallback(write_response).addErrback(log.err, _why="Unhandled Error writing response") return server.NOT_DONE_YET klein-17.2.0/src/klein/__init__.py0000664000175000017500000000107413054444052017546 0ustar moshezmoshez00000000000000from __future__ import absolute_import, division from klein.app import Klein, run, route, resource from klein._plating import Plating from ._version import __version__ as _incremental_version # Make it a str, for backwards compatibility __version__ = _incremental_version.base() __author__ = "The Klein contributors (see AUTHORS)" __license__ = "MIT" __copyright__ = "Copyright 2016 {0}".format(__author__) __all__ = [ 'Klein', 'Plating', '__author__', '__copyright__', '__license__', '__version__', 'resource', 'route', 'run', ] klein-17.2.0/src/klein/interfaces.py0000664000175000017500000000066113054444052020133 0ustar moshezmoshez00000000000000from __future__ import absolute_import, division from zope.interface import Interface, Attribute class IKleinRequest(Interface): branch_segments = Attribute("Segments consumed by a branch route.") mapper = Attribute("L{werkzeug.routing.MapAdapter}") def url_for(self, endpoint, values=None, method=None, force_external=False, append_unknown=True): """ L{werkzeug.routing.MapAdapter.build} """ klein-17.2.0/src/klein/app.py0000664000175000017500000002745713054444052016604 0ustar moshezmoshez00000000000000""" Applications are great. Lets have more of them. """ from __future__ import absolute_import, division import sys import weakref from collections import namedtuple from contextlib import contextmanager from functools import wraps try: from inspect import iscoroutine except ImportError: def iscoroutine(*args, **kwargs): return False from werkzeug.routing import Map, Rule, Submount from twisted.python import log from twisted.python.components import registerAdapter from twisted.web.iweb import IRenderable from twisted.web.template import renderElement from twisted.web.server import Site, Request from twisted.internet import reactor, endpoints try: from twisted.internet.defer import ensureDeferred except ImportError: def ensureDeferred(*args, **kwagrs): raise NotImplementedError("Coroutines support requires Twisted>=16.6") from zope.interface import implementer from klein.resource import KleinResource from klein.interfaces import IKleinRequest __all__ = ['Klein', 'run', 'route', 'resource'] def _call(instance, f, *args, **kwargs): if instance is None: result = f(*args, **kwargs) else: result = f(instance, *args, **kwargs) if iscoroutine(result): result = ensureDeferred(result) return result @implementer(IKleinRequest) class KleinRequest(object): def __init__(self, request): self.branch_segments = [''] self.mapper = None def url_for(self, *args, **kwargs): return self.mapper.build(*args, **kwargs) registerAdapter(KleinRequest, Request, IKleinRequest) class Klein(object): """ L{Klein} is an object which is responsible for maintaining the routing configuration of our application. @ivar _url_map: A C{werkzeug.routing.Map} object which will be used for routing resolution. @ivar _endpoints: A C{dict} mapping endpoint names to handler functions. """ _bound_klein_instances = weakref.WeakKeyDictionary() def __init__(self): self._url_map = Map() self._endpoints = {} self._error_handlers = [] self._instance = None def __eq__(self, other): if isinstance(other, Klein): return vars(self) == vars(other) return NotImplemented def __ne__(self, other): result = self.__eq__(other) if result is NotImplemented: return result return not result @property def url_map(self): """ Read only property exposing L{Klein._url_map}. """ return self._url_map @property def endpoints(self): """ Read only property exposing L{Klein._endpoints}. """ return self._endpoints def execute_endpoint(self, endpoint, *args, **kwargs): """ Execute the named endpoint with all arguments and possibly a bound instance. """ endpoint_f = self._endpoints[endpoint] return endpoint_f(self._instance, *args, **kwargs) def execute_error_handler(self, handler, request, failure): """ Execute the passed error handler, possibly with a bound instance. """ return handler(self._instance, request, failure) def resource(self): """ Return an L{IResource} which suitably wraps this app. @returns: An L{IResource} """ return KleinResource(self) def __get__(self, instance, owner): """ Get an instance of L{Klein} bound to C{instance}. """ if instance is None: return self k = self._bound_klein_instances.get(instance) if k is None: k = self.__class__() k._url_map = self._url_map k._endpoints = self._endpoints k._error_handlers = self._error_handlers k._instance = instance self._bound_klein_instances[instance] = k return k def route(self, url, *args, **kwargs): """ Add a new handler for C{url} passing C{args} and C{kwargs} directly to C{werkzeug.routing.Rule}. The handler function will be passed at least one argument an L{twisted.web.server.Request} and any keyword arguments taken from the C{url} pattern. :: @app.route("/") def index(request): return "Hello" @param url: A werkzeug URL pattern given to C{werkzeug.routing.Rule}. @type url: str @param branch: A bool indiciated if a branch endpoint should be added that allows all child path segments that don't match some other route to be consumed. Default C{False}. @type branch: bool @returns: decorated handler function. """ segment_count = url.count('/') if url.endswith('/'): segment_count -= 1 def deco(f): kwargs.setdefault('endpoint', f.__name__) if kwargs.pop('branch', False): branchKwargs = kwargs.copy() branchKwargs['endpoint'] = branchKwargs['endpoint'] + '_branch' @wraps(f) def branch_f(instance, request, *a, **kw): IKleinRequest(request).branch_segments = kw.pop('__rest__', '').split('/') return _call(instance, f, request, *a, **kw) branch_f.segment_count = segment_count self._endpoints[branchKwargs['endpoint']] = branch_f self._url_map.add(Rule(url.rstrip('/') + '/' + '', *args, **branchKwargs)) @wraps(f) def _f(instance, request, *a, **kw): return _call(instance, f, request, *a, **kw) _f.segment_count = segment_count self._endpoints[kwargs['endpoint']] = _f self._url_map.add(Rule(url, *args, **kwargs)) return f return deco @contextmanager def subroute(self, prefix): """ Within this block, C{@route} adds rules to a C{werkzeug.routing.Submount}. This is implemented by tinkering with the instance's C{_url_map} variable. A context manager allows us to gracefully use the pattern of "change a variable, do some things with the new value, then put it back to how it was before. Named "subroute" to try and give callers a better idea of its relationship to C{@route}. Usage: :: with app.subroute("/prefix") as app: @app.route("/foo") def foo_handler(request): return 'I respond to /prefix/foo' @type prefix: string @param prefix: The string that will be prepended to the paths of all routes established during the with-block. @return: Returns None. """ _map_before_submount = self._url_map submount_map = namedtuple( 'submount', ['rules', 'add'])( [], lambda r: submount_map.rules.append(r)) try: self._url_map = submount_map yield self _map_before_submount.add( Submount(prefix, submount_map.rules)) finally: self._url_map = _map_before_submount def handle_errors(self, f_or_exception, *additional_exceptions): """ Register an error handler. This decorator supports two syntaxes. The simpler of these can be used to register a handler for all C{Exception} types:: @app.handle_errors def error_handler(request, failure): request.setResponseCode(500) return 'Uh oh' Alternately, a handler can be registered for one or more specific C{Exception} tyes:: @app.handle_errors(EncodingError, ValidationError): def error_handler(request, failure) request.setResponseCode(400) return failure.getTraceback() The handler will be passed a L{twisted.web.server.Request} as well as a L{twisted.python.failure.Failure} instance. Error handlers may return a deferred, a failure or a response body. If more than one error handler is registered, the handlers will be executed in the order in which they are defined, until a handler is encountered which completes successfully. If no handler completes successfully, L{twisted.web.server.Request}'s processingFailed() method will be called. In addition to handling errors that occur within a route handler, error handlers also handle any C{werkzeug.exceptions.HTTPException} which is raised during routing. In particular, C{werkzeug.exceptions.NotFound} will be raised if no matching route is found, so to return a custom 404 users can do the following:: @app.handle_errors(NotFound) def error_handler(request, failure): request.setResponseCode(404) return 'Not found' @param f_or_exception: An error handler function, or an C{Exception} subclass to scope the decorated handler to. @type f_or_exception: C{function} or C{Exception} @param additional_exceptions Additional C{Exception} subclasses to scope the decorated function to. @type additional_exceptions C{list} of C{Exception}s @returns: decorated error handler function. """ # Try to detect calls using the "simple" @app.handle_error syntax by # introspecting the first argument - if it isn't a type which # subclasses Exception we assume the simple syntax was used. if not isinstance(f_or_exception, type) or not issubclass(f_or_exception, Exception): return self.handle_errors(Exception)(f_or_exception) def deco(f): @wraps(f) def _f(instance, request, failure): r = _call(instance, f, request, failure) if IRenderable.providedBy(r): return renderElement(request, r) return r self._error_handlers.append(([f_or_exception] + list(additional_exceptions), _f)) return _f return deco def run(self, host=None, port=None, logFile=None, endpoint_description=None): """ Run a minimal twisted.web server on the specified C{port}, bound to the interface specified by C{host} and logging to C{logFile}. This function will run the default reactor for your platform and so will block the main thread of your application. It should be the last thing your klein application does. @param host: The hostname or IP address to bind the listening socket to. "0.0.0.0" will allow you to listen on all interfaces, and "127.0.0.1" will allow you to listen on just the loopback interface. @type host: str @param port: The TCP port to accept HTTP requests on. @type port: int @param logFile: The file object to log to, by default C{sys.stdout} @type logFile: file object @param endpoint_description: specification of endpoint. Must contain protocol, port and interface. May contain other optional arguments, e.g. to use SSL: "ssl:443:privateKey=key.pem:certKey=crt.pem" @type endpoint_description: str """ if logFile is None: logFile = sys.stdout log.startLogging(logFile) if not endpoint_description: endpoint_description = "tcp:port={0}:interface={1}".format(port, host) endpoint = endpoints.serverFromString(reactor, endpoint_description) endpoint.listen(Site(self.resource())) reactor.run() _globalKleinApp = Klein() route = _globalKleinApp.route run = _globalKleinApp.run resource = _globalKleinApp.resource klein-17.2.0/src/klein/_plating.py0000664000175000017500000001033113054444052017600 0ustar moshezmoshez00000000000000# -*- test-case-name: klein.test.test_plating -*- """ Templating wrapper support for Klein. """ from functools import wraps from json import dumps from six import text_type, integer_types from twisted.web.template import TagLoader, Element from twisted.web.error import MissingRenderMethod def _should_return_json(request): """ Should the given request result in a JSON entity-body? """ return bool(request.args.get(b"json")) def json_serialize(item): """ A function similar to L{dumps}. """ def helper(unknown): if isinstance(unknown, PlatedElement): return unknown.slot_data else: raise TypeError("{input} not JSON serializable" .format(input=unknown)) return dumps(item, default=helper) def _extra_types(input): """ Renderability for a few additional types. """ if isinstance(input, (float,) + integer_types): return text_type(input) return input class PlatedElement(Element): """ The element type returned by L{Plating}. This contains several utility renderers. """ def __init__(self, slot_data, preloaded): """ @param slot_data: A dictionary mapping names to values. @param preloaded: The pre-loaded data. """ self.slot_data = slot_data super(PlatedElement, self).__init__( loader=TagLoader(preloaded.fillSlots( **{k: _extra_types(v) for k, v in slot_data.items()} )) ) def lookupRenderMethod(self, name): """ @return: a renderer. """ if ":" not in name: raise MissingRenderMethod(self, name) slot, type = name.split(":", 1) def renderList(request, tag): for item in self.slot_data[slot]: yield tag.fillSlots(item=_extra_types(item)) types = { "list": renderList, } if type in types: return types[type] else: raise MissingRenderMethod(self, name) class Plating(object): """ A L{Plating} is a container which can be used to generate HTML from data. Its name is derived both from tem-I{plating} and I{chrome plating}. """ CONTENT = "klein:plating:content" def __init__(self, defaults=None, tags=None, presentation_slots=frozenset()): """ """ self._defaults = {} if defaults is None else defaults self._loader = TagLoader(tags) self._presentation_slots = {self.CONTENT} | set(presentation_slots) def routed(self, routing, content_template): """ """ @wraps(routing) def mydecorator(method): loader = TagLoader(content_template) @routing @wraps(method) def mymethod(request, *args, **kw): data = method(request, *args, **kw) if _should_return_json(request): json_data = self._defaults.copy() json_data.update(data) for ignored in self._presentation_slots: json_data.pop(ignored, None) request.setHeader(b'content-type', b'text/json; charset=utf-8') return json_serialize(json_data) else: request.setHeader(b'content-type', b'text/html; charset=utf-8') data[self.CONTENT] = loader.load() return self._elementify(data) return method return mydecorator def _elementify(self, to_fill_with): """ """ slot_data = self._defaults.copy() slot_data.update(to_fill_with) [loaded] = self._loader.load() loaded = loaded.clone() return PlatedElement(slot_data=slot_data, preloaded=loaded) def widgeted(self, function): """ """ @wraps(function) def wrapper(*a, **k): data = function(*a, **k) return self._elementify(data) wrapper.__name__ += ".widget" function.widget = wrapper return function klein-17.2.0/src/klein/_version.py0000664000175000017500000000037613056444165017646 0ustar moshezmoshez00000000000000""" Provides Klein version information. """ # This file is auto-generated! Do not edit! # Use `python -m incremental.update Klein` to change this file. from incremental import Version __version__ = Version('Klein', 17, 2, 0) __all__ = ["__version__"] klein-17.2.0/src/klein/test/0000775000175000017500000000000013056447076016425 5ustar moshezmoshez00000000000000klein-17.2.0/src/klein/test/test_plating.py0000664000175000017500000002077613054444052021475 0ustar moshezmoshez00000000000000""" Tests for L{klein.plating}. """ from __future__ import ( absolute_import, division, print_function, unicode_literals ) import json from klein import Plating from twisted.web.template import tags, slot from twisted.web.error import FlattenerError, MissingRenderMethod from klein.test.test_resource import requestMock, _render from klein.test.util import TestCase from klein import Klein page = Plating( defaults={ "title": "default title unchanged", Plating.CONTENT: "NEVER MIND THE CONTENT", }, tags=tags.html( tags.head(tags.title(slot("title"))), tags.body( tags.h1(slot("title")), tags.div(slot(Plating.CONTENT), Class="content") ) ), ) element = Plating( defaults={ "a": "NO VALUE FOR A", "b": "NO VALUE FOR B", }, tags=tags.div(tags.span("a: ", slot("a")), tags.span("b: ", slot("b"))), ) @element.widgeted def enwidget(a, b): """ Provide some values for the L{widget} template. """ return {"a": a, "b": b} class PlatingTests(TestCase): """ Tests for L{Plating}. """ def setUp(self): """ Create an app and a resource wrapping that app for this test. """ self.app = Klein() self.kr = self.app.resource() def get(self, uri): """ Issue a virtual GET request to the given path that is expected to succeed synchronously, and return the generated request object and written bytes. """ request = requestMock(uri) d = _render(self.kr, request) self.successResultOf(d) return request, request.getWrittenData() def test_template_html(self): """ Rendering a L{Plating.routed} decorated route results in templated HTML. """ @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request): return {"ok": "test-data-present"} request, written = self.get(b"/") self.assertIn(b'test-data-present', written) self.assertIn(b'default title unchanged', written) def test_template_json(self): """ Rendering a L{Plating.routed} decorated route with a query parameter asking for JSON will yield JSON instead. """ @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request): return {"ok": "an-plating-test"} request, written = self.get(b"/?json=true") self.assertEqual( request.responseHeaders.getRawHeaders(b'content-type')[0], b'text/json; charset=utf-8' ) self.assertEquals({"ok": "an-plating-test", "title": "default title unchanged"}, json.loads(written.decode('utf-8'))) def test_template_numbers(self): """ Data returned from a plated method may include numeric types (integers, floats, and possibly longs), which although they are not normally serializable by twisted.web.template, will be converted by plating into their decimal representation. """ @page.routed(self.app.route("/"), tags.div(tags.span(slot("anInteger")), tags.i(slot("anFloat")), tags.b(slot("anLong")), )) def plateMe(result): return {"anInteger": 7, "anFloat": 3.2, "anLong": 0x10000000000000001} request, written = self.get(b"/") self.assertIn(b"7", written) self.assertIn(b"3.2", written) self.assertIn(b"18446744073709551617", written) def test_render_list(self): """ The C{:list} renderer suffix will render the slot named by the renderer as a list, filling each slot. """ @page.routed(self.app.route("/"), tags.ul(tags.li(slot("item"), render="subplating:list"))) def rsrc(request): return {"subplating": [1, 2, 3]} request, written = self.get(b"/") self.assertIn(b'
  • 1
  • 2
  • 3
', written) self.assertIn(b'default title unchanged', written) def test_widget_html(self): """ When L{Plating.widgeted} is applied as a decorator, it gives the decorated function a C{widget} attribute which is a version of the function with a modified return type that turns it into a renderable HTML sub-element that may fill a slot. """ @page.routed(self.app.route("/"), tags.div(slot("widget"))) def rsrc(request): return {"widget": enwidget.widget(3, 4)} request, written = self.get(b"/") self.assertIn(b"a: 3", written) self.assertIn(b"b: 4", written) def test_widget_json(self): """ When L{Plating.widgeted} is applied as a decorator, and the result is serialized to JSON, it appears the same as the returned value despite the HTML-friendly wrapping described above. """ @page.routed(self.app.route("/"), tags.div(slot("widget"))) def rsrc(request): return {"widget": enwidget.widget(3, 4)} request, written = self.get(b"/?json=1") self.assertEqual(json.loads(written.decode('utf-8')), {"widget": {"a": 3, "b": 4}, "title": "default title unchanged"}) def test_prime_directive_return(self): """ Nothing within these Articles Of Federation shall authorize the United Federation of Planets to alter the return value of a callable by applying a decorator to it... """ exact_result = {"ok": "some nonsense value"} @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request): return exact_result self.assertIdentical(plateMe(None), exact_result) def test_prime_directive_arguments(self): """ ... or shall require the function to modify its signature under these Articles Of Federation. """ @page.routed(self.app.route("/"), tags.span(slot("ok"))) def plateMe(request, one, two, three): return (one, two, three) exact_one = {"one": "and"} exact_two = {"two": "and"} exact_three = {"three": "and"} result_one, result_two, result_three = plateMe( None, exact_one, exact_two, three=exact_three ) self.assertIdentical(result_one, exact_one) self.assertIdentical(result_two, exact_two) self.assertIdentical(result_three, exact_three) def test_presentation_only_json(self): """ Slots marked as "presentation only" will not be reflected in the output. """ plating = Plating(tags=tags.span(slot("title")), presentation_slots={"title"}) @plating.routed(self.app.route("/"), tags.span(slot("data"))) def justJson(request): return {"title": "uninteresting", "data": "interesting"} request, written = self.get(b"/?json=1") self.assertEqual(json.loads(written.decode("utf-8")), {"data": "interesting"}) def test_missing_renderer(self): """ Missing renderers will result in an exception during rendering. """ def test(missing): plating = Plating(tags=tags.span(slot(Plating.CONTENT))) @plating.routed(self.app.route("/"), tags.span(tags.span(render=missing))) def no(request): return {} self.get(b"/") [fe] = self.flushLoggedErrors(FlattenerError) self.assertIsInstance(fe.value.args[0], MissingRenderMethod) test("garbage") test("garbage:missing") def test_json_serialize_unknown_type(self): """ The JSON serializer will raise a L{TypeError} when it can't find an appropriate type. """ from klein._plating import json_serialize class reprish(object): def __repr__(self): return '' te = self.assertRaises(TypeError, json_serialize, {"an": reprish()}) self.assertIn("", str(te)) klein-17.2.0/src/klein/test/__init__.py0000664000175000017500000000000013054444052020511 0ustar moshezmoshez00000000000000klein-17.2.0/src/klein/test/util.py0000664000175000017500000001302713054444052017744 0ustar moshezmoshez00000000000000""" Shared tools for Klein's test suite. """ import twisted from twisted.trial.unittest import TestCase from twisted.python import failure from twisted.python.versions import Version if twisted.version < Version('twisted', 13, 1, 0): class TestCase(TestCase): def successResultOf(self, deferred): result = [] deferred.addBoth(result.append) if not result: self.fail( "Success result expected on %r, found no result instead" % ( deferred,)) elif isinstance(result[0], failure.Failure): self.fail( "Success result expected on %r, " "found failure result instead:\n%s" % ( deferred, result[0].getTraceback())) else: return result[0] def failureResultOf(self, deferred, *expectedExceptionTypes): result = [] deferred.addBoth(result.append) if not result: self.fail( "Failure result expected on %r, found no result instead" % ( deferred,)) elif not isinstance(result[0], failure.Failure): self.fail( "Failure result expected on %r, " "found success result (%r) instead" % (deferred, result[0])) elif (expectedExceptionTypes and not result[0].check(*expectedExceptionTypes)): expectedString = " or ".join([ '.'.join((t.__module__, t.__name__)) for t in expectedExceptionTypes]) self.fail( "Failure of type (%s) expected on %r, " "found type %r instead: %s" % ( expectedString, deferred, result[0].type, result[0].getTraceback())) else: return result[0] class EqualityTestsMixin(object): """ A mixin defining tests for the standard implementation of C{==} and C{!=}. """ def anInstance(self): """ Return an instance of the class under test. Each call to this method must return a different object. All objects returned must be equal to each other. """ raise NotImplementedError() def anotherInstance(self): """ Return an instance of the class under test. Each call to this method must return a different object. The objects must not be equal to the objects returned by C{anInstance}. They may or may not be equal to each other (they will not be compared against each other). """ raise NotImplementedError() def test_identicalEq(self): """ An object compares equal to itself using the C{==} operator. """ o = self.anInstance() self.assertTrue(o == o) def test_identicalNe(self): """ An object doesn't compare not equal to itself using the C{!=} operator. """ o = self.anInstance() self.assertFalse(o != o) def test_sameEq(self): """ Two objects that are equal to each other compare equal to each other using the C{==} operator. """ a = self.anInstance() b = self.anInstance() self.assertTrue(a == b) def test_sameNe(self): """ Two objects that are equal to each other do not compare not equal to each other using the C{!=} operator. """ a = self.anInstance() b = self.anInstance() self.assertFalse(a != b) def test_differentEq(self): """ Two objects that are not equal to each other do not compare equal to each other using the C{==} operator. """ a = self.anInstance() b = self.anotherInstance() self.assertFalse(a == b) def test_differentNe(self): """ Two objects that are not equal to each other compare not equal to each other using the C{!=} operator. """ a = self.anInstance() b = self.anotherInstance() self.assertTrue(a != b) def test_anotherTypeEq(self): """ The object does not compare equal to an object of an unrelated type (which does not implement the comparison) using the C{==} operator. """ a = self.anInstance() b = object() self.assertFalse(a == b) def test_anotherTypeNe(self): """ The object compares not equal to an object of an unrelated type (which does not implement the comparison) using the C{!=} operator. """ a = self.anInstance() b = object() self.assertTrue(a != b) def test_delegatedEq(self): """ The result of comparison using C{==} is delegated to the right-hand operand if it is of an unrelated type. """ class Delegate(object): def __eq__(self, other): # Do something crazy and obvious. return [self] a = self.anInstance() b = Delegate() self.assertEqual(a == b, [b]) def test_delegateNe(self): """ The result of comparison using C{!=} is delegated to the right-hand operand if it is of an unrelated type. """ class Delegate(object): def __ne__(self, other): # Do something crazy and obvious. return [self] a = self.anInstance() b = Delegate() self.assertEqual(a != b, [b]) klein-17.2.0/src/klein/test/test_app.py0000664000175000017500000002351113054444052020605 0ustar moshezmoshez00000000000000from __future__ import absolute_import, division from twisted.trial import unittest import sys from mock import Mock, patch from twisted.python.components import registerAdapter from klein import Klein from klein.app import KleinRequest from klein.interfaces import IKleinRequest from klein.test.util import EqualityTestsMixin class DummyRequest(object): def __init__(self, n): self.n = n def __eq__(self, other): return other.n == self.n def __repr__(self): return ''.format(n=self.n) registerAdapter(KleinRequest, DummyRequest, IKleinRequest) class KleinEqualityTestCase(unittest.TestCase, EqualityTestsMixin): """ Tests for L{Klein}'s implementation of C{==} and C{!=}. """ class _One(object): app = Klein() def __eq__(self, other): return True def __ne__(self, other): return False def __hash__(self): return id(self) _another = Klein() def anInstance(self): # This is actually a new Klein instance every time since Klein.__get__ # creates a new Klein instance for every instance it is retrieved from. # The different _One instance, at least, will not cause the Klein # instances to be not-equal to each other since an instance of _One is # equal to everything. return self._One().app def anotherInstance(self): return self._another class KleinTestCase(unittest.TestCase): def test_route(self): """ L{Klein.route} adds functions as routable endpoints. """ app = Klein() @app.route("/foo") def foo(request): return "foo" c = app.url_map.bind("foo") self.assertEqual(c.match("/foo"), ("foo", {})) self.assertEqual(len(app.endpoints), 1) self.assertEqual(app.execute_endpoint("foo", DummyRequest(1)), "foo") def test_submountedRoute(self): """ L{Klein.subroute} adds functions as routable endpoints. """ app = Klein() with app.subroute("/sub") as app: @app.route("/prefixed_uri") def foo_endpoint(request): return b"foo" c = app.url_map.bind("sub/prefixed_uri") self.assertEqual( c.match("/sub/prefixed_uri"), ("foo_endpoint", {})) self.assertEqual( len(app.endpoints), 1) self.assertEqual( app.execute_endpoint("foo_endpoint", DummyRequest(1)), b"foo") def test_stackedRoute(self): """ L{Klein.route} can be stacked to create multiple endpoints of a single function. """ app = Klein() @app.route("/foo") @app.route("/bar", endpoint="bar") def foobar(request): return "foobar" self.assertEqual(len(app.endpoints), 2) c = app.url_map.bind("foo") self.assertEqual(c.match("/foo"), ("foobar", {})) self.assertEqual(app.execute_endpoint("foobar", DummyRequest(1)), "foobar") self.assertEqual(c.match("/bar"), ("bar", {})) self.assertEqual(app.execute_endpoint("bar", DummyRequest(2)), "foobar") def test_branchRoute(self): """ L{Klein.route} should create a branch path which consumes all children when the branch keyword argument is True. """ app = Klein() @app.route("/foo/", branch=True) def foo(request): return "foo" c = app.url_map.bind("foo") self.assertEqual(c.match("/foo/"), ("foo", {})) self.assertEqual( c.match("/foo/bar"), ("foo_branch", {'__rest__': 'bar'})) self.assertEquals(app.endpoints["foo"].__name__, "foo") self.assertEquals( app.endpoints["foo_branch"].__name__, "foo") def test_classicalRoute(self): """ L{Klein.route} may be used a method decorator when a L{Klein} instance is defined as a class variable. """ bar_calls = [] class Foo(object): app = Klein() @app.route("/bar") def bar(self, request): bar_calls.append((self, request)) return "bar" foo = Foo() c = foo.app.url_map.bind("bar") self.assertEqual(c.match("/bar"), ("bar", {})) self.assertEquals(foo.app.execute_endpoint("bar", DummyRequest(1)), "bar") self.assertEqual(bar_calls, [(foo, DummyRequest(1))]) def test_classicalRouteWithTwoInstances(self): """ Multiple instances of a class with a L{Klein} attribute and L{Klein.route}'d methods can be created and their L{Klein}s used independently. """ class Foo(object): app = Klein() def __init__(self): self.bar_calls = [] @app.route("/bar") def bar(self, request): self.bar_calls.append((self, request)) return "bar" foo_1 = Foo() foo_1_app = foo_1.app foo_2 = Foo() foo_2_app = foo_2.app dr1 = DummyRequest(1) dr2 = DummyRequest(2) foo_1_app.execute_endpoint('bar', dr1) foo_2_app.execute_endpoint('bar', dr2) self.assertEqual(foo_1.bar_calls, [(foo_1, dr1)]) self.assertEqual(foo_2.bar_calls, [(foo_2, dr2)]) def test_classicalRouteWithBranch(self): """ Multiple instances of a class with a L{Klein} attribute and L{Klein.route}'d methods can be created and their L{Klein}s used independently. """ class Foo(object): app = Klein() def __init__(self): self.bar_calls = [] @app.route("/bar/", branch=True) def bar(self, request): self.bar_calls.append((self, request)) return "bar" foo_1 = Foo() foo_1_app = foo_1.app foo_2 = Foo() foo_2_app = foo_2.app dr1 = DummyRequest(1) dr2 = DummyRequest(2) foo_1_app.execute_endpoint('bar_branch', dr1) foo_2_app.execute_endpoint('bar_branch', dr2) self.assertEqual(foo_1.bar_calls, [(foo_1, dr1)]) self.assertEqual(foo_2.bar_calls, [(foo_2, dr2)]) def test_branchDoesntRequireTrailingSlash(self): """ L{Klein.route} should create a branch path which consumes all children, when the branch keyword argument is True and there is no trailing / on the path. """ app = Klein() @app.route("/foo", branch=True) def foo(request): return "foo" c = app.url_map.bind("foo") self.assertEqual(c.match("/foo/bar"), ("foo_branch", {"__rest__": "bar"})) @patch('klein.app.KleinResource') @patch('klein.app.Site') @patch('klein.app.log') @patch('klein.app.reactor') def test_run(self, reactor, mock_log, mock_site, mock_kr): """ L{Klein.run} configures a L{KleinResource} and a L{Site} listening on the specified interface and port, and logs to stdout. """ app = Klein() app.run("localhost", 8080) reactor.listenTCP.assert_called_with( 8080, mock_site.return_value, backlog=50, interface="localhost") reactor.run.assert_called_with() mock_site.assert_called_with(mock_kr.return_value) mock_kr.assert_called_with(app) mock_log.startLogging.assert_called_with(sys.stdout) @patch('klein.app.KleinResource') @patch('klein.app.Site') @patch('klein.app.log') @patch('klein.app.reactor') def test_runWithLogFile(self, reactor, mock_log, mock_site, mock_kr): """ L{Klein.run} logs to the specified C{logFile}. """ app = Klein() logFile = Mock() app.run("localhost", 8080, logFile=logFile) reactor.listenTCP.assert_called_with( 8080, mock_site.return_value, backlog=50, interface="localhost") reactor.run.assert_called_with() mock_site.assert_called_with(mock_kr.return_value) mock_kr.assert_called_with(app) mock_log.startLogging.assert_called_with(logFile) @patch('klein.app.KleinResource') @patch('klein.app.log') @patch('klein.app.endpoints.serverFromString') @patch('klein.app.reactor') def test_runTCP6(self, reactor, mock_sfs, mock_log, mock_kr): """ L{Klein.run} called with tcp6 endpoint description. """ app = Klein() interface = "2001\:0DB8\:f00e\:eb00\:\:1" spec = "tcp6:8080:interface={0}".format(interface) app.run(endpoint_description=spec) reactor.run.assert_called_with() mock_sfs.assert_called_with(reactor, spec) mock_log.startLogging.assert_called_with(sys.stdout) mock_kr.assert_called_with(app) @patch('klein.app.KleinResource') @patch('klein.app.log') @patch('klein.app.endpoints.serverFromString') @patch('klein.app.reactor') def test_runSSL(self, reactor, mock_sfs, mock_log, mock_kr): """ L{Klein.run} called with SSL endpoint specification. """ app = Klein() key = "key.pem" cert = "cert.pem" dh_params = "dhparam.pem" spec_template = "ssl:443:privateKey={0}:certKey={1}" spec = spec_template.format(key, cert, dh_params) app.run(endpoint_description=spec) reactor.run.assert_called_with() mock_sfs.assert_called_with(reactor, spec) mock_log.startLogging.assert_called_with(sys.stdout) mock_kr.assert_called_with(app) @patch('klein.app.KleinResource') def test_resource(self, mock_kr): """ L{Klien.resource} returns a L{KleinResource}. """ app = Klein() resource = app.resource() mock_kr.assert_called_with(app) self.assertEqual(mock_kr.return_value, resource) klein-17.2.0/src/klein/test/py3_test_resource.py0000664000175000017500000000315313054444052022447 0ustar moshezmoshez00000000000000import twisted from klein import Klein from klein.resource import KleinResource from klein.test.util import TestCase from .test_resource import LeafResource, requestMock, _render class PY3KleinResourceTests(TestCase): def assertFired(self, deferred, result=None): """ Assert that the given deferred has fired with the given result. """ self.assertEqual(self.successResultOf(deferred), result) def test_asyncResourceRendering(self): app = Klein() resource = KleinResource(app) request = requestMock(b"/resource/leaf") @app.route("/resource/leaf") async def leaf(request): return LeafResource() if (twisted.version.major, twisted.version.minor) >= (16, 6): expected = b"I am a leaf in the wind." d = _render(resource, request) else: expected = b"** Twisted>=16.6 is required **" # Twisted version in use does not have ensureDeferred, so # attempting to use an async resource will raise # NotImplementedError. # resource.render(), and therefore _render(), does not return the # deferred object that does the rendering, so we need to check for # errors indirectly via handle_errors(). @app.handle_errors(NotImplementedError) def notImplementedError(request, failure): return expected d = _render(resource, request) def assertResult(_): self.assertEqual(request.getWrittenData(), expected) d.addCallback(assertResult) return d klein-17.2.0/src/klein/test/test_resource.py0000664000175000017500000007774413054444052021675 0ustar moshezmoshez00000000000000from __future__ import absolute_import, division import os from io import BytesIO from six.moves.urllib.parse import parse_qs from mock import Mock, call from twisted.internet.defer import succeed, Deferred, fail, CancelledError from twisted.internet.error import ConnectionLost from twisted.internet.unix import Server from twisted.web import server from twisted.web.http_headers import Headers from twisted.web.resource import Resource from twisted.web.static import File from twisted.web.template import Element, XMLString, renderer from twisted.web.test.test_web import DummyChannel from twisted.python.compat import unicode, _PY3 from werkzeug.exceptions import NotFound from klein import Klein from klein.interfaces import IKleinRequest from klein.resource import ( KleinResource, _URLDecodeError, _extractURLparts, ensure_utf8_bytes, ) from klein.test.util import TestCase, EqualityTestsMixin def requestMock(path, method=b"GET", host=b"localhost", port=8080, isSecure=False, body=None, headers=None): if not headers: headers = {} if not body: body = b'' path, qpath = (path.split(b"?", 1) + [b""])[:2] request = server.Request(DummyChannel(), False) request.site = Mock(server.Site) request.gotLength(len(body)) request.content = BytesIO() request.content.write(body) request.content.seek(0) request.args = parse_qs(qpath) request.requestHeaders = Headers(headers) request.setHost(host, port, isSecure) request.uri = path request.prepath = [] request.postpath = path.split(b'/')[1:] request.method = method request.clientproto = b'HTTP/1.1' request.setHeader = Mock(wraps=request.setHeader) request.setResponseCode = Mock(wraps=request.setResponseCode) request._written = BytesIO() request.finishCount = 0 request.writeCount = 0 def registerProducer(producer, streaming): request.producer = producer for x in range(2): if request.producer: request.producer.resumeProducing() def unregisterProducer(): request.producer = None def finish(): request.finishCount += 1 if not request.startedWriting: request.write(b'') if not request.finished: request.finished = True request._cleanup() def write(data): request.writeCount += 1 request.startedWriting = True if not request.finished: request._written.write(data) else: raise RuntimeError('Request.write called on a request after ' 'Request.finish was called.') def getWrittenData(): return request._written.getvalue() request.finish = finish request.write = write request.getWrittenData = getWrittenData request.registerProducer = registerProducer request.unregisterProducer = unregisterProducer request.processingFailed = Mock(wraps=request.processingFailed) return request def _render(resource, request, notifyFinish=True): result = resource.render(request) if isinstance(result, bytes): request.write(result) request.finish() return succeed(None) elif result is server.NOT_DONE_YET: if request.finished or not notifyFinish: return succeed(None) else: return request.notifyFinish() else: raise ValueError("Unexpected return value: %r" % (result,)) class SimpleElement(Element): loader = XMLString("""

""") def __init__(self, name): self._name = name @renderer def name(self, request, tag): return tag(self._name) class DeferredElement(SimpleElement): @renderer def name(self, request, tag): self.deferred = Deferred() self.deferred.addCallback(lambda ignored: tag(self._name)) return self.deferred class LeafResource(Resource): isLeaf = True def render(self, request): return b"I am a leaf in the wind." class ChildResource(Resource): isLeaf = True def __init__(self, name): self._name = name def render(self, request): return b"I'm a child named " + self._name + b"!" class ChildrenResource(Resource): def render(self, request): return b"I have children!" def getChild(self, path, request): if path == b'': return self return ChildResource(path) class ProducingResource(Resource): def __init__(self, path, strings): self.path = path self.strings = strings def render_GET(self, request): producer = MockProducer(request, self.strings) producer.start() return server.NOT_DONE_YET class MockProducer(object): def __init__(self, request, strings): self.request = request self.strings = strings def start(self): self.request.registerProducer(self, False) def resumeProducing(self): if self.strings: self.request.write(self.strings.pop(0)) else: self.request.unregisterProducer() self.request.finish() class KleinResourceEqualityTests(TestCase, EqualityTestsMixin): """ Tests for L{KleinResource}'s implementation of C{==} and C{!=}. """ class _One(object): oneKlein = Klein() @oneKlein.route("/foo") def foo(self): pass _one = _One() class _Another(object): anotherKlein = Klein() @anotherKlein.route("/bar") def bar(self): pass _another = _Another() def anInstance(self): return self._one.oneKlein def anotherInstance(self): return self._another.anotherKlein class KleinResourceTests(TestCase): def setUp(self): self.app = Klein() self.kr = KleinResource(self.app) def assertFired(self, deferred, result=None): """ Assert that the given deferred has fired with the given result. """ self.assertEqual(self.successResultOf(deferred), result) def assertNotFired(self, deferred): """ Assert that the given deferred has not fired with a result. """ _pawn = object() result = getattr(deferred, 'result', _pawn) if result != _pawn: self.fail("Expected deferred not to have fired, " "but it has: %r" % (deferred,)) def test_simplePost(self): app = self.app # The order in which these functions are defined # matters. If the more generic one is defined first # then it will eat requests that should have been handled # by the more specific handler. @app.route("/", methods=["POST"]) def handle_post(request): return b'posted' @app.route("/") def handle(request): return b'gotted' request = requestMock(b'/', b'POST') request2 = requestMock(b'/') d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), b'posted') d2 = _render(self.kr, request2) self.assertFired(d2) self.assertEqual(request2.getWrittenData(), b'gotted') def test_simpleRouting(self): app = self.app @app.route("/") def slash(request): return b'ok' request = requestMock(b'/') d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), b'ok') def test_branchRendering(self): app = self.app @app.route("/", branch=True) def slash(request): return b'ok' request = requestMock(b'/foo') d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), b'ok') def test_branchWithExplicitChildrenRouting(self): app = self.app @app.route("/") def slash(request): return b'ok' @app.route("/zeus") def wooo(request): return b'zeus' request = requestMock(b'/zeus') request2 = requestMock(b'/') d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), b'zeus') d2 = _render(self.kr, request2) self.assertFired(d2) self.assertEqual(request2.getWrittenData(), b'ok') def test_branchWithExplicitChildBranch(self): app = self.app @app.route("/", branch=True) def slash(request): return b'ok' @app.route("/zeus/", branch=True) def wooo(request): return b'zeus' request = requestMock(b'/zeus/foo') request2 = requestMock(b'/') d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), b'zeus') d2 = _render(self.kr, request2) self.assertFired(d2) self.assertEqual(request2.getWrittenData(), b'ok') def test_deferredRendering(self): app = self.app deferredResponse = Deferred() @app.route("/deferred") def deferred(request): return deferredResponse request = requestMock(b"/deferred") d = _render(self.kr, request) self.assertNotFired(d) deferredResponse.callback(b'ok') self.assertFired(d) self.assertEqual(request.getWrittenData(), b'ok') def test_elementRendering(self): app = self.app @app.route("/element/") def element(request, name): return SimpleElement(name) request = requestMock(b"/element/foo") d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), b"\n

foo

") def test_deferredElementRendering(self): app = self.app elements = [] @app.route("/element/") def element(request, name): it = DeferredElement(name) elements.append(it) return it request = requestMock(b"/element/bar") d = _render(self.kr, request) self.assertEqual(len(elements), 1) [oneElement] = elements self.assertNoResult(d) oneElement.deferred.callback(None) self.assertFired(d) self.assertEqual(request.getWrittenData(), b"\n

bar

") def test_leafResourceRendering(self): app = self.app request = requestMock(b"/resource/leaf") @app.route("/resource/leaf") def leaf(request): return LeafResource() d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), b"I am a leaf in the wind.") def test_childResourceRendering(self): app = self.app request = requestMock(b"/resource/children/betty") @app.route("/resource/children/", branch=True) def children(request): return ChildrenResource() d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), b"I'm a child named betty!") def test_childrenResourceRendering(self): app = self.app request = requestMock(b"/resource/children/") @app.route("/resource/children/", branch=True) def children(request): return ChildrenResource() d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), b"I have children!") def test_producerResourceRendering(self): """ Test that Klein will correctly handle producing L{Resource}s. Producing Resources close the connection by themselves, sometimes after Klein has 'finished'. This test lets Klein finish its handling of the request before doing more producing. """ app = self.app request = requestMock(b"/resource") @app.route("/resource", branch=True) def producer(request): return ProducingResource(request, [b"a", b"b", b"c", b"d"]) d = _render(self.kr, request, notifyFinish=False) self.assertNotEqual(request.getWrittenData(), b"abcd", "The full " "response should not have been written at this " "point.") while request.producer: request.producer.resumeProducing() self.assertEqual(self.successResultOf(d), None) self.assertEqual(request.getWrittenData(), b"abcd") self.assertEqual(request.writeCount, 4) self.assertEqual(request.finishCount, 1) self.assertEqual(request.producer, None) def test_notFound(self): request = requestMock(b"/fourohofour") d = _render(self.kr, request) self.assertFired(d) request.setResponseCode.assert_called_with(404) self.assertIn(b"404 Not Found", request.getWrittenData()) def test_renderUnicode(self): app = self.app request = requestMock(b"/snowman") @app.route("/snowman") def snowman(request): return u'\u2603' d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), b"\xE2\x98\x83") def test_renderNone(self): app = self.app request = requestMock(b"/None") @app.route("/None") def none(request): return None d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), b'') self.assertEqual(request.finishCount, 1) self.assertEqual(request.writeCount, 1) def test_staticRoot(self): app = self.app request = requestMock(b"/__init__.py") @app.route("/", branch=True) def root(request): return File(os.path.dirname(__file__)) d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), open( os.path.join( os.path.dirname(__file__), "__init__.py"), 'rb').read()) self.assertEqual(request.finishCount, 1) def test_explicitStaticBranch(self): app = self.app request = requestMock(b"/static/__init__.py") @app.route("/static/", branch=True) def root(request): return File(os.path.dirname(__file__)) d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.getWrittenData(), open( os.path.join( os.path.dirname(__file__), "__init__.py"), 'rb').read()) self.assertEqual(request.writeCount, 1) self.assertEqual(request.finishCount, 1) def test_staticDirlist(self): app = self.app request = requestMock(b"/") @app.route("/", branch=True) def root(request): return File(os.path.dirname(__file__)) d = _render(self.kr, request) self.assertFired(d) self.assertIn(b'Directory listing', request.getWrittenData()) self.assertEqual(request.writeCount, 1) self.assertEqual(request.finishCount, 1) def test_addSlash(self): app = self.app request = requestMock(b"/foo") @app.route("/foo/") def foo(request): return "foo" d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.setHeader.call_count, 3) request.setHeader.assert_has_calls( [call(b'Content-Type', b'text/html; charset=utf-8'), call(b'Content-Length', b'259'), call(b'Location', b'http://localhost:8080/foo/')]) def test_methodNotAllowed(self): app = self.app request = requestMock(b"/foo", method=b'DELETE') @app.route("/foo", methods=['GET']) def foo(request): return "foo" d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.code, 405) def test_methodNotAllowedWithRootCollection(self): app = self.app request = requestMock(b"/foo/bar", method=b'DELETE') @app.route("/foo/bar", methods=['GET']) def foobar(request): return b"foo/bar" @app.route("/foo/", methods=['DELETE']) def foo(request): return b"foo" d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.code, 405) def test_noImplicitBranch(self): app = self.app request = requestMock(b"/foo") @app.route("/") def root(request): return b"foo" d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.code, 404) def test_strictSlashes(self): app = self.app request = requestMock(b"/foo/bar") request_url = [None] @app.route("/foo/bar/", strict_slashes=False) def root(request): request_url[0] = request.URLPath() return b"foo" d = _render(self.kr, request) self.assertFired(d) self.assertEqual(str(request_url[0]), "http://localhost:8080/foo/bar") self.assertEqual(request.getWrittenData(), b'foo') self.assertEqual(request.code, 200) def test_URLPath(self): app = self.app request = requestMock(b'/egg/chicken') request_url = [None] @app.route("/egg/chicken") def wooo(request): request_url[0] = request.URLPath() return b'foo' d = _render(self.kr, request) self.assertFired(d) self.assertEqual(str(request_url[0]), 'http://localhost:8080/egg/chicken') def test_URLPath_root(self): app = self.app request = requestMock(b'/') request_url = [None] @app.route("/") def root(request): request_url[0] = request.URLPath() d = _render(self.kr, request) self.assertFired(d) self.assertEqual(str(request_url[0]), 'http://localhost:8080/') def test_URLPath_traversedResource(self): app = self.app request = requestMock(b'/resource/foo') request_url = [None] class URLPathResource(Resource): def render(self, request): request_url[0] = request.URLPath() def getChild(self, request, segment): return self @app.route("/resource/", branch=True) def root(request): return URLPathResource() d = _render(self.kr, request) self.assertFired(d) self.assertEqual(str(request_url[0]), 'http://localhost:8080/resource/foo') def test_handlerRaises(self): app = self.app request = requestMock(b"/") failures = [] class RouteFailureTest(Exception): pass @app.route("/") def root(request): def _capture_failure(f): failures.append(f) return f return fail(RouteFailureTest("die")).addErrback(_capture_failure) d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.code, 500) request.processingFailed.assert_called_once_with(failures[0]) self.flushLoggedErrors(RouteFailureTest) def test_genericErrorHandler(self): app = self.app request = requestMock(b"/") failures = [] class RouteFailureTest(Exception): pass @app.route("/") def root(request): raise RouteFailureTest("not implemented") @app.handle_errors def handle_errors(request, failure): failures.append(failure) request.setResponseCode(501) return d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.code, 501) assert not request.processingFailed.called def test_typeSpecificErrorHandlers(self): app = self.app request = requestMock(b"/") type_error_handled = False generic_error_handled = False failures = [] class TypeFilterTestError(Exception): pass @app.route("/") def root(request): return fail(TypeFilterTestError("not implemented")) @app.handle_errors(TypeError) def handle_type_error(request, failure): global type_error_handled type_error_handled = True return @app.handle_errors(TypeFilterTestError) def handle_type_filter_test_error(request, failure): failures.append(failure) request.setResponseCode(501) return @app.handle_errors def handle_generic_error(request, failure): global generic_error_handled generic_error_handled = True return d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.processingFailed.called, False) self.assertEqual(type_error_handled, False) self.assertEqual(generic_error_handled, False) self.assertEqual(len(failures), 1) self.assertEqual(request.code, 501) def test_notFoundException(self): app = self.app request = requestMock(b"/") generic_error_handled = False @app.handle_errors(NotFound) def handle_not_found(request, failure): request.setResponseCode(404) return b'Custom Not Found' @app.handle_errors def handle_generic_error(request, failure): global generic_error_handled generic_error_handled = True return d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.processingFailed.called, False) self.assertEqual(generic_error_handled, False) self.assertEqual(request.code, 404) self.assertEqual(request.getWrittenData(), b'Custom Not Found') self.assertEqual(request.writeCount, 1) def test_errorHandlerNeedsRendering(self): """ Renderables returned by L{handle_errors} are rendered. """ app = self.app request = requestMock(b"/") @app.handle_errors(NotFound) def handle_not_found(request, failure): return SimpleElement("Not Found Element") d = _render(self.kr, request) rendered = b"\n

Not Found Element

" self.assertFired(d) self.assertEqual(request.processingFailed.called, False) self.assertEqual(request.getWrittenData(), rendered) def test_requestWriteAfterFinish(self): app = self.app request = requestMock(b"/") @app.route("/") def root(request): request.finish() return b'foo' d = _render(self.kr, request) self.assertFired(d) self.assertEqual(request.writeCount, 2) self.assertEqual(request.getWrittenData(), b'') [failure] = self.flushLoggedErrors(RuntimeError) self.assertEqual( str(failure.value), ("Request.write called on a request after Request.finish was " "called.")) def test_requestFinishAfterConnectionLost(self): app = self.app request = requestMock(b"/") finished = Deferred() @app.route("/") def root(request): request.notifyFinish().addBoth(lambda _: finished.callback(b'foo')) return finished d = _render(self.kr, request) def _eb(result): [failure] = self.flushLoggedErrors(RuntimeError) self.assertEqual( str(failure.value), ("Request.finish called on a request after its connection was " "lost; use Request.notifyFinish to keep track of this.")) d.addErrback(lambda _: finished) d.addErrback(_eb) self.assertNotFired(d) request.connectionLost(ConnectionLost()) self.assertFired(d) def test_routeHandlesRequestFinished(self): app = self.app request = requestMock(b"/") cancelled = [] @app.route("/") def root(request): _d = Deferred() _d.addErrback(cancelled.append) request.notifyFinish().addCallback(lambda _: _d.cancel()) return _d d = _render(self.kr, request) request.finish() self.assertFired(d) cancelled[0].trap(CancelledError) self.assertEqual(request.getWrittenData(), b'') self.assertEqual(request.writeCount, 1) self.assertEqual(request.processingFailed.call_count, 0) def test_url_for(self): app = self.app request = requestMock(b'/foo/1') relative_url = [None] @app.route("/foo/") def foo(request, bar): krequest = IKleinRequest(request) relative_url[0] = krequest.url_for('foo', {'bar': bar + 1}) d = _render(self.kr, request) self.assertFired(d) self.assertEqual(relative_url[0], '/foo/2') def test_cancelledDeferred(self): app = self.app request = requestMock(b"/") inner_d = Deferred() @app.route("/") def root(request): return inner_d d = _render(self.kr, request) inner_d.cancel() self.assertFired(d) self.flushLoggedErrors(CancelledError) def test_external_url_for(self): app = self.app request = requestMock(b'/foo/1') relative_url = [None] @app.route("/foo/") def foo(request, bar): krequest = IKleinRequest(request) relative_url[0] = krequest.url_for('foo', {'bar': bar + 1}, force_external=True) d = _render(self.kr, request) self.assertFired(d) self.assertEqual(relative_url[0], 'http://localhost:8080/foo/2') def test_cancelledIsEatenOnConnectionLost(self): app = self.app request = requestMock(b"/") @app.route("/") def root(request): _d = Deferred() request.notifyFinish().addErrback(lambda _: _d.cancel()) return _d d = _render(self.kr, request) self.assertNotFired(d) request.connectionLost(ConnectionLost()) def _cb(result): self.assertEqual(request.processingFailed.call_count, 0) d.addErrback(lambda f: f.trap(ConnectionLost)) d.addCallback(_cb) self.assertFired(d) def test_cancelsOnConnectionLost(self): app = self.app request = requestMock(b"/") handler_d = Deferred() @app.route("/") def root(request): return handler_d d = _render(self.kr, request) self.assertNotFired(d) request.connectionLost(ConnectionLost()) handler_d.addErrback(lambda f: f.trap(CancelledError)) d.addErrback(lambda f: f.trap(ConnectionLost)) d.addCallback(lambda _: handler_d) self.assertFired(d) def test_ensure_utf8_bytes(self): self.assertEqual(ensure_utf8_bytes(u"abc"), b"abc") self.assertEqual(ensure_utf8_bytes(u"\u2202"), b"\xe2\x88\x82") self.assertEqual(ensure_utf8_bytes(b"\xe2\x88\x82"), b"\xe2\x88\x82") def test_decodesPath(self): """ server_name, path_info, and script_name are decoded as UTF-8 before being handed to werkzeug. """ request = requestMock(b"/f\xc3\xb6\xc3\xb6") _render(self.kr, request) kreq = IKleinRequest(request) self.assertIsInstance(kreq.mapper.server_name, unicode) self.assertIsInstance(kreq.mapper.path_info, unicode) self.assertIsInstance(kreq.mapper.script_name, unicode) def test_failedDecodePathInfo(self): """ If decoding of one of the URL parts (in this case PATH_INFO) fails, the error is logged and 400 returned. """ request = requestMock(b"/f\xc3\xc3\xb6") _render(self.kr, request) rv = request.getWrittenData() self.assertEqual(b"Non-UTF-8 encoding in URL.", rv) self.assertEqual(1, len(self.flushLoggedErrors(UnicodeDecodeError))) def test_urlDecodeErrorReprPy2(self): """ URLDecodeError.__repr__ formats properly. """ self.assertEqual( ")>", repr(_URLDecodeError(ValueError)), ) def test_urlDecodeErrorReprPy3(self): """ URLDecodeError.__repr__ formats properly. """ self.assertEqual( ")>", repr(_URLDecodeError(ValueError)), ) if _PY3: test_urlDecodeErrorReprPy2.skip = "Only works on Py2" else: test_urlDecodeErrorReprPy3.skip = "Only works on Py3" class ExtractURLpartsTests(TestCase): """ Tests for L{klein.resource._extractURLparts}. """ def test_types(self): """ Returns the correct types. """ url_scheme, server_name, server_port, path_info, script_name = \ _extractURLparts(requestMock(b"/f\xc3\xb6\xc3\xb6")) self.assertIsInstance(url_scheme, unicode) self.assertIsInstance(server_name, unicode) self.assertIsInstance(server_port, int) self.assertIsInstance(path_info, unicode) self.assertIsInstance(script_name, unicode) def assertDecodingFailure(self, exception, part): """ Checks whether C{exception} consists of a single L{UnicodeDecodeError} for C{part}. """ self.assertEqual(1, len(exception.errors)) actualPart, actualFail = exception.errors[0] self.assertEqual(part, actualPart) self.assertIsInstance(actualFail.value, UnicodeDecodeError) def test_failServerName(self): """ Raises URLDecodeError if SERVER_NAME can't be decoded. """ request = requestMock(b"/foo") request.getRequestHostname = lambda: b"f\xc3\xc3\xb6" e = self.assertRaises(_URLDecodeError, _extractURLparts, request) self.assertDecodingFailure(e, "SERVER_NAME") def test_failPathInfo(self): """ Raises URLDecodeError if PATH_INFO can't be decoded. """ request = requestMock(b"/f\xc3\xc3\xb6") e = self.assertRaises(_URLDecodeError, _extractURLparts, request) self.assertDecodingFailure(e, "PATH_INFO") def test_failScriptName(self): """ Raises URLDecodeError if SCRIPT_NAME can't be decoded. """ request = requestMock(b"/foo") request.prepath = [b"f\xc3\xc3\xb6"] e = self.assertRaises(_URLDecodeError, _extractURLparts, request) self.assertDecodingFailure(e, "SCRIPT_NAME") def test_failAll(self): """ If multiple parts fail, they all get appended to the errors list of URLDecodeError. """ request = requestMock(b"/f\xc3\xc3\xb6") request.prepath = [b"f\xc3\xc3\xb6"] request.getRequestHostname = lambda: b"f\xc3\xc3\xb6" e = self.assertRaises(_URLDecodeError, _extractURLparts, request) self.assertEqual( set(["SERVER_NAME", "PATH_INFO", "SCRIPT_NAME"]), set(part for part, _ in e.errors) ) def test_afUnixSocket(self): """ Test proper handling of AF_UNIX sockets """ request = requestMock(b"/f\xc3\xb6\xc3\xb6") server_mock = Mock(Server) server_mock.getRequestHostname = u'/var/run/twisted.socket' request.host = server_mock url_scheme, server_name, server_port, path_info, script_name = _extractURLparts(request) self.assertIsInstance(url_scheme, unicode) self.assertIsInstance(server_name, unicode) self.assertIsInstance(server_port, int) self.assertIsInstance(path_info, unicode) self.assertIsInstance(script_name, unicode) if _PY3: import sys if sys.version_info >= (3, 5): from .py3_test_resource import PY3KleinResourceTests PY3KleinResourceTests # shh pyflakes klein-17.2.0/PKG-INFO0000664000175000017500000000676113056447076014664 0ustar moshezmoshez00000000000000Metadata-Version: 1.1 Name: klein Version: 17.2.0 Summary: werkzeug + twisted.web Home-page: https://github.com/twisted/klein Author: Amber Brown (HawkOwl) Author-email: hawkowl@twistedmatrix.com License: MIT Description: ============================ Klein, a Web Micro-Framework ============================ .. image:: https://travis-ci.org/twisted/klein.svg?branch=master :target: http://travis-ci.org/twisted/klein :alt: Build Status .. image:: https://codecov.io/github/twisted/klein/coverage.svg?branch=master :target: https://codecov.io/github/twisted/klein?branch=master :alt: Code Coverage .. image:: https://img.shields.io/pypi/pyversions/klein.svg :target: https://pypi.python.org/pypi/klein :alt: Python Version Compatibility Klein is a micro-framework for developing production-ready web services with Python. It is 'micro' in that it has an incredibly small API similar to `Bottle `_ and `Flask `_. It is not 'micro' in that it depends on things outside the standard library. This is primarily because it is built on widely used and well tested components like `Werkzeug `_ and `Twisted `_. A `Klein bottle `_ is an example of a non-orientable surface, and a glass Klein bottle looks like a twisted bottle or twisted flask. This, of course, made it too good of a pun to pass up. Klein's documentation can be found at `Read The Docs `_. Example ======= This is a sample Klein application that returns 'Hello, world!', running on port ``8080``. .. code-block:: python from klein import run, route @route('/') def home(request): return 'Hello, world!' run("localhost", 8080) Contribute ========== ``klein`` is hosted on `GitHub `_ and is an open source project that welcomes contributions of all kinds from the community, including: - code patches, - `documentation `_ improvements, - `bug reports `_, - reviews for `contributed patches `_. For more information about contributing, see `the contributor guidelines `_. Keywords: twisted flask werkzeug web Platform: UNKNOWN Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules klein-17.2.0/setup.py0000664000175000017500000000277613054444052015270 0ustar moshezmoshez00000000000000from setuptools import setup if __name__ == "__main__": with open('README.rst', 'r') as f: long_description = f.read() setup( classifiers=[ 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Libraries :: Python Modules' ], description="werkzeug + twisted.web", long_description=long_description, setup_requires=["incremental"], use_incremental=True, install_requires=[ "six", "Twisted>=13.2", "werkzeug", "incremental", ], keywords="twisted flask werkzeug web", license="MIT", name="klein", packages=["klein", "klein.test"], package_dir={"": "src"}, url="https://github.com/twisted/klein", maintainer='Amber Brown (HawkOwl)', maintainer_email='hawkowl@twistedmatrix.com', ) klein-17.2.0/MANIFEST.in0000664000175000017500000000010413054444052015273 0ustar moshezmoshez00000000000000include LICENSE include README.rst include AUTHORS include NEWS.rst klein-17.2.0/README.rst0000664000175000017500000000417413054444052015237 0ustar moshezmoshez00000000000000============================ Klein, a Web Micro-Framework ============================ .. image:: https://travis-ci.org/twisted/klein.svg?branch=master :target: http://travis-ci.org/twisted/klein :alt: Build Status .. image:: https://codecov.io/github/twisted/klein/coverage.svg?branch=master :target: https://codecov.io/github/twisted/klein?branch=master :alt: Code Coverage .. image:: https://img.shields.io/pypi/pyversions/klein.svg :target: https://pypi.python.org/pypi/klein :alt: Python Version Compatibility Klein is a micro-framework for developing production-ready web services with Python. It is 'micro' in that it has an incredibly small API similar to `Bottle `_ and `Flask `_. It is not 'micro' in that it depends on things outside the standard library. This is primarily because it is built on widely used and well tested components like `Werkzeug `_ and `Twisted `_. A `Klein bottle `_ is an example of a non-orientable surface, and a glass Klein bottle looks like a twisted bottle or twisted flask. This, of course, made it too good of a pun to pass up. Klein's documentation can be found at `Read The Docs `_. Example ======= This is a sample Klein application that returns 'Hello, world!', running on port ``8080``. .. code-block:: python from klein import run, route @route('/') def home(request): return 'Hello, world!' run("localhost", 8080) Contribute ========== ``klein`` is hosted on `GitHub `_ and is an open source project that welcomes contributions of all kinds from the community, including: - code patches, - `documentation `_ improvements, - `bug reports `_, - reviews for `contributed patches `_. For more information about contributing, see `the contributor guidelines `_. klein-17.2.0/NEWS.rst0000664000175000017500000000356113054444052015055 0ustar moshezmoshez00000000000000NEWS ==== 15.0.0 - 2015-01-11 ------------------- * [BUG] Klein now includes its test package as part of the distribution. [`#65 `_] 14.0.0 - 2014-12-18 ------------------- * [BUG] Klein now attempts to decode non-ASCII URLs as UTF-8 and serves a 400 if that fails instead of always serving a 500. [`#62 `_] 0.2.3 - 2014-01-14 ------------------ * [BUG] Klein now correctly handles producing resources [`#30 `_] * [ENHANCEMENT] Klein now supports using tox for local testing [`#36 `_] * [ENHANCEMENT] Klein has improved tests and an expanded range of test platforms [`#33 `_, `#35 `_, `#37 `_] 0.2.2 - 2013-11-21 ------------------ * [ENHANCEMENT] Klein.handle_errors now allows the definition of custom error handling behavior. [`#26 `_] 0.2.1 - 2013-07-23 ------------------ * [BUG] Klein has been updated to support the latest werkzeug release: [`#21 `_] * [BUG] request.URLPath inside a handler points to the correct path. [`#15 `_] * [ENHANCEMENT] IKleinRequest(request).url_for is supported: [`#16 `_] 0.2.0 - 2013-02-27 ------------------ * [BUG] Remove support for implicit branch routes. [`#12 `_] * [FEATURE] Support creating Klein apps that are bound to an instance of a class. [`#9 `_] 0.1.1 - 2013-02-25 ------------------ * Include headers when handling werkzeug HTTPExceptions. 0.1.0 - 2013-01-04 ------------------ * Initial release klein-17.2.0/LICENSE0000664000175000017500000000212413054444052014546 0ustar moshezmoshez00000000000000This is the MIT license. Copyright (c) 2011-2016, Klein Contributors (see AUTHORS) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. klein-17.2.0/AUTHORS0000664000175000017500000000034113056157020014606 0ustar moshezmoshez00000000000000Aaron Gallagher Alex Gaynor Amber Brown / HawkOwl Brighid McDonnell Corbin Simpson David Reid Deven Phillips Glyph Lefkowitz Hynek Schlawack Matt Haggard Moshe Zadka Paul Hummer Russel Haering Tom Most Wilfredo Sánchez Vega klein-17.2.0/setup.cfg0000664000175000017500000000012213056447076015371 0ustar moshezmoshez00000000000000[wheel] universal = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0