تاريخ الكتابة على مثال لمشروع كبير واحد

مرحبا بالجميع! اليوم سأحكي لك قصة تطور الكتابة على مثال أحد المشاريع في Ostrovok.ru .



بدأت هذه القصة قبل وقت طويل من الضجيج المكتوب حول python3.5 ، علاوة على ذلك ، بدأت داخل مشروع مكتوب بـ python2.7 .

2013 : تم إصدار python3.3 مؤخرًا ، ولم تكن هناك فائدة من الترحيل إلى الإصدار الجديد ، لأنه لم يضف أي ميزات محددة ، وسيكون هناك الكثير من الألم والمعاناة أثناء الانتقال.

لقد شاركت في مشروع الشركاء على Ostrovok.ru - كانت هذه الخدمة مسؤولة عن كل ما يتعلق بتكامل الشركاء والحجوزات والإحصاءات وحسابي الشخصي. استخدمنا كلاً من واجهات برمجة التطبيقات (APIs) الداخلية للخدمات الأخرى للشركة ، وواجهة برمجة التطبيقات الخارجية لشركائنا.

في مرحلة ما ، شكل الفريق النهج التالي لكتابة معالجات HTTP أو نوع من منطق الأعمال:

1) يجب وصف بيانات المدخلات والمخرجات بواسطة هيكل (فئة) ،
2) يجب التحقق من صحة محتويات مثيلات وفقا للوصف ،
3) الوظيفة التي تأخذ بنية عند المدخلات وتعطي البنية في المخرجات يجب أن تتحقق من أنواع البيانات عند المدخلات والمخرجات ، على التوالي.

لن أتناول كل نقطة بالتفصيل ، يجب أن يكون المثال أدناه كافياً لفهم ما هو على المحك.

مثال
.
import datetime as dt from contracts import new_contract, contract from schematics.models import Model from schematics.types import IntType, DateType # in class OrderInfoData(Model): order_id = IntType(required=True) # out class OrderInfoResult(Model): order_id = IntType(required=True) checkin_at = DateType(required=True) checkout_at = DateType(required=True) cancelled_at = DateType(required=False) @new_contract def pyOrderInfoData(x): return isinstance(x, OrderInfoData) @new_contract def pyOrderInfoResult(x): return isinstance(x, OrderInfoResult) @contract def get_order_info(data_in): """ :type data_in: pyOrderInfoData :rtype: pyOrderInfoResult """ return OrderInfoResult( dict( order_id=data_in.order_id, checkin_at=dt.datetime.today(), checkout_at=dt.datetime.today() + dt.timedelta(days=1), cancelled_at=None, ) ) if __name__ == '__main__': data_in = OrderInfoData(dict(order_id=777)) data_out = get_order_info(data_in) print(data_out.to_native()) 


يستخدم المثال المكتبات: الخطط و pycontracts .

* الخطط - طريقة لوصف والتحقق من صحة البيانات.
* pycontracts - طريقة للتحقق من إدخال / إخراج وظيفة في وقت التشغيل.

يسمح لك هذا النهج بـ:

  • من الأسهل كتابة الاختبارات - لا تنشأ مشاكل التحقق من الصحة ، ويتم تغطية منطق العمل فقط.
  • لضمان تنسيق وجودة الاستجابة في واجهة برمجة التطبيقات - يظهر إطار صارم لما نحن على استعداد لقبوله وما يمكننا تقديمه.
  • من الأسهل فهم / إعادة تشكيل تنسيق الاستجابة إذا كان بنية معقدة ذات مستويات تداخل مختلفة.

من المهم أن نفهم أن التحقق من النوع (عدم التحقق من الصحة) يعمل فقط في وقت التشغيل ، وهذا مناسب للتطوير المحلي ، وإجراء الاختبارات في CI والتحقق من أن إصدار المرشح يعمل في بيئة مرحلية . في بيئة الإنتاج ، يجب تعطيل هذا ، وإلا فإن الخادم سيتباطأ.

مرت السنوات ، نما مشروعنا ، وظهر منطق عمل جديد ومعقد أكثر ، ولم ينخفض ​​عدد مؤشرات API على الأقل.

في مرحلة ما ، بدأت ألاحظ أن إطلاق المشروع استغرق بالفعل بضع ثوان ملحوظة - كان هذا مزعجًا ، لأنه في كل مرة أقوم فيها بتحرير الكود وأجري الاختبارات اضطررت للجلوس والانتظار لفترة طويلة. عندما بدأ هذا الانتظار يستغرق من 8 إلى 10 ثوانٍ ، قررنا أخيرًا معرفة ما يجري تحت الغطاء.

في الواقع ، تحول كل شيء إلى أنه بسيط للغاية. عند بدء مشروع ما ، تقوم مكتبة pycontracts بتوزيع كل docstring التي يتم تغطيتها بواسطة contract من أجل تسجيل جميع الهياكل في الذاكرة ومن ثم التحقق منها بشكل صحيح. عندما يصل عدد الهياكل في المشروع إلى الآلاف ، يبدأ هذا الأمر برمته في التباطؤ.

ماذا تفعل حيال ذلك؟ الإجابة الصحيحة هي البحث عن حلول أخرى ، لحسن الحظ في الفناء هو بالفعل 2018 ( python3.5 - python3.6 ) ، وقد انتقلنا بالفعل إلى python3.6 .

بدأت دراسة الحلول البديلة والتفكير في كيفية ترحيل مشروع من " pycontract + type type in docstring " إلى "شيء + type type في كتابة تعليق توضيحي ". اتضح أنه إذا قمت بترقية عمليات pycontracts إلى أحدث إصدار ، فيمكنك وصف الأنواع في كتابة نمط التعليق التوضيحي ، على سبيل المثال ، قد يبدو كما يلي:

 @contract def get_order_info(data_in: OrderInfoData) -> OrderInfoResult: return OrderInfoResult( dict( order_id=data_in.order_id, checkin_at=dt.datetime.today(), checkout_at=dt.datetime.today() + dt.timedelta(days=1), cancelled_at=None, ) ) 

تبدأ المشاكل إذا كنت بحاجة إلى استخدام الهياكل من الكتابة ، على سبيل المثال اختياري أو الاتحاد ، حيث أن pycontracts لا تعرف كيفية التعامل معها:

 from typing import Optional @contract def get_order_info(data_in: OrderInfoData) -> Optional[OrderInfoResult]: return OrderInfoResult( dict( order_id=data_in.order_id, checkin_at=dt.datetime.today(), checkout_at=dt.datetime.today() + dt.timedelta(days=1), cancelled_at=None, ) ) 

بدأت أبحث عن مكتبات بديلة للتحقق من النوع في وقت التشغيل :

* فرض
* typeguard
* أنواع

فرض في ذلك الوقت لم يدعم python3.7 ، لكننا قد قمنا بالتحديث بالفعل ، فإن أنواع pytypes لم تعجب بناء الجملة ، ونتيجة لذلك ، وقع الاختيار على typeguard .

 from typeguard import typechecked @typechecked def get_order_info(data_in: OrderInfoData) -> Optional[OrderInfoResult]: return OrderInfoResult( dict( order_id=data_in.order_id, checkin_at=dt.datetime.today(), checkout_at=dt.datetime.today() + dt.timedelta(days=1), cancelled_at=None, ) ) 

فيما يلي أمثلة من مشروع حقيقي:

 @typechecked def view( request: HttpRequest, data_in: AffDeeplinkSerpIn, profile: Profile, contract: Contract, ) -> AffDeeplinkSerpOut: ... @typechecked def create_contract( user: Union[User, AnonymousUser], user_uid: Optional[str], params: RegistrationCreateSchemaIn, account_manager: Manager, support_manager: Manager, sales_manager: Optional[Manager], legal_entity: LegalEntity, partner: Partner, ) -> tuple: ... @typechecked def get_metaorder_ids_from_ordergroup_orders( orders: Tuple[OrderGroupOrdersIn, ...], contract: Contract ) -> list: ... 

نتيجةً لذلك ، وبعد عملية إعادة بناء طويلة ، تمكنا من نقل المشروع تمامًا إلى كتابة تعليقات توضيحية + كتابة .

ما هي النتائج التي حققناها:

  • يبدأ المشروع في 2-3 ثواني ، وهو على الأقل ليس مزعجًا.
  • تحسنت قراءة التعليمات البرمجية.
  • أصبح المشروع أصغر في عدد الخطوط وفي الملفات ، حيث لم يعد هناك تسجيلات هيكلية عبر new_contract .
  • أصبحت بيئات IDE الذكية PyCharm أفضل في فهرسة المشروع وصياغة تلميحات مختلفة ، لأنه الآن ليس تعليقات ، بل واردات صادقة.
  • يمكنك استخدام أجهزة التحليل الثابتة مثل mypy و pyre -check ، لأنها تدعم العمل مع كتابة التعليقات التوضيحية .
  • يتجه مجتمع بيثون ككل نحو الكتابة بشكل أو بآخر ، أي أن الإجراءات الحالية هي استثمارات في مستقبل المشروع.
  • في بعض الأحيان توجد مشاكل في الواردات الدورية ، ولكن هناك القليل منها ، ويمكن إهمالها.

آمل أن تكون هذه المقالة مفيدة لك!

المراجع:
* فرض
* typeguard
* أنواع
* pycontracts
* الخطط

Source: https://habr.com/ru/post/ar443470/


All Articles