microdot --- إطار عمل HTTP بسيط#

Microdot هو إطار عمل HTTP صغير مستوحى من Flask لأجل MicroPython. يعمل فوق asyncio، ويتعامل مع عملاء متزامنين متعددين دون استخدام خيوط، ويوفر واجهة مألوفة قائمة على مزينات المسارات. يبدو التطبيق البسيط هكذا:

from microdot import Microdot

app = Microdot()

@app.route('/')
async def index(request):
    return 'Hello, world!'

app.run(host='0.0.0.0', port=80)

class Microdot#

class microdot.Microdot#

مثيل تطبيق HTTP. يُنشأ مثيل واحد قرب أعلى البرنامج النصي ويُزيَّن بمعالجات المسارات؛ ويبدأ التقديم باستدعاء run() أو انتظار start_server().

تسجيل المسارات

route(url_pattern: str, methods: list | None = None) Callable#

مزيِّن يسجل معالجًا لأجل url_pattern ضمن طرق HTTP المذكورة (الافتراضي ['GET']). يُرجع المزيِّن الذي يسجل الدالة كمعالج عند تطبيقه عليها ويُرجع الدالة دون تغيير.

url_pattern

نمط مسار. يدعم المقاطع الثابتة (/users) والمقاطع الديناميكية المحاطة بـ < / >. تقبل المقاطع الديناميكية بادئة نوع اختيارية مفصولة بـ : -- <int:id> و <path:rest> و <re:[0-9a-f]+:hex>. والنوع الافتراضي هو string.

methods

قائمة بأسماء طرق HTTP. إذا حُذفت، فلن يُطابق سوى GET.

يُستدعى المعالج بكائن الطلب أولًا، ثم بأي مقاطع ديناميكية ملتقطة كوسائط بأسماء مفاتيح. تصبح القيمة المُرجعة من المعالج استجابة HTTP: إما Response، أو سلسلة نصية، أو صف (body, status_code[, headers])، أو قاموس / قائمة (تُرسل بصيغة JSON).

get(url_pattern: str) Callable#

اسم بديل مريح لـ route(url_pattern, methods=['GET']) -- يسجل الدالة المزيَّنة كمعالج GET لأجل url_pattern. يُرجع المزيِّن.

post(url_pattern: str) Callable#

اسم بديل مريح لـ route(url_pattern, methods=['POST']) -- يسجل الدالة المزيَّنة كمعالج POST لأجل url_pattern. يُرجع المزيِّن.

put(url_pattern: str) Callable#

اسم بديل مريح لـ route(url_pattern, methods=['PUT']) -- يسجل الدالة المزيَّنة كمعالج PUT لأجل url_pattern. يُرجع المزيِّن.

patch(url_pattern: str) Callable#

اسم بديل مريح لـ route(url_pattern, methods=['PATCH']) -- يسجل الدالة المزيَّنة كمعالج PATCH لأجل url_pattern. يُرجع المزيِّن.

delete(url_pattern: str) Callable#

اسم بديل مريح لـ route(url_pattern, methods=['DELETE']) -- يسجل الدالة المزيَّنة كمعالج DELETE لأجل url_pattern. يُرجع المزيِّن.

خطافات دورة الحياة

before_request(f: Callable) Callable#

مزيِّن يسجل f لتُنفَّذ قبل كل طلب. تأخذ f كائن الطلب؛ وتُتجاهَل قيمتها المُرجعة عادةً. ولاختصار مسار الطلب، أرجع Response (أو قيمة تتحول إليها) -- عندئذ يُتخطى باقي خط المعالجة وتُرسل تلك الاستجابة. تُرجع f دون تغيير.

after_request(f: Callable) Callable#

مزيِّن يسجل f لتُنفَّذ بعد كل طلب ناجح. تأخذ f الوسيطين (request, response) ويجب أن تُرجع كائن الاستجابة (ربما بعد تعديله). تُرجع f دون تغيير.

after_error_request(f: Callable) Callable#

مزيِّن يسجل f لتُنفَّذ بعد توليد Microdot لاستجابة خطأ (404، 500، استثناء مرفوع، إلخ.). تأخذ f الوسيطين (request, response) ويجب أن تُرجع كائن الاستجابة (ربما بعد تعديله). تُرجع f دون تغيير.

errorhandler(status_code_or_exception_class) Callable#

مزيِّن يسجل معالجًا مخصصًا لرمز حالة HTTP أو لصنف استثناء Python. بالنسبة لرموز الحالة، يأخذ المعالج الطلب فقط؛ وبالنسبة لأصناف الاستثناءات، يأخذ (request, exception). يُرجع المزيِّن.

التركيب والإجهاض

mount(subapp: Microdot, url_prefix: str = '', local: bool = False) None#

يربط مسارات مثيل Microdot آخر تحت url_prefix. عندما تكون local قيمتها False (الافتراضي)، تُربط أيضًا معالجات before / after / error الخاصة بالتطبيق الفرعي بالتطبيق الأصل. وعندما تكون True، تُنفَّذ تلك المعالجات لمسارات التطبيق الفرعي فقط. يُرجع None.

static abort(status_code: int, reason: str | None = None) None#

ارفع HTTPException لإجهاض الطلب الحالي برمز الحالة المعطى. لا يعود أبدًا بصورة طبيعية -- يلتقط الإطار الاستثناء ويحوله إلى استجابة الخطأ المقابلة. مريح داخل أجسام المسارات:

from microdot import abort

@app.get('/users/<int:id>')
def get_user(request, id):
    user = lookup(id)
    if user is None:
        abort(404)
    return user.to_dict()

تشغيل الخادم

run(host: str = '0.0.0.0', port: int = 5000, debug: bool = False, ssl=None) None#

يحجب الخيط المستدعي، ويُشغِّل asyncio.run() على start_server(). لا يُرجع None إلا بعد استدعاء shutdown() وخروج حلقة الإصغاء. أبسط طريقة لتشغيل تطبيق مستقل:

app.run(host='0.0.0.0', port=80)
async start_server(host: str = '0.0.0.0', port: int = 5000, debug: bool = False, ssl=None, start_serving: bool = True) None#

يبدأ الخادم كروتين متزامن (coroutine). استخدم هذا عند الدمج مع حلقة أحداث asyncio قائمة إلى جانب مهام أخرى. لا يعود الروتين المتزامن حتى يُستدعى shutdown()

async def main():
    await asyncio.gather(
        app.start_server(port=80),
        capture_loop(),
    )
host

واجهة الشبكة التي يُصغى عليها. '0.0.0.0' (الافتراضي) تعني جميع الواجهات؛ و '127.0.0.1' تعني الاسترجاع المحلي (loopback) فقط.

port

منفذ TCP الذي يُصغى عليه. الافتراضي 5000 -- اختر 80 لأجل HTTP العادي، و 443 لأجل HTTPS.

debug

إذا كانت True، فسجِّل كل طلب وأخرِج تتبعات المكدس إلى stdout.

ssl

كائن ssl.SSLContext لتغليف الاتصالات الواردة بـ TLS. القيمة None (الافتراضي) تعني HTTP عاديًا.

start_serving

ذو صلة على CPython فقط؛ ويُتجاهَل على MicroPython.

shutdown() None#

يطلب إيقاف تشغيل الخادم بسلاسة. يمكن استدعاؤه بأمان من معالج مسار -- يكتمل الطلب الحالي قبل خروج الحلقة. يعود فورًا؛ ويحدث الإيقاف الفعلي بمجرد انتهاء الطلب الجاري تنفيذه.

السمات

url_map: list#

قائمة بالمسارات المسجلة على هيئة صفوف (methods, URLPattern, handler, url_prefix, subapp).

before_request_handlers: list#

قائمة بالكائنات القابلة للاستدعاء المسجلة بـ before_request()، بترتيب التسجيل. يُنفَّذ كل منها مع كائن الطلب قبل معالج المسار. يعمل تعديل القائمة مباشرةً لكنه نادرًا ما يُحتاج إليه -- يُفضَّل استخدام المزيِّن.

after_request_handlers: list#

قائمة بالكائنات القابلة للاستدعاء المسجلة بـ after_request()، بترتيب التسجيل. يُنفَّذ كل منها مع (request, response) بعد طلب ناجح ويجب أن يُرجع الاستجابة (ربما بعد تعديلها).

after_error_request_handlers: list#

قائمة بالكائنات القابلة للاستدعاء المسجلة بـ after_error_request()، بترتيب التسجيل. يُنفَّذ كل منها مع (request, response) بعد توليد استجابة خطأ (بواسطة الإطار أو بواسطة معالج خطأ في التطبيق) ويجب أن يُرجع الاستجابة.

error_handlers: dict#

تعيين (mapping) لمفاتيح الأخطاء إلى كائنات معالجة قابلة للاستدعاء، تُملأ بواسطة errorhandler(). المفاتيح إما رموز حالة HTTP (int) أو أصناف استثناءات Python؛ والقيم هي المعالجات المسجلة. تأخذ معالجات رموز الحالة (request)؛ وتأخذ معالجات أصناف الاستثناءات (request, exception).

debug: bool#

تكون True أثناء تشغيل الخادم بـ debug=True.

class Request#

class microdot.Request#

طلب HTTP وارد. تُمرَّر المثيلات إلى معالجات المسارات كأول وسيط موضعي. لا تنشئ التطبيقات Request مباشرةً.

سمات الصنف

max_content_length: int#

ترفض الطلبات التي يتجاوز Content-Length فيها هذا العدد من البايتات باستجابة 413. الافتراضي 16 كيلوبايت.

max_body_length: int#

أكبر جسم يُخزَّن مؤقتًا في الذاكرة ويُعرَض عبر body. تبقى الأجسام الأكبر (حتى max_content_length) على المقبس (socket) ويجب قراءتها عبر stream. الافتراضي 16 كيلوبايت.

max_readline: int#

أقصى طول لسطر طلب / سطر ترويسة واحد بالبايتات. الافتراضي 2 كيلوبايت.

سمات المثيل

app: Microdot#

مثيل Microdot الذي يعالج الطلب.

client_addr: tuple#

عنوان العميل على هيئة (host, port).

method: str#

سلسلة طريقة HTTP ('GET' و 'POST' ...).

scheme: str#

إما 'http' أو 'https'.

url: str#

مسار عنوان URL الكامل للطلب وسلسلة الاستعلام (كل ما يأتي بعد المضيف).

path: str#

جزء المسار فقط.

query_string: str | None#

جزء سلسلة الاستعلام الخام، أو None.

args: MultiDict#

سلسلة الاستعلام بعد تحليلها على هيئة MultiDict.

headers: NoCaseDict#

ترويسات الطلب على هيئة NoCaseDict (بحث غير حساس لحالة الأحرف).

cookies: dict#

ترويسة Cookie بعد تحليلها على هيئة dict.

content_length: int#

قيمة Content-Length الصحيحة، أو 0 في حال غيابها.

content_type: str | None#

قيمة ترويسة Content-Type، أو None.

g: object#

حاوية حرة الشكل خاصة بكل طلب (كائن فارغ). أسنِد إليها سمات لتمرير القيم بين الخطافات والمعالجات (request.g.user = ...).

route: Callable#

دالة المعالج التي طابقت هذا الطلب.

url_prefix: str#

البادئة التي رُكِّب المسار تحتها، أو ''.

subapp: Microdot | None#

مثيل التطبيق الفرعي المُركَّب، أو None.

الوصول إلى الجسم

body: bytes#

جسم الطلب الكامل على هيئة bytes. يكون فارغًا عندما يُبَثُّ الجسم (انظر stream).

stream: object#

كائن دفق غير متزامن يعرض read() على الجسم. استخدم هذا لأجل الأجسام الأكبر من max_body_length.

json: dict | list | str | int | float | bool | None#

الجسم بعد تحليله بصيغة JSON (إما dict أو list أو str أو int أو float أو bool -- أيًا كان ما تشفره الحمولة)، أو None إذا لم يكن Content-Type هو application/json.

form: MultiDict | None#

حقول النموذج المشفرة بصيغة URL على هيئة MultiDict، أو None. لأجل multipart/form-data، زيِّن المسار بـ microdot.multipart.with_form_data().

files: dict | None#

الملفات المرفوعة على هيئة {name: FileUpload}، تُملأ بواسطة microdot.multipart.with_form_data(). تكون None حتى يُنفَّذ ذلك المزيِّن.

after_request(f: Callable) Callable#

يسجل خطاف ما بعد الطلب محليًا للطلب -- يُنفَّذ بعد معالجات ما بعد الطلب على مستوى التطبيق، عند النجاح فقط. تأخذ f الوسيطين (request, response) ويجب أن تُرجع كائن الاستجابة (ربما بعد تعديله). تُرجع f دون تغيير.

class Response#

class microdot.Response(body=b'', status_code: int = 200, headers: dict | None = None, reason: str | None = None)#

استجابة HTTP. تُرجع معظم المعالجات قيمًا يحولها Microdot إلى Response تلقائيًا؛ أنشئ واحدة مباشرةً عندما تحتاج إلى ترويسات مخصصة أو رمز حالة محدد.

body

جسم الاستجابة. تُشفَّر str بترميز UTF-8؛ وتُشفَّر dict / list بصيغة JSON ويُضبَط Content-Type تبعًا لذلك؛ وتُرسَل bytes كما هي؛ ويُبَث كائن شبيه بالملف أو مولِّد غير متزامن.

status_code

حالة HTTP رقمية. الافتراضي 200.

headers

قاموس بترويسات الاستجابة (غير حساس لحالة الأحرف).

reason

عبارة سبب مخصصة. القيمة الافتراضية "OK" لأجل 200 و "N/A" فيما عدا ذلك.

سمات الصنف

default_content_type: str#

نوع المحتوى المستخدم عندما لا يُضبَط أي نوع صراحةً. الافتراضي 'text/plain'.

default_send_file_max_age: int | None#

قيمة Cache-Control: max-age لأجل send_file() عندما لا تُعطى قيمة max_age. القيمة None (الافتراضي) تعني عدم وجود ترويسة Cache-Control.

send_file_buffer_size: int#

حجم القطعة لأجل بث send_file(). الافتراضي 1024.

already_handled: None#

قيمة حارسة (sentinel) تُرجَع من المعالجات التي كتبت الاستجابة مباشرةً بالفعل (تستخدمها ترقيات WebSocket). تكون دائمًا None؛ والمهم هو هويتها.

types_map: dict#

خريطة من الامتداد إلى نوع mime يستخدمها send_file() لاستنتاج نوع المحتوى. تربط css و gif و html و jpg و js و json و png و txt و svg.

الطرائق

يضيف ترويسة Set-Cookie. قد تكون expires سلسلة منسقة مسبقًا أو كائنًا شبيهًا بـ datetime له timetuple(). يُرجع None؛ وتُلحَق الترويسة بهذه الاستجابة في مكانها.

يضبط Set-Cookie تنتهي صلاحية ملف تعريف الارتباط المعطى فورًا. تقبل kwargs الخيارات نفسها التي تقبلها set_cookie() (path و domain و secure و http_only و partitioned)؛ وتُتجاهَل expires و max_age. يُرجع None؛ وتُلحَق الترويسة بهذه الاستجابة في مكانها.

classmethod redirect(location: str, status_code: int = 302) Response#

يُرجع استجابة إعادة توجيه:

@app.get('/old')
def old(request):
    return Response.redirect('/new')
classmethod send_file(filename: str, status_code: int = 200, content_type: str | None = None, stream=None, max_age: int | None = None, compressed: bool | str = False, file_extension: str = '') Response#

يبث ملفًا من نظام الملفات كجسم للاستجابة. يُستنتَج content_type من الامتداد عبر types_map إن لم يُعطَ. ويضبط compressed=True الترويسة Content-Encoding: gzip (يجب أن يكون الملف مضغوطًا بالفعل).

تحذير

يُفتح filename مباشرةً. لا تمرر أبدًا مسارًا مزودًا من المستخدم دون تطهير -- فالقيام بذلك يتيح كشف ملفات اعتباطية.

الاستثناءات#

exception microdot.HTTPException#

يُرفع بواسطة abort() لاختصار مسار طلب برمز حالة محدد. يلتقط Microdot هذا ويحوله إلى استجابة الخطأ المقابلة.

status_code: int#

رمز حالة HTTP الرقمي المراد إرجاعه -- القيمة المُمرَّرة إلى abort().

reason: str#

عبارة السبب المراد تضمينها في استجابة الخطأ. إذا لم تُعطَ إلى abort()، فالقيمة الافتراضية "<status_code> error" (مثل "404 error").

class URLPattern#

class microdot.URLPattern(url_pattern: str)#

الصيغة المُجمَّعة لنمط عنوان URL الخاص بمسار. يُنشأ تلقائيًا بواسطة Microdot.route()؛ ونادرًا ما تنشئ التطبيقات واحدًا مباشرةً.

classmethod register_type(type_name: str, pattern: str = '[^/]+', parser: Callable | None = None) None#

يسجل نوع مقطع ديناميكي جديد للاستخدام في أنماط المسارات. يُرجع None؛ ويُضاف النوع إلى سجل الأنواع على مستوى الصنف. على سبيل المثال لإضافة نوع <uuid:...>

URLPattern.register_type('uuid', '[0-9a-f-]{36}')

parser هو كائن قابل للاستدعاء اختياري يحوِّل السلسلة الملتقطة قبل أن تصل إلى المعالج.

match(path: str) dict | None#

يطابق path مع النمط ويُرجع قاموسًا بالمجموعات الملتقطة، أو None في حال عدم وجود تطابق.

أصناف مساعِدة#

class microdot.MultiDict(initial_dict: dict | None = None)#

صنف فرعي من dict يخزن قيمًا متعددة لكل مفتاح. يُستخدم لسلاسل الاستعلام وأجسام النماذج المشفرة بصيغة URL حيث يمكن أن يظهر المفتاح نفسه أكثر من مرة (?tag=a&tag=b).

get(key, default=None, type: Callable | None = None)#

يُرجع القيمة الأولى لأجل key، مع تحويلها اختياريًا بـ type (كائن قابل للاستدعاء). يُرجع default إذا كان المفتاح مفقودًا أو فشل التحويل؛ وإلا فيُرجع القيمة (المحوَّلة اختياريًا).

getlist(key, type: Callable | None = None) list#

يُرجع كل القيم لأجل key على هيئة قائمة، مع تحويل كل قيمة اختياريًا بـ type. يُرجع قائمة فارغة إذا لم يكن key موجودًا.

class microdot.NoCaseDict#

صنف فرعي من dict بمفاتيح نصية غير حساسة لحالة الأحرف. يُستخدم لأجل Request.headers و Response.headers.

دوال على مستوى الوحدة#

microdot.abort(status_code: int, reason: str | None = None) None#

اختصار لـ Microdot.abort(). لا يعود أبدًا بصورة طبيعية -- يرفع HTTPException. قابل للاستيراد بصيغة from microdot import abort.

microdot.redirect(location: str, status_code: int = 302) Response#

اختصار لـ Response.redirect(). قابل للاستيراد بصيغة from microdot import redirect.

microdot.send_file(filename: str, **kwargs) Response#

اختصار لـ Response.send_file().

microdot.urlencode(s: str) str#

يشفِّر مكوِّن عنوان URL بترميز النسبة المئوية (percent-encode). يستبدل الأحرف ذات المعنى المحجوز في عنوان URL (/ و ? و & و = و # ومسافة ...) بهرَوبها الست عشري %xx بحيث يمكن أن توضع النتيجة بأمان داخل مقطع مسار أو قيمة استعلام. يُرجع str المشفرة.

microdot.urldecode(s: str) str#

يفك تشفير مكوِّن عنوان URL من ترميز النسبة المئوية -- معكوس urlencode(). تُستبدَل هرَوبات %xx بالبايت الذي تشفره، وتُحوَّل + إلى مسافة (وفق العرف التاريخي لسلسلة الاستعلام). يُرجع str المفكوكة.

الوحدات الفرعية#