<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: qing</title>
    <description>The latest articles on DEV Community by qing (@qingluan).</description>
    <link>https://dev.to/qingluan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3897160%2F88fcfc25-1d56-4adc-b6b7-f5b10bc9f8d8.png</url>
      <title>DEV Community: qing</title>
      <link>https://dev.to/qingluan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/qingluan"/>
    <language>en</language>
    <item>
      <title>How to Use OpenAI API to Build Profitable Apps</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 20:00:54 +0000</pubDate>
      <link>https://dev.to/qingluan/how-to-use-openai-api-to-build-profitable-apps-1l8j</link>
      <guid>https://dev.to/qingluan/how-to-use-openai-api-to-build-profitable-apps-1l8j</guid>
      <description>&lt;h1&gt;
  
  
  How to Use OpenAI API to Build Profitable Apps
&lt;/h1&gt;

&lt;p&gt;tags: python, mobile, kivy, money&lt;/p&gt;




&lt;p&gt;tags: python, mobile, kivy, money&lt;/p&gt;




&lt;p&gt;tags: python, mobile, kivy, money&lt;/p&gt;




&lt;p&gt;tags: python, mobile, kivy, money&lt;/p&gt;




&lt;p&gt;tags: python, mobile, kivy, money&lt;/p&gt;




&lt;p&gt;tags: python, mobile, kivy, money&lt;/p&gt;




&lt;p&gt;tags: python, mobile, kivy, money&lt;/p&gt;




&lt;h1&gt;
  
  
  How to Build Profitable Mobile Apps as a Python Dev
&lt;/h1&gt;

&lt;p&gt;You’ve spent years mastering Python for data science, backend APIs, and automation, but the mobile world feels like a foreign country dominated by Swift and Kotlin. The good news? You don’t need to abandon your favorite language to build a mobile app that generates real revenue. While Python isn’t natively compiled for mobile like JavaScript (via React Native) or Dart (via Flutter), frameworks like &lt;strong&gt;Kivy&lt;/strong&gt; and &lt;strong&gt;BeeWare&lt;/strong&gt; let you write cross-platform mobile apps entirely in Python. The secret to profitability isn’t just writing code—it’s leveraging Python’s unique strengths in AI, data processing, and rapid backend development to solve problems other devs can’t.&lt;/p&gt;

&lt;p&gt;Here is your actionable roadmap to building, launching, and monetizing a profitable mobile app using Python, without ever touching a line of Swift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the Right Framework for Your Business Model
&lt;/h2&gt;

&lt;p&gt;The first mistake many Python devs make is picking a framework based on hype rather than business goals. Your choice dictates how easily you can monetize, scale, and update your app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kivy: The UI Powerhouse
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Kivy&lt;/strong&gt; is ideal for apps that need complex, custom interfaces or heavy offline functionality. It uses a &lt;code&gt;.kv&lt;/code&gt; file system for layouts, separating UI from logic, which makes maintenance easier as your app grows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Best for:&lt;/strong&gt; Data-heavy apps, games, and tools requiring custom UI widgets.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Monetization:&lt;/strong&gt; Excellent support for native ads and in-app purchases via plugins like &lt;code&gt;pyjnius&lt;/code&gt; (Android) and &lt;code&gt;pyobjus&lt;/code&gt; (iOS).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Packaging:&lt;/strong&gt; Use &lt;strong&gt;Buildozer&lt;/strong&gt; to generate APKs for Android and &lt;strong&gt;PyInstaller&lt;/strong&gt; for iOS (though iOS requires Xcode).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  BeeWare (Briefcase): The Native Look
&lt;/h3&gt;

&lt;p&gt;If your goal is to look like a standard native app on both iOS and Android without a custom UI overhaul, &lt;strong&gt;BeeWare&lt;/strong&gt; is your winner. It uses native widget sets, meaning your app feels like it was built with Swift or Kotlin, but you wrote it in Python.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Best for:&lt;/strong&gt; Business tools, productivity apps, and simple utilities where "native feel" drives user trust.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Monetization:&lt;/strong&gt; Seamless integration with platform-specific payment APIs because it uses native widgets.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Packaging:&lt;/strong&gt; Use the &lt;code&gt;briefcase&lt;/code&gt; command to build platform-specific binaries effortlessly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Build an AI-Driven Backend That Sells
&lt;/h2&gt;

&lt;p&gt;Your frontend might be Python, but your &lt;strong&gt;profit engine&lt;/strong&gt; will likely be your Python backend. Mobile users pay for value, and Python is the undisputed king of AI and data. Instead of building a simple CRUD app, build an app that solves a complex problem using machine learning.&lt;/p&gt;

&lt;p&gt;Start by defining the &lt;strong&gt;core business problem&lt;/strong&gt; your app solves. Is it a fitness app that uses object detection to count reps? A finance app that predicts market trends? Or a language app that uses speech recognition?&lt;/p&gt;

&lt;p&gt;Design a scalable architecture where the mobile frontend (Kivy/BeeWare) communicates with a &lt;strong&gt;FastAPI&lt;/strong&gt; or &lt;strong&gt;Django&lt;/strong&gt; backend. FastAPI is particularly powerful for AI-heavy apps due to its async capabilities and automatic documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Working Python Code Example: The Monetizable AI Feature
&lt;/h3&gt;

&lt;p&gt;Here is a practical example of a backend API using &lt;strong&gt;FastAPI&lt;/strong&gt; that processes user-uploaded images to detect objects—a feature you can easily monetize as a "Pro" subscription.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UploadFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;PIL&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;io&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Mock function for AI detection (replace with real TensorFlow/PyTorch model)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;detect_objects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# In a real app, load your model and run inference here
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dog&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ball&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grass&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/analyze-image/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UploadFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Analyzes an uploaded image and returns detected objects.
    Monetize this endpoint by limiting free users to 1 scan/day.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BytesIO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;detected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;detect_objects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;objects_detected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;detected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence_score&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.95&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code snippet is the core of your value proposition. You can wrap this API in a subscription model using Stripe or RevenueCat. Free users get one scan; premium users get unlimited scans and detailed analytics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monetization Strategies That Actually Work for Python Apps
&lt;/h2&gt;

&lt;p&gt;Once you have a working app, how do you turn it into revenue? Don’t rely on a single method. Successful Python mobile apps often combine several strategies.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The "Freemium" AI Model
&lt;/h3&gt;

&lt;p&gt;This is the most effective strategy for Python devs. Offer basic functionality for free (e.g., standard text analysis) and charge for &lt;strong&gt;AI-powered features&lt;/strong&gt; (e.g., predictive analytics, image recognition, or natural language generation). Since Python handles ML libraries like &lt;strong&gt;TensorFlow&lt;/strong&gt;, &lt;strong&gt;NumPy&lt;/strong&gt;, and &lt;strong&gt;Pandas&lt;/strong&gt; effortlessly, you can build features that competitors using only JavaScript might struggle to implement efficiently.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. In-App Subscriptions via RevenueCat
&lt;/h3&gt;

&lt;p&gt;For iOS and Android, &lt;strong&gt;RevenueCat&lt;/strong&gt; is the gold standard for managing subscriptions. It handles the complex logic of receipt validation and server-side notifications. You can integrate it with your Python backend to unlock premium features.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Niche B2B Solutions
&lt;/h3&gt;

&lt;p&gt;Don’t just build another generic game. Build a tool for a specific industry. Python is perfect for data visualization and automation. Consider building a logistics tracker for small delivery companies or a medical data analyzer for private clinics. These B2B apps often command higher prices and have lower churn rates than consumer apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Code to Cash: Deployment and Testing
&lt;/h2&gt;

&lt;p&gt;Writing the code is only half the battle. To be profitable, your app must be stable, fast, and compliant with store guidelines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rigorous Testing on Real Devices
&lt;/h3&gt;

&lt;p&gt;Emulators are great, but they miss memory issues and layout quirks that happen on real hardware. Use &lt;strong&gt;Android Studio emulators&lt;/strong&gt; for Android and &lt;strong&gt;TestFlight&lt;/strong&gt; for iOS. Test your app on older devices too; optimizing Python modules to avoid sluggish performance on legacy hardware is critical for user retention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Packaging for the Stores
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;For Android:&lt;/strong&gt; Use &lt;strong&gt;Buildozer&lt;/strong&gt; with Kivy to generate your APK. Ensure you’ve configured the &lt;code&gt;AndroidManifest.xml&lt;/code&gt; correctly for permissions (camera, storage, network).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;For iOS:&lt;/strong&gt; Use &lt;strong&gt;Briefcase&lt;/strong&gt; with BeeWare. You must have Xcode installed and a valid Apple Developer account. The build process can take 30+ minutes, so plan accordingly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CI/CD for Profit
&lt;/h3&gt;

&lt;p&gt;Don’t let a broken update kill your revenue. Set up &lt;strong&gt;CI/CD pipelines&lt;/strong&gt; (using GitHub Actions or GitLab CI) to automatically test and build your app. This ensures that future updates don’t break what’s already working, keeping your user base happy and your subscription revenue steady.&lt;/p&gt;

&lt;h2&gt;
  
  
  Launch and Iterate: The Profit Loop
&lt;/h2&gt;

&lt;p&gt;Your first launch isn’t the finish line; it’s the starting point. Prepare your store listings with high-quality screenshots and concise descriptions that highlight your &lt;strong&gt;AI features&lt;/strong&gt;. Comply with Google and Apple’s review guidelines early to avoid rejection delays.&lt;/p&gt;

&lt;p&gt;Once live, monitor your analytics. Use libraries to track user behavior and identify where users drop off. If your "Pro" feature isn’t converting, tweak the pricing or the onboarding flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Building Your First Profitable App Today
&lt;/h2&gt;

&lt;p&gt;You don’t need to be a Swift expert to build a mobile business. Your Python skills are your unfair advantage. By combining &lt;strong&gt;Kivy&lt;/strong&gt; or &lt;strong&gt;BeeWare&lt;/strong&gt; for the frontend with a powerful &lt;strong&gt;FastAPI&lt;/strong&gt; backend for AI, you can create apps that solve real problems and generate revenue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your Action Plan for Today:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Pick a niche:&lt;/strong&gt; Identify a problem you can solve with AI or data.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Install the tool:&lt;/strong&gt; Run &lt;code&gt;pip install kivy&lt;/code&gt; or &lt;code&gt;briefcase new&lt;/code&gt; to start your project.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Build the core:&lt;/strong&gt; Write the API endpoint that solves the problem (use the code example above as a template).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Package it:&lt;/strong&gt; Generate your first APK or IPA.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The mobile market is hungry for smart, data-driven apps. Stop waiting for the "perfect" idea and start building the one you can monetize today. Your next paycheck might just come from a Python script running on someone’s phone.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>openai</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build a Slack Bot That Automates Your Workflow</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 20:00:49 +0000</pubDate>
      <link>https://dev.to/qingluan/build-a-slack-bot-that-automates-your-workflow-22i0</link>
      <guid>https://dev.to/qingluan/build-a-slack-bot-that-automates-your-workflow-22i0</guid>
      <description>&lt;h1&gt;
  
  
  Build a Slack Bot That Automates Your Workflow
&lt;/h1&gt;

&lt;p&gt;tags: python, slack, bot, automation&lt;/p&gt;




&lt;p&gt;tags: python, slack, bot, automation&lt;/p&gt;




&lt;p&gt;tags: python, slack, bot, automation&lt;/p&gt;




&lt;p&gt;tags: python, slack, bot, automation&lt;/p&gt;




&lt;p&gt;tags: python, slack, bot, automation&lt;/p&gt;




&lt;p&gt;tags: python, slack, bot, automation&lt;/p&gt;




&lt;p&gt;tags: python, slack, bot, automation&lt;/p&gt;




&lt;p&gt;Imagine your team is drowning in repetitive tasks: manually copying data from emails to spreadsheets, scheduling recurring meetings, or chasing people for status updates. You’re not lazy—you’re just wasting precious brainpower on things a bot could handle in seconds. The good news? You don’t need a engineering degree or a months-long project to fix this. With a few lines of Python and Slack’s powerful API, you can build a custom Slack bot that automates your workflow &lt;em&gt;today&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let’s get practical. This isn’t about theory—it’s about shipping a working bot that saves you time immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Build a Slack Bot Instead of Using Built-in Tools?
&lt;/h2&gt;

&lt;p&gt;Slack offers Workflow Builder, a no-code tool for automations [3][4]. It’s great for simple triggers like “send a reminder every Monday” or “post a new GitHub issue to a channel.” But Workflow Builder has limits: it can’t fetch dynamic data from external APIs, process complex logic, or integrate with services Slack doesn’t natively support.&lt;/p&gt;

&lt;p&gt;A custom Python bot, on the other hand, gives you full control. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query your database, CRM, or analytics tools&lt;/li&gt;
&lt;li&gt;Parse JSON responses and format them into Slack messages&lt;/li&gt;
&lt;li&gt;Handle errors, retries, and conditional logic&lt;/li&gt;
&lt;li&gt;Scale to hundreds of users without hitting no-code platform limits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need &lt;em&gt;real&lt;/em&gt; automation—not just reminders—a bot is your best bet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create Your Slack App and Bot User
&lt;/h2&gt;

&lt;p&gt;Before writing code, you need a Slack app with bot permissions. Here’s how:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://api.slack.com/" rel="noopener noreferrer"&gt;Slack API&lt;/a&gt; and click &lt;strong&gt;Your Apps &amp;gt; Create New App&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Name your app (e.g., &lt;code&gt;WorkflowBot&lt;/code&gt;) and select your workspace.&lt;/li&gt;
&lt;li&gt;In the &lt;strong&gt;Features&lt;/strong&gt; section, enable &lt;strong&gt;Bot Token Scopes&lt;/strong&gt;. Add these essential scopes:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;chat:write&lt;/code&gt; (to post messages)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;commands&lt;/code&gt; (to handle slash commands)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;im:write&lt;/code&gt; (to send direct messages)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;OAuth &amp;amp; Permissions&lt;/strong&gt;, click &lt;strong&gt;Install App to Workspace&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Copy the &lt;strong&gt;Bot User OAuth Token&lt;/strong&gt; (starts with &lt;code&gt;xoxb-&lt;/code&gt;). You’ll need this to authenticate your bot.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ Never share this token. Store it in an environment variable, not in your code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 2: Set Up Your Python Environment
&lt;/h2&gt;

&lt;p&gt;You’ll need Python 3.7+ and the &lt;code&gt;slack-bolt&lt;/code&gt; library, which simplifies handling Slack events and commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;slack-bolt python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in your project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SLACK_BOT_TOKEN=xoxb-your-bot-token-here
SLACK_SIGNING_SECRET=your-signing-secret-here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grab the &lt;strong&gt;Signing Secret&lt;/strong&gt; from your Slack app page under &lt;strong&gt;Basic Information&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Write Your First Automating Bot
&lt;/h2&gt;

&lt;p&gt;Here’s a complete, working example that listens for a &lt;code&gt;/status&lt;/code&gt; slash command, fetches data from an external API (we’ll use a mock endpoint), and posts a formatted summary to Slack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;slack_bolt&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Bolt&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;slack_bolt.adapter.lambda_handler&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;handle_request&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Bolt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SLACK_BOT_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;signing_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SLACK_SIGNING_SECRET&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_status_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;ack&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Acknowledge the command immediately
&lt;/span&gt;
    &lt;span class="c1"&gt;# Fetch data from external API (replace with your real endpoint)
&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.example.com/status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

    &lt;span class="c1"&gt;# Format the message
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Failed to fetch status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;uptime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uptime&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;N/A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ **System Status**: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;⏱️ **Uptime**: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uptime&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; hours&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Post to the channel where the command was used
&lt;/span&gt;    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat_postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channel_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;reply_broadcast&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When a user types &lt;code&gt;/status&lt;/code&gt; in Slack, the bot triggers &lt;code&gt;handle_status_command&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It calls an external API (swap &lt;code&gt;https://api.example.com/status&lt;/code&gt; with your real endpoint).&lt;/li&gt;
&lt;li&gt;It formats the response into a clean Slack message with emojis and bold text.&lt;/li&gt;
&lt;li&gt;The message is posted back to the same channel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run this locally with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python bot.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For production, deploy it to AWS Lambda, Google Cloud Functions, or any server that can handle HTTP requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Automate Real Workflows Today
&lt;/h2&gt;

&lt;p&gt;Now that your bot is running, here are three actionable automations you can implement &lt;em&gt;immediately&lt;/em&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Daily Digest of Sales Metrics
&lt;/h3&gt;

&lt;p&gt;Set a time-based trigger (using a cron job or cloud scheduler) to call your bot’s internal endpoint. The bot fetches your sales dashboard API, summarizes the data, and posts it to a Slack channel every morning.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Auto-Remind Team Members Before Deadlines
&lt;/h3&gt;

&lt;p&gt;Use Slack’s &lt;code&gt;im:write&lt;/code&gt; scope to send direct messages. When a Jira or Asana task is updated, your bot checks the due date and sends a reminder 24 hours before if it’s overdue.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Interactive Onboarding Checklist
&lt;/h3&gt;

&lt;p&gt;Create a slash command &lt;code&gt;/onboard&lt;/code&gt; that posts a checklist with buttons (“Task 1”, “Task 2”). When a user clicks a button, the bot updates the message to mark it complete and logs the progress in your database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: No-Code Alternative for Quick Wins
&lt;/h2&gt;

&lt;p&gt;If Python feels too heavy right now, Slack’s &lt;strong&gt;Workflow Builder&lt;/strong&gt; is a great starting point for simple automations [3][6]. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create workflows from templates (e.g., “New Employee Onboarding”)&lt;/li&gt;
&lt;li&gt;Trigger actions based on keywords, forms, or time&lt;/li&gt;
&lt;li&gt;Post messages, assign tasks, or tag team members&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But remember: once you hit the limits of no-code logic, a custom bot is your next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Next Move
&lt;/h2&gt;

&lt;p&gt;Don’t wait for “the perfect time.” Build this bot &lt;em&gt;today&lt;/em&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create your Slack app and get your token.&lt;/li&gt;
&lt;li&gt;Install &lt;code&gt;slack-bolt&lt;/code&gt; and write the code above.&lt;/li&gt;
&lt;li&gt;Test it with &lt;code&gt;/status&lt;/code&gt; in your Slack channel.&lt;/li&gt;
&lt;li&gt;Replace the mock API with your real data source.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In under an hour, you’ll have a bot that automates a real part of your workflow. And once you see how much time you save, you’ll start building more.&lt;/p&gt;

&lt;p&gt;What’s the first task you’ll automate? Share your bot idea in the comments—or drop a link if you’ve already built one. Let’s automate the boring stuff together.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>slack</category>
      <category>bot</category>
      <category>automation</category>
    </item>
    <item>
      <title>Python Context Managers: The Complete Guide</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 20:00:08 +0000</pubDate>
      <link>https://dev.to/qingluan/python-context-managers-the-complete-guide-2mgp</link>
      <guid>https://dev.to/qingluan/python-context-managers-the-complete-guide-2mgp</guid>
      <description>&lt;h1&gt;
  
  
  Python Context Managers: The Complete Guide
&lt;/h1&gt;

&lt;p&gt;tags: python, programming, tutorial, advanced&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, advanced&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, advanced&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, advanced&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, advanced&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, advanced&lt;/p&gt;




&lt;p&gt;You’ve probably written code like this before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works, but it’s tedious, fragile, and easy to forget. What if you could replace that entire block with two lines that &lt;strong&gt;automatically&lt;/strong&gt; handle opening, reading, and closing—even if an exception crashes your program? That’s exactly what Python context managers do, and once you master them, you’ll write cleaner, safer, and more professional code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a Context Manager?
&lt;/h2&gt;

&lt;p&gt;A context manager is an object that defines how to set up and clean up a block of code. It implements the &lt;strong&gt;context management protocol&lt;/strong&gt;: two methods, &lt;code&gt;__enter__()&lt;/code&gt; and &lt;code&gt;__exit__()&lt;/code&gt;. When you use the &lt;code&gt;with&lt;/code&gt; statement, Python calls &lt;code&gt;__enter__()&lt;/code&gt; at the start and &lt;code&gt;__exit__()&lt;/code&gt; at the end, regardless of whether the block succeeded or raised an error [1][9].&lt;/p&gt;

&lt;p&gt;This pattern &lt;strong&gt;factors out standard uses of &lt;code&gt;try/finally&lt;/code&gt;&lt;/strong&gt;, making resource handling reliable and boilerplate-free [7].&lt;/p&gt;

&lt;p&gt;Common uses include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Files and sockets&lt;/li&gt;
&lt;li&gt;Database connections&lt;/li&gt;
&lt;li&gt;Locks and synchronization&lt;/li&gt;
&lt;li&gt;Transactions&lt;/li&gt;
&lt;li&gt;Temporary state changes&lt;/li&gt;
&lt;li&gt;Timing and profiling [1]&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why You Should Use Them TODAY
&lt;/h2&gt;

&lt;p&gt;Context managers give you three massive benefits:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Automatic cleanup&lt;/strong&gt;: Resources are released even if exceptions occur [1].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleaner code&lt;/strong&gt;: No manual &lt;code&gt;try/finally&lt;/code&gt; blocks cluttering your logic [1].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliable patterns&lt;/strong&gt;: It’s hard to forget cleanup because the syntax enforces it [1].&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you’re still manually calling &lt;code&gt;.close()&lt;/code&gt;, &lt;code&gt;.release()&lt;/code&gt;, or &lt;code&gt;.disconnect()&lt;/code&gt; after every operation, you’re already losing. Context managers fix that.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Use the Built-in &lt;code&gt;with&lt;/code&gt; Statement
&lt;/h2&gt;

&lt;p&gt;Python’s built-in &lt;code&gt;with&lt;/code&gt; statement is your gateway. Most standard objects already support it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="c1"&gt;# File is automatically closed here, even if an exception occurs
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Behind the scenes, &lt;code&gt;open()&lt;/code&gt; returns a context manager. Python calls &lt;code&gt;f.__enter__()&lt;/code&gt; to get the file object, runs your code, then calls &lt;code&gt;f.__exit__()&lt;/code&gt; to close it [9].&lt;/p&gt;

&lt;p&gt;You can chain multiple resources:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;inp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating Your Own Context Manager: Class-Based
&lt;/h2&gt;

&lt;p&gt;When you need to manage state or handle complex setup/teardown, create a class with &lt;code&gt;__enter__&lt;/code&gt; and &lt;code&gt;__exit__&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatabaseConnection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db_path&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__enter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Connecting to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;db_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# hypothetical method
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__exit__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc_tb&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Closing connection&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="c1"&gt;# Return False to propagate exceptions, True to suppress
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="c1"&gt;# Usage
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;DatabaseConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mydb.sqlite&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Connection is guaranteed to close
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;__exit__&lt;/code&gt; method receives exception details if one occurred. Returning &lt;code&gt;False&lt;/code&gt; lets the exception propagate; &lt;code&gt;True&lt;/code&gt; suppresses it [6].&lt;/p&gt;

&lt;h2&gt;
  
  
  The Easier Way: &lt;code&gt;@contextmanager&lt;/code&gt; Decorator
&lt;/h2&gt;

&lt;p&gt;For simple cases, the &lt;code&gt;@contextmanager&lt;/code&gt; decorator from &lt;code&gt;contextlib&lt;/code&gt; lets you write a context manager as a &lt;strong&gt;generator&lt;/strong&gt;. This is often the nicest and easiest approach [7].&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;contextmanager&lt;/span&gt;

&lt;span class="nd"&gt;@contextmanager&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;timed_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
    &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;  &lt;span class="c1"&gt;# code inside 'with' runs here
&lt;/span&gt;    &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; took &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Usage
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;timed_block&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data processing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;yield&lt;/code&gt; marks where the &lt;code&gt;with&lt;/code&gt; block executes. Everything before &lt;code&gt;yield&lt;/code&gt; is setup; everything after is teardown [7].&lt;/p&gt;

&lt;p&gt;This approach is perfect for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Timing and profiling&lt;/li&gt;
&lt;li&gt;Temporary state changes&lt;/li&gt;
&lt;li&gt;Simple locks or transactions [1]&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Managing Multiple Dynamic Resources: &lt;code&gt;ExitStack&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;What if you need to open resources dynamically, like reading files from a list? &lt;code&gt;ExitStack&lt;/code&gt; handles this elegantly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ExitStack&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_paths&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;ExitStack&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;file_paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enter_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="c1"&gt;# All files closed automatically, even on exception
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ExitStack&lt;/code&gt; lets you push context managers dynamically and ensures they’re all cleaned up in reverse order [1].&lt;/p&gt;

&lt;h2&gt;
  
  
  Async Context Managers: &lt;code&gt;asynccontextmanager&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;When working with &lt;code&gt;async&lt;/code&gt; code, use &lt;code&gt;asynccontextmanager&lt;/code&gt; and &lt;code&gt;async with&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asynccontextmanager&lt;/span&gt;

&lt;span class="nd"&gt;@asynccontextmanager&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;async_db_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# hypothetical
&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;
    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Usage
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;async_db_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres://localhost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This mirrors the sync pattern but uses &lt;code&gt;await&lt;/code&gt; and &lt;code&gt;async yield&lt;/code&gt; [3].&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Pitfalls and Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don’t suppress exceptions accidentally&lt;/strong&gt;: Only return &lt;code&gt;True&lt;/code&gt; from &lt;code&gt;__exit__&lt;/code&gt; if you truly want to hide errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep &lt;code&gt;__exit__&lt;/code&gt; safe&lt;/strong&gt;: It should handle cleanup even if &lt;code&gt;__enter__&lt;/code&gt; partially failed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer &lt;code&gt;@contextmanager&lt;/code&gt;&lt;/strong&gt; for simple cases; use classes for stateful or complex logic [7].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;ExitStack&lt;/code&gt;&lt;/strong&gt; when resources are dynamic or created in loops [1].&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Put This Into Practice Today
&lt;/h2&gt;

&lt;p&gt;Here’s your action plan:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Find one place&lt;/strong&gt; in your code where you manually call &lt;code&gt;.close()&lt;/code&gt;, &lt;code&gt;.release()&lt;/code&gt;, or &lt;code&gt;.disconnect()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrap it&lt;/strong&gt; in a &lt;code&gt;with&lt;/code&gt; block using a built-in context manager (like &lt;code&gt;open()&lt;/code&gt;) or create a simple &lt;code&gt;@contextmanager&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test&lt;/strong&gt; that it still works when an exception occurs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You’ll immediately reduce bugs and clean up your code.&lt;/p&gt;

&lt;p&gt;Context managers are one of Python’s most powerful yet underused tools. They turn fragile, error-prone resource handling into something elegant, automatic, and trustworthy. Once you start using them, you’ll never go back to manual cleanup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it now&lt;/strong&gt;: Pick a function in your project that opens a file or connects to a database. Wrap it in a &lt;code&gt;with&lt;/code&gt; statement or a custom context manager. Share your before/after code on Dev.to and let the community see the difference.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>advanced</category>
    </item>
    <item>
      <title>How to Scrape JavaScript-Heavy Sites with Playwright</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 20:00:04 +0000</pubDate>
      <link>https://dev.to/qingluan/how-to-scrape-javascript-heavy-sites-with-playwright-4hoc</link>
      <guid>https://dev.to/qingluan/how-to-scrape-javascript-heavy-sites-with-playwright-4hoc</guid>
      <description>&lt;h1&gt;
  
  
  How to Scrape JavaScript-Heavy Sites with Playwright
&lt;/h1&gt;

&lt;p&gt;tags: python, playwright, webscraping, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, playwright, webscraping, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, playwright, webscraping, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, playwright, webscraping, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, playwright, webscraping, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, playwright, webscraping, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, playwright, webscraping, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, playwright, webscraping, tutorial&lt;/p&gt;




&lt;h1&gt;
  
  
  How to Scrape JavaScript-Heavy Sites with Playwright
&lt;/h1&gt;

&lt;p&gt;You’ve probably hit this wall: you write a clean script with &lt;code&gt;requests&lt;/code&gt; or &lt;code&gt;httpx&lt;/code&gt;, fire it off, and get back a blank page or a skeleton HTML with zero data. That’s because the site you’re targeting doesn’t render its content until JavaScript runs in the browser. Traditional scrapers can’t execute that JS, so they see nothing. The solution? Use a tool that &lt;em&gt;actually&lt;/em&gt; runs a browser. Enter &lt;strong&gt;Playwright&lt;/strong&gt;—a modern, fast, and reliable browser automation library that lets you scrape JavaScript-heavy sites, single-page applications (SPAs), and dynamic content with ease.&lt;/p&gt;

&lt;p&gt;Unlike older tools like Selenium, Playwright is built for speed, supports multiple browsers (Chromium, Firefox, WebKit), and has native async support. It’s also smart about waiting: you don’t need to guess how long to wait for content to load. Playwright can wait for selectors, network events, or even specific DOM states. Let’s get you building a scraper that works today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Playwright Beats Traditional Scrapers
&lt;/h2&gt;

&lt;p&gt;Most HTTP-based scrapers just fetch the raw HTML response. But modern websites—especially those built with React, Vue, or Angular—load content dynamically via AJAX or WebSockets. The initial HTML might be empty, and the real data only appears after JS executes.&lt;/p&gt;

&lt;p&gt;Playwright solves this by &lt;strong&gt;controlling a real browser&lt;/strong&gt;. It launches Chromium (or another browser), navigates to the page, executes all JavaScript, and gives you the fully rendered DOM. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You get the &lt;strong&gt;actual content&lt;/strong&gt; users see.&lt;/li&gt;
&lt;li&gt;You can &lt;strong&gt;interact&lt;/strong&gt; with the page: click buttons, fill forms, scroll, and handle pop-ups.&lt;/li&gt;
&lt;li&gt;You avoid many &lt;strong&gt;blocking issues&lt;/strong&gt; because Playwright mimics human behavior more closely than raw HTTP requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;According to industry guides, Playwright is “highly effective for web scraping JavaScript-heavy sites, SPAs, and any dynamic content page” [3]. It’s also a great alternative to Selenium because of its asynchronous nature and cross-browser support [5].&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Your Python Environment
&lt;/h2&gt;

&lt;p&gt;Before writing code, let’s set up a clean Python environment. You’ll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.8+&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;playwright&lt;/code&gt; package&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pandas&lt;/code&gt; (optional, for data handling)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how to install everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv playwright_scraper
&lt;span class="nb"&gt;source &lt;/span&gt;playwright_scraper/bin/activate  &lt;span class="c"&gt;# On Windows: playwright_scraper\Scripts\activate&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;playwright pandas
playwright &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;playwright install&lt;/code&gt; command downloads the optimized browser binaries Playwright uses [6].&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Your First Scraper: A Real Example
&lt;/h2&gt;

&lt;p&gt;Let’s scrape a simple JavaScript-heavy e-commerce sandbox that mimics real product listings. We’ll use the Oxylabs sandbox site, which is perfect for practice [6].&lt;/p&gt;

&lt;p&gt;Our goals:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launch a browser and navigate to the URL.&lt;/li&gt;
&lt;li&gt;Wait for product items to load.&lt;/li&gt;
&lt;li&gt;Extract product name, price, and URL.&lt;/li&gt;
&lt;li&gt;Save results to a CSV.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s a complete, working Python script using Playwright’s &lt;strong&gt;sync&lt;/strong&gt; API (simpler for beginners):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;playwright.sync_api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sync_playwright&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;

&lt;span class="n"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://sandbox.oxylabs.io/products&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;OUTPUT_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;products.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scrape_products&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;sync_playwright&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headless&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Set headless=False to see the browser
&lt;/span&gt;        &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Wait for product items to be rendered
&lt;/span&gt;        &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_selector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[data-content=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product-item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;attached&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Get all product locators
&lt;/span&gt;        &lt;span class="n"&gt;product_items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[data-content=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product-item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;product_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;h2 a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;text_content&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;text_content&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;h2 a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get_attribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;href&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;

        &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Write to CSV
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_FILE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fieldnames&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;price&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeheader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writerows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saved &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; products to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;OUTPUT_FILE&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;scrape_products&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this script, and you’ll get a &lt;code&gt;products.csv&lt;/code&gt; file with real product data. No manual waits, no guesswork—just reliable scraping [6].&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Dynamic Content and Waiting Strategies
&lt;/h2&gt;

&lt;p&gt;One of Playwright’s biggest strengths is its &lt;strong&gt;smart waiting&lt;/strong&gt;. You don’t need &lt;code&gt;time.sleep(5)&lt;/code&gt; anymore. Instead, use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;page.wait_for_selector()&lt;/code&gt; – waits for an element to appear.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;page.wait_for_load_state("networkidle")&lt;/code&gt; – waits until network activity stops.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;page.locator()&lt;/code&gt; – automatically waits and retries until the element is ready [6].&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if your site loads content after scrolling, you can scroll and then wait:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;window.scrollTo(0, document.body.scrollHeight)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_load_state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;networkidle&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures all AJAX-loaded products are visible before scraping [4].&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoiding Detection and Rate Limiting
&lt;/h2&gt;

&lt;p&gt;Since Playwright runs a real browser, it’s harder for sites to detect you as a bot. But you can still get blocked if you’re too aggressive. Here are pro tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use headless mode&lt;/strong&gt; in production (&lt;code&gt;headless=True&lt;/code&gt;) to reduce overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add random delays&lt;/strong&gt; between requests to mimic human behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement retry logic&lt;/strong&gt; with exponential backoff for 403 errors [2].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Block unnecessary resources&lt;/strong&gt; (images, fonts) to speed up scraping:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;block_resources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
        &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;resource_type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;font&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stylesheet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;continue_&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new_page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;block_resources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This technique reduces load time and bandwidth [6].&lt;/p&gt;

&lt;h2&gt;
  
  
  Debugging and Testing Selectors
&lt;/h2&gt;

&lt;p&gt;Before writing code, inspect the page in &lt;strong&gt;Browser DevTools&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click an element → “Inspect”.&lt;/li&gt;
&lt;li&gt;Find a stable selector (e.g., &lt;code&gt;data-content="product-item"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Copy it and drop it into &lt;code&gt;page.locator()&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Playwright also offers helper methods like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get_by_role()&lt;/code&gt; – finds elements by accessibility role.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_by_text()&lt;/code&gt; – matches visible text.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_by_test_id()&lt;/code&gt; – targets elements with a test ID [6].&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These make your scraper more robust against DOM changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Playwright vs. Other Tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Limitations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Playwright&lt;/td&gt;
&lt;td&gt;JS-heavy sites, SPAs, interactions&lt;/td&gt;
&lt;td&gt;Slower than HTTP scrapers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selenium&lt;/td&gt;
&lt;td&gt;Legacy automation, cross-browser&lt;/td&gt;
&lt;td&gt;Less modern, slower sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Puppeteer&lt;/td&gt;
&lt;td&gt;Node.js-only JS scraping&lt;/td&gt;
&lt;td&gt;Chromium-only, no Firefox/WebKit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Requests&lt;/td&gt;
&lt;td&gt;Static HTML, APIs&lt;/td&gt;
&lt;td&gt;Can’t handle JS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Playwright is the best choice when you need &lt;strong&gt;real browser execution&lt;/strong&gt; and &lt;strong&gt;interaction capabilities&lt;/strong&gt; [2][3].&lt;/p&gt;

&lt;h2&gt;
  
  
  Take Action Today
&lt;/h2&gt;

&lt;p&gt;You don’t need to reverse-engineer APIs or struggle with blank pages. With Playwright, you can scrape any JavaScript-heavy site by simply running a browser. Try the script above on a sandbox site, then adapt it to your target.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your next step:&lt;/strong&gt; Pick a real JavaScript-heavy site you’ve struggled with, install Playwright, and run the scraper. Share your results or questions in the comments—let’s build together.&lt;/p&gt;

&lt;p&gt;Got a tricky site you’re scraping? Drop the URL and what you’re trying to extract. I’ll help you craft a selector strategy. Let’s make scraping easier, one page at a time.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>playwright</category>
      <category>webscraping</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build an Email Notification System with Python</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 18:00:55 +0000</pubDate>
      <link>https://dev.to/qingluan/build-an-email-notification-system-with-python-2l5m</link>
      <guid>https://dev.to/qingluan/build-an-email-notification-system-with-python-2l5m</guid>
      <description>&lt;h1&gt;
  
  
  Build an Email Notification System with Python
&lt;/h1&gt;

&lt;p&gt;tags: python, email, automation, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, email, automation, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, email, automation, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, email, automation, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, email, automation, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, email, automation, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, email, automation, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, email, automation, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, email, automation, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, email, automation, tutorial&lt;/p&gt;




&lt;h1&gt;
  
  
  Build an Email Notification System with Python
&lt;/h1&gt;

&lt;p&gt;Imagine your server just crashed, a critical database backup failed, or a user reported a severe bug—but you’re asleep, at dinner, or completely offline. Without a way to alert you instantly, those minutes turn into hours of downtime, lost revenue, and frustrated customers. The good news? You can solve this today with a simple, automated email notification system built entirely in Python. No expensive third-party services, no complex infrastructure—just a few lines of code and your existing Gmail account.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll build a production-ready email notifier that sends alerts when specific events occur. We’ll cover everything from setting up secure SMTP connections to crafting reusable notification functions you can drop into any project. Let’s get coding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Python Is Perfect for Email Notifications
&lt;/h2&gt;

&lt;p&gt;Python comes with built-in libraries like &lt;code&gt;smtplib&lt;/code&gt; and &lt;code&gt;email.message&lt;/code&gt; that make sending emails straightforward. Unlike Node.js or Ruby, where you often need external packages for basic SMTP functionality, Python handles secure connections, message formatting, and authentication natively. Plus, its readability means your notification logic stays clean and maintainable—even as your system scales.&lt;/p&gt;

&lt;p&gt;For developers who want a quicker alternative, libraries like &lt;code&gt;yagmail&lt;/code&gt; simplify Gmail-specific workflows by handling app passwords and OAuth2 automatically [3][4]. But for maximum control and portability across mail providers (not just Gmail), we’ll stick with the standard &lt;code&gt;smtplib&lt;/code&gt; approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up Your Python Environment
&lt;/h2&gt;

&lt;p&gt;Before writing code, create a clean virtual environment to isolate dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; venv &lt;span class="nb"&gt;env
source env&lt;/span&gt;/bin/activate  &lt;span class="c"&gt;# On Windows: .\env\Scripts\activate.ps1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now install the necessary libraries. While &lt;code&gt;smtplib&lt;/code&gt; is built-in, you’ll need &lt;code&gt;secure-smtplib&lt;/code&gt; for modern TLS handling (though in practice, &lt;code&gt;smtplib&lt;/code&gt; alone suffices with &lt;code&gt;starttls()&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;smtplib secure-smtplib
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: &lt;code&gt;smtplib&lt;/code&gt; is part of Python’s standard library—you don’t actually need to install it with pip. The &lt;code&gt;secure-smtplib&lt;/code&gt; package is optional; we’ll use &lt;code&gt;smtplib.SMTP_SSL&lt;/code&gt; or &lt;code&gt;starttls()&lt;/code&gt; directly for security [1][5].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 2: Configure Gmail for SMTP Access
&lt;/h2&gt;

&lt;p&gt;To send emails via Gmail, you must enable SMTP access. Here’s how:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to your &lt;a href="https://myaccount.google.com/security" rel="noopener noreferrer"&gt;Google Account Security Settings&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;2-Factor Authentication&lt;/strong&gt; (required).&lt;/li&gt;
&lt;li&gt;Generate an &lt;strong&gt;App Password&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Under “2-Step Verification,” select “App passwords.”&lt;/li&gt;
&lt;li&gt;Create a new app named “Email Notifier.”&lt;/li&gt;
&lt;li&gt;Copy the 16-character password (you won’t see it again).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Never use your regular Gmail password. App passwords are designed for third-party apps and are revoked automatically if compromised [6].&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Write the Notification Function
&lt;/h2&gt;

&lt;p&gt;Here’s a complete, reusable function that sends an email using Gmail’s SMTP server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;email.message&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;EmailMessage&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;email.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;formataddr&lt;/span&gt;

&lt;span class="n"&gt;SMTP_SERVER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;smtp.gmail.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;SMTP_PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;465&lt;/span&gt;  &lt;span class="c1"&gt;# SSL port
&lt;/span&gt;&lt;span class="n"&gt;SENDER_EMAIL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_email@gmail.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;SENDER_PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your_app_password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Use app password, not regular password
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_email_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Send an email notification via Gmail SMTP.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmailMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Subject&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;From&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;formataddr&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;sender_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SENDER_EMAIL&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;To&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recipient&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SMTP_SSL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SMTP_SERVER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SMTP_PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SENDER_EMAIL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SENDER_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ Email sent successfully to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;❌ Failed to send email: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="c1"&gt;# Example usage
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;send_email_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;friend@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🚨 Critical Alert: Server Down&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Your server at 192.168.1.10 is unreachable. Please check immediately.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code uses &lt;code&gt;EmailMessage&lt;/code&gt; for clean header handling and &lt;code&gt;SMTP_SSL&lt;/code&gt; for encrypted connections [1][5]. It’s secure, readable, and ready to integrate into scripts, web apps, or monitoring tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Automate Alerts for Real Events
&lt;/h2&gt;

&lt;p&gt;Now let’s make this practical. Suppose you’re running a background job that checks if a service is alive. If it fails, send an alert:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_service&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Returns True if service is running, False otherwise.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;curl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8000/health&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;monitor_and_alert&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;check_service&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="nf"&gt;send_email_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;🚨 Service Health Check Failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The service at http://localhost:8000 is down. Immediate action required.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;# Avoid spamming: wait 10 minutes before next check
&lt;/span&gt;            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;✅ Service is healthy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Check every 30 seconds
&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;monitor_and_alert&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern—check → alert if failed → wait—is the backbone of most monitoring systems [7]. You can extend it to check databases, APIs, file systems, or even custom business logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Add HTML and Attachments
&lt;/h2&gt;

&lt;p&gt;For richer alerts, send HTML emails or attach logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;email.mime.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;MIMEBase&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;encoders&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_html_email_with_attachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;html_body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EmailMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Subject&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;From&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SENDER_EMAIL&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;To&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;recipient&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html_body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Attach file
&lt;/span&gt;    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;part&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MIMEBase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;octet-stream&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_payload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;encoders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode_base64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Content-Disposition&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;attachment; filename=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;smtplib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SMTP_SSL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SMTP_SERVER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SMTP_PORT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SENDER_EMAIL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SENDER_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets you send formatted dashboards, error logs, or screenshots as attachments [4].&lt;/p&gt;

&lt;h2&gt;
  
  
  Make It Production-Ready
&lt;/h2&gt;

&lt;p&gt;To deploy this safely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use environment variables&lt;/strong&gt; for credentials:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
  &lt;span class="n"&gt;SENDER_EMAIL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GMAIL_USER&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;SENDER_PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;GMAIL_APP_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add retry logic&lt;/strong&gt; for network failures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log failures&lt;/strong&gt; to a file or monitoring system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate-limit alerts&lt;/strong&gt; to avoid spamming recipients.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For scalable systems, consider offloading notifications to background workers like Celery or using dedicated services like SendGrid or Mailtrap [5][9]. But for 90% of projects, this simple Python script is all you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Alerting Today
&lt;/h2&gt;

&lt;p&gt;You now have a working, secure email notification system that can alert you about crashes, failures, or critical events—no matter where you are. The code is minimal, reusable, and ready to paste into your next project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your next step?&lt;/strong&gt; Pick one thing in your workflow that should trigger an alert (a failed backup, a slow API, a login spike) and wrap it with &lt;code&gt;send_email_notification()&lt;/code&gt;. In under 10 minutes, you’ll have real-time visibility into your system’s health.&lt;/p&gt;

&lt;p&gt;Drop your use case in the comments below—what’s the first alert you’ll build? And if you found this helpful, share it with a teammate who’s still debugging at 3 AM instead of sleeping. 🛠️📬&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>email</category>
      <category>automation</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Mastering Python Dataclasses: A Complete Guide</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 18:00:50 +0000</pubDate>
      <link>https://dev.to/qingluan/mastering-python-dataclasses-a-complete-guide-2p7e</link>
      <guid>https://dev.to/qingluan/mastering-python-dataclasses-a-complete-guide-2p7e</guid>
      <description>&lt;h1&gt;
  
  
  Mastering Python Dataclasses: A Complete Guide
&lt;/h1&gt;

&lt;p&gt;tags: python, programming, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: python, programming, tutorial, beginners&lt;/p&gt;




&lt;h1&gt;
  
  
  Mastering Python Dataclasses: A Complete Guide
&lt;/h1&gt;

&lt;p&gt;Remember the last time you wrote a class that was just a glorified dictionary? You spent twenty minutes typing &lt;code&gt;__init__&lt;/code&gt;, &lt;code&gt;__repr__&lt;/code&gt;, and &lt;code&gt;__eq__&lt;/code&gt; methods, only to realize you forgot to handle default values correctly. It’s tedious, repetitive, and frankly, a waste of your brilliant brainpower. Python dataclasses were introduced in version 3.7 to solve this exact problem, letting you define classes for storing data with virtually zero boilerplate [4]. If you want to write cleaner, more type-safe code today, you need to master this feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Dataclasses and Why Use Them?
&lt;/h2&gt;

&lt;p&gt;A dataclass is a class that is primarily used for storing data. While there are no strict restrictions on what a dataclass can do, its main purpose is to hold data attributes [4]. The magic happens through the &lt;code&gt;@dataclass&lt;/code&gt; decorator, which automatically generates special methods like &lt;code&gt;__init__()&lt;/code&gt;, &lt;code&gt;__repr__()&lt;/code&gt;, and &lt;code&gt;__eq__()&lt;/code&gt; for you [2].&lt;/p&gt;

&lt;p&gt;Think of dataclasses as "records" or lightweight data-only structures containing a fixed set of named, typed fields [5]. They are the perfect alternative when you’re tempted to pass around a tuple or a dictionary but want the safety of attribute access and type checking. As a rule of thumb: if you’re considering returning or passing a group of multiple values, consider a dataclass instead of a tuple [5].&lt;/p&gt;

&lt;p&gt;The benefits are immediate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Reduced Boilerplate:&lt;/strong&gt; No more writing &lt;code&gt;__init__&lt;/code&gt; manually.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Type Safety:&lt;/strong&gt; Fields are defined with type annotations, making them friendly to linters and IDEs [6].&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Readability:&lt;/strong&gt; The code is shorter and expresses intent clearly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Basics: Defining Your First Dataclass
&lt;/h2&gt;

&lt;p&gt;Let’s get practical. Here is how you define a simple dataclass to represent a user profile. Notice how we skip the &lt;code&gt;__init__&lt;/code&gt; method entirely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;is_active&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="c1"&gt;# Creating an instance is intuitive
&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The magic methods are auto-generated
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Output: User(name='Alice', age=30, is_active=True)
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# Output: True
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;@dataclass&lt;/code&gt; decorator generated the initialization logic, the string representation, and the equality comparison automatically [4]. The &lt;code&gt;:&lt;/code&gt; notation you see is using &lt;strong&gt;variable annotations&lt;/strong&gt;, which is the standard way to define fields in a dataclass [4].&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Features: Customizing Behavior
&lt;/h2&gt;

&lt;p&gt;While the default behavior is great, real-world applications often need more control. The &lt;code&gt;@dataclass&lt;/code&gt; decorator accepts several parameters to customize how your class behaves.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making Objects Immutable with &lt;code&gt;frozen&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;By default, dataclass attributes are mutable—you can reassign them after creation [8]. However, if you need an object that cannot change once created (like a configuration setting), you can make it immutable using the &lt;code&gt;frozen&lt;/code&gt; parameter [5].&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secret123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# config.timeout = 60  # This will raise an error: FrozenInstanceError
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;frozen=True&lt;/code&gt; is set, the dataclass throws an error if anyone tries to mutate the object at runtime [5]. This is a powerful pattern for preventing accidental state changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customizing Fields with &lt;code&gt;field()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Sometimes you need to set a default value that is a function call (like a list) rather than a static value. You can’t just write &lt;code&gt;tags: list = []&lt;/code&gt; because that would create a shared list across all instances. Instead, use the &lt;code&gt;field()&lt;/code&gt; specifier with &lt;code&gt;default_factory&lt;/code&gt; [4].&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default_factory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;p1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alpha&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;p2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Beta&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bug&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Output: [] - No shared state!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;field()&lt;/code&gt; function supports parameters like &lt;code&gt;default&lt;/code&gt;, &lt;code&gt;default_factory&lt;/code&gt;, &lt;code&gt;init&lt;/code&gt;, &lt;code&gt;repr&lt;/code&gt;, and &lt;code&gt;eq&lt;/code&gt; to customize individual fields [4].&lt;/p&gt;

&lt;h3&gt;
  
  
  Enforcing Keyword Arguments with &lt;code&gt;kw_only&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;One common pain point with dataclasses is accidentally passing arguments in the wrong order. Python 3.10 introduced the &lt;code&gt;kw_only&lt;/code&gt; flag to force all fields to be passed as keyword arguments, making your code more explicit and readable [5].&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kw_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;

&lt;span class="c1"&gt;# point = Point(1, 2)  # Error: Point takes 0 positional arguments
&lt;/span&gt;&lt;span class="n"&gt;point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Works perfectly
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;kw_only=True&lt;/code&gt; ensures every field must be explicitly set, forcing you to read the field name before setting it [5].&lt;/p&gt;

&lt;h2&gt;
  
  
  Dataclasses vs. Other Data Structures
&lt;/h2&gt;

&lt;p&gt;It’s easy to get confused about when to use a dataclass versus a &lt;code&gt;namedtuple&lt;/code&gt;, &lt;code&gt;dict&lt;/code&gt;, or a regular class.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Dataclass&lt;/th&gt;
&lt;th&gt;Namedtuple&lt;/th&gt;
&lt;th&gt;Dict&lt;/th&gt;
&lt;th&gt;Regular Class&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Boilerplate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low (Auto &lt;code&gt;__init__&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mutability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Mutable (default)&lt;/td&gt;
&lt;td&gt;Immutable&lt;/td&gt;
&lt;td&gt;Mutable&lt;/td&gt;
&lt;td&gt;Custom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type Hints&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Native Support&lt;/td&gt;
&lt;td&gt;Native Support&lt;/td&gt;
&lt;td&gt;No (usually)&lt;/td&gt;
&lt;td&gt;Native Support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Attribute Access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Yes (&lt;code&gt;obj.x&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Yes (&lt;code&gt;obj.x&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No (&lt;code&gt;obj['x']&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Defaults&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;td&gt;Harder&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;td&gt;Custom&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Dataclasses are essentially "mutable namedtuples with defaults" [8]. They offer the attribute access of a class but the simplicity of a tuple, with the added flexibility of default values. If you need a lightweight container for data, dataclasses are generally the best choice [4].&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices and Gotchas
&lt;/h2&gt;

&lt;p&gt;To truly master dataclasses, you need to know a few pitfalls.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Avoid Shared Defaults:&lt;/strong&gt; As mentioned earlier, never use a mutable object (like a list or dict) as a direct default value. Always use &lt;code&gt;default_factory&lt;/code&gt; [4].&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Order Matters:&lt;/strong&gt; Fields without default values must be defined before fields with default values, unless you use &lt;code&gt;kw_only=True&lt;/code&gt; [5].&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Slots for Performance:&lt;/strong&gt; If you are creating thousands of instances and care about memory, you can set &lt;code&gt;slots=True&lt;/code&gt;. This replaces the object’s &lt;code&gt;__dict__&lt;/code&gt; with a fixed-size array, reducing memory usage and improving lookup speed [6].&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Inheritance:&lt;/strong&gt; Dataclasses support inheritance just like regular classes. You can inherit fields from a parent dataclass and add new ones in the child [4].
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Employee&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="n"&gt;mgr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Manager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bob&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Sales&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Start Using Them Today
&lt;/h2&gt;

&lt;p&gt;Dataclasses are one of the best features to have happened to Python for object-oriented programming fans [3]. They strip away the noise of boilerplate code, leaving you with clear, typed, and structured data containers.&lt;/p&gt;

&lt;p&gt;Stop writing &lt;code&gt;__init__&lt;/code&gt; methods for simple data holders. Start using &lt;code&gt;@dataclass&lt;/code&gt; to define your &lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Product&lt;/code&gt;, or &lt;code&gt;Config&lt;/code&gt; objects today. The next time you’re tempted to return a tuple of five values, pause and ask yourself: "Would a dataclass be better here?" The answer is almost always yes.&lt;/p&gt;

&lt;p&gt;Go ahead and refactor one of your old classes into a dataclass right now. You’ll see how much cleaner your code becomes immediately. Happy coding!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Build a CLI Tool with Python and Click</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 18:00:08 +0000</pubDate>
      <link>https://dev.to/qingluan/how-to-build-a-cli-tool-with-python-and-click-3omk</link>
      <guid>https://dev.to/qingluan/how-to-build-a-cli-tool-with-python-and-click-3omk</guid>
      <description>&lt;h1&gt;
  
  
  How to Build a CLI Tool with Python and Click
&lt;/h1&gt;

&lt;p&gt;tags: python, cli, tutorial, tools&lt;/p&gt;




&lt;p&gt;tags: python, cli, tutorial, tools&lt;/p&gt;




&lt;p&gt;tags: python, cli, tutorial, tools&lt;/p&gt;




&lt;p&gt;tags: python, cli, tutorial, tools&lt;/p&gt;




&lt;p&gt;tags: python, cli, tutorial, tools&lt;/p&gt;




&lt;p&gt;tags: python, testing, devops, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, testing, devops, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, automation, tools, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, automation, tools, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, automation, tools, tutorial&lt;/p&gt;




&lt;h1&gt;
  
  
  Build a File Sync Tool with Python and Watchdog
&lt;/h1&gt;

&lt;p&gt;Imagine you’re working on a project where you constantly edit files in one folder, but you need those changes instantly reflected in another location—maybe a backup drive, a cloud staging folder, or a deployment directory. Doing this manually is tedious, and relying on cron jobs introduces lag. What if you could build a tool that watches for changes and syncs them &lt;strong&gt;in real time&lt;/strong&gt;? That’s exactly what we’re going to do today using Python and the powerful &lt;code&gt;watchdog&lt;/code&gt; library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Watchdog?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;watchdog&lt;/code&gt; is a cross-platform Python library that lets you monitor file system events—like file creation, modification, and deletion—and trigger custom actions in response [1][8]. Unlike polling-based solutions, it’s event-driven, meaning it reacts immediately when a change happens. This makes it perfect for building real-time sync tools, automated testers, or even live documentation generators.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You’ll Build
&lt;/h2&gt;

&lt;p&gt;By the end of this post, you’ll have a working &lt;strong&gt;file sync tool&lt;/strong&gt; that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watches a source directory for changes&lt;/li&gt;
&lt;li&gt;Automatically copies new or modified files to a destination directory&lt;/li&gt;
&lt;li&gt;Deletes files in the destination if they’re removed from the source&lt;/li&gt;
&lt;li&gt;Runs continuously without blocking your main workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And yes, you can use this &lt;strong&gt;today&lt;/strong&gt; to automate your own workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up Your Environment
&lt;/h2&gt;

&lt;p&gt;First, make sure you have Python 3 installed. Then, install &lt;code&gt;watchdog&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;watchdog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll also need Python’s built-in &lt;code&gt;os&lt;/code&gt; and &lt;code&gt;shutil&lt;/code&gt; modules for file operations, so no extra installs there [2][4].&lt;/p&gt;

&lt;p&gt;Create a project folder and set up two directories: one for your source files and one for the synced destination.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;file_sync_demo
&lt;span class="nb"&gt;cd &lt;/span&gt;file_sync_demo
&lt;span class="nb"&gt;mkdir source &lt;/span&gt;destination
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Build the Event Handler
&lt;/h2&gt;

&lt;p&gt;The core of &lt;code&gt;watchdog&lt;/code&gt; is the &lt;strong&gt;event handler&lt;/strong&gt;—a class that defines what to do when specific file system events occur. We’ll create a custom handler that inherits from &lt;code&gt;FileSystemEventHandler&lt;/code&gt; and overrides methods like &lt;code&gt;on_created&lt;/code&gt;, &lt;code&gt;on_modified&lt;/code&gt;, and &lt;code&gt;on_deleted&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s the full working code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;shutil&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;watchdog.observers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Observer&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;watchdog.events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileSystemEventHandler&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SyncHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FileSystemEventHandler&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;source_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;source_dir&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dest_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dest_dir&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_created&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sync_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;src_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_modified&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sync_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;src_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_deleted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_directory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="n"&gt;dest_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;src_path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dest_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deleted: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dest_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sync_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;src_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;dest_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dest_dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;src_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dest_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Synced: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;destination&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="n"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SyncHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Observer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recursive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Watching &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; for changes. Syncing to &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;destination&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;pass&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;KeyboardInterrupt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save this as &lt;code&gt;sync.py&lt;/code&gt; in your project folder [2][8].&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: How It Works
&lt;/h2&gt;

&lt;p&gt;Let’s break down the key parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;SyncHandler&lt;/code&gt;&lt;/strong&gt;: Our custom event handler. It stores the source and destination paths and defines behavior for each event type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;on_created&lt;/code&gt; / &lt;code&gt;on_modified&lt;/code&gt;&lt;/strong&gt;: Both trigger the same &lt;code&gt;sync_file&lt;/code&gt; method, which copies the file using &lt;code&gt;shutil.copy2&lt;/code&gt; (this preserves metadata like timestamps).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;on_deleted&lt;/code&gt;&lt;/strong&gt;: Removes the corresponding file from the destination if it exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Observer&lt;/code&gt;&lt;/strong&gt;: The engine that watches the directory. We schedule it to watch the &lt;code&gt;source&lt;/code&gt; folder with &lt;code&gt;recursive=False&lt;/code&gt; to avoid watching subdirectories [3][7].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;observer.start()&lt;/code&gt;&lt;/strong&gt;: Begins monitoring in a background thread, so your script doesn’t block [7].&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Test It Out
&lt;/h2&gt;

&lt;p&gt;Now, let’s see it in action.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the script:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   python sync.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;In another terminal, navigate to the &lt;code&gt;source&lt;/code&gt; folder and create a file:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;cd source
   echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello, sync!"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; test.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Watch the output: you should see &lt;code&gt;Synced: test.txt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Check the &lt;code&gt;destination&lt;/code&gt; folder—you’ll find &lt;code&gt;test.txt&lt;/code&gt; there, identical to the source.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Try editing &lt;code&gt;test.txt&lt;/code&gt;, moving it, or deleting it. The tool will react instantly [2].&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Tips &amp;amp; Enhancements
&lt;/h2&gt;

&lt;p&gt;This is a minimal but functional sync tool. Here are a few ways to level it up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Filter by file type&lt;/strong&gt;: Add a &lt;code&gt;patterns&lt;/code&gt; argument to &lt;code&gt;observer.schedule()&lt;/code&gt; to watch only &lt;code&gt;.txt&lt;/code&gt; or &lt;code&gt;.py&lt;/code&gt; files [9].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add logging&lt;/strong&gt;: Use Python’s &lt;code&gt;logging&lt;/code&gt; module instead of &lt;code&gt;print()&lt;/code&gt; for production-ready output [4].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle race conditions&lt;/strong&gt;: If a file is being written, &lt;code&gt;on_modified&lt;/code&gt; might fire multiple times. You can add a small delay or check file size stability before syncing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make it recursive&lt;/strong&gt;: Set &lt;code&gt;recursive=True&lt;/code&gt; in &lt;code&gt;schedule()&lt;/code&gt; if you want to sync subdirectories too [3].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add CLI args&lt;/strong&gt;: Use &lt;code&gt;argparse&lt;/code&gt; to let users specify source/dest paths at runtime [1].&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Real-time file sync isn’t just convenient—it’s essential for modern development workflows. Whether you’re syncing config files to a server, mirroring assets for a website, or backing up critical data, having a lightweight, scriptable tool gives you full control without relying on third-party services.&lt;/p&gt;

&lt;p&gt;And because it’s built in Python, you can extend it endlessly: add email notifications, integrate with cloud APIs, or even turn it into a web service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Turn
&lt;/h2&gt;

&lt;p&gt;Grab the code above, run it, and tweak it for your own needs. Try syncing a folder of your project files, or automate a backup for your documents. Once you see it working, you’ll never go back to manual copying.&lt;/p&gt;

&lt;p&gt;If you build something cool with this, share it on Dev.to or tag me in your post. And if you hit any bugs or have ideas for improvements, drop a comment—let’s keep building better tools together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ready to sync?&lt;/strong&gt; Run &lt;code&gt;python sync.py&lt;/code&gt; and watch your files move in real time.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>cli</category>
      <category>tutorial</category>
      <category>tools</category>
    </item>
    <item>
      <title>Build a Real-Time Chat App with FastAPI and WebSockets</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 18:00:04 +0000</pubDate>
      <link>https://dev.to/qingluan/build-a-real-time-chat-app-with-fastapi-and-websockets-5hap</link>
      <guid>https://dev.to/qingluan/build-a-real-time-chat-app-with-fastapi-and-websockets-5hap</guid>
      <description>&lt;h1&gt;
  
  
  Build a Real-Time Chat App with FastAPI and WebSockets
&lt;/h1&gt;

&lt;p&gt;tags: python, fastapi, websocket, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, fastapi, websocket, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, fastapi, websocket, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, fastapi, websocket, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, fastapi, websocket, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, fastapi, websocket, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, fastapi, websocket, tutorial&lt;/p&gt;




&lt;p&gt;tags: python, fastapi, websocket, tutorial&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Real-Time Chat App with FastAPI and WebSockets: A Step-by-Step Guide
&lt;/h2&gt;

&lt;p&gt;As developers, we've all been there - we see a demo of a real-time chat app and think, "That's exactly what I need for my project." But, we all know that building such an app from scratch can be a daunting task. You need to worry about scalability, performance, and most importantly, real-time updates. In this post, we'll show you how to build a real-time chat app using FastAPI and WebSockets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up the Project
&lt;/h3&gt;

&lt;p&gt;Before we dive into the nitty-gritty of building a chat app, let's set up the project structure. FastAPI is a modern Python web framework that allows us to build high-performance APIs quickly. We'll create a new project using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;real-time-chat-app
&lt;span class="nb"&gt;cd &lt;/span&gt;real-time-chat-app
&lt;span class="nb"&gt;mkdir &lt;/span&gt;app models routes utils
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our project structure will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;real-time-chat-app/
app/
__init__.py
main.py
models/
__init__.py
user.py
message.py
routes/
__init__.py
auth.py
chat.py
utils/
__init__.py
websocket.py
requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let's install the required dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;fastapi uvicorn aiomysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Defining the Data Models
&lt;/h3&gt;

&lt;p&gt;In our chat app, we'll have two main data models: &lt;code&gt;User&lt;/code&gt; and &lt;code&gt;Message&lt;/code&gt;. We'll define these models in the &lt;code&gt;models&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/user.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/message.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;sender_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;receiver_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;
    &lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting Up the Database
&lt;/h3&gt;

&lt;p&gt;For this example, we'll use a MySQL database. You can use any other database engine if you prefer. We'll use the &lt;code&gt;aiomysql&lt;/code&gt; library to connect to the database.&lt;/p&gt;

&lt;p&gt;First, let's create the database schema:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="n"&gt;AUTO_INCREMENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;sender_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;receiver_id&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sender_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;FOREIGN&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receiver_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let's create a database connection using &lt;code&gt;aiomysql&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/utils/db.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;aiomysql&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;aiomysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;localhost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;root&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;password&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;real_time_chat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building the Chat API
&lt;/h3&gt;

&lt;p&gt;Now that we have our data models and database connection set up, let's build the chat API. We'll create a new route for sending messages and another for retrieving messages.&lt;/p&gt;

&lt;p&gt;First, let's create a message sending route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/routes/chat.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.models.message&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.utils.websocket&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send_message_to_all&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@router.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/messages/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO messages (content, sender_id, receiver_id) VALUES (%s, %s, %s)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sender_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;receiver_id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send_message_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Message sent successfully&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let's create a route for retrieving messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/routes/chat.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APIRouter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.models.message&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@router.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/messages/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_messages&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cur&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sender_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;receiver_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting Up WebSockets
&lt;/h3&gt;

&lt;p&gt;Now that we have our chat API set up, let's set up WebSockets. WebSockets allow us to establish a persistent connection between the client and server, enabling real-time updates.&lt;/p&gt;

&lt;p&gt;First, let's create a new file for handling WebSocket connections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/utils/websocket.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_message_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_running_connections&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_connection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;receive_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;send_message_to_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;disconnect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, let's create a new route for handling WebSocket connections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/routes/chat.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WebSocket&lt;/span&gt;

&lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;APIRouter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@router.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/ws/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_websocket&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;websocket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running the App
&lt;/h3&gt;

&lt;p&gt;Now that we have our chat app set up, let's run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uvicorn app.main:app &lt;span class="nt"&gt;--host&lt;/span&gt; 0.0.0.0 &lt;span class="nt"&gt;--port&lt;/span&gt; 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In this post, we showed you how to build a real-time chat app using FastAPI and WebSockets. With this guide, you should be able to build a chat app that supports real-time updates and persistent connections.&lt;/p&gt;

&lt;p&gt;Remember to replace the database connection settings and WebSocket connection settings with your own. With this app, you can create a real-time chat experience for your users.&lt;/p&gt;

&lt;p&gt;What's next? You can extend this app to support file uploads, user authentication, and more. The possibilities are endless. Happy coding!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>websocket</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>TypeScript for Python Developers: A Practical Guide</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 16:00:57 +0000</pubDate>
      <link>https://dev.to/qingluan/typescript-for-python-developers-a-practical-guide-8p0</link>
      <guid>https://dev.to/qingluan/typescript-for-python-developers-a-practical-guide-8p0</guid>
      <description>&lt;h1&gt;
  
  
  TypeScript for Python Developers: A Practical Guide
&lt;/h1&gt;

&lt;p&gt;tags: typescript, python, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: typescript, python, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: typescript, python, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: typescript, python, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: typescript, python, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: typescript, python, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: typescript, python, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: typescript, python, tutorial, beginners&lt;/p&gt;




&lt;p&gt;tags: typescript, python, tutorial, beginners&lt;/p&gt;




&lt;h3&gt;
  
  
  From Python to TypeScript: Why the Switch?
&lt;/h3&gt;

&lt;p&gt;As a Python developer, you're likely no stranger to the excitement of writing clean, readable, and maintainable code. But, have you ever wanted to take your code to the next level by adding the benefits of static typing? Look no further than TypeScript, the popular superset of JavaScript that's been gaining traction among developers across the globe.&lt;/p&gt;

&lt;p&gt;But, before you start thinking, "JavaScript? Isn't that for web development?" Think again! TypeScript can be used for a wide range of applications, from backend services to desktop applications, and even mobile apps. And, with the rise of Node.js and the growing popularity of frameworks like Express.js and Nest.js, TypeScript is becoming an increasingly attractive choice for developers who want to build robust, scalable, and maintainable systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is TypeScript, and Why Should I Care?
&lt;/h3&gt;

&lt;p&gt;TypeScript is a statically-typed language that compiles to JavaScript, allowing you to write robust, maintainable, and scalable code for a wide range of applications. With TypeScript, you can take advantage of features like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Static typing&lt;/strong&gt;: TypeScript allows you to define the types of your variables, function parameters, and return types, making it easier to catch errors at compile-time rather than runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interfaces&lt;/strong&gt;: TypeScript provides a powerful way to define interfaces, which can be used to ensure that objects conform to a specific shape and structure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modules&lt;/strong&gt;: TypeScript supports modules, which allow you to organize your code into reusable, independent units.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type inference&lt;/strong&gt;: TypeScript can automatically infer the types of variables and function parameters, making it easier to write type-safe code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Setting Up TypeScript for Your Python Project
&lt;/h3&gt;

&lt;p&gt;So, how do you get started with TypeScript? Fortunately, it's easier than you think! Here are the basic steps to set up TypeScript for your Python project:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install the TypeScript compiler&lt;/strong&gt;: You'll need to install the TypeScript compiler, which can be done using npm (Node Package Manager) or yarn. Run &lt;code&gt;npm install -g typescript&lt;/code&gt; or &lt;code&gt;yarn global add typescript&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a &lt;code&gt;tsconfig.json&lt;/code&gt; file&lt;/strong&gt;: Create a &lt;code&gt;tsconfig.json&lt;/code&gt; file in the root of your project, which will contain configuration settings for the TypeScript compiler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install the required dependencies&lt;/strong&gt;: You'll need to install the required dependencies for your project, including any external libraries or frameworks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write your TypeScript code&lt;/strong&gt;: Start writing your TypeScript code, making sure to follow the basic syntax and conventions of the language.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  A Practical Example: Converting a Python Function to TypeScript
&lt;/h3&gt;

&lt;p&gt;Let's take a look at a simple example of converting a Python function to TypeScript. Here's the Python code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;John&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's convert this code to TypeScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the TypeScript code is almost identical to the Python code, with the main differences being:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type annotations&lt;/strong&gt;: We've added type annotations for the &lt;code&gt;name&lt;/code&gt; parameter and the return type of the function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function signature&lt;/strong&gt;: We've used a &lt;code&gt;function&lt;/code&gt; keyword to define the function, rather than the &lt;code&gt;def&lt;/code&gt; keyword used in Python.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Console.log&lt;/strong&gt;: We've used &lt;code&gt;console.log&lt;/code&gt; instead of &lt;code&gt;print&lt;/code&gt; to log the greeting message to the console.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Best Practices for Writing TypeScript Code
&lt;/h3&gt;

&lt;p&gt;So, how do you write effective TypeScript code? Here are some best practices to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use type annotations&lt;/strong&gt;: Type annotations are a key feature of TypeScript, and they can help you catch errors at compile-time rather than runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Follow the basic syntax&lt;/strong&gt;: TypeScript has a syntax that's similar to JavaScript, but with some key differences. Make sure to follow the basic syntax and conventions of the language.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use interfaces&lt;/strong&gt;: Interfaces can help you define the shape and structure of objects, making it easier to write maintainable and scalable code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep your code organized&lt;/strong&gt;: Keep your code organized by using modules and separate files for different components.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion and Call to Action
&lt;/h3&gt;

&lt;p&gt;TypeScript is a powerful tool that can help you write robust, maintainable, and scalable code for a wide range of applications. By following the best practices outlined in this article, you can take the first steps towards converting your Python project to TypeScript.&lt;/p&gt;

&lt;p&gt;So, what are you waiting for? Take the leap and start exploring the world of TypeScript today! Whether you're a seasoned developer or just starting out, TypeScript has something to offer everyone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Further Reading
&lt;/h3&gt;

&lt;p&gt;If you want to learn more about TypeScript, here are some resources to get you started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Official TypeScript documentation&lt;/strong&gt;: The official TypeScript documentation is a comprehensive resource that covers everything from the basics of the language to advanced topics like type inference and type guards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript tutorials&lt;/strong&gt;: There are many online tutorials and courses that can help you learn TypeScript, including tutorials on Udemy, Coursera, and edX.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript communities&lt;/strong&gt;: Join online communities like the TypeScript subreddit, TypeScript Discord, and TypeScript Slack to connect with other developers who are using TypeScript in their projects.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>python</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Build a GitHub Actions Bot That Pays You</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 16:00:52 +0000</pubDate>
      <link>https://dev.to/qingluan/build-a-github-actions-bot-that-pays-you-416l</link>
      <guid>https://dev.to/qingluan/build-a-github-actions-bot-that-pays-you-416l</guid>
      <description>&lt;h1&gt;
  
  
  Build a GitHub Actions Bot That Pays You
&lt;/h1&gt;

&lt;p&gt;tags: github, automation, money, devops&lt;/p&gt;




&lt;p&gt;tags: github, automation, money, devops&lt;/p&gt;




&lt;p&gt;tags: github, automation, money, devops&lt;/p&gt;




&lt;p&gt;tags: github, automation, money, devops&lt;/p&gt;




&lt;p&gt;tags: github, automation, money, devops&lt;/p&gt;




&lt;p&gt;tags: github, automation, money, devops&lt;/p&gt;




&lt;p&gt;tags: github, automation, money, devops&lt;/p&gt;




&lt;p&gt;tags: github, automation, money, devops&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Paying Your Way: How to Build a GitHub Actions Bot That Earns You Money&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Are you tired of feeling like your open-source contributions are going unnoticed? Well, we've got some good news for you: GitHub Actions can now pay you for your work. In this post, we'll show you how to build a bot that uses GitHub Actions to automatically detect and reward you for your contributions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the GitHub Actions Bot
&lt;/h2&gt;

&lt;p&gt;Before we dive into the code, let's cover the basics of what we're trying to achieve. Our bot will use the GitHub Actions API to monitor your repositories, detect new contributions, and then trigger a payment to your bank account. Sounds simple, right?&lt;/p&gt;

&lt;p&gt;To get started, you'll need to create a new GitHub token with the necessary permissions. You can do this by going to your GitHub account settings, clicking on "Developer settings," and then selecting "Personal access tokens." Create a new token with the following permissions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;repo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;workflow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;actions&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have your token, you'll need to store it securely. We recommend using a secrets manager like Hashicorp's Vault or a simple environment variable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing the Required Dependencies
&lt;/h3&gt;

&lt;p&gt;Next, let's set up our Python environment. We'll need the following dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;requests&lt;/code&gt; for making API calls&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;github&lt;/code&gt; for interacting with the GitHub API&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;schedule&lt;/code&gt; for scheduling our bot to run at regular intervals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can install these dependencies using pip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests github schedule
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Writing the Bot Code
&lt;/h3&gt;

&lt;p&gt;Now it's time to write our bot code. We'll use the &lt;code&gt;schedule&lt;/code&gt; library to schedule our bot to run every hour. In each run, our bot will check your repositories for new contributions and trigger a payment if necessary.&lt;/p&gt;

&lt;p&gt;Here's the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;schedule&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Github&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RateLimitExceededException&lt;/span&gt;

&lt;span class="c1"&gt;# Set your GitHub token and repository name
&lt;/span&gt;&lt;span class="n"&gt;GITHUB_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;REPO_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;your-repo-name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;# Set your payment details
&lt;/span&gt;&lt;span class="n"&gt;PAYMENT_METHOD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bank-account&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;PAYMENT_AMOUNT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;  &lt;span class="c1"&gt;# in dollars
&lt;/span&gt;
&lt;span class="c1"&gt;# Set your schedule
&lt;/span&gt;&lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;job&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Create a GitHub API instance
&lt;/span&gt;    &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Github&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Get your repository
&lt;/span&gt;    &lt;span class="n"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_repo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REPO_NAME&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Check for new contributions
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pull_request&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_pulls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;open&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Trigger a payment
&lt;/span&gt;            &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;method&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PAYMENT_METHOD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;amount&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PAYMENT_AMOUNT&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://api.github.com/repos/{}/actions/runs&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REPO_NAME&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Bearer {}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)})&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;201&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Payment triggered successfully!&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Error triggering payment:&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting Up the Payment Trigger
&lt;/h3&gt;

&lt;p&gt;Now that we have our bot code, let's talk about how to set up the payment trigger. We'll use the GitHub API to create a new workflow that triggers a payment when a new contribution is detected.&lt;/p&gt;

&lt;p&gt;Here's an example workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pay contributors&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pay&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Trigger payment&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;curl -X POST \&lt;/span&gt;
            &lt;span class="s"&gt;https://api.github.com/repos/${GITHUB_REPOSITORY}/actions/runs \&lt;/span&gt;
            &lt;span class="s"&gt;-H 'Authorization: Bearer $GITHUB_TOKEN' \&lt;/span&gt;
            &lt;span class="s"&gt;-H 'Content-Type: application/json' \&lt;/span&gt;
            &lt;span class="s"&gt;-d '{"method": "bank-account", "amount": 10}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this post, we've shown you how to build a GitHub Actions bot that automatically detects and rewards your contributions with a payment. By following these steps, you can start earning money for your open-source work today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get started today by following these steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new GitHub token with the necessary permissions.&lt;/li&gt;
&lt;li&gt;Store your token securely using a secrets manager or environment variable.&lt;/li&gt;
&lt;li&gt;Install the required dependencies using pip.&lt;/li&gt;
&lt;li&gt;Write your bot code using the example provided above.&lt;/li&gt;
&lt;li&gt;Set up the payment trigger using the GitHub API.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following these steps, you'll be well on your way to building a bot that pays you for your contributions. Happy coding!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>automation</category>
      <category>money</category>
      <category>devops</category>
    </item>
    <item>
      <title>Automate Your Taxes: Python Scripts for Freelancers</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 16:00:11 +0000</pubDate>
      <link>https://dev.to/qingluan/automate-your-taxes-python-scripts-for-freelancers-3khc</link>
      <guid>https://dev.to/qingluan/automate-your-taxes-python-scripts-for-freelancers-3khc</guid>
      <description>&lt;h1&gt;
  
  
  Automate Your Taxes: Python Scripts for Freelancers
&lt;/h1&gt;

&lt;p&gt;tags: python, freelance, taxes, automation&lt;/p&gt;




&lt;p&gt;tags: python, freelance, taxes, automation&lt;/p&gt;




&lt;p&gt;tags: python, freelance, taxes, automation&lt;/p&gt;




&lt;p&gt;tags: python, freelance, taxes, automation&lt;/p&gt;




&lt;p&gt;tags: python, freelance, taxes, automation&lt;/p&gt;




&lt;p&gt;tags: python, freelance, taxes, automation&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Automate Your Taxes: Python Scripts for Freelancers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a freelancer, managing your finances can be a daunting task, especially when it comes to taxes. You have to keep track of your income, expenses, and deductions, all while meeting the deadlines for tax submissions. But what if you could automate some of these tasks using Python scripts? Not only would it save you time and reduce stress, but it would also ensure accuracy and efficiency. In this article, we'll explore how you can create Python scripts to streamline your tax-related tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Understanding Tax Requirements&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before we dive into the coding, let's take a look at the tax requirements for freelancers. As a freelancer, you're considered self-employed and are required to report your income and expenses on your tax return. You'll need to file a Schedule C (Form 1040) to report your business income and expenses. You'll also need to report your business mileage, travel expenses, and any other business-related expenses on your tax return.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Gathering Data&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To automate your taxes, you'll need to gather data on your income and expenses. This can include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Business income from clients&lt;/li&gt;
&lt;li&gt;  Business expenses, such as equipment, software, and travel costs&lt;/li&gt;
&lt;li&gt;  Business mileage and travel expenses&lt;/li&gt;
&lt;li&gt;  Any other business-related expenses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are several tools available to help you track your income and expenses, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Accounting software like QuickBooks or Xero&lt;/li&gt;
&lt;li&gt;  Expense tracking apps like Expensify or Concur&lt;/li&gt;
&lt;li&gt;  Spreadsheets like Google Sheets or Microsoft Excel&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Using Python to Automate Tax-Related Tasks&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now that we have our data, let's look at how we can use Python to automate some tax-related tasks. We'll focus on creating a script to calculate business income and expenses, as well as a script to calculate business mileage and travel expenses.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Business Income and Expenses&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let's start with a script to calculate business income and expenses. We'll use the &lt;code&gt;pandas&lt;/code&gt; library to read in our data from a spreadsheet and the &lt;code&gt;numpy&lt;/code&gt; library to perform calculations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="c1"&gt;# Load in data from spreadsheet
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_excel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;business_data.xlsx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Calculate total business income
&lt;/span&gt;&lt;span class="n"&gt;business_income&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Business Income&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Calculate total business expenses
&lt;/span&gt;&lt;span class="n"&gt;business_expenses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Business Expenses&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Calculate net business income
&lt;/span&gt;&lt;span class="n"&gt;net_business_income&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;business_income&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;business_expenses&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Business Income: $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;business_income&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Business Expenses: $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;business_expenses&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Net Business Income: $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;net_business_income&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script assumes that you have a spreadsheet with columns for business income and business expenses. You can modify the script to fit your specific needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Business Mileage and Travel Expenses&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Next, let's look at a script to calculate business mileage and travel expenses. We'll use the &lt;code&gt;pandas&lt;/code&gt; library to read in our data from a spreadsheet and the &lt;code&gt;numpy&lt;/code&gt; library to perform calculations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;

&lt;span class="c1"&gt;# Load in data from spreadsheet
&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_excel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;travel_data.xlsx&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Calculate total business mileage
&lt;/span&gt;&lt;span class="n"&gt;business_mileage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Business Mileage&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Calculate total travel expenses
&lt;/span&gt;&lt;span class="n"&gt;travel_expenses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Travel Expenses&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Calculate net business travel expenses
&lt;/span&gt;&lt;span class="n"&gt;net_business_travel_expenses&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;travel_expenses&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;business_mileage&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.58&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Business Mileage: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;business_mileage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; miles&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Travel Expenses: $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;travel_expenses&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Net Business Travel Expenses: $&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;net_business_travel_expenses&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script assumes that you have a spreadsheet with columns for business mileage and travel expenses. You can modify the script to fit your specific needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Automating your taxes using Python scripts can save you time, reduce stress, and ensure accuracy and efficiency. By using scripts to calculate business income and expenses, as well as business mileage and travel expenses, you can streamline your tax-related tasks and focus on growing your business.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get started today!&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Download Python and install the necessary libraries (pandas and numpy).&lt;/li&gt;
&lt;li&gt;  Create a spreadsheet to track your income and expenses.&lt;/li&gt;
&lt;li&gt;  Modify the scripts provided to fit your specific needs.&lt;/li&gt;
&lt;li&gt;  Run the scripts to automate your tax-related tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these scripts, you'll be able to automate your taxes and focus on what matters most – growing your business and achieving success as a freelancer.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>freelance</category>
      <category>taxes</category>
      <category>automation</category>
    </item>
    <item>
      <title>How to Build a Profitable Niche Website as a Developer</title>
      <dc:creator>qing</dc:creator>
      <pubDate>Wed, 26 Aug 2026 16:00:06 +0000</pubDate>
      <link>https://dev.to/qingluan/how-to-build-a-profitable-niche-website-as-a-developer-1d0j</link>
      <guid>https://dev.to/qingluan/how-to-build-a-profitable-niche-website-as-a-developer-1d0j</guid>
      <description>&lt;h1&gt;
  
  
  How to Build a Profitable Niche Website as a Developer
&lt;/h1&gt;

&lt;p&gt;tags: website, money, seo, passive&lt;/p&gt;




&lt;p&gt;tags: website, money, seo, passive&lt;/p&gt;




&lt;p&gt;tags: website, money, seo, passive&lt;/p&gt;




&lt;p&gt;tags: website, money, seo, passive&lt;/p&gt;




&lt;p&gt;tags: website, money, seo, passive&lt;/p&gt;




&lt;p&gt;tags: website, money, seo, passive&lt;/p&gt;




&lt;p&gt;tags: website, money, seo, passive&lt;/p&gt;




&lt;p&gt;tags: website, money, seo, passive&lt;/p&gt;




&lt;h2&gt;
  
  
  Building a Profitable Niche Website as a Developer
&lt;/h2&gt;

&lt;p&gt;Imagine having a side hustle that not only brings in a steady stream of income, but also allows you to leverage your skills as a developer to build something meaningful. A profitable niche website can be that side hustle, and in this article, we'll walk through the steps to build one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing a Profitable Niche
&lt;/h2&gt;

&lt;p&gt;Before we dive into the technical aspects of building a niche website, we need to choose a profitable niche. This can be a challenging step, as there are countless options to choose from. However, here are a few tips to help you get started:&lt;/p&gt;

&lt;h3&gt;
  
  
  Research Popular Niches
&lt;/h3&gt;

&lt;p&gt;Start by researching popular niches online. Use tools like Google Trends, Keyword Planner, or Ahrefs to identify trending topics. You can also use social media platforms to see what people are talking about.&lt;/p&gt;

&lt;h3&gt;
  
  
  Identify Your Expertise
&lt;/h3&gt;

&lt;p&gt;Next, think about what you're knowledgeable about. What skills do you have that could be applicable to a niche website? Perhaps you're a coding expert, a writer, or a designer. Whatever your skills, use them to your advantage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validate Your Niche
&lt;/h3&gt;

&lt;p&gt;Once you've identified a potential niche, it's time to validate it. Use social media, online forums, or Reddit to see if there's interest in your chosen niche. Create a Facebook group or a Discord channel to gauge interest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Your Website
&lt;/h2&gt;

&lt;p&gt;Now that we've chosen a profitable niche, it's time to build our website. Here are the technical steps to follow:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Choose a Platform
&lt;/h3&gt;

&lt;p&gt;There are two primary options for building a niche website: a Content Management System (CMS) or a custom-built solution. For most developers, a CMS like WordPress or Ghost is a good starting point. However, if you want more control over your website, consider building a custom solution using a framework like Django or Flask.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Set Up Hosting
&lt;/h3&gt;

&lt;p&gt;Once you've chosen a platform, it's time to set up hosting. You can choose from a range of options, including shared hosting, VPS hosting, or cloud hosting. For most small websites, shared hosting is sufficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Design Your Website
&lt;/h3&gt;

&lt;p&gt;Next, design your website. Use a tool like WordPress' built-in page builder or a third-party plugin like Elementor to create a visually appealing website. For custom-built solutions, use a framework like Bootstrap to create a responsive design.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Develop Your Website
&lt;/h3&gt;

&lt;p&gt;Now it's time to develop your website. Use your chosen framework or CMS to create the necessary pages, templates, and functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Optimize for SEO
&lt;/h3&gt;

&lt;p&gt;Finally, optimize your website for SEO. Use tools like Google Search Console and Google Analytics to monitor your website's performance. Use meta tags, header tags, and internal linking to improve your website's search engine ranking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Content
&lt;/h2&gt;

&lt;p&gt;Creating content is the heart of a niche website. Here are a few tips to keep in mind:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create High-Quality Content
&lt;/h3&gt;

&lt;p&gt;Create content that's informative, engaging, and well-written. Use a conversational tone and avoid jargon.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Use Content Marketing Strategies
&lt;/h3&gt;

&lt;p&gt;Use content marketing strategies like email marketing, social media marketing, and influencer marketing to promote your content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Repurpose Your Content
&lt;/h3&gt;

&lt;p&gt;Repurpose your content to reach a wider audience. Turn blog posts into videos, podcasts, or infographics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monetizing Your Website
&lt;/h2&gt;

&lt;p&gt;Monetizing a niche website can be a challenge, but here are a few strategies to consider:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Use Affiliate Marketing
&lt;/h3&gt;

&lt;p&gt;Use affiliate marketing to earn commissions on products or services that align with your niche.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create Digital Products
&lt;/h3&gt;

&lt;p&gt;Create digital products like ebooks, courses, or software that solve problems for your audience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Offer Services
&lt;/h3&gt;

&lt;p&gt;Offer services like consulting, coaching, or freelance work that align with your expertise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Example: Creating a Simple Blog with Flask
&lt;/h2&gt;

&lt;p&gt;Here's a simple example of creating a blog using Flask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;render_template&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask_sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLAlchemy&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SQLALCHEMY_DATABASE_URI&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sqlite:///blog.db&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SQLAlchemy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;primary_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code creates a simple blog with a SQLite database and a Flask app. The &lt;code&gt;Post&lt;/code&gt; model represents a blog post, and the &lt;code&gt;index&lt;/code&gt; function returns a list of all posts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a profitable niche website as a developer requires a combination of technical skills, creativity, and marketing expertise. By choosing a profitable niche, building a website with a CMS or custom-built solution, creating high-quality content, and monetizing your website with affiliate marketing, digital products, or services, you can create a side hustle that brings in a steady stream of income. Remember to optimize your website for SEO, use content marketing strategies, and repurpose your content to reach a wider audience. With this guide, you're ready to start building your profitable niche website today!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you found this helpful, consider &lt;a href="https://ko-fi.com/qingluan" rel="noopener noreferrer"&gt;buying me a coffee ☕&lt;/a&gt; — it keeps these articles coming!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Also check out my AI tools collection: &lt;a href="https://ai.xn--4ds5cia.xn--rhqv96g/" rel="noopener noreferrer"&gt;AI 次元世界&lt;/a&gt; — free AI tools for developers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>website</category>
      <category>money</category>
      <category>seo</category>
      <category>passive</category>
    </item>
  </channel>
</rss>
