پٿون ڪوڊ جون 4 ملين لائينون ٽائپ چيڪ ڪرڻ جو رستو. حصو 2

اڄ اسان مواد جي ترجمي جو ٻيو حصو شايع ڪري رهيا آهيون ته ڪيئن ڊراپ باڪس پٿون ڪوڊ جي ڪيترن ئي ملين لائنن لاءِ ٽائپ ڪنٽرول منظم ڪيو.

پٿون ڪوڊ جون 4 ملين لائينون ٽائپ چيڪ ڪرڻ جو رستو. حصو 2

پڙهو حصو پهريون

سرڪاري قسم جي سپورٽ (PEP 484)

اسان ھيڪ ھفتو 2014 دوران ڊراپ باڪس تي mypy سان پنھنجو پھريون سنجيده تجربو ڪيو. ھيڪ ھفتو ھڪڙو ھفتي وارو واقعو آھي جنھن جي ميزباني Dropbox پاران ڪئي وئي آھي. هن عرصي دوران، ملازم جيڪي چاهين تي ڪم ڪري سگهن ٿا! ڊروپباڪس جا سڀ کان وڌيڪ مشهور ٽيڪنالاجي منصوبن مان ڪجهه اهڙن واقعن تي شروع ٿيا. هن تجربي جي نتيجي ۾، اسان اهو نتيجو ڪيو ته mypy واعدو نظر اچي ٿو، جيتوڻيڪ اهو منصوبو اڃا تائين وسيع استعمال لاء تيار ناهي.

ان وقت، پٿون قسم جي اشارن واري نظام کي معياري بڻائڻ جو خيال هوا ۾ هو. جيئن مون چيو ته، Python 3.0 کان وٺي اهو ممڪن هو ته فعلن لاءِ قسم جي تشريحن کي استعمال ڪيو وڃي، پر اهي صرف پنهنجي مرضي وارا اظهار هئا، بنا ڪنهن وضاحت جي نحو ۽ اصطلاح جي. پروگرام جي عمل جي دوران، اهي تشريحون، اڪثر حصو لاء، صرف نظر انداز ڪيا ويا. هيڪ هفتو کان پوء، اسان ڪم ڪرڻ شروع ڪيو معيار کي معياري ڪرڻ تي. اهو ڪم ظهور جو سبب بڻيو پي اي پي 484 (Guido van Rossum، Łukasz Langa ۽ مون هن دستاويز تي تعاون ڪيو).

اسان جا مقصد ٻن پاسن کان ڏسي سگهجن ٿا. پهرين، اسان اميد ڪئي ته پوري پٿون ماحولياتي نظام ٽائپ اشارن کي استعمال ڪرڻ لاءِ هڪ عام طريقو اختيار ڪري سگهي ٿي (پٿون ۾ استعمال ٿيل اصطلاح ”قسم جي تشريح“ جي برابر آهي). اهو، ممڪن خطرن کي ڏنو ويو، ڪيترن ئي باہمي غير مطابقت واري طريقن کي استعمال ڪرڻ کان بهتر هوندو. ٻيو، اسان پٿون ڪميونٽي جي ڪيترن ئي ميمبرن سان قسم جي تشريح واري ميڪانيزم تي کليل بحث ڪرڻ چاهيون ٿا. اها خواهش جزوي طور تي هن حقيقت جي ذريعي طئي ڪئي وئي هئي ته اسان پٿون پروگرامرز جي وسيع عوام جي نظر ۾ ٻوليء جي بنيادي خيالن کان "مرتد" وانگر ڏسڻ نه چاهيندا. اها هڪ متحرڪ ٽائيپ ٿيل ٻولي آهي، جنهن کي ”ڊڪ ٽائپنگ“ جي نالي سان سڃاتو وڃي ٿو. ڪميونٽي ۾، شروعات ۾، جامد ٽائپنگ جي خيال جي باري ۾ ڪجهه مشڪوڪ رويو مدد نه ڪري سگهيو پر پيدا ٿيو. پر اهو جذبو آخرڪار ختم ٿي ويو جڏهن اهو واضح ٿي ويو ته جامد ٽائپنگ لازمي ٿيڻ وارو نه هو (۽ بعد ۾ ماڻهن کي اهو احساس ٿيو ته اهو اصل ۾ مفيد هو).

قسم جو اشارو نحو جيڪو آخرڪار اختيار ڪيو ويو ان سان تمام گهڻو ملندڙ جلندڙ هو جنهن وقت mypy سپورٽ ڪئي هئي. PEP 484 Python 3.5 سان 2015 ۾ جاري ڪيو ويو. پٿون هاڻي متحرڪ طور تي ٽائپ ڪيل ٻولي نه هئي. مان هن واقعي کي پٿون جي تاريخ ۾ هڪ اهم سنگ ميل جي طور تي سوچڻ چاهيان ٿو.

لڏپلاڻ جي شروعات

2015 جي آخر ۾، ڊروپباڪس ٽن ماڻهن جي هڪ ٽيم ٺاهي وئي جيڪا mypy تي ڪم ڪري ٿي. انهن ۾ گيدو وين راسم، گريگ پرائس ۽ ڊيوڊ فشر شامل هئا. ان وقت کان وٺي، صورتحال تمام تيزيء سان ترقي ڪرڻ لڳو. ميپي جي ترقي ۾ پهرين رڪاوٽ ڪارڪردگي هئي. جيئن مون مٿي اشارو ڪيو، پراجيڪٽ جي شروعاتي ڏينهن ۾ مون سوچيو هو ته Mypy عمل کي سي ۾ ترجمو ڪرڻ، پر اهو خيال هاڻي لاءِ فهرست مان ڪڍيو ويو. اسان CPython مترجم استعمال ڪندي سسٽم کي هلائڻ سان پڪڙي رهيا هئاسين، جيڪو mypy وانگر اوزارن لاءِ ڪافي تيز ناهي. (PyPy پروجيڪٽ، هڪ متبادل پٿون عمل جي JIT ڪمپيلر سان، اسان جي مدد نه ڪئي.)

خوشقسمتيءَ سان، ڪي الورورٿمڪ سڌارا آيا آهن اسان جي مدد لاءِ هتي. پهريون طاقتور "تيز رفتار" اضافو چيڪنگ تي عمل درآمد هو. هن سڌاري جي پويان خيال بلڪل سادو هو: جيڪڏهن ماڊيول جي سڀني انحصار کي mypy جي پوئين رن کان تبديل نه ڪيو ويو آهي، ته پوء اسان انحصار سان ڪم ڪرڻ دوران پوئين رن دوران ڪيش ڪيل ڊيٽا استعمال ڪري سگهون ٿا. اسان کي صرف تبديل ٿيل فائلن تي قسم جي چڪاس ڪرڻ جي ضرورت آهي ۽ انهن فائلن تي جيڪي انهن تي منحصر آهن. Mypy اڃا ٿورو اڳتي وڌيو: جيڪڏهن هڪ ماڊل جو خارجي انٽرفيس تبديل نه ٿيو، mypy فرض ڪيو ته ٻيا ماڊل جيڪي هن ماڊل کي درآمد ڪيا آهن انهن کي ٻيهر چيڪ ڪرڻ جي ضرورت ناهي.

موجوده ڪوڊ جي وڏي مقدار جي تشريح ڪرڻ وقت وڌندڙ چيڪنگ اسان کي تمام گهڻي مدد ڪئي آهي. نقطو اهو آهي ته اهو عمل عام طور تي mypy جي ڪيترن ئي ورهاڱي واري رنن کي شامل ڪندو آهي جيئن تشريحون تدريجي طور تي ڪوڊ ۾ شامل ڪيون وينديون آهن ۽ تدريجي طور تي بهتر ڪيون وينديون آهن. mypy جو پهريون رن اڃا به تمام سست هو ڇاڪاڻ ته ان کي چڪاس ڪرڻ لاءِ تمام گهڻو انحصار هو. پوء، صورتحال کي بهتر ڪرڻ لاء، اسان هڪ ريموٽ ڪيشنگ ميڪانيزم تي عمل ڪيو. جيڪڏهن mypy اهو معلوم ڪري ٿو ته مقامي ڪيش جي تاريخ کان ٻاهر ٿيڻ جو امڪان آهي، اهو موجوده ڪيش سنيپ شاٽ ڊائون لوڊ ڪري ٿو پوري ڪوڊ بيس لاءِ مرڪزي ذخيري مان. ان کان پوء هن سنيپ شاٽ استعمال ڪندي هڪ واڌارو چيڪ انجام ڏئي ٿو. اهو اسان کي ورتو آهي هڪ وڌيڪ وڏو قدم mypy جي ڪارڪردگي کي وڌائڻ لاءِ.

اهو دور هو ڊروپباڪس تي قسم جي چڪاس جي تيز ۽ قدرتي اپنائڻ جو. 2016 جي آخر تائين، اسان وٽ اڳ ۾ ئي تقريبن 420000 لائينون پيٿون ڪوڊ جون تشريحن سان گڏ آھن. ڪيترائي صارف قسم جي چڪاس بابت پرجوش هئا. وڌيڪ ۽ وڌيڪ ڊولپمينٽ ٽيمون استعمال ڪري رهيا هئا Dropbox mypy.

تڏهن ته سڀ ڪجهه سٺو لڳندو هو، پر اسان کي اڃا گهڻو ڪجهه ڪرڻو هو. اسان وقتي طور تي اندروني صارف سروي ڪرڻ شروع ڪيو ته جيئن منصوبي جي مسئلن جي علائقن کي سڃاڻڻ ۽ سمجھڻ جي ضرورت آهي ته ڪھڙي مسئلن کي حل ڪرڻ جي ضرورت آھي (اھو عمل اڃا تائين ڪمپني ۾ استعمال ڪيو ويندو آھي). سڀ کان اهم، جيئن اهو واضح ٿيو، ٻه ڪم هئا. پهرين، اسان کي ڪوڊ جي وڌيڪ قسم جي ڪوريج جي ضرورت آهي، ٻيو، اسان کي تيز ڪم ڪرڻ لاء mypy جي ضرورت آهي. اهو بلڪل واضح هو ته اسان جو ڪم تيزيءَ سان مائيپي کي تيز ڪرڻ ۽ ان کي ڪمپني جي منصوبن ۾ لاڳو ڪرڻ اڃا مڪمل ٿيڻ کان پري هو. اسان، انهن ٻن ڪمن جي اهميت کان پوريءَ طرح واقف آهيون، انهن کي حل ڪرڻ لاءِ تيار آهيون.

وڌيڪ پيداوار!

وڌندڙ چيڪن mypy کي تيز ڪيو، پر اوزار اڃا تائين ڪافي تيز نه هو. ڪيتريون ئي واڌارو چيڪون لڳ ڀڳ هڪ منٽ هليون. ان جو سبب سائيڪل جي واردات هئي. اهو شايد هر ڪنهن کي حيران نه ڪندو جنهن پٿون ۾ لکيل وڏي ڪوڊ بيس سان ڪم ڪيو آهي. اسان وٽ سوين ماڊلز جا سيٽ هئا، جن مان هر هڪ اڻ سڌي طرح ٻين سڀني کي درآمد ڪيو. جيڪڏهن امپورٽ لوپ ۾ ڪا به فائل تبديل ڪئي وئي هئي، mypy کي ان لوپ ۾ موجود سڀني فائلن کي پروسيس ڪرڻو پوندو هو، ۽ اڪثر ماڊلز جيڪي ان لوپ مان ماڊلز کي درآمد ڪندا هئا. هڪ اهڙو چڪر بدنام "انحصار ٽنگ" هو جيڪو ڊروپباڪس تي تمام گهڻي مصيبت جو سبب بڻيو. هڪ دفعو هن ڍانچي ۾ ڪيترائي سؤ ماڊل شامل هئا، جڏهن ته ان کي درآمد ڪيو ويو، سڌي يا اڻ سڌي طرح، ڪيترائي ٽيسٽ، اهو پڻ پيداوار ڪوڊ ۾ استعمال ڪيو ويو.

اسان غور ڪيو ته "اڻ ڳنڍڻ" سرڪلر انحصار جي امڪان، پر اسان وٽ ان کي ڪرڻ لاء وسيلا نه هئا. اتي تمام گهڻو ڪوڊ هو جنهن کان اسان واقف نه هئاسين. نتيجي طور، اسان هڪ متبادل طريقي سان آيا آهيون. اسان فيصلو ڪيو ته ”انحصار جي ٽنگن“ جي موجودگيءَ ۾ به جلدي mypy ڪم ڪرڻ جو. اسان هي مقصد حاصل ڪيو mypy ڊيمون استعمال ڪندي. ڊيمون ھڪڙو سرور عمل آھي جيڪو ٻن دلچسپ صلاحيتن کي لاڳو ڪري ٿو. پهرين، اهو ميموري ۾ پوري ڪوڊ بيس بابت معلومات محفوظ ڪري ٿو. هن جو مطلب اهو آهي ته هر وقت توهان mypy هلائيندا آهيو، توهان کي هزارين درآمد ٿيل انحصار سان لاڳاپيل ڪيش ٿيل ڊيٽا لوڊ ڪرڻ جي ضرورت ناهي. ٻيو، هو احتياط سان، ننڍن ساختي يونٽن جي سطح تي، ڪمن ۽ ٻين ادارن جي وچ ۾ انحصار جو تجزيو ڪري ٿو. مثال طور، جيڪڏهن فنڪشن foo هڪ فنڪشن سڏي ٿو bar، پوء اتي هڪ انحصار آهي foo от bar. جڏهن هڪ فائل تبديل ٿئي ٿي، ڊيمون پهريون، اڪيلائي ۾، صرف تبديل ٿيل فائل کي پروسيس ڪري ٿو. ان کان پوء ان فائل ۾ خارجي طور تي ظاهر ٿيندڙ تبديلين کي ڏسڻ ۾ اچي ٿو، جهڙوڪ تبديل ٿيل فنڪشن دستخط. ڊيمون درآمدات بابت تفصيلي معلومات استعمال ڪري ٿو صرف انهن ڪمن کي ٻه ڀيرا چيڪ ڪرڻ لاءِ جيڪي اصل ۾ تبديل ٿيل فنڪشن استعمال ڪن ٿا. عام طور تي، هن طريقي سان، توهان کي تمام ٿورن ڪمن جي جانچ ڪرڻو پوندو.

انهن سڀني تي عمل ڪرڻ آسان نه هو، ڇاڪاڻ ته اصل mypy عمل درآمد هڪ وقت ۾ هڪ فائل جي پروسيسنگ تي تمام گهڻو ڌيان ڏنو ويو. اسان کي ڪيترن ئي سرحدن جي حالتن سان معاملو ڪرڻو پيو، جن جي واقعن جي صورت ۾ بار بار چيڪ ڪرڻ جي ضرورت آهي جتي ڪوڊ ۾ ڪجهه تبديل ٿي وئي. مثال طور، اهو ٿئي ٿو جڏهن هڪ طبقي کي نئين بيس ڪلاس لڳايو ويو آهي. هڪ دفعو اسان اهو ڪيو جيڪو اسان چاهيون ٿا، اسان صرف چند سيڪنڊن تائين تمام گهڻي وڌندڙ چيڪن جي عمل جي وقت کي گهٽائڻ جي قابل هئا. اها اسان کي وڏي فتح لڳي.

اڃا به وڌيڪ پيداوار!

ريموٽ ڪيشنگ سان گڏ جنهن تي مون مٿي ذڪر ڪيو آهي، mypy ڊيمون تقريبن مڪمل طور تي انهن مسئلن کي حل ڪري ٿو جيڪي پيدا ٿين ٿا جڏهن هڪ پروگرامر اڪثر ڪري ٽائپ چيڪنگ کي هلائي ٿو، فائلن جي ننڍڙي تعداد ۾ تبديليون ڪندي. بهرحال، گهٽ ۾ گهٽ سازگار استعمال جي صورت ۾ سسٽم جي ڪارڪردگي اڃا تائين بهتر کان پري هئي. mypy جي صاف شروعات 15 منٽن کان مٿي وٺي سگھي ٿي. ۽ اهو گهڻو وڌيڪ هو جنهن سان اسان خوش ٿين ها. هر هفتي صورتحال خراب ٿي وئي جيئن پروگرامر نوان ڪوڊ لکڻ ۽ موجوده ڪوڊ ۾ تشريح شامل ڪرڻ جاري رکي. اسان جا صارف اڃا به وڌيڪ ڪارڪردگي لاء بکيا هئا، پر اسان انهن کي اڌ ۾ ملڻ لاء خوش ٿيا.

اسان Mypy جي حوالي سان اڳوڻي خيالن مان هڪ ڏانهن موٽڻ جو فيصلو ڪيو. يعني، پٿون ڪوڊ کي سي ڪوڊ ۾ تبديل ڪرڻ لاءِ. Cython (هڪ سسٽم جيڪو توهان کي Python ۾ لکيل ڪوڊ کي C ڪوڊ ۾ ترجمو ڪرڻ جي اجازت ڏئي ٿو) سان تجربو ڪرڻ اسان کي ڪا به ڏسڻ واري رفتار نه ڏني، ان ڪري اسان فيصلو ڪيو ته اسان جي پنهنجي ڪمپلر کي لکڻ جي خيال کي بحال ڪرڻ. جيئن ته mypy ڪوڊ بيس (Python ۾ لکيل) اڳ ۾ ئي سڀني ضروري قسم جي تشريح تي مشتمل آهي، اسان سوچيو ته سسٽم کي تيز ڪرڻ لاء انهن تشريحن کي استعمال ڪرڻ جي ڪوشش ڪرڻ مناسب ٿيندو. مون هن خيال کي جانچڻ لاءِ جلدي هڪ پروٽوٽائپ ٺاهيو. اهو مختلف مائڪرو بينچ مارڪ تي ڪارڪردگي ۾ 10 گنا کان وڌيڪ اضافو ڏيکاري ٿو. اسان جو خيال اهو هو ته پائٿون ماڊلز کي سي ماڊيولز ۾ گڏ ڪيو وڃي Cython استعمال ڪندي، ۽ ٽائيپ اينوٽيشنز کي رن ٽائم ٽائيپ چيڪن ۾ تبديل ڪيو وڃي (عام طور تي ٽائپ اينوٽيشنز کي رن ٽائيم تي نظر انداز ڪيو ويندو آهي ۽ صرف ٽائپ چيڪنگ سسٽم ذريعي استعمال ڪيو ويندو آهي). اسان اصل ۾ منصوبابندي ڪئي هئي ته Mypy پليپشن کي Python مان هڪ ٻولي ۾ ترجمو ڪرڻ لاءِ جيڪا ٺهيل هئي مستحڪم طور تي ٽائپ ڪرڻ لاءِ، اها نظر ايندي (۽، گهڻو ڪري، ڪم لاءِ) بلڪل Python وانگر. (هن قسم جي پار-ٻوليءَ جي لڏپلاڻ mypy منصوبي جي هڪ روايت بڻجي وئي آهي. اصل mypy نفاذ الور ۾ لکيو ويو هو، پوءِ جاوا ۽ پٿون جو هڪ نحوي هائبرڊ هو).

CPython ايڪسٽينشن API تي ڌيان ڏيڻ اهم هو پراجيڪٽ جي انتظام جي صلاحيتن کي نه وڃائڻ لاءِ. اسان کي هڪ مجازي مشين يا ڪنهن به لائبريري کي لاڳو ڪرڻ جي ضرورت نه هئي جيڪا mypy جي ضرورت هئي. ان کان علاوه، اسان کي اڃا تائين پوري پٿون ايڪو سسٽم ۽ سڀني اوزارن تائين رسائي هوندي (جهڙوڪ pytest). ان جو مطلب اهو ٿيو ته اسان ترقي جي دوران تعبير ٿيل پٿون ڪوڊ استعمال ڪرڻ جاري رکي سگهون ٿا، اسان کي ڪوڊ تبديل ڪرڻ ۽ ان جي جانچ ڪرڻ جي تمام تيز نموني سان ڪم جاري رکڻ جي اجازت ڏئي ٿي، بجاء ڪوڊ مرتب ڪرڻ جي انتظار ۾. اهو ڏسڻ ۾ اچي ٿو ته اسان ٻن ڪرسي تي ويهڻ جو هڪ بهترين ڪم ڪري رهيا آهيون، تنهنڪري ڳالهائڻ، ۽ اسان ان کي پسند ڪيو.

گڏ ڪرڻ وارو، جنهن کي اسان mypyc سڏين ٿا (جيئن ته اهو mypy استعمال ڪري ٿو فرنٽ-اينڊ طور قسمن جي تجزيو لاءِ)، هڪ تمام ڪامياب منصوبو ثابت ٿيو. مجموعي طور تي، اسان ڪيچنگ کان سواءِ بار بار mypy رن لاءِ تقريبن 4x اسپيڊ اپ حاصل ڪيو. Mypyc پروجيڪٽ جي بنيادي کي ترقي ڪرڻ ۾ مائيڪل سليوان، ايوان ليوڪيوسڪي، هيگ هان ۽ پاڻ جي هڪ ننڍڙي ٽيم لڳ ڀڳ 4 ڪئلينڊر مهينن ۾ ورتي. ڪم جي اها رقم ان کان تمام ننڍي هئي جيڪا mypy کي ٻيهر لکڻ لاءِ گهربل هجي ها، مثال طور، C++ يا Go ۾. ۽ اسان کي پروجيڪٽ ۾ تمام گھٽ تبديليون ڪرڻيون ھيون ان کان وڌيڪ جيڪي اسان کي ڪرڻيون پونديون ھيون جڏھن ان کي ٻي ٻوليءَ ۾ وري لکجي. اسان کي اها به اميد هئي ته اسان mypyc کي اهڙي سطح تي آڻي سگهون ٿا ته ٻيا ڊراپ باڪس پروگرامر ان کي استعمال ڪري سگهن ٿا انهن جي ڪوڊ کي گڏ ڪرڻ ۽ تيز ڪرڻ لاءِ.

ڪارڪردگي جي هن سطح کي حاصل ڪرڻ لاء، اسان کي ڪجهه دلچسپ انجنيئرنگ حل لاڳو ڪرڻو پوندو. اهڙيءَ طرح، ڪمپلر تيز، گهٽ-سطح جي سي تعميرات کي استعمال ڪندي ڪيترن ئي عملن کي تيز ڪري سگهي ٿو. مثال طور، هڪ مرتب ڪيل فنڪشن ڪال کي C فنڪشن ڪال ۾ ترجمو ڪيو ويندو آهي. ۽ اهڙي ڪال هڪ تشريح ڪيل فنڪشن کي سڏڻ کان گهڻو تيز آهي. ڪجھ آپريشنز، جھڙوڪ ڊڪشنري لوڪ اپ، اڃا تائين CPython مان باقاعده C-API ڪالون استعمال ڪرڻ ۾ شامل آھن، جيڪي مرتب ٿيڻ وقت صرف معمولي تيز ھيون. اسان تفسير جي ٺاهيل سسٽم تي اضافي لوڊ ختم ڪرڻ جي قابل هئا، پر هن معاملي ۾ ڪارڪردگي جي لحاظ کان صرف هڪ ننڍڙو فائدو ڏنو.

سڀ کان وڌيڪ عام "سست" عملن کي سڃاڻڻ لاء، اسان ڪوڊ پروفائلنگ ڪيو. هن ڊيٽا سان هٿياربند، اسان يا ته mypyc کي ٽوڪ ڪرڻ جي ڪوشش ڪئي ته جيئن اهو اهڙين عملن لاءِ تيزيءَ سان سي ڪوڊ ٺاهي، يا لاڳاپيل پٿون ڪوڊ کي تيزيءَ سان استعمال ڪندي ٻيهر لکجي (۽ ڪڏهن ڪڏهن اسان وٽ ان يا ٻئي مسئلي لاءِ ڪو سادو ڪافي حل نه هوندو هو) . پٿون ڪوڊ کي ٻيهر لکڻ اڪثر ڪري مسئلي جو آسان حل هوندو هو انهي کان سواءِ ته مرتب ڪندڙ پاڻمرادو ساڳيو تبديلي انجام ڏئي. ڊگهي مدت ۾، اسان انهن مان ڪيترن ئي تبديلين کي خودڪار ڪرڻ چاهيندا هئاسين، پر هن وقت اسان کي گهٽ ۾ گهٽ ڪوشش سان mypy کي تيز ڪرڻ تي ڌيان ڏنو ويو. ۽ هن مقصد ڏانهن وڌڻ ۾، اسان ڪيترن ئي ڪنڊن کي ڪٽي ڇڏيو.

جاري رکڻ گهرجي…

پيارا پڙهندڙن! Mypy پروجيڪٽ جا توهان جا تاثر ڇا هئا جڏهن توهان ان جي وجود جي باري ۾ سکيو؟

پٿون ڪوڊ جون 4 ملين لائينون ٽائپ چيڪ ڪرڻ جو رستو. حصو 2
پٿون ڪوڊ جون 4 ملين لائينون ٽائپ چيڪ ڪرڻ جو رستو. حصو 2

جو ذريعو: www.habr.com

تبصرو شامل ڪريو