Metadata-Version: 1.1
Name: nanohttp
Version: 0.24.0
Summary: A very micro http framework.
Home-page: http://github.com/Carrene/nanohttp
Author: Vahid Mardani
Author-email: vahid.mardani@gmail.com
License: MIT
Description-Content-Type: UNKNOWN
Description: nanohttp
        ========
        
        .. image:: http://img.shields.io/pypi/v/nanohttp.svg
             :target: https://pypi.python.org/pypi/nanohttp
        
        .. image:: https://travis-ci.org/Carrene/nanohttp.svg?branch=master
             :target: https://travis-ci.org/Carrene/nanohttp
        
        .. image:: https://coveralls.io/repos/github/Carrene/nanohttp/badge.svg?branch=master
             :target: https://coveralls.io/github/Carrene/nanohttp?branch=master
        
        .. image:: https://badges.gitter.im/Carrene/nanohttp.svg
           :alt: Join the chat at https://gitter.im/Carrene/nanohttp
           :target: https://gitter.im/Carrene/nanohttp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
        
        A very micro HTTP framework.
        
        Features
        --------
        
        - Very simple, less-code & fast
        - Using object dispatcher instead of regex route dispatcher.
        - Url-Encoded, Multipart and JSON form parsing.
        - No ``request`` and or ``response`` objects is available, everything is combined in ``nanohttp.context``.
        - A very flexible configuration system: `pymlconf <https://github.com/pylover/pymlconf>`_
        - Method(verb) dispatcher.
        - Use Python's `keywordonly` arguments for query strings >= `0.24.0`
        
        Roadmap
        -------
        
        The road map is to keep it simple with 100% code coverage. no built-in template engine and or ORM will be included.
        
        
        Install
        -------
        
        PyPI
        ^^^^
        
        ..  code-block:: bash
        
            $ pip install nanohttp
        
        
        From Source
        ^^^^^^^^^^^
        
        ..  code-block:: bash
        
            $ cd path/to/nanohttp
            $ pip install -e .
        
        
        Quick Start
        -----------
        
        ``demo.py``
        
        ..  code-block:: python
        
            from nanohttp import Controller, RestController, context, html, json, HttpFound
        
        
            class TipsControllers(RestController):
                @json
                def get(self, tip_id: int = None):
                    if tip_id is None:
                        return [dict(id=i, title="Tip %s" % i) for i in range(1, 4)]
                    else:
                        return dict(
                            id=tip_id,
                            title="Tip %s" % tip_id
                        )
        
                @json
                def post(self, tip_id: int = None):
                    tip_title = context.form.get('title')
                    print(tip_id, tip_title)
        
                    # Updating the tips title
                    # TipStore.get(tip_id).update(tip_title)
                    raise HttpFound('/tips/')
        
        
            class Root(Controller):
                tips = TipsControllers()
        
                @html
                def index(self):
                    yield """
                    <html><head><title>nanohttp Demo</title></head><body>
                    <form method="POST" action="/tips/2">
                        <input type="text" name="title" />
                        <input type="submit" value="Update" />
                    </form>
                    </body></html>
                    """
        
        
        ..  code-block:: bash
            
            $ nanohttp demo
        
        Or
        
        ..  code-block:: python
            
            from nanohttp import quickstart, configure
        
            configure()
            quickstart(Root())
        
        
        WSGI
        ----
        
        Do you need a ``WSGI`` application?
        
        ``wsgi.py``
        
        ..  code-block:: python
        
            from nanohttp import configure, Application
        
            configure(init_value='<yaml config string>', files=['path/to/config.file', '...'], dirs=['path/to/config/directory', '...'])
            app = Application(root=Root())
            # Pass the ``app`` to any ``WSGI`` server you want.
        
        
        Serve it by gunicorn:
        
        ..  code-block:: bash
        
            gunicorn --reload wsgi:app
        
        
        Config File
        -----------
        
        Create a ``demo.yml`` file. The file below is same as the default configuration.
        
        ..  code-block:: yml
        
            debug: true
        
            domain:
        
            cookie:
              http_only: false
              secure: false
        
        
        You may use ``nanohttp.settings`` anywhere to access the config values.
        
        ..  code-block:: python
        
            from nanohttp import Controller, html, settings
        
            class Root(Controller):
        
                @html
                def index(self):
                    yield '<html><head><title>nanohttp demo</title></head><body>'
                    yield '<h2>debug flag is: %s</h2>' % settings.debug
                    yield '</body></html>'
        
        Passing the config file(s) using command line:
        
        ..  code-block:: bash
        
            $ nanohttp -c demo.yml [-c another.yml] demo
        
        
        Passing the config file(s) Using python:
        
        ..  code-block:: bash
        
            from nanohttp import quickstart
        
            quickstart(Root(), config='<YAML config string>')
        
        
        Command Line Interface
        ----------------------
        
        ..  code-block:: bash
        
            $ nanohttp -h
        
            usage: nanohttp [-h] [-c CONFIG_FILE] [-d CONFIG_DIRECTORY] [-b {HOST:}PORT]
                            [-C DIRECTORY] [-V]
                            [{MODULE{.py}}{:CLASS}]
        
            positional arguments:
              {MODULE{.py}}{:CLASS}
                                    The python module and controller class to launch.
                                    default is python built-in's : `demo_app`, And the
                                    default value for `:CLASS` is `:Root` if omitted.
        
            optional arguments:
              -h, --help            show this help message and exit
              -c CONFIG_FILE, --config-file CONFIG_FILE
                                    This option may be passed multiple times.
              -d CONFIG_DIRECTORY, --config-directory CONFIG_DIRECTORY
                                    This option may be passed multiple times.
              -b {HOST:}PORT, --bind {HOST:}PORT
                                    Bind Address. default: 8080
              -C DIRECTORY, --directory DIRECTORY
                                    Change to this path before starting the server default
                                    is: `.`
              -V, --version         Show the version.
        
        
        Cookies
        -------
        
        Accessing the request cookies:
        
        
        ..  code-block:: python
        
            from nanohttp import context
        
            counter = context.cookies.get('counter')
        
        Setting cookie:
        
        ..  code-block:: python
        
            from nanohttp import context
        
            context.cookies['dummy-cookie1'] = 'dummy-value'
            context.cookies['dummy-cookie1']['http_only'] = True
        
        For more information on how to use cookies, please check the python builtin's `http.cookies<https://docs.python.org/3/library/http.cookies.html>`_.
        
        
        Trailing slashes
        ----------------
        
        If the ``Controller.__remove_trailing_slash__`` is ``True``, then all trailing slashes are ignored.
        
        ..  code-block:: python
        
            def test_trailing_slash(self):
                self.assert_get('/users/10/jobs/', expected_response='User: 10\nAttr: jobs\n')
        
        Decorators
        ----------
        
        Available decorators are: ``action``, ``html``, ``text``, ``json``, ``xml``, ``binary``
        
        Those decorators are useful to encapsulate response preparation such as setting ``Content-Type`` HTTP header.
        
        Take a look at the code of the ``action`` decorator, all other decorators are derived from this:
        
        
        ..  code-block:: python
        
            def action(*verbs, encoding='utf-8', content_type=None, inner_decorator=None):
                def _decorator(func):
        
                    if inner_decorator is not None:
                        func = inner_decorator(func)
        
                    func.__http_methods__ = verbs if verbs else 'any'
        
                    func.__response_encoding__ = encoding
        
                    if content_type:
                        func.__content_type__ = content_type
        
                    return func
        
                if verbs and callable(verbs[0]):
                    f = verbs[0]
                    verbs = tuple()
                    return _decorator(f)
                else:
                    return _decorator
        
        Other decorators are defined using ``functools.partial``:
        
        ..  code-block:: python
        
            html = functools.partial(action, content_type='text/html')
            text = functools.partial(action, content_type='text/plain')
            json = functools.partial(action, content_type='application/json', inner_decorator=jsonify)
            xml = functools.partial(action, content_type='application/xml')
            binary = functools.partial(action, content_type='application/octet-stream', encoding=None)
        
        Of-course, you can set the response content type using:
        
        ..  code-block:: python
        
            context.response_content_type = 'application/pdf'
        
        Of-course, you can define your very own decorator to make your code DRY:
        
        ..  code-block:: python
        
            import functools
            from nanohttp import action, RestController
        
            pdf = functools.partial(action, content_type='application/pdf')
        
            class MyController(RestController)
        
                @pdf
                def get(index):
                    .......
        
        
        Serving Static file(s)
        ----------------------
        
        The ``nanohttp.Static`` class is responsible to serve static files:
        
        ..  code-block:: python
        
            from nanohttp import Controller, Static
        
            class Root(Controller):
                static = Static('path/to/static/directory', default_document='index.html')
        
        Then you can access static files on ``/static/filename.ext``
        
        A simple way to run server and only serve static files is:
        
        ..  code-block:: bash
        
            cd path/to/static/directory
            nanohttp :Static
        
        
        Accessing request payload
        -------------------------
        
        The `context.form` is a dictionary representing the request payload, supported request formats are ``query-string``,
        ``multipart/form-data``, ``application/x-www-form-urlencoded`` and ``json``.
        
        ..  code-block:: python
        
            from nanohttp import context, RestController
        
            class TipsControllers(RestController):
        
                @json
                def post(self, tip_id: int = None):
                    tip_title = context.form.get('title')
        
        
        Dispatcher
        ----------
        
        The requested path will be split-ed by ``/`` and python's ``getattr`` will be used on the ``Root`` controller
        recursively to find specific callable to handle request.
        
        ..  code-block:: python
        
            from nanohttp import RestController
        
            class Nested(RestController):
                pass
        
            class Root()
                children = Nested()
        
        Then you can access methods on nested controller using: ``http://host:port/children``
        
        On the ``RestController`` dispatcher tries to dispatch request using HTTP method(verb) at first.
        
        
        Context
        -------
        
        The ``context`` object is a proxy to an instance of ``nanohttp.Context`` which is ``unique per request``.
        
        .. TODO: ADD link to documentation
        
        Hooks
        -----
        
        A few hooks are available in ``Controller`` class: ``begin_request``, ``begin_response``,
        ``end_response``.
        
        For example this how I detect JWT token and refresh it if possible:
        
        
        ..  code-block:: python
        
            from nanohttp import Application, Controller, context
        
            class JwtApplication(Application):
                token_key = 'HTTP_AUTHORIZATION'
                refresh_token_cookie_key = 'refresh-token'
        
                def begin_request(self):
                    if self.token_key in context.environ:
                        encoded_token = context.environ[self.token_key]
                        try:
                            context.identity = JwtPrincipal.decode(encoded_token)
                        except itsdangerous.SignatureExpired as ex:
                            refresh_token_encoded = context.cookies.get(self.refresh_token_cookie_key)
                            if refresh_token_encoded:
                                # Extracting session_id
                                session_id = ex.payload.get('sessionId')
                                if session_id:
                                    context.identity = new_token = self.refresh_jwt_token(refresh_token_encoded, session_id)
                                    if new_token:
                                        context.response_headers.add_header('X-New-JWT-Token', new_token.encode().decode())
        
                        except itsdangerous.BadData:
                            pass
        
                    if not hasattr(context, 'identity'):
                        context.identity = None
        
        Rendering templates
        -------------------
        
        This is how to use mako template engine with the nanohttp:
        
        
        main.py
        
        
        ..  code-block:: python
        
            import functools
            from os.path import dirname, abspath, join
        
            from mako.lookup import TemplateLookup
        
            from nanohttp import Controller, context, Static, settings, action
        
        
            here = abspath(dirname(__file__))
            lookup = TemplateLookup(directories=[join(here, 'templates')])
        
        
            def render_template(func, template_name):
        
                @functools.wraps(func)
                def wrapper(*args, **kwargs):
        
                    result = func(*args, **kwargs)
                    if hasattr(result, 'to_dict'):
                        result = result.to_dict()
                    elif not isinstance(result, dict):
                        raise ValueError('The result must be an instance of dict, not: %s' % type(result))
        
                    template_ = lookup.get_template(template_name)
                    return template_.render(**result)
        
                return wrapper
        
        
            template = functools.partial(action, content_type='text/html', inner_decorator=render_template)
        
        
            class Root(Controller):
                static = Static(here)
        
                @template('index.mak')
                def index(self):
                    return dict(
                        settings=settings,
                        environ=context.environ
                    )
        
        
        
        templates/index.html
        
        ..  code-block:: html
        
            <html>
            <head>
                <title>nanohttp mako example</title>
            </head>
            <body>
                <h1>WSGI environ</h1>
                <ul>
                %for key, value in environ.items():
                  <li><b>${key}:</b> ${value}</li>
                %endfor
                </ul>
            </body>
            </html>
        
        
Platform: UNKNOWN
Classifier: Environment :: Console
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: Development Status :: 4 - Beta
Classifier: License :: Other/Proprietary License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.5
Classifier: Topic :: Software Development
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
