MicroPython 基于microdot框架搭建网页服务器
MicroPython 基于microdot框架搭建网页服务器
- 简介
- 简单demo
简介
Microdot是一个极简的Python web框架,灵感来自于Flask,它被设计用来运行在资源有限的系统上,如微控制器。它运行在标准的Python和MicroPython上。
API参考microdot
资源下载microdot
简单demo
main.py
from microdot import Microdot
import networkdef connect_wifi():# 连接wifiwlan = network.WLAN(network.STA_IF)wlan.active(True)if not wlan.isconnected():print('connecting to network...')wlan.connect('xa_001', '88889999')while not wlan.isconnected():passprint('网络配置:', wlan.ifconfig())connect_wifi()app = Microdot()@app.route('/users/active')
def active_users(request):return 'Active users: Susan, Joe, and Bob'@app.route('/')
def index(request):return 'Hello, World Microdot!'# run(self, host='0.0.0.0', port=5000, debug=False, ssl=None)
app.run(debug=True)
microdot.py
"""
microdot
--------The ``microdot`` module defines a few classes that help implement HTTP-based
servers for MicroPython and standard Python, with multithreading support for
Python interpreters that support it.
"""
try:from sys import print_exception
except ImportError: # pragma: no coverimport tracebackdef print_exception(exc):traceback.print_exc()
try:import uerrno as errno
except ImportError:import errnoconcurrency_mode = 'threaded'try: # pragma: no coverimport threadingdef create_thread(f, *args, **kwargs):# use the threading modulethreading.Thread(target=f, args=args, kwargs=kwargs).start()
except ImportError: # pragma: no coverdef create_thread(f, *args, **kwargs):# no threads available, call function synchronouslyf(*args, **kwargs)concurrency_mode = 'sync'try:import ujson as json
except ImportError:import jsontry:import ure as re
except ImportError:import resocket_timeout_error = OSError
try:import usocket as socket
except ImportError:try:import socketsocket_timeout_error = socket.timeoutexcept ImportError: # pragma: no coversocket = NoneMUTED_SOCKET_ERRORS = [32, # Broken pipe54, # Connection reset by peer104, # Connection reset by peer128, # Operation on closed socket
]def urldecode_str(s):s = s.replace('+', ' ')parts = s.split('%')if len(parts) == 1:return sresult = [parts[0]]for item in parts[1:]:if item == '':result.append('%')else:code = item[:2]result.append(chr(int(code, 16)))result.append(item[2:])return ''.join(result)def urldecode_bytes(s):s = s.replace(b'+', b' ')parts = s.split(b'%')if len(parts) == 1:return s.decode()result = [parts[0]]for item in parts[1:]:if item == b'':result.append(b'%')else:code = item[:2]result.append(bytes([int(code, 16)]))result.append(item[2:])return b''.join(result).decode()def urlencode(s):return s.replace('+', '%2B').replace(' ', '+').replace('%', '%25').replace('?', '%3F').replace('#', '%23').replace('&', '%26').replace('=', '%3D')class NoCaseDict(dict):"""A subclass of dictionary that holds case-insensitive keys.:param initial_dict: an initial dictionary of key/value pairs toinitialize this object with.Example::>>> d = NoCaseDict()>>> d['Content-Type'] = 'text/html'>>> print(d['Content-Type'])text/html>>> print(d['content-type'])text/html>>> print(d['CONTENT-TYPE'])text/html>>> del d['cOnTeNt-TyPe']>>> print(d){}"""def __init__(self, initial_dict=None):super().__init__(initial_dict or {})self.keymap = {k.lower(): k for k in self.keys() if k.lower() != k}def __setitem__(self, key, value):kl = key.lower()key = self.keymap.get(kl, key)if kl != key:self.keymap[kl] = keysuper().__setitem__(key, value)def __getitem__(self, key):kl = key.lower()return super().__getitem__(self.keymap.get(kl, kl))def __delitem__(self, key):kl = key.lower()super().__delitem__(self.keymap.get(kl, kl))def __contains__(self, key):kl = key.lower()return self.keymap.get(kl, kl) in self.keys()def get(self, key, default=None):kl = key.lower()return super().get(self.keymap.get(kl, kl), default)def update(self, other_dict):for key, value in other_dict.items():self[key] = valuedef mro(cls): # pragma: no cover"""Return the method resolution order of a class.This is a helper function that returns the method resolution order of aclass. It is used by Microdot to find the best error handler to invoke forthe raised exception.In CPython, this function returns the ``__mro__`` attribute of the class.In MicroPython, this function implements a recursive depth-first scanningof the class hierarchy."""if hasattr(cls, 'mro'):return cls.__mro__def _mro(cls):m = [cls]for base in cls.__bases__:m += _mro(base)return mmro_list = _mro(cls)# If a class appears multiple times (due to multiple inheritance) remove# all but the last occurence. This matches the method resolution order# of MicroPython, but not CPython.mro_pruned = []for i in range(len(mro_list)):base = mro_list.pop(0)if base not in mro_list:mro_pruned.append(base)return mro_prunedclass MultiDict(dict):"""A subclass of dictionary that can hold multiple values for the samekey. It is used to hold key/value pairs decoded from query strings andform submissions.:param initial_dict: an initial dictionary of key/value pairs toinitialize this object with.Example::>>> d = MultiDict()>>> d['sort'] = 'name'>>> d['sort'] = 'email'>>> print(d['sort'])'name'>>> print(d.getlist('sort'))['name', 'email']"""def __init__(self, initial_dict=None):super().__init__()if initial_dict:for key, value in initial_dict.items():self[key] = valuedef __setitem__(self, key, value):if key not in self:super().__setitem__(key, [])super().__getitem__(key).append(value)def __getitem__(self, key):return super().__getitem__(key)[0]def get(self, key, default=None, type=None):"""Return the value for a given key.:param key: The key to retrieve.:param default: A default value to use if the key does not exist.:param type: A type conversion callable to apply to the value.If the multidict contains more than one value for the requested key,this method returns the first value only.Example::>>> d = MultiDict()>>> d['age'] = '42'>>> d.get('age')'42'>>> d.get('age', type=int)42>>> d.get('name', default='noname')'noname'"""if key not in self:return defaultvalue = self[key]if type is not None:value = type(value)return valuedef getlist(self, key, type=None):"""Return all the values for a given key.:param key: The key to retrieve.:param type: A type conversion callable to apply to the values.If the requested key does not exist in the dictionary, this methodreturns an empty list.Example::>>> d = MultiDict()>>> d.getlist('items')[]>>> d['items'] = '3'>>> d.getlist('items')['3']>>> d['items'] = '56'>>> d.getlist('items')['3', '56']>>> d.getlist('items', type=int)[3, 56]"""if key not in self:return []values = super().__getitem__(key)if type is not None:values = [type(value) for value in values]return valuesclass Request():"""An HTTP request."""#: Specify the maximum payload size that is accepted. Requests with larger#: payloads will be rejected with a 413 status code. Applications can#: change this maximum as necessary.#:#: Example::#:#: Request.max_content_length = 1 * 1024 * 1024 # 1MB requests allowedmax_content_length = 16 * 1024#: Specify the maximum payload size that can be stored in ``body``.#: Requests with payloads that are larger than this size and up to#: ``max_content_length`` bytes will be accepted, but the application will#: only be able to access the body of the request by reading from#: ``stream``. Set to 0 if you always access the body as a stream.#:#: Example::#:#: Request.max_body_length = 4 * 1024 # up to 4KB bodies readmax_body_length = 16 * 1024#: Specify the maximum length allowed for a line in the request. Requests#: with longer lines will not be correctly interpreted. Applications can#: change this maximum as necessary.#:#: Example::#:#: Request.max_readline = 16 * 1024 # 16KB lines allowedmax_readline = 2 * 1024#: Specify a suggested read timeout to use when reading the request. Set to#: 0 to disable the use of a timeout. This timeout should be considered a#: suggestion only, as some platforms may not support it. The default is#: 1 second.socket_read_timeout = 1class G:passdef __init__(self, app, client_addr, method, url, http_version, headers,body=None, stream=None, sock=None):#: The application instance to which this request belongs.self.app = app#: The address of the client, as a tuple (host, port).self.client_addr = client_addr#: The HTTP method of the request.self.method = method#: The request URL, including the path and query string.self.url = url#: The path portion of the URL.self.path = url#: The query string portion of the URL.self.query_string = None#: The parsed query string, as a#: :class:`MultiDict <microdot.MultiDict>` object.self.args = {}#: A dictionary with the headers included in the request.self.headers = headers#: A dictionary with the cookies included in the request.self.cookies = {}#: The parsed ``Content-Length`` header.self.content_length = 0#: The parsed ``Content-Type`` header.self.content_type = None#: A general purpose container for applications to store data during#: the life of the request.self.g = Request.G()self.http_version = http_versionif '?' in self.path:self.path, self.query_string = self.path.split('?', 1)self.args = self._parse_urlencoded(self.query_string)if 'Content-Length' in self.headers:self.content_length = int(self.headers['Content-Length'])if 'Content-Type' in self.headers:self.content_type = self.headers['Content-Type']if 'Cookie' in self.headers:for cookie in self.headers['Cookie'].split(';'):name, value = cookie.strip().split('=', 1)self.cookies[name] = valueself._body = bodyself.body_used = Falseself._stream = streamself.stream_used = Falseself.sock = sockself._json = Noneself._form = Noneself.after_request_handlers = []@staticmethoddef create(app, client_stream, client_addr, client_sock=None):"""Create a request object.:param app: The Microdot application instance.:param client_stream: An input stream from where the request data canbe read.:param client_addr: The address of the client, as a tuple.:param client_sock: The low-level socket associated with the request.This method returns a newly created ``Request`` object."""# request lineline = Request._safe_readline(client_stream).strip().decode()if not line:return Nonemethod, url, http_version = line.split()http_version = http_version.split('/', 1)[1]# headersheaders = NoCaseDict()while True:line = Request._safe_readline(client_stream).strip().decode()if line == '':breakheader, value = line.split(':', 1)value = value.strip()headers[header] = valuereturn Request(app, client_addr, method, url, http_version, headers,stream=client_stream, sock=client_sock)def _parse_urlencoded(self, urlencoded):data = MultiDict()if len(urlencoded) > 0:if isinstance(urlencoded, str):for kv in [pair.split('=', 1)for pair in urlencoded.split('&') if pair]:data[urldecode_str(kv[0])] = urldecode_str(kv[1]) \if len(kv) > 1 else ''elif isinstance(urlencoded, bytes): # pragma: no branchfor kv in [pair.split(b'=', 1)for pair in urlencoded.split(b'&') if pair]:data[urldecode_bytes(kv[0])] = urldecode_bytes(kv[1]) \if len(kv) > 1 else b''return data@propertydef body(self):"""The body of the request, as bytes."""if self.stream_used:raise RuntimeError('Cannot use both stream and body')if self._body is None:self._body = b''if self.content_length and \self.content_length <= Request.max_body_length:while len(self._body) < self.content_length:data = self._stream.read(self.content_length - len(self._body))if len(data) == 0: # pragma: no coverraise EOFError()self._body += dataself.body_used = Truereturn self._body@propertydef stream(self):"""The input stream, containing the request body."""if self.body_used:raise RuntimeError('Cannot use both stream and body')self.stream_used = Truereturn self._stream@propertydef json(self):"""The parsed JSON body, or ``None`` if the request does not have aJSON body."""if self._json is None:if self.content_type is None:return Nonemime_type = self.content_type.split(';')[0]if mime_type != 'application/json':return Noneself._json = json.loads(self.body.decode())return self._json@propertydef form(self):"""The parsed form submission body, as a:class:`MultiDict <microdot.MultiDict>` object, or ``None`` if therequest does not have a form submission."""if self._form is None:if self.content_type is None:return Nonemime_type = self.content_type.split(';')[0]if mime_type != 'application/x-www-form-urlencoded':return Noneself._form = self._parse_urlencoded(self.body)return self._formdef after_request(self, f):"""Register a request-specific function to run after the request ishandled. Request-specific after request handlers run at the very end,after the application's own after request handlers. The function musttake two arguments, the request and response objects. The return valueof the function must be the updated response object.Example::@app.route('/')def index(request):# register a request-specific after request handler@req.after_requestdef func(request, response):# ...return responsereturn 'Hello, World!'Note that the function is not called if the request handler raises anexception and an error response is returned instead."""self.after_request_handlers.append(f)return f@staticmethoddef _safe_readline(stream):line = stream.readline(Request.max_readline + 1)if len(line) > Request.max_readline:raise ValueError('line too long')return lineclass Response():"""An HTTP response class.:param body: The body of the response. If a dictionary or list is given,a JSON formatter is used to generate the body. If a file-likeobject or a generator is given, a streaming response is used.If a string is given, it is encoded from UTF-8. Else, thebody should be a byte sequence.:param status_code: The numeric HTTP status code of the response. Thedefault is 200.:param headers: A dictionary of headers to include in the response.:param reason: A custom reason phrase to add after the status code. Thedefault is "OK" for responses with a 200 status code and"N/A" for any other status codes."""types_map = {'css': 'text/css','gif': 'image/gif','html': 'text/html','jpg': 'image/jpeg','js': 'application/javascript','json': 'application/json','png': 'image/png','txt': 'text/plain',}send_file_buffer_size = 1024#: The content type to use for responses that do not explicitly define a#: ``Content-Type`` header.default_content_type = 'text/plain'#: The default cache control max age used by :meth:`send_file`. A value#: of ``None`` means that no ``Cache-Control`` header is added.default_send_file_max_age = None#: Special response used to signal that a response does not need to be#: written to the client. Used to exit WebSocket connections cleanly.already_handled = Nonedef __init__(self, body='', status_code=200, headers=None, reason=None):if body is None and status_code == 200:body = ''status_code = 204self.status_code = status_codeself.headers = NoCaseDict(headers or {})self.reason = reasonif isinstance(body, (dict, list)):self.body = json.dumps(body).encode()self.headers['Content-Type'] = 'application/json; charset=UTF-8'elif isinstance(body, str):self.body = body.encode()else:# this applies to bytes, file-like objects or generatorsself.body = bodyself.is_head = Falsedef set_cookie(self, cookie, value, path=None, domain=None, expires=None,max_age=None, secure=False, http_only=False):"""Add a cookie to the response.:param cookie: The cookie's name.:param value: The cookie's value.:param path: The cookie's path.:param domain: The cookie's domain.:param expires: The cookie expiration time, as a ``datetime`` objector a correctly formatted string.:param max_age: The cookie's ``Max-Age`` value.:param secure: The cookie's ``secure`` flag.:param http_only: The cookie's ``HttpOnly`` flag."""http_cookie = '{cookie}={value}'.format(cookie=cookie, value=value)if path:http_cookie += '; Path=' + pathif domain:http_cookie += '; Domain=' + domainif expires:if isinstance(expires, str):http_cookie += '; Expires=' + expireselse:http_cookie += '; Expires=' + expires.strftime('%a, %d %b %Y %H:%M:%S GMT')if max_age:http_cookie += '; Max-Age=' + str(max_age)if secure:http_cookie += '; Secure'if http_only:http_cookie += '; HttpOnly'if 'Set-Cookie' in self.headers:self.headers['Set-Cookie'].append(http_cookie)else:self.headers['Set-Cookie'] = [http_cookie]def complete(self):if isinstance(self.body, bytes) and \'Content-Length' not in self.headers:self.headers['Content-Length'] = str(len(self.body))if 'Content-Type' not in self.headers:self.headers['Content-Type'] = self.default_content_typeif 'charset=' not in self.headers['Content-Type']:self.headers['Content-Type'] += '; charset=UTF-8'def write(self, stream):self.complete()# status codereason = self.reason if self.reason is not None else \('OK' if self.status_code == 200 else 'N/A')stream.write('HTTP/1.0 {status_code} {reason}\r\n'.format(status_code=self.status_code, reason=reason).encode())# headersfor header, value in self.headers.items():values = value if isinstance(value, list) else [value]for value in values:stream.write('{header}: {value}\r\n'.format(header=header, value=value).encode())stream.write(b'\r\n')# bodyif not self.is_head:can_flush = hasattr(stream, 'flush')try:for body in self.body_iter():if isinstance(body, str): # pragma: no coverbody = body.encode()stream.write(body)if can_flush: # pragma: no coverstream.flush()except OSError as exc: # pragma: no coverif exc.errno in MUTED_SOCKET_ERRORS:passelse:raisedef body_iter(self):if self.body:if hasattr(self.body, 'read'):while True:buf = self.body.read(self.send_file_buffer_size)if len(buf):yield bufif len(buf) < self.send_file_buffer_size:breakif hasattr(self.body, 'close'): # pragma: no coverself.body.close()elif hasattr(self.body, '__next__'):yield from self.bodyelse:yield self.body@classmethoddef redirect(cls, location, status_code=302):"""Return a redirect response.:param location: The URL to redirect to.:param status_code: The 3xx status code to use for the redirect. Thedefault is 302."""if '\x0d' in location or '\x0a' in location:raise ValueError('invalid redirect URL')return cls(status_code=status_code, headers={'Location': location})@classmethoddef send_file(cls, filename, status_code=200, content_type=None,stream=None, max_age=None, compressed=False,file_extension=''):"""Send file contents in a response.:param filename: The filename of the file.:param status_code: The 3xx status code to use for the redirect. Thedefault is 302.:param content_type: The ``Content-Type`` header to use in theresponse. If omitted, it is generatedautomatically from the file extension of the``filename`` parameter.:param stream: A file-like object to read the file contents from. Ifa stream is given, the ``filename`` parameter is onlyused when generating the ``Content-Type`` header.:param max_age: The ``Cache-Control`` header's ``max-age`` value inseconds. If omitted, the value of the:attr:`Response.default_send_file_max_age` attribute isused.:param compressed: Whether the file is compressed. If ``True``, the``Content-Encoding`` header is set to ``gzip``. Astring with the header value can also be passed.Note that when using this option the file must havebeen compressed beforehand. This option only setsthe header.:param file_extension: A file extension to append to the ``filename``parameter when opening the file, including thedot. The extension given here is not consideredwhen generating the ``Content-Type`` header.Security note: The filename is assumed to be trusted. Never passfilenames provided by the user without validating and sanitizing themfirst."""if content_type is None:ext = filename.split('.')[-1]if ext in Response.types_map:content_type = Response.types_map[ext]else:content_type = 'application/octet-stream'headers = {'Content-Type': content_type}if max_age is None:max_age = cls.default_send_file_max_ageif max_age is not None:headers['Cache-Control'] = 'max-age={}'.format(max_age)if compressed:headers['Content-Encoding'] = compressed \if isinstance(compressed, str) else 'gzip'f = stream or open(filename + file_extension, 'rb')return cls(body=f, status_code=status_code, headers=headers)class URLPattern():def __init__(self, url_pattern):self.url_pattern = url_patternself.pattern = ''self.args = []use_regex = Falsefor segment in url_pattern.lstrip('/').split('/'):if segment and segment[0] == '<':if segment[-1] != '>':raise ValueError('invalid URL pattern')segment = segment[1:-1]if ':' in segment:type_, name = segment.rsplit(':', 1)else:type_ = 'string'name = segmentif type_ == 'string':pattern = '[^/]+'elif type_ == 'int':pattern = '-?\\d+'elif type_ == 'path':pattern = '.+'elif type_.startswith('re:'):pattern = type_[3:]else:raise ValueError('invalid URL segment type')use_regex = Trueself.pattern += '/({pattern})'.format(pattern=pattern)self.args.append({'type': type_, 'name': name})else:self.pattern += '/{segment}'.format(segment=segment)if use_regex:self.pattern = re.compile('^' + self.pattern + '$')def match(self, path):if isinstance(self.pattern, str):if path != self.pattern:returnreturn {}g = self.pattern.match(path)if not g:returnargs = {}i = 1for arg in self.args:value = g.group(i)if arg['type'] == 'int':value = int(value)args[arg['name']] = valuei += 1return argsclass HTTPException(Exception):def __init__(self, status_code, reason=None):self.status_code = status_codeself.reason = reason or str(status_code) + ' error'def __repr__(self): # pragma: no coverreturn 'HTTPException: {}'.format(self.status_code)class Microdot():"""An HTTP application class.This class implements an HTTP application instance and is heavilyinfluenced by the ``Flask`` class of the Flask framework. It is typicallydeclared near the start of the main application script.Example::from microdot import Microdotapp = Microdot()"""def __init__(self):self.url_map = []self.before_request_handlers = []self.after_request_handlers = []self.after_error_request_handlers = []self.error_handlers = {}self.shutdown_requested = Falseself.options_handler = self.default_options_handlerself.debug = Falseself.server = Nonedef route(self, url_pattern, methods=None):"""Decorator that is used to register a function as a request handlerfor a given URL.:param url_pattern: The URL pattern that will be compared againstincoming requests.:param methods: The list of HTTP methods to be handled by thedecorated function. If omitted, only ``GET`` requestsare handled.The URL pattern can be a static path (for example, ``/users`` or``/api/invoices/search``) or a path with dynamic components enclosedin ``<`` and ``>`` (for example, ``/users/<id>`` or``/invoices/<number>/products``). Dynamic path components can alsoinclude a type prefix, separated from the name with a colon (forexample, ``/users/<int:id>``). The type can be ``string`` (thedefault), ``int``, ``path`` or ``re:[regular-expression]``.The first argument of the decorated function must bethe request object. Any path arguments that are specified in the URLpattern are passed as keyword arguments. The return value of thefunction must be a :class:`Response` instance, or the arguments tobe passed to this class.Example::@app.route('/')def index(request):return 'Hello, world!'"""def decorated(f):self.url_map.append(([m.upper() for m in (methods or ['GET'])],URLPattern(url_pattern), f))return freturn decorateddef get(self, url_pattern):"""Decorator that is used to register a function as a ``GET`` requesthandler for a given URL.:param url_pattern: The URL pattern that will be compared againstincoming requests.This decorator can be used as an alias to the ``route`` decorator with``methods=['GET']``.Example::@app.get('/users/<int:id>')def get_user(request, id):# ..."""return self.route(url_pattern, methods=['GET'])def post(self, url_pattern):"""Decorator that is used to register a function as a ``POST`` requesthandler for a given URL.:param url_pattern: The URL pattern that will be compared againstincoming requests.This decorator can be used as an alias to the``route`` decorator with``methods=['POST']``.Example::@app.post('/users')def create_user(request):# ..."""return self.route(url_pattern, methods=['POST'])def put(self, url_pattern):"""Decorator that is used to register a function as a ``PUT`` requesthandler for a given URL.:param url_pattern: The URL pattern that will be compared againstincoming requests.This decorator can be used as an alias to the ``route`` decorator with``methods=['PUT']``.Example::@app.put('/users/<int:id>')def edit_user(request, id):# ..."""return self.route(url_pattern, methods=['PUT'])def patch(self, url_pattern):"""Decorator that is used to register a function as a ``PATCH`` requesthandler for a given URL.:param url_pattern: The URL pattern that will be compared againstincoming requests.This decorator can be used as an alias to the ``route`` decorator with``methods=['PATCH']``.Example::@app.patch('/users/<int:id>')def edit_user(request, id):# ..."""return self.route(url_pattern, methods=['PATCH'])def delete(self, url_pattern):"""Decorator that is used to register a function as a ``DELETE``request handler for a given URL.:param url_pattern: The URL pattern that will be compared againstincoming requests.This decorator can be used as an alias to the ``route`` decorator with``methods=['DELETE']``.Example::@app.delete('/users/<int:id>')def delete_user(request, id):# ..."""return self.route(url_pattern, methods=['DELETE'])def before_request(self, f):"""Decorator to register a function to run before each request ishandled. The decorated function must take a single argument, therequest object.Example::@app.before_requestdef func(request):# ..."""self.before_request_handlers.append(f)return fdef after_request(self, f):"""Decorator to register a function to run after each request ishandled. The decorated function must take two arguments, the requestand response objects. The return value of the function must be anupdated response object.Example::@app.after_requestdef func(request, response):# ...return response"""self.after_request_handlers.append(f)return fdef after_error_request(self, f):"""Decorator to register a function to run after an error response isgenerated. The decorated function must take two arguments, the requestand response objects. The return value of the function must be anupdated response object. The handler is invoked for error responsesgenerated by Microdot, as well as those returned by application-definederror handlers.Example::@app.after_error_requestdef func(request, response):# ...return response"""self.after_error_request_handlers.append(f)return fdef errorhandler(self, status_code_or_exception_class):"""Decorator to register a function as an error handler. Error handlerfunctions for numeric HTTP status codes must accept a single argument,the request object. Error handler functions for Python exceptionsmust accept two arguments, the request object and the exceptionobject.:param status_code_or_exception_class: The numeric HTTP status code orPython exception class tohandle.Examples::@app.errorhandler(404)def not_found(request):return 'Not found'@app.errorhandler(RuntimeError)def runtime_error(request, exception):return 'Runtime error'"""def decorated(f):self.error_handlers[status_code_or_exception_class] = freturn freturn decorateddef mount(self, subapp, url_prefix=''):"""Mount a sub-application, optionally under the given URL prefix.:param subapp: The sub-application to mount.:param url_prefix: The URL prefix to mount the application under."""for methods, pattern, handler in subapp.url_map:self.url_map.append((methods, URLPattern(url_prefix + pattern.url_pattern),handler))for handler in subapp.before_request_handlers:self.before_request_handlers.append(handler)for handler in subapp.after_request_handlers:self.after_request_handlers.append(handler)for handler in subapp.after_error_request_handlers:self.after_error_request_handlers.append(handler)for status_code, handler in subapp.error_handlers.items():self.error_handlers[status_code] = handler@staticmethoddef abort(status_code, reason=None):"""Abort the current request and return an error response with thegiven status code.:param status_code: The numeric status code of the response.:param reason: The reason for the response, which is included in theresponse body.Example::from microdot import abort@app.route('/users/<int:id>')def get_user(id):user = get_user_by_id(id)if user is None:abort(404)return user.to_dict()"""raise HTTPException(status_code, reason)def run(self, host='0.0.0.0', port=5000, debug=False, ssl=None):"""Start the web server. This function does not normally return, asthe server enters an endless listening loop. The :func:`shutdown`function provides a method for terminating the server gracefully.:param host: The hostname or IP address of the network interface thatwill be listening for requests. A value of ``'0.0.0.0'``(the default) indicates that the server should listen forrequests on all the available interfaces, and a value of``127.0.0.1`` indicates that the server should listenfor requests only on the internal networking interface ofthe host.:param port: The port number to listen for requests. The default isport 5000.:param debug: If ``True``, the server logs debugging information. Thedefault is ``False``.:param ssl: An ``SSLContext`` instance or ``None`` if the server shouldnot use TLS. The default is ``None``.Example::from microdot import Microdotapp = Microdot()@app.route('/')def index(request):return 'Hello, world!'app.run(debug=True)"""self.debug = debugself.shutdown_requested = Falseself.server = socket.socket()ai = socket.getaddrinfo(host, port)addr = ai[0][-1]if self.debug: # pragma: no coverprint('Starting {mode} server on {host}:{port}...'.format(mode=concurrency_mode, host=host, port=port))self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.server.bind(addr)self.server.listen(5)if ssl:self.server = ssl.wrap_socket(self.server, server_side=True)while not self.shutdown_requested:try:sock, addr = self.server.accept()except OSError as exc: # pragma: no coverif exc.errno == errno.ECONNABORTED:breakelse:print_exception(exc)except Exception as exc: # pragma: no coverprint_exception(exc)else:create_thread(self.handle_request, sock, addr)def shutdown(self):"""Request a server shutdown. The server will then exit its requestlistening loop and the :func:`run` function will return. This functioncan be safely called from a route handler, as it only schedules theserver to terminate as soon as the request completes.Example::@app.route('/shutdown')def shutdown(request):request.app.shutdown()return 'The server is shutting down...'"""self.shutdown_requested = Truedef find_route(self, req):method = req.method.upper()if method == 'OPTIONS' and self.options_handler:return self.options_handler(req)if method == 'HEAD':method = 'GET'f = 404for route_methods, route_pattern, route_handler in self.url_map:req.url_args = route_pattern.match(req.path)if req.url_args is not None:if method in route_methods:f = route_handlerbreakelse:f = 405return fdef default_options_handler(self, req):allow = []for route_methods, route_pattern, route_handler in self.url_map:if route_pattern.match(req.path) is not None:allow.extend(route_methods)if 'GET' in allow:allow.append('HEAD')allow.append('OPTIONS')return {'Allow': ', '.join(allow)}def handle_request(self, sock, addr):if Request.socket_read_timeout and \hasattr(sock, 'settimeout'): # pragma: no coversock.settimeout(Request.socket_read_timeout)if not hasattr(sock, 'readline'): # pragma: no coverstream = sock.makefile("rwb")else:stream = sockreq = Noneres = Nonetry:req = Request.create(self, stream, addr, sock)res = self.dispatch_request(req)except socket_timeout_error as exc: # pragma: no coverif exc.errno and exc.errno != errno.ETIMEDOUT:print_exception(exc) # not a timeoutexcept Exception as exc: # pragma: no coverprint_exception(exc)try:if res and res != Response.already_handled: # pragma: no branchres.write(stream)stream.close()except OSError as exc: # pragma: no coverif exc.errno in MUTED_SOCKET_ERRORS:passelse:print_exception(exc)except Exception as exc: # pragma: no coverprint_exception(exc)if stream != sock: # pragma: no coversock.close()if self.shutdown_requested: # pragma: no coverself.server.close()if self.debug and req: # pragma: no coverprint('{method} {path} {status_code}'.format(method=req.method, path=req.path,status_code=res.status_code))def dispatch_request(self, req):after_request_handled = Falseif req:if req.content_length > req.max_content_length:if 413 in self.error_handlers:res = self.error_handlers[413](req)else:res = 'Payload too large', 413else:f = self.find_route(req)try:res = Noneif callable(f):for handler in self.before_request_handlers:res = handler(req)if res:breakif res is None:res = f(req, **req.url_args)if isinstance(res, tuple):body = res[0]if isinstance(res[1], int):status_code = res[1]headers = res[2] if len(res) > 2 else {}else:status_code = 200headers = res[1]res = Response(body, status_code, headers)elif not isinstance(res, Response):res = Response(res)for handler in self.after_request_handlers:res = handler(req, res) or resfor handler in req.after_request_handlers:res = handler(req, res) or resafter_request_handled = Trueelif isinstance(f, dict):res = Response(headers=f)elif f in self.error_handlers:res = self.error_handlers[f](req)else:res = 'Not found', fexcept HTTPException as exc:if exc.status_code in self.error_handlers:res = self.error_handlers[exc.status_code](req)else:res = exc.reason, exc.status_codeexcept Exception as exc:print_exception(exc)exc_class = Noneres = Noneif exc.__class__ in self.error_handlers:exc_class = exc.__class__else:for c in mro(exc.__class__)[1:]:if c in self.error_handlers:exc_class = cbreakif exc_class:try:res = self.error_handlers[exc_class](req, exc)except Exception as exc2: # pragma: no coverprint_exception(exc2)if res is None:if 500 in self.error_handlers:res = self.error_handlers[500](req)else:res = 'Internal server error', 500else:if 400 in self.error_handlers:res = self.error_handlers[400](req)else:res = 'Bad request', 400if isinstance(res, tuple):res = Response(*res)elif not isinstance(res, Response):res = Response(res)if not after_request_handled:for handler in self.after_error_request_handlers:res = handler(req, res) or resres.is_head = (req and req.method == 'HEAD')return resabort = Microdot.abort
Response.already_handled = Response()
redirect = Response.redirect
send_file = Response.send_file
相关文章:

MicroPython 基于microdot框架搭建网页服务器
MicroPython 基于microdot框架搭建网页服务器 简介简单demo 简介 Microdot是一个极简的Python web框架,灵感来自于Flask,它被设计用来运行在资源有限的系统上,如微控制器。它运行在标准的Python和MicroPython上。 API参考microdot 资源下载m…...

FL Studio21.2汉化永久中文语言包
FL Studio21.2这款软件在国内被广泛使用,因此又被称为"水果"。它提供音符编辑器,可以针对作曲者的要求编辑出不同音律的节奏,例如鼓、镲、锣、钢琴、笛、大提琴、筝、扬琴等等任何乐器的节奏律动。此外,它还提供了方便快…...

Glide结合OkHttp保证短信验证接口携带图形验证码接口返回Cookie值去做网络请求
一、实现效果 二、步骤 注意:仅展示核心部分代码 1、导入依赖 api com.github.bumptech.glide:glide:4.10.0 kapt com.github.bumptech.glide:compiler:4.10.0 api com.squareup.okhttp3:okhttp:3.11.0 api com.squareup.okhttp3:logging-interceptor:3.11.02、自…...

怎样用Ajax提交from表单并接收其中的json数据
怎样用Ajax提交表单并接收其中的json数据 需求:实现点击按钮后,数据以表单形式提交至服务器,并接收来自服务器的返回数据。过程中页面不刷新。 AJAX 不是新的编程语言,而是一种使用现有标准的新方法。AJAX 是与服务器交换数据并…...

【动态规划】LeetCode-746LCR 088.使用最小花费爬楼梯
🎈算法那些事专栏说明:这是一个记录刷题日常的专栏,每个文章标题前都会写明这道题使用的算法。专栏每日计划至少更新1道题目,在这立下Flag🚩 🏠个人主页:Jammingpro 📕专栏链接&…...

Unity 接入TapADN播放广告时闪退 LZ4JavaSafeCompressor
通过跟踪安卓日志,发现报如下错误 Didnt find class "com.tapadn.lz4.LZ4JavaSafeCompressor" 解决方案: 去掉Minify这边的勾选,再打包即可。...

【九】linux下部署frp客户端服务端实践(内网穿透)
linux下部署frp客户端服务端实践 简介: 今天有一个这样的需求,部署在公司内部局域网虚拟机上的服务需要在外网能够访问到,这不就是内网穿透的需求吗,之前通过路由器实现过,现在公司这块路由器不具备这个功能了&#x…...

华为1+x网络系统建设与运维(中级)-练习题2
一.设备命令 LSW1 [Huawei]sys LSW1 同理可得,给所有设备改名 二.VLAN LSW1 [LSW1]vlan ba 10 20 [LSW1]int g0/0/1 [LSW1-GigabitEthernet0/0/1]port link-type trunk [LSW1-GigabitEthernet0/0/1]port trunk allow-pass vlan 10 20 [LSW1-GigabitEthernet0/0/1]in…...

自定义类型-结构体,联合体和枚举-C语言
引言 能看到结构体,说明C语言想必学习的时间也不少了,在之前肯定也学习过基本数据类型,包括整型int,浮点型float等等。可是在日常生活中,想要描述一个事物并没有那么简单。比如,你要描述一本书,…...

Windows 安装redis,设置开机自启动
Windows 安装redis,设置开机自启动 文章目录 Windows 安装redis,设置开机自启动下载, 解压到指定目录设置redis密码启动redis服务端停止redis服务端设置自启动 下载, 解压到指定目录 官网地址: https://redis.io/ 安装包下载地址: https://github.com/tporadowski/redis/relea…...

Windows安装Mysql Workbench及常用操作
Mysql Workbench是mysql自带的可视化操作界面,功能是强大的,但界面和navicat比,就是觉得别扭,但其实用惯了也还好,各有特色吧。这里记录一下常用的操作。 官方手册:MySQL Workbench 一、安装 1. 下载 官方…...

【计算机网络】15、NAT、NAPT 网络地址转换、打洞
文章目录 一、概念二、分类(主要是传统 NAT)2.1 基本 NAT2.2 NAPT 三、访问NAT下的内网设备的方式3.1 多拨3.2 端口转发、DMZ3.3 UPnP IGD、NAT-PMP3.4 服务器中转:frp 内网穿透3.4.1 NAT 打洞3.4.2 NAT 类型与打洞成功率3.4.2.1 完全圆锥形 …...

【送书活动三期】解决docker服务假死问题
工作中使用docker-compose部署容器,有时候会出现使用docker-compose stop或docker-compose down命令想停掉容器,但是依然无法停止或者一直卡顿在停止中的阶段,这种问题很让人头疼啊! 目录 问题描述问题排查问题解决终极杀招-最粗暴…...

【每日一题】拼车+【差分数组】
文章目录 Tag题目来源解题思路方法一:差分 写在最后 Tag 【差分数组】【数组】【2023-12-02】 题目来源 1094. 拼车 解题思路 本题朴素的解题思路是统计题目中提到的每一个站点的车上人数,如果某个站点的车上人数大于车上的座位数直接返回 false&…...

【开源】基于JAVA的农村物流配送系统
项目编号: S 024 ,文末获取源码。 \color{red}{项目编号:S024,文末获取源码。} 项目编号:S024,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统登录、注册界面2.2 系统功能2.2…...

7、Jenkins+Nexus3+Docker+K8s实现CICD
文章目录 基本环境配置一、Jenkins安装必要插件二、Jenkins系统配置三、新建流水线四、在项目工程里添加Jenkinsfile、deploy.yml五、在项目工程里添加Dockerfile在这里插入图片描述 总结 提示:本章主要记录各基本环境搭建好后如何配置Jenkins流水线部署微服务到K8s…...

解决git action发布失败报错:Error: Resource not accessible by integration
现象: 网上说的解决方法都是什么到github个人中心setting里面的action设置里面去找。 可这玩意根本就没有! 正确解决办法: 在你的仓库页面,注意是仓库页面的setting里面: Actions> General>Workflow permisss…...

[传智杯 #2 决赛] 补刀
题目描述 UIM 在写程序的空闲玩一款 MOBA 游戏。 当敌方的小兵进入到我方防御塔的范围内,就会持续受到防御塔造成的伤害;当然我方英雄也可以对它造成伤害。当小兵的血量降到了 0 或者更低,就会被击杀。为了获得经验,UIM 希望在防…...

C语言:求Sn=a+aa+aaa+aaaa+……(n个a)之值,其中a表示一个数字,n表示a的位数,n由键盘录入。
分析: 在主函数 main 中,程序首先定义四个整型变量 a、n、i 和 sn,并初始化 a、n 和 i 的值,其中 sn 用于记录数列的和。然后使用 scanf 函数从标准输入中读取用户输入的两个整数 a 和 n。 接下来,程序通过 while …...

【nlp】4.1 fasttext工具介绍(文本分类、训练词向量、词向量迁移)
fasttext工具介绍与文本分类 1 fasttext介绍1.1 fasttext作用1.2 fasttext工具包的优势1.3 fasttext的安装1.4 验证安装2 fasttext文本分类2.1 文本分类概念2.2 文本分类种类2.3 文本分类的过程2.4 文本分类代码实现2.4.1 获取数据2.4.2 训练集与验证集的划分2.4.3 训练模型2.4…...

Spring中的事务管理
1 基本概念 事务:将一组操作抽象成一个不可再分的单位,这组操作可以有很多个,但是它们要么就全部都执行成功,这时算作事务执行成功;要不其中有操作执行失败,则其余操作都视为执行失败,这时候需…...

量子光学的进步:光子学的“下一件小事”
量子光学是量子力学和光学交叉领域中发展迅速的一门学科,探索光的基本特性及其与物质在量子水平上的相互作用。通过利用光的独特特性,量子光学为通信、计算、密码学和传感等各个学科的变革性进步铺平了道路。 如今,量子光学领域的研究人员和工…...

微信小程序获取定位显示在百度地图上位置出现偏差
项目场景: 背景: 微信小程序端获取手机定位坐标,以及正确展示位置通过详细地址解析为定位坐标显示在小程序端以及PC后台小程序获取的地理坐标与百度地图坐标相互转化 相关知识 目前国内主要有以下三种坐标系: WGS84:…...

【LeetCode 0170】【哈希】两数之和(3) 数据结构设计
https://leetcode.com/problems/two-sum-iii-data-structure-design/ 描述 Design and implement a TwoSum class. It should support the following operations: add and find. add(input) – Add the number input to an internal data structure. find(value) – Find if …...

005、简单页面-容器组件
之——布局 目录 之——布局 杂谈 正文 1.布局基础知识 2.Column 3.Row 4.实践 杂谈 布局容器组件。 一个丰富的页面需要很多组件组成,那么,我们如何才能让这些组件有条不紊地在页面上布局呢?这就需要借助容器组件来实现。 容器组件是…...

stm32中断调用流程
USART1_IRQHandler(void)(中断服务函数) -> HAL_UART_IRQHandler(UART_HandleTypeDef *huart)(中断处理函数) -> UART_Receive_IT(UART_HandleTypeDef *huart) (接收函数) -> HAL_UART_RxCpltCallback(huart);(中断回调函数) HAL_UART_IRQHandler(UART_HandleTypeDef…...

18487.1 - 2015 电动汽车充电系统标准 第1部分 关键点梳理
一、部分知识介绍 1、连接方式 使用电缆和连接器将电动汽车接入电网(电源)的方法。 1.1、连接方式A 1.2、连接方式B 1.3、连接方式C 2、电动汽车控电设备 2.1、按照输出电压分类 1)交流 单相 220V,三相 380V. 2)…...

WPF实战项目十八(客户端):添加新增、查询、编辑功能
1、ToDoView.xmal添加引用,添加微软的行为类 xmlns:i"http://schemas.microsoft.com/xaml/behaviors" 2、给项目添加行为 <i:Interaction.Triggers><i:EventTrigger EventName"MouseLeftButtonUp"><i:InvokeCommandAction Com…...

职位招聘管理与推荐系统Python+Django网页界面+协同过滤推荐算法
一、介绍 职位招聘管理与推荐系统。本系统使用Python作为主要开发语言,以WEB网页平台的方式进行呈现。前端使用HTML、CSS、Ajax、BootStrap等技术,后端使用Django框架处理用户请求。 系统创新点:相对于传统的管理系统,本系统使用…...

C#文件流二进制文件的读写
目录 一、BinaryWriter类 二、BinaryReader类 三、示例 1.源码 2.生成效果 二进制文件的写入与读取主要是通过BinaryWriter类和BinaryReader类来实现的。 一、BinaryWriter类 BinaryWriter类以二进制形式将基元类型写入流,并支持用特定的编码写入字符串&#…...