pylover / nanohttp Goto Github PK
View Code? Open in Web Editor NEWA very micro HTTP framework.
Home Page: http://nanohttp.org
License: Other
A very micro HTTP framework.
Home Page: http://nanohttp.org
License: Other
tests
module installed automatically on site-packages.
Adding custom exception to validation class.
POST /apiv1/sessions HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: 15302aa4-b157-c61c-720d-66ee6ab486cf
email=ha%example.com&password=123456
Traceback (most recent call last):\n File "/home/dev/.virtualenvs/leo/lib/python3.5/site-packages/nanohttp.py", line 453, in _handle_request\n buffer = next(resp_generator)\n File "/home/dev/.virtualenvs/leo/lib/python3.5/site-packages/nanohttp.py", line 375, in wrapper\n result = func(*args, **kwargs)\n File "/home/dev/workspace/leo/leo/controllers/sessions.py", line 16, in post\n email = context.form.get('email')\n File "/home/dev/.virtualenvs/leo/lib/python3.5/site-packages/nanohttp.py", line 342, in getattr\n return getattr(Context.get_current(), key)\n File "/home/dev/.virtualenvs/leo/lib/python3.5/site-packages/nanohttp.py", line 141, in get\n val = f(obj)\n File "/home/dev/.virtualenvs/leo/lib/python3.5/site-packages/nanohttp.py", line 291, in form\n keep_blank_values=True\n File "/usr/lib/python3.5/cgi.py", line 561, in init\n self.read_single()\n File "/usr/lib/python3.5/cgi.py", line 740, in read_single\n self.read_binary()\n File "/usr/lib/python3.5/cgi.py", line 762, in read_binary\n self.file.write(data)\n\nTypeError: write() argument must be str, not bytes
from datetime import time
from nanohttp import Controller, RestController, html, json, quickstart
class ObjectControllers(RestController):
@json
def get(self, obj_id: int = None):
return dict(some_key=time(15, 15, 15))
class Root(Controller):
objects = ObjectControllers()
@html
def index(self):
yield
Serving using nanohttp module_name
and visiting http://localhost:8080/objects kills the server with a one line log:
Segmentation fault: 11
Are we dealing with resource constraints due to an infinite loop or something?
Replacement for #30
Traceback (most recent call last):
File "/usr/local/lib/python3.6/site-packages/gunicorn/arbiter.py", line 578, in spawn_worker
worker.init_process()
File "/usr/local/lib/python3.6/site-packages/gunicorn/workers/base.py", line 126, in init_process
self.load_wsgi()
File "/usr/local/lib/python3.6/site-packages/gunicorn/workers/base.py", line 135, in load_wsgi
self.wsgi = self.app.wsgi()
File "/usr/local/lib/python3.6/site-packages/gunicorn/app/base.py", line 67, in wsgi
self.callable = self.load()
File "/usr/local/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 65, in load
return self.load_wsgiapp()
File "/usr/local/lib/python3.6/site-packages/gunicorn/app/wsgiapp.py", line 52, in load_wsgiapp
return util.import_app(self.app_uri)
File "/usr/local/lib/python3.6/site-packages/gunicorn/util.py", line 376, in import_app
__import__(module)
File "/Users/mohammad/Desktop/tehran/wsgi.py", line 5, in <module>
configure(config_files='config.yml')
File "/usr/local/lib/python3.6/site-packages/nanohttp.py", line 399, in configure
settings.load(*args, builtin=BUILTIN_CONFIG, **kwargs)
File "/usr/local/lib/python3.6/site-packages/pymlconf/proxy.py", line 78, in load
self._set_instance(ConfigManager(**kw))
TypeError: __init__() got an unexpected keyword argument 'config_files'
We decided to return stack trace in response body in debug mode.
At current behavior, when using request_error
hook it returns text/plain
content type with prepended Internal Server Error
message. But i want an HTML page without any extra things.
P.S: It's clear to us, Errors are important, specially if we have sensitive clients.
It's OK to use the context_
instead of ctx
to prevent name shadowing.
Thoughts?
@validate(
title=dict(required=(True, 704, 'Message'),
pattern, min_length, max_length, min, max, type, .....),
id=dict(required=lambda v: False, 400, 'message'), remaining=(400, 'message')
)
def index(self):
pass
strict_validate = functools.partial(validate, remaining=400)
This is how to test this:
v = ActionValidator(firlds=dict(), )
form = dict(......)
with self.assertRaises(HttpBadRequest):
v(form)
self.assertRaises(Error, func, params)
context.isssl should be added to nanohttp.contex to check if a connection is SSL.
What is going happens if the controller's action returns a string ?
def _response():
try:
if buffer is not None:
yield ctx.encode_response(buffer)
if resp_generator:
# noinspection PyTypeChecker
for chunk in resp_generator:
yield ctx.encode_response(chunk)
else:
yield b''
except Exception as ex_: # pragma: no cover
self.__logger__.exception('Exception while serving the response.')
if settings.debug:
# FIXME: Proper way to handle exceptions after start_response
yield str(ex_).encode()
raise ex_
finally:
self._hook('end_response')
context.__exit__(*sys.exc_info())
Assume this handler:
@text
def index(self, param1, param2=None, *, qs1=None, qs2='default'):
pass
We can call this action using one of:
@meyt Thoughts?
Merge status_code
and status_text
to status
field like below:
status_code = 400
status_text = 'Bad Request'
Should be
status = '400 Bad Request'
Hooks, Authenticator and Exception handling will be moved into this object
The WSGI and hooks section in the readme should be updated
With each request in the web, the request is added to a request queue. Then, Thread pool
chooses a thread to serve the request and send back the response.
Finally, this thread is added to the thread pool. In nanohttp, whole the concept is managed through the Context. In nanohttp, it is possible to make nested context. For each request, a thread stack is created.
Since two requests can use a single context
, this can run into problems. So the context should be handled thread safe using thread_local
.
ERROR:main:Internal server error
Traceback (most recent call last):
File "/home/vahid/.virtualenvs/restfulpy-client.js/lib/python3.5/site-packages/nanohttp/application.py", line 56, in __call__
result = self.__root__(*remaining_paths)
File "/home/vahid/workspace/restfulpy/restfulpy/controllers.py", line 21, in __call__
return super().__call__(*remaining_paths)
File "/home/vahid/.virtualenvs/restfulpy-client.js/lib/python3.5/site-packages/nanohttp/controllers.py", line 59, in __call__
return self._serve_handler(handler, *remaining_paths)
File "/home/vahid/.virtualenvs/restfulpy-client.js/lib/python3.5/site-packages/nanohttp/controllers.py", line 30, in _serve_handler
return handler(*remaining_paths)
File "/home/vahid/.virtualenvs/restfulpy-client.js/lib/python3.5/site-packages/nanohttp/decorators.py", line 35, in wrapper
result = func(*args, **kwargs)
File "./tests/mockup-server.py", line 56, in echo
return context.form
File "/home/vahid/.virtualenvs/restfulpy-client.js/lib/python3.5/site-packages/nanohttp/contexts.py", line 159, in __getattr__
return getattr(Context.get_current(), key)
File "/home/vahid/.virtualenvs/restfulpy-client.js/lib/python3.5/site-packages/nanohttp/helpers.py", line 37, in __get__
val = f(obj)
File "/home/vahid/.virtualenvs/restfulpy-client.js/lib/python3.5/site-packages/nanohttp/contexts.py", line 97, in form
data = fp.read(self.request_content_length)
File "/home/vahid/.virtualenvs/restfulpy-client.js/lib/python3.5/site-packages/nanohttp/helpers.py", line 37, in __get__
val = f(obj)
File "/home/vahid/.virtualenvs/restfulpy-client.js/lib/python3.5/site-packages/nanohttp/contexts.py", line 41, in request_content_length
return None if v is None else int(v)
ValueError: invalid literal for int() with base 10: ''
WSGI middlewares for debugging like werkzeug
doesn't work currently, and exceptions handled by nanohttp
.
Based this sample:
from werkzeug.debug import DebuggedApplication
from nanohttp import Controller, configure, html
class Root(Controller):
@html
def index(self):
x = 1 / 0
return 'test'
configure()
app = DebuggedApplication(Root().load_app(), evalex=True)
results show nanohttp
exception handler instead of werkzeug
debugger.
It would be great if we get the field name (and maybe the reason failing) in response when validation failed.
Steps to reproduce:
from nanohttp import Controller, RestController, html, json, quickstart
class ObjectControllers(RestController):
@json
def get(self, obj_id: int = None):
return dict(some_key='some_value')
class Root(Controller):
objects = ObjectControllers()
@html
def index(self):
yield
quickstart(Root())
visiting http://localhost:8080/objects raises:
File "/Users/etemin/workspace/nanohttp/nanohttp/application.py", line 86, in call
status, response_body = self._handle_exception(ex)
File "/Users/etemin/workspace/nanohttp/nanohttp/application.py", line 39, in _handle_exception
raise ex
File "/Users/etemin/workspace/nanohttp/nanohttp/application.py", line 65, in call
response_body = self.root(*remaining_paths)
File "/Users/etemin/workspace/nanohttp/nanohttp/controllers.py", line 59, in call
return self._serve_handler(handler, *remaining_paths)
File "/Users/etemin/workspace/nanohttp/nanohttp/controllers.py", line 30, in _serve_handler
return handler(*remaining_paths)
File "/Users/etemin/workspace/nanohttp/nanohttp/controllers.py", line 59, in call
return self._serve_handler(handler, *remaining_paths)
File "/Users/etemin/workspace/nanohttp/nanohttp/controllers.py", line 30, in _serve_handler
return handler(*remaining_paths)
File "/Users/etemin/workspace/nanohttp/nanohttp/decorators.py", line 42, in wrapper
return ujson.dumps(result, indent=settings.json.indent)
File "/Users/etemin/virtualenvs/blog/lib/python3.5/site-packages/pymlconf/proxy.py", line 44, in getattr
return getattr(object.getattribute(self, 'proxied_object'), key)
File "/Users/etemin/virtualenvs/blog/lib/python3.5/site-packages/pymlconf/config_nodes.py", line 138, in getattr
raise ConfigKeyError(key)
pymlconf.errors.ConfigKeyError: Config key was not found: "json"
What's the deal here? Should we specify the json manipulation library in the config?
Please note that it works well using nanohttp module_name
Environment:
python: 3.5.2
os: macOS Sierra 10.12.5
From the cherrypy
if '=?' in value:
dict.__setitem__(headers, name, httputil.decode_TEXT(value))
else:
dict.__setitem__(headers, name, value)
HttpStatus
class.The If-None-Match HTTP request header makes the request conditional. For GET and HEAD methods, the server will send back the requested resource, with a 200 status, only if it doesn't have an ETag matching the given ones. For other methods, the request will be processed only if the eventually existing resource's ETag doesn't match any of the values listed.
Why is response status set to 408 in this line?
Also, response object has 'status' : 408
, that must be status:408
.
Reported by @meyt
quickstart(Root(), config='<YAML config string>')
pymlconf.errors.ConfigurationNotInitializedError: Configuration manager object is not initialized yet.
ERROR:main:Internal server error
Traceback (most recent call last):
File "/home/mohammad/workspace/nanohttp/nanohttp/application.py", line 64, in __call__
response_body = self.__root__(*remaining_paths)
File "/home/mohammad/workspace/restfulpy/restfulpy/controllers.py", line 18, in __call__
return super().__call__(*remaining_paths)
File "/home/mohammad/workspace/nanohttp/nanohttp/controllers.py", line 83, in __call__
return self._serve_handler(handler, remaining_paths)
File "/home/mohammad/workspace/nanohttp/nanohttp/controllers.py", line 78, in _serve_handler
return handler(*remaining_paths, **kwargs)
File "/home/mohammad/workspace/nanohttp/nanohttp/decorators.py", line 52, in wrapper
result = func(*args, **kwargs)
File "/home/mohammad/workspace/restfulpy/restfulpy/mockupservers/simple.py", line 144, in echo
return {k: v for i in (context.form, context.query_string) for k, v in i.items()}
File "/home/mohammad/workspace/nanohttp/nanohttp/contexts.py", line 184, in __getattr__
return getattr(Context.get_current(), key)
File "/home/mohammad/workspace/nanohttp/nanohttp/helpers.py", line 40, in __get__
val = f(obj)
File "/home/mohammad/workspace/nanohttp/nanohttp/contexts.py", line 115, in form
content_type=self.request_content_type
File "/home/mohammad/workspace/nanohttp/nanohttp/helpers.py", line 133, in parse_any_form
content_length = int(environ.get('CONTENT_LENGTH', 0))
ValueError: invalid literal for int() with base 10: ''
PEP8
requirements. However, the codes should be reviewed.Enhancement idea comes from cherrypy:
class HeaderMap(CaseInsensitiveDict):
"""A dict subclass for HTTP request and response headers.
Each key is changed on entry to str(key).title(). This allows headers
to be case-insensitive and avoid duplicates.
Values are header values (decoded according to :rfc:`2047` if necessary).
"""
protocol = (1, 1)
encodings = ['ISO-8859-1']
# Someday, when http-bis is done, this will probably get dropped
# since few servers, clients, or intermediaries do it. But until then,
# we're going to obey the spec as is.
# "Words of *TEXT MAY contain characters from character sets other than
# ISO-8859-1 only when encoded according to the rules of RFC 2047."
use_rfc_2047 = True
def elements(self, key):
"""Return a sorted list of HeaderElements for the given header."""
key = str(key).title()
value = self.get(key)
return header_elements(key, value)
def values(self, key):
"""Return a sorted list of HeaderElement.value for the given header."""
return [e.value for e in self.elements(key)]
def output(self):
"""Transform self into a list of (name, value) tuples."""
return list(self.encode_header_items(self.items()))
@classmethod
def encode_header_items(cls, header_items):
"""
Prepare the sequence of name, value tuples into a form suitable for
transmitting on the wire for HTTP.
"""
for k, v in header_items:
if isinstance(k, six.text_type):
k = cls.encode(k)
if not isinstance(v, text_or_bytes):
v = str(v)
if isinstance(v, six.text_type):
v = cls.encode(v)
# See header_translate_* constants above.
# Replace only if you really know what you're doing.
k = k.translate(header_translate_table,
header_translate_deletechars)
v = v.translate(header_translate_table,
header_translate_deletechars)
yield (k, v)
@classmethod
def encode(cls, v):
"""Return the given header name or value, encoded for HTTP output."""
for enc in cls.encodings:
try:
return v.encode(enc)
except UnicodeEncodeError:
continue
if cls.protocol == (1, 1) and cls.use_rfc_2047:
# Encode RFC-2047 TEXT
# (e.g. u"\u8200" -> "=?utf-8?b?6IiA?=").
# We do our own here instead of using the email module
# because we never want to fold lines--folding has
# been deprecated by the HTTP working group.
v = b2a_base64(v.encode('utf-8'))
return (ntob('=?utf-8?b?') + v.strip(ntob('\n')) + ntob('?='))
raise ValueError('Could not encode header part %r using '
'any of the encodings %r.' %
(v, cls.encodings))
It helps the user to override the default message of the exception.
It seems we must encode the start_response
function's arguments too:
From PEP3333
Note also that strings passed to start_response() as a status or as response headers must follow RFC 2616 with respect to encoding. That is, they must either be ISO-8859-1 characters, or use RFC 2047 MIME encoding.
LazyAttribute
decorator not fully wrapping inner function attributes like __name__
or __doc__
.
Nice idea!
@validate
def index(self, a: int, b: str, c: date = datetime.now):
...
This behaviour should be implemented inside the validation system, so the @validate
decorator is mandatory to enable this feature.
A decorator to calculate the checksum of a response and send 304 if it's not changed.
nanohttp/nanohttp/tests/helpers.py
Line 44 in eb60f12
the nanohttp.org
when a new tag is pushed.Assume two contexts are nested within the same thread.
from nanohttp.contexts import context, Context
with Context() as ctx1:
with Context() as ctx2:
context['blahblah'] = 'something'
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.