Python BackendHub – Telegram
Python BackendHub
7.51K subscribers
314 photos
46 videos
11 files
432 links
Learning python & Backend Engineering, with Mani!

Youtube: https://www.youtube.com/@GitOverHere
Github: https://github.com/ManiMozaffar
Linkedin: https://www.linkedin.com/in/manimozaffar

تبلیغات نداریم

Admin: @Mani_nikou
Download Telegram
همیشه قرار نیست چون یک چیزی یک فیچری داره پس حتما ازش استفاده کنید. باید دلیل منطقی باشه پشت استفاده از یک فیچر. و البته آگاه باشین از روش کارکردش.

این اشتباهو من اخیرا انجام دادم. تو دیتابیس postgresql چون یک لاجیک transactional داشتم بین دو تا دیتابیس , از two phase commit استفاده کردم. اینطوریه که شما یک بار کامیت میکنی و دیتابیس بهت میگه که کامیتت انجام میشه یا نه. و بعد باره دوم واقعا کامیت میکنید. (اینکه چرا لاجیک transactional داشتم بین دو تا دیتابیس خارج از کنترله من بوده)

حالا این مکانیزم تو لایه زیر چطوری کار میکنه؟
داره از prepare transaction استفاده میکنه برای اینکار. درواقع یک کامیت الکی هم مثل کامیت واقعی باید تو WAL نوشته شه. تو postgresql تو قسمت لاگ فایلاش یک فایلی هست به اسم pg_twophase که این رکورد هارو نگه میداره. اگه این وسط کرش شه دیتابیس, این لاگ ها باقی میمونه و وقتی که دوباره استارت شه prepare transaction ها restore میشن.
فایل ها دیلیت میشن اگه اون transaction که prepared بود رول بک شه یا کامیت شه.

حالا مشکل کجاست؟
اگه دیتابیس crash شه prepared transaction ها باقی میمونن. مثلا یک prepared transaction میتونه یک row ای رو lock کرده باشه. و این میتونه باعث deadlock میشه. یا میتونه تداخل ایجاد کنه برای VACUUM پی جی.

چرا؟ مگه transaction ها rollback نمیشن اگه دیتابیس کرش شه؟
چرا میشن, ولی از قصد اینطوری دیزاین شده که prepared transaction ها نشن. چون اگه کامیت فاز اول خورده, رول بک کردنشون ممکنه باعث inconsistent شدن اون یکی دیتابیسی شه که داره two phase commit میزنه. برای همین از قصد footprint میذارن از خودشون که db admin ها بیان چک کنند و اگه inconsistency وجود داره درستش کنند.

وقتی که دیتابیس رو ریستارت میکنید و ریکاور میشه دیگه پروسسی وجود نداره برای اون prepared transaction ها. چون کانکشن قطع شده و پروسس بسته شده. ولی لاگش هنوز اونجاست. خلاصه من داشتم از این فیچر استفاده میکردم بدون اینکه بدونم همچین اتفاقی ممکنه بیفته. و یک جایی دیدم یک سری لاک دارم تو postgresql که اصلا process id ندارن ولی transactional id دارن 😅 که با خوندن داکیومنت postgresql متوجه این چیزا شدم. و در آخر تصمیم گرفتم از این فیچر استفاده نکنم.

@PyBackendHub
👍19🤯1
Python BackendHub
لایو یک شنبه هفته بعد، ۲۵ ام August به وقت ۸ شب تهران خواهد بود یک نکته راجب این لایوی که میخوام بذارم بگم: من قبلش اموزش fastapi و pydantic نمیدم. تو لایو هم دیگه توضیحات ابتدایی که تو دوره SQLAlchemy هست رو نمیدم. صرفا دارم با یک سری پرکتیس نوشتن api خوب،…
به دلیل سرما خوردگی لایو به یک شنبه یک هفته بعد موکول خواهد شد (۲۵ ام August).
متاسفانه نتونستم ویدیو آخر که راجب ماگریشن نویسی با alembic هست رو ظبط کنم. ایشالا اونم طی این آخر هفته انجام میدم وقتی بهتر شدم :)
35💊23👍3🤬3😡2🙏1🍌1🍓1
سورس کدی که خوب تست نداره هر PR ای که زده میشه بهش، مثل رولت روسی (Russian roulette) میمونه 😁

@PyBackendHub
😁10👍8🤣3
Forwarded from Sadra Codes
یه مدت سعی کردم هر ریپازیتوری که می‌سازم، تاجایی که ممکنه علاوه بر تست خوب، کاورج رو بالای ۹۵ نگه دارم. (اون ۵ درصدم چیزایی که بخاطر دیزاین بد نتونستم تست کنم و باید سریع ریفکتور شه)

اوایل ایگنور می‌کردم اون فایل‌ها یا خط‌ها رو تا Badge کاورج ۱۰۰ درصد خودنمایی کنه توی ریدمی ریپازیتوری، ولی خب چراا مرد؟ 😂

نمی‌دونید تست‌هایی که صد در صد کدبیس رو تست می‌کنن چقدر خوبن! فرض کن ۱۰۰۰ تا فیچر توی پروژه‌ات داری. یهو یه PR میاد. راحت با یه کامند می‌تونی مطمئن شی که این PR هیچ چیزی رو Break نمیکنه و (حداقل) از این بابت ایمنه. این دقیقا یه لول بعد از لاجیک برنامه هست. یعنی شما به توافق رسیدی که فلان PR باید زده شه، فلان بخش باید تغییر کنه. حالا این اتفاق افتاده و تست‌ها کارشون رو انجام می‌دن.

من اولش که TDD رو مطالعه می‌کردم اینجوری بودم که: هنن؟ چرا باید واسه هیچی (پروژه‌ای که خالیه) تست بنویسی؟ اصلا چی بنویسی؟ چیو می‌خوای تست کنی وقتی چیزی وجود نداره اصلا؟ ولی بعد که رفتم جلوتر، دیدم فرهنگ جالبی داره و خیلی UX رو می‌بره بالا. منظورم UXی هست که یه توسعه‌دهنده دیگه از ابزار شما به دست میاره. (فرضا ابزارتون یه کتابخونه هست)

راجع به TDD در Integration Testing و مشکلاتش، اینکه چرا زیاد جالب نیست هم بعدا یه پست می‌ذارم. :)) 🙌
👍14👎3🤣3
Sadra Codes
یه مدت سعی کردم هر ریپازیتوری که می‌سازم، تاجایی که ممکنه علاوه بر تست خوب، کاورج رو بالای ۹۵ نگه دارم. (اون ۵ درصدم چیزایی که بخاطر دیزاین بد نتونستم تست کنم و باید سریع ریفکتور شه) اوایل ایگنور می‌کردم اون فایل‌ها یا خط‌ها رو تا Badge کاورج ۱۰۰ درصد خودنمایی…
یک نکته به حرفای صدرا اضافه کنم
قانون goodhart میگه که اگه یک measure (معیار) تبدیل به تارگت بشه، دیگه معیار خوبی نیست.

برای همین من اصلا چیزایی مثل تست کاوریج و استوری پوینتو اینا رو قبول ندارم. چون اینا measure نیستن هیچوقت همیشه تارگت میشن.

همین اتفاقی که واسه صدرا افتاد، شما به جای اینکه دنبال این باشین که تست بنویسید که یوزکیس و ادجکیس هارو کاور کنید، تست مینویسید که صد در صد شه 😅. برای همین قبلا گفتم تست کاوریج یک دروغه. خوبه که داشته باشیم، بدونیم عه فلان فایلمون اصلا کاور نشده، ولی target نیست! همین موضوع راجب استوری پوینت، استوری پوینت تارگت نیست!

تارگت باید این باشه: تست هایی که business requirement رو تست میکنن، که گارانتی میدن نرم افزار اون requirement رو satisfy میکنه طبق اون شرایط

استوری پونینتم همینه ها هیچ فرقی نداره. اونایی که از هر اسپرینت میام جمع میزنن استوری پوینتو و هدفشون میشه استوری پوینت دقیقا تو همین دستن.

@PyBackendHub
👍13👎3👌1
آیوکلاک (AioClock) یک فریم ورک برای scheduling و یا تسک منیجمنت هست و هر چیزی که هر فریم ورکی نیاز داره رو داخلش داره, مثل دپندسی اینجکشن و startup/stop ایونت, ساپورت از ماژولار کد نوشتن و ...

امشب وقت گذاشتم و داکیونتشو خیلی بهتر کردم که کاملا متوجه شید فریم ورک چطوری کار میکنه. تو عکس واضح نیست کامل تصیه میکنم سری به داکیومنت بزنید.

داکیومنت
گیتهاب


@PyBackendHub
21👍2🏆2
This media is not supported in your browser
VIEW IN TELEGRAM
نکنید اینکارو با همکاراتون 😂😂😂
@PyBackendHub
😁12🤣10🍌2
Forwarded from Sadra Codes
🚩 پایتون ۳.۱۳؛ فیچرهای جدید و دپریکیشن‌ها!


🔥 گیل (GIL) آپشنال: امکان بیلد گرفتن از CPython و غیرفعال کردن GIL. (در حالت عادی شما از GIL استفاده می‌کنید)

🔥 کامپایلر JIT: قراره در این پچ جدید، از یک کامپایلر just in time رونمایی شه که در یک سری از سناریوهای خاص، سرعت اجرای کدتون رو افزایش میده. این رو موقع بیلد گرفتن دستی از CPython میشه تنظیم‌ کرد و بصورت پیشفرض غيرفعال هست.

🔥 تایپ هینت IsType و ReadOnly: دوتا تایپ جدید به typing اضافه شده. در مقاله مثال زدم.

🔥 ساپورت از سیستم‌عامل iOS: یک رلیز قابل نصب روی iOS قراره در این پچ قرار داده بشه. هنوز خبری از رلیز اندروید نیست ولی گویا دارن روش کار می‌کنن. (چیزی که بعنوان پایتون روی دیوایس‌های اندرویدتون نصب دارید، رلیز لینوکس پایتون هست.)

🔥 بهبود Interaction: ارورها و تریس‌بک‌ها دقیق‌تر و هوشمندتر شدن. همچنین ارورها بصورت رنگی نمایش داده میشن.

🔥 بهبود REPL: کامندهای exit، help و quit تغییر کردن.

و کلی فیچر و امکانات جدید که توی ۵ دقیقه در مقاله زیر توضیح دادم به همراه مثال‌های ساده و قابل فهم:

🔗 https://blog.imsadra.me/python-313-new-features-deprecations


For more 👉 @lnxpylnxpy
👍19🔥3
Sadra Codes
🚩 پایتون ۳.۱۳؛ فیچرهای جدید و دپریکیشن‌ها! 🔥 گیل (GIL) آپشنال: امکان بیلد گرفتن از CPython و غیرفعال کردن GIL. (در حالت عادی شما از GIL استفاده می‌کنید) 🔥 کامپایلر JIT: قراره در این پچ جدید، از یک کامپایلر just in time رونمایی شه که در یک سری از سناریوهای…
بحث خوبی شد تو گروه, یکی پرسید که JIT چیه و چی کار میکنه دقیقا. ‌سعی میکنم خیلی ساده توضیح بدم که قابل درک باشه برای همه.

وقتی یه اسکریپت پایتونی رو ران می‌کنی، یه سری فایل با پسوند .pyc تو پوشه‌ی pycache ساخته میشه. اینا بایت کد هستن. بایت کد چیه؟ یه low level representation از کدی که نوشتی. بایت کد platform independent هست یعنی مهم نیست رو چی داری رانش میکنی. اما این بایت کد برای CPU قابل فهم نیست. CPU فقط ماشین کد رو می‌فهمه، یعنی همون باینری صفر و یک. پس وقتی بایت کد تولید میشه، PVM (Python Virtual Machine) میاد و بایت کد رو به ماشین کد تفسیر می‌کنه تا CPU بتونه اجراش کنه.

حالا JIT چیه؟ تبدیل بایت کد به ماشین کد زمان و منابع مصرف می‌کنه. JIT توی ران‌تایم این تبدیل رو انجام میده و ماشین کد رو توی حافظه نگه می‌داره تا دفعات بعدی که همون کد اجرا میشه، دوباره نیاز به تبدیل نباشه. اینکار رو فقط برای بخش‌هایی از کد که زیاد اجرا میشن (بهشون میگن hot loop) انجام میده. چرا؟ چون خوده ذخیره کردن این دیتا پرهزینست و مموری اشغال میکنه پس کل کد رو نمیاد اینکارو کنه. یعنی JIT Engine نگاه می‌کنه ببینه این بخش از کد اونقدری داره اجرا میشه که ارزش داشته باشه ماشین کدش رو نگه داره یا نه.

چرا کد پایتون مستقیم کد ماشین نمیشه؟ دلیلش اینه که PVM و Python runtime environment داره تو پایتون کارای دیگه‌ای هم میکنه تو ران تایم مثل مدیریت حافظه، لود کردن ماژول‌ها و پکیج‌ها و... این باعث میشه که پایتون انعطاف‌پذیر و راحت باشه، ولی خب به قیمت افت عملکرد در مقایسه با زبان های کامپایلری مثل C.


@PyBackendHub
👍21🔥3👎2
Python BackendHub
چرا کد پایتون مستقیم کد ماشین نمیشه؟ دلیلش اینه که PVM و Python runtime environment داره تو پایتون کارای دیگه‌ای هم میکنه تو ران تایم مثل مدیریت حافظه، لود کردن ماژول‌ها و پکیج‌ها و... این باعث میشه که پایتون انعطاف‌پذیر و راحت باشه، ولی خب به قیمت افت عملکرد در مقایسه با زبان های کامپایلری مثل C.
۳ خط اخر رو یکم باز میکنم حس میکنم کافی توضیح ندادم (این پست دیگه خیلی ربطی به JIT نداره). راجب اینکه چرا پایتون نمیتونه به راحتی از قبل تبدیل به ماشین کد شه.

یک مثال خیلی ساده بزنم شما تو پایتون میتونید یک سورس پایتونی از اینترنت دانلود کنید, و بعد از تابع eval() استفاده کنید که درجا رانش کنه! چطور این کد میتونه تبدیل به کد ماشینی بشه با یک کامپایل مستقیما؟ موقع کامپایل هنوز مشخص نیست چی قراره دانلود شه و ران شه! این اولین خاصیت پایتونه تو انعطاف پذیریش.

دومین خاصیش داینامیک تایپ بودنشه. شما یک فانکشن دارین foo(a,b,c). اگه شما تایپ ورودی این فانکشن رو نمیدونید, نمیتونید کامپایلش کنید. ولی اگه بدونید ممکنه چه تایپ هایی باشن, میتونید برای هر combination یک نسخه متفاوت کمپایل کنید. برای همینه که زبونای statically typed راحت کمپایل میشن به کد ماشین.

این دو خصوصیت باعث میشه پایتون نتونه به راحتی مستقیم تبدیل به ماشین کد بشه. پروژه هایی هستش که اینکارو میکنن ولی با static analysis و تکنیک های دیگه با لیمیتشن های خیلی زیاد. کلا زبون های داینامیک تایپ خیلی سخت از قبل میتونن تبدیل به ماشین کد بشن‌(اگه نگم غیر ممکن) ولی زبونای استتیک تایپ خیلی راحت تر اینکارو انجام میدن.

@PyBackendHub
👍12🔥2
فرض کنید یک دیتا بسیار سنگین دارید که از دیتابیس به صورت سورت‌شده بر اساس تاریخ ایجاد کتاب‌ها میاد. حالا شما دو تابع دارین:

۱. تابع get_books که کتاب‌ها را از دیتابیس به صورت سورت‌شده بر اساس تاریخ ایجاد میگیره.
۲. تابع process_book که کتاب‌ها را پردازش میکنه، ولی به شرطی درست کار میکنه که ورودی تابع سورت شده باشه (مثلا حتما از تابع اول اومده باشه).

مسئله اینه که این دو تابع نمیتونن با هم ترکیب بشن، چون نمی‌خوایم منطق پردازش با منطق دریافت دیتا یک جا باشه و منطق پردازش رو abstract کردیم.

سوال اینجاست: در تابع process_book چه کار می‌کنید؟

۱. ورودی را دوباره سورت می‌کنید تا ۱۰۰ درصد مطمئن شین که سورت انجام شده.
۲. ورودی را سورت نمی‌کنید اما در داک استرینگ می‌نویسید که ورودی باید سورت‌شده باشد.
یا راهکار دیگه ای دارین؟‌ کامنت کنید پاسختون رو.


@PyBackendHub
👍7
Python BackendHub
فرض کنید یک دیتا بسیار سنگین دارید که از دیتابیس به صورت سورت‌شده بر اساس تاریخ ایجاد کتاب‌ها میاد. حالا شما دو تابع دارین: ۱. تابع get_books که کتاب‌ها را از دیتابیس به صورت سورت‌شده بر اساس تاریخ ایجاد میگیره. ۲. تابع process_book که کتاب‌ها را پردازش میکنه،…
مهدی تو کامنتا اشاره کرد که میتونید NewType بزنید. اسم دیگه New Type تو تایپ سیستم بهش Brand Type هم میگن. شما دارین یک تایپ رو برند میکنید. برند کردن یک تایپ یعنی چی؟‌

به مثال زیر دقت کنید

from typing import NewType

EmailType = NewType("EmailType", str)
email = EmailType("foo@gmail.com")
print(type(email))


ایمیل تو ران تایم, تایپش str معمولیه. هیچ فرقی نکرده. ولی برای تایپ چکر ایمیل EmailType هست. اینطوری شما اگه یک تابع داشتی send_email که یک ایمیل میگرفت نیاز نیست هربار ولیدیت کنی که ایا واقعا این استرینگی که داده شده به کد شما درواقع ایمیل هست یا نه. به جاش EmailType استفاده میکنی.

اینجام دقیقا همینه. شما نیازی نیست که تو ران تایم تو توابعت وقتی یک همچین کیسی داری که دو جا دو چیز به هم لینک شدن بیای هربار چک کنی که این ورودی از اون تابع اومده یا نه. به جاش تو فانکشن get_books خروجی رو Brand میکنی. مثلا SortedBookList. و ورودی این تابع رو فقط میذاری SortedBookList.

البته که هنوز شما میتونی خطا کنی. شما همچنان میتونی تو ران تایم قبل پاس دادن ورودی به اون فانکشن لیستی که سورت نشده یا استرینگی که واقعا ایمیل نیست رو cast کنی به اون تایپ. ولی پیدا کردن همچین خطایی تو code review خیلی راحته و احتمالش خیلی ناچیزه که خطا غیرعمدی انجام شه.

یک مقاله خیلی خوب راجب typing تو پایتون
یک مقاله با typenoscript که داره Branded type و یوزکیس هاشو خیلی قشنگ توضیح میده

@PyBackendHub
👍15
یکی از سخت ترین و مجیک ترین کد هایی که نوشتم تو لایبری های اوپن سورسم این بوده:
https://github.com/ManiMozaffar/aioclock/blob/main/tests/test_examples.py

تو این کد, دارم کل مثال هایی که تو داکیومنت لایبری aioclock ام چه به صورت داک استرینگ و چه تو یک فایل .md هست رو تست میکنم که ران شه و مشکلی نداشته باشه.

@PyBackendHub
👍9🔥4
Forwarded from Sadra Codes
👍 or 👎?
👍57👎3🤷‍♂11
Sadra Codes
👍 or 👎?
حق اگه توییت بود
👍9
Forwarded from ‌BenDev
امشب یعنی سه شنبه ساعت ۹ شب به وقت ایران لایو جنجالی داریم از دستش ندید

قراره که یه گپ و گفتی راجع به این داستان چت ناشناس داشته باشیم و پیاده سازیش هم می‌کنیم

@BenDevelop
11😁2
Forwarded from Yasha
همونطور که می‌دونید چند روز پیش ربات‌های ناشناس تلگرام هک شدن و همه متوجه ناامن بودنش شدیم. من و چند تا دیگه از بچه‌های کامیونیتی روی ربات پیام ناشناسی کار کردیم که می‌کنیم که اوپن‌سورسه، پیام‌ها رو با استفاده از الگوریتم‌های رمزنگاری End2End رمزشده جابجا می‌کنه و سرور قابلیت خوندن پیام‌ها رو نداره. تمام فرایند رمزنگاری رو سمت کلاینت انجام میده و سرور هیچ دخالتی توی مکانیزمش حتی نداره. این ربات الان توی مرحله تسته، ازتون می‌خوام که ربات رو تست کنید و نظرتون رو اعلام کنید.

🔗 @E2EChatbot
🔗 Source Code

برای حمایت از پروژه می‌تونید توی گیت‌هاب به ریپازیتوری استار بدید ⭐️ روی کدش مشارکت کنید، باگ‌ها رو گزارش کنید یا اینکه این پست رو به دیگران و افراد فنی صاحب نظر بفرستید تا دیده بشه و ما رو کمک‌مون کنن. 🤍

@Yasha
4👍4👎4🤔3💯1
Yasha
همونطور که می‌دونید چند روز پیش ربات‌های ناشناس تلگرام هک شدن و همه متوجه ناامن بودنش شدیم. من و چند تا دیگه از بچه‌های کامیونیتی روی ربات پیام ناشناسی کار کردیم که می‌کنیم که اوپن‌سورسه، پیام‌ها رو با استفاده از الگوریتم‌های رمزنگاری End2End رمزشده جابجا…
این چیه دیگه؟!
ببینید وقتی داریم از e2e encryption صحبت میکنیم همچین فلویی داریم

من فرستنده یک private key و public key دارم. شما هم همینطور.
من برای اینکه پیامی به شما بفرستم که کسی جز شما نخونه باید با پابلیک کی شما پیام رو encrypt کنم. بعدش با private key خودم میام اون پیام رو digital signature میزنم. اینکار باعث میشه که شما بدونی من این پیام رو فرستادم برات.
شما که پیام رو میخونی نیاز به پابلیک کی من داری. که بتونی چک کنی ایا این پیامو واقعا من فرستادم برات؟

دوستان ربات ناشناس و e2e encryption کاملا در تضاد هستن
چرا؟‌ فکر کن من بهت پیام دادم سلام روت کراش زدم. شما شک کردی که مانی همچین پیامی داده. به من میگی مانی میتونی یک سلام کنی تو لینک ناشناسم؟ همین که من یک سلام بنویسم public key پیام جدیدم با قبلیه رو مقایسه میکنی و متوجه میشی که من همونیم که گفتم روت کراش زدم.

نکته دوم نحوه ذخیره خوده تلگرامه. من به صدرا لینکو دادم گفتم یک پیام بفرست. صدرا که پیامو فرستاد من هم با سیستم بازش کردم هم با گوشی. تو جفت حالت تونستم پیام رو ببینم. پس درواقع secret key من رو گوشی یا سیستمم ذخیره نشده. رو دیتابیسه تلگرامه! کارمند تلگرام اراده کنه میتونه بیاد پیام های منو بخونه. e2e encryption یعنی فقط و فقط مقصد و مبدا سکرت کی داشته باشن و کسی این وسط نتونه بخونه.

متاسفانه e2e encryption صرفا یک buzz word هست. تو تلگرام فقط secret chat انکریپت میشه. نه چت معمولی. واتس اپ هم به شما قابلیت بک آپ دیتا و ریکاور کردنش رو یک گوشی دیگه میده. که دوباره طبق لاجیک بالا همچین چیزی فقط به شرطی ممکنه که واتس اپ سکرت کی شما رو نگه داشته باشه.

امنیت یک بخش بزرگیش فرهنگه اجتماعی و قانون گذاری هست. شما هرچی برنامت رو ایمن تر کنی UX بدتری خواهی داشت. پس این درست نیست که بگم من الان یک برنامه خیلی امن و خفن میسازم و همه قراره حال کنند. ترید آفه.

اما اینکه مشکلات بزرگی رخ نده (که هر ۱ ماه یک بار تو ایران اتفاق میفته) جلوگیریش با روش های تکنیکال و بیشتر قفل زدن نیست. تو اروپا آمریکا هیچ چیزی e2e انکریپت نمیشه. به جاش data regulatory خوب دارن. قوانین سنگین دارن. یک شرکت ۵ نفری باید فکر GDPR (قوانین مربوط به دیتا تو اروپا)‌ باشه. شما میتونی ایمیل بدی یا تو خوده اپ درخواست بزنی دیتات کامل پاک شه.

و در نهایت دیتایی که شما میفرستی تو اینترنت مشخص نیست واقعا چه بلایی سرش میاد.

یک مثال:
من یک فیلم ۱ گیگی براتون میفرستم. شما میفرستی تو save message ات. من فیلمو یک ساعت بعد پاک میکنم (دو طرفه). اون فیلم شما هنوز تو save message هست. عملیات forward به شدت سریعه. پس سوال اینجاست که آیا با دیلیت چت دو طرفه واقعا دیتا داره پاک میشه از سرور تلگرام؟ بعید بدونم.


دو ساعت راجب این چیزا تو لایو امروز امیربهادر صحبت کردیم. بحثای جالبی شد. اگه وقت و علاقه داشتین توصیه میکنم ببینید.
@PyBackendHub
👍272👎1