<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Marvin Lanhenke on Medium]]></title>
        <description><![CDATA[Stories by Marvin Lanhenke on Medium]]></description>
        <link>https://medium.com/@marvinlanhenke?source=rss-1ea0548a5421------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*iEJ9qn9i9dTw3uyXG2jsTA.jpeg</url>
            <title>Stories by Marvin Lanhenke on Medium</title>
            <link>https://medium.com/@marvinlanhenke?source=rss-1ea0548a5421------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 11 May 2026 16:54:46 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@marvinlanhenke/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Breaking Through the GIL Barrier With Asyncio and Multithreading]]></title>
            <link>https://python.plainenglish.io/breaking-through-the-gil-barrier-with-asyncio-and-multithreading-d6b44bb6e0e9?source=rss-1ea0548a5421------2</link>
            <guid isPermaLink="false">https://medium.com/p/d6b44bb6e0e9</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[multithreading]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[developer]]></category>
            <category><![CDATA[asynchronous]]></category>
            <dc:creator><![CDATA[Marvin Lanhenke]]></dc:creator>
            <pubDate>Tue, 21 Mar 2023 13:16:46 GMT</pubDate>
            <atom:updated>2023-03-21T13:16:46.131Z</atom:updated>
            <content:encoded><![CDATA[<h4>Python Concurrency</h4><h4>How to run blocking libraries concurrently with Asyncio, Multithreading, and Python</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*I-qHg3uk0sVns5Fp" /><figcaption>Photo by <a href="https://unsplash.com/@cavespider?utm_source=medium&amp;utm_medium=referral">Crispin Jones</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Python is extremely popular. It’s widely used. And it offers a plethora of third-party libraries and frameworks to choose from.</p><p>When it comes to developing asynchronous applications, Asyncio and other non-blocking libraries like Aiohttp are natural fits and easy choices to make. Unfortunately <em>(or luckily — that depends)</em>, we don’t always start from scratch.</p><blockquote>Most of the time, we manage existing code. Existing code that uses blocking I/O libraries.</blockquote><p>And with blocking libraries there is no concurrency. There are no sweet performance gains. Asynchrony? Forget about it!</p><p>Fortunately, this is not quite the case.</p><p>In the following sections, we’ll dive into the details of how we can leverage multithreading, thread pools, and Asyncio to run blocking I/O operations concurrently. Allowing us to still harness some of the desired performance gains.</p><p>So, if you are looking to unlock the full potential of your Python code, keep reading to find out how to use Asyncio with multithreading.</p><h4>Release the GIL (and not the Kraken)</h4><p>The Global Interpreter Lock (GIL) is a mechanism in Python that ensures only one thread executes Python bytecode at a time.</p><p><a href="https://medium.com/geekculture/unlock-the-power-of-python-a-beginners-guide-to-concurrency-c80b6f2aef3a">Unlock the Power of Python: A Beginner’s Guide to Concurrency</a></p><p>This means that even if we have multiple threads in our program, only one thread can execute Python code at a given time. So after all, multithreading may not seem like an effective approach for improving performance in Python.</p><p>However, when it comes to I/O-bound tasks, such as reading from or writing to a file, network, or database, the GIL is temporarily released, allowing other threads to run Python code. This means that even though Python only allows one thread to run Python code at a time, it is possible to use multiple threads to achieve better performance when performing I/O-bound tasks.</p><p>When working with existing code that uses blocking I/O libraries, using Asyncio with multithreading can be an effective way to improve performance.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/666/1*C3yDNRK8ZDV8_P7uAJF1vw.png" /><figcaption>Multithreading with blocking I/O [Image by Author]</figcaption></figure><p>Python’s threading module allows us to manage threads, so we can take advantage of the temporary release of the GIL to run multiple threads concurrently. This way, multiple operations never block each other and only block the thread they are running in.</p><h4>A Pool Full of Threads</h4><p>While Python’s threading module is a great place to start, we don’t really want to create and keep track of every single thread individually. Luckily for us, we can utilize something called a ThreadPoolExecutor.</p><p>But before doing any of that, let’s set the stage with an easy example. Let’s make a few basic web requests by making use of the library requests <em>(well, that came as a surprise…)</em>.</p><pre>import time<br>import requests<br><br>def get_status_code(url: str) -&gt; int:<br>    response = requests.get(url)<br>    return response.status_code<br><br>def main() -&gt; None:<br>    start = time.time()<br><br>    urls = [&#39;https://www.example.com&#39; for _ in range(100)]<br>    for url in urls:<br>        get_status_code(url)<br><br>    end = time.time()<br>    print(f&quot;Total time: {end-start:.4f} seconds&quot;)<br><br>main()</pre><p>In this example we make 10 sequential requests, fetching the status code from the specified URL. Running this simple script takes roughly 48 seconds to finish.</p><p>Now, let’s try to improve it by using multiple threads.</p><pre>import time<br>import requests<br><br>from concurrent.futures import ThreadPoolExecutor<br><br>def get_status_code(url: str) -&gt; int:<br>    response = requests.get(url)<br>    return response.status_code<br><br>def main() -&gt; None:<br>    start = time.time()<br><br>    with ThreadPoolExecutor() as pool:<br>        urls = [&#39;https://www.example.com&#39; for _ in range(100)]<br>        pool.map(get_status_code, urls)<br><br>    end = time.time()<br>    print(f&quot;Total time: {end-start:.4f} seconds&quot;)<br><br>main()</pre><p>The ThreadPoolExecutor provides a convenient way to execute tasks concurrently without having to manage threads directly. It automatically creates and manages a pool of threads to which we can submit work.</p><p>This allows us to run the blocking requests in separate threads concurrently. Running the example from above takes about 4.5 seconds. That’s 10x faster than before.</p><blockquote><strong>Note</strong>: Determining how many worker threads to create is quite complicated. The formula for the default number of threads is min(32, os.cpu_count() +4).</blockquote><h4>Pool Skimming with Asyncio</h4><p>Making use of the ThreadPoolExecutor is just fine. It works. However, we can still improve our code by incorporating the use of Asyncio.</p><p>In Python 3.9, the asyncio.to_thread coroutine was introduced. This allows us to further simplify putting work on the default thread pool executor. It takes in a function and a set of arguments to run in a thread.</p><pre>import time<br>import asyncio<br>import requests<br><br>def get_status_code(url: str) -&gt; int:<br>    response = requests.get(url)<br>    return response.status_code<br><br>async def main() -&gt; None:<br>    start = time.time()<br><br>    urls = [&#39;https://www.example.com&#39; for _ in range(100)]<br>    # Create a list of coroutines, putting work on the thread pool executor<br>    coroutines = [asyncio.to_thread(get_status_code, url) for url in urls]<br><br>    await asyncio.gather(*coroutines)<br><br>    end = time.time()<br>    print(f&quot;Total time: {end-start:.4f} seconds&quot;)<br><br><br>asyncio.run(main())</pre><p>And that’s all there is to it.</p><p>By using asyncio.to_thread, we were able to reduce the number of lines of code and simplify our program. Despite this, it still takes around 4.5 seconds to execute, which seems reasonable. Essentially, we have just employed a more straightforward and compact syntax.</p><p>Up to this point, we have compared two distinct methods for making requests: One sequentially and another concurrently using multiple threads. However, there exists a third approach that involves making requests concurrently within a single thread using Aiohttp.</p><pre>import time<br>import aiohttp<br>import asyncio<br><br>from aiohttp import ClientSession<br><br>async def fetch_status(session: ClientSession, url: str) -&gt; int:<br>    async with session.get(url) as response:<br>        return response.status<br><br>async def main() -&gt; None:<br>    start = time.time()<br><br>    async with aiohttp.ClientSession() as session:<br>        urls = [&#39;https://www.example.com&#39; for _ in range(100)]<br>        requests = [fetch_status(session, url) for url in urls]<br><br>        await asyncio.gather(*requests)<br><br>    end = time.time()<br>    print(f&quot;Total time: {end-start:.4f} seconds&quot;)<br><br><br>loop = asyncio.get_event_loop()<br>loop.run_until_complete(main())</pre><p>Executing the single-threaded version of the program takes merely 0.6 seconds, which is 7.5 times faster than the multi-threaded approach.</p><p>The reason behind this difference in speed is that creating threads involves additional overhead. When threads are created, the operating system has to allocate more resources than it does for coroutines. Moreover, threads have to undergo context-switching at the OS level, which can incur an additional cost.</p><p>By using Aiohttp and a single-threaded approach, we can avoid all these extra costs and achieve even greater performance gains. In comparison to the synchronous blocking method, which took 48 seconds to execute, we have boosted our performance by a factor of 80.</p><h4>Conclusion</h4><p>Python offers various techniques to attain concurrency and enhance the performance of your applications. Using Asyncio and multithreading, we can create more efficient programs that can handle multiple tasks concurrently.</p><p>By utilizing thread pools and async/await syntax, we can achieve concurrency, even with blocking libraries, without having to manage threads directly. Additionally, we learned that Aiohttp offers a more streamlined and effective way to execute requests concurrently within a single thread, resulting in even better performance gains compared to traditional multithreading techniques.</p><p><a href="https://medium.com/@marvinlanhenke/list/fe3c84a64389">Python Concurrency</a></p><p><em>If you enjoyed the read, make sure to hit ‘follow’ for more on Python concurrency and advanced techniques to take your programming skills to the next level.</em></p><p><em>Consider becoming a </em><a href="https://medium.com/@marvinlanhenke/membership"><em>Medium member</em></a><em> and continue learning with no limits. I’ll receive a portion of your membership fee if you use the following link, at no extra cost to you.</em></p><p><a href="https://medium.com/@marvinlanhenke/membership">Join Medium with my referral link - Marvin Lanhenke</a></p><p><strong>References / Further Material:</strong></p><ul><li>Fowler, Matthew. (2022). Python Concurrency with Asyncio. Manning Publications.</li></ul><p><em>More content at </em><a href="https://plainenglish.io/"><strong><em>PlainEnglish.io</em></strong></a><em>.</em></p><p><em>Sign up for our </em><a href="http://newsletter.plainenglish.io/"><strong><em>free weekly newsletter</em></strong></a><em>. Follow us on </em><a href="https://twitter.com/inPlainEngHQ"><strong><em>Twitter</em></strong></a>, <a href="https://www.linkedin.com/company/inplainenglish/"><strong><em>LinkedIn</em></strong></a><em>, </em><a href="https://www.youtube.com/channel/UCtipWUghju290NWcn8jhyAw"><strong><em>YouTube</em></strong></a><em>, and </em><a href="https://discord.gg/GtDtUAvyhW"><strong><em>Discord</em></strong></a><strong><em>.</em></strong></p><p><strong><em>Interested in scaling your software startup</em></strong><em>? Check out </em><a href="https://circuit.ooo?utm=publication-post-cta"><strong><em>Circuit</em></strong></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d6b44bb6e0e9" width="1" height="1" alt=""><hr><p><a href="https://python.plainenglish.io/breaking-through-the-gil-barrier-with-asyncio-and-multithreading-d6b44bb6e0e9">Breaking Through the GIL Barrier With Asyncio and Multithreading</a> was originally published in <a href="https://python.plainenglish.io">Python in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Concurrent Web Requests with Aiohttp: Get More Done in Less Time]]></title>
            <link>https://levelup.gitconnected.com/concurrent-web-requests-with-aiohttp-get-more-done-in-less-time-9b4adada2a07?source=rss-1ea0548a5421------2</link>
            <guid isPermaLink="false">https://medium.com/p/9b4adada2a07</guid>
            <category><![CDATA[asynchronous]]></category>
            <category><![CDATA[asyncio]]></category>
            <category><![CDATA[python-programming]]></category>
            <category><![CDATA[aiohttp]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Marvin Lanhenke]]></dc:creator>
            <pubDate>Tue, 14 Mar 2023 15:19:32 GMT</pubDate>
            <atom:updated>2023-03-14T15:19:32.088Z</atom:updated>
            <content:encoded><![CDATA[<h4>Python Concurrency</h4><h4>Discovering Aiohttp for Faster, Concurrent Web Requests</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*b-_IfcF65eYFpx9e" /><figcaption>Photo by <a href="https://unsplash.com/@aronvisuals?utm_source=medium&amp;utm_medium=referral">Aron Visuals</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Are you tired?</p><p>Tired of waiting for your requests to complete one by one.</p><p>Being stuck. Waiting. Only to be met and overwhelmed with the feeling of frustration and disappointment when the request finally times out? Have you tried using async/await everywhere, only to find out that most libraries are blocking anyway?</p><blockquote>Fear not, as the answer to your problems lies in Aiohttp.</blockquote><p>In the following sections, we will explore the beautifully concurrent world of <a href="http://docs.aiohttp.org/en/stable">Aiohttp</a>. A popular asynchronous HTTP client/server library for Python.</p><p>We will discover how to make non-blocking web requests that run concurrently and improve the application&#39;s performance. By the end of this blog post, you’ll not only be armed with the knowledge of how to use Aiohttp but also how exceptions can be handled or asynchronous context managers work.</p><p>So don’t go anywhere, take a seat, fire up your IDE, and let’s get started.</p><h4>No more Blocking: Introducing Aiohttp</h4><p>It’s all about concurrency. Allowing multiple tasks to be executed simultaneously. That’s why asynchronous programming and libraries like Python’s Asyncio exist in the first place.</p><p><a href="https://levelup.gitconnected.com/the-beginners-guide-to-asyncio-in-python-a-deeper-dive-into-coroutines-and-tasks-9a289e061b88">The Beginner’s Guide to Asyncio in Python: A Deeper Dive into Coroutines and Tasks</a></p><p>However, one of the most common mistakes we tend to make <em>(Yep, I did it too)</em> is to apply the async/await syntax to every line of code we can get our hands on and hope for the best.</p><p>Well, most of the time the best is — nothing. Nothing happens at all. No concurrency. No sweet performance gains. But why?</p><p>Unfortunately, most libraries are blocking, meaning that they will block the main thread and event loop, rendering async/await basically ineffective. This is where non-blocking libraries like Aiohttp come into play. By using non-blocking sockets and utilizing asynchronous context managers, Aiohttp allows for efficient acquisition and closure of HTTP sessions, leading to improved performance and a more pythonic way of working [1].</p><p>Before diving deep into the inner workings of Aiohttp, let’s take a small detour and talk about asynchronous context managers first.</p><h4>Managing Asynchrony: The Pythonic Way</h4><p>It’s very common to deal with resources in a way that requires them to be opened and then to be closed. Think of a file for example.</p><p>We open it. We read it. We close it. Nothing fancy so far.</p><p>However, we need to be careful not to leak any resources. If for any reason an exception is raised our resource might never be properly closed. To avoid any leaking resources we have several options to choose from.</p><p>First, we can wrap our code in a try/finally block, making sure the resource will be closed no matter what. Second, we can apply a more pythonic way of dealing with resources. Context managers [2].</p><pre># Use of a synchronous context manager<br>with open(&quot;example.txt&quot;, &quot;r&quot;) as f:<br>    contents = f.read()<br>    print(contents)</pre><p>In Python, context managers are used to ensure that resources are properly closed even if an exception is raised during runtime [3]. However, traditional context managers only work with synchronous code.</p><p>With the introduction of asynchronous context managers [4], we can now manage resources asynchronously by using the async with syntax. Now, we can acquire and close resources like HTTP sessions more cleanly and in a more Pythonic way. This is why asynchronous context managers lay at the core of Aiohttp.</p><p>Let’s take a look at a super basic example to illustrate and understand the way asynchronous context managers work.</p><pre>import asyncio<br><br># Implement the context manager protocol<br>class AsyncContextManager:<br>    async def __aenter__(self):<br>        print(&quot;Entering async context...&quot;)<br>        return self<br>    <br>    async def __aexit__(self, exc_type, exc_value, traceback):<br>        print(&quot;Exiting async context...&quot;)<br>        return False<br><br># Define the main coroutine<br>async def main():<br>    async with AsyncContextManager():<br>        print(&quot;Inside async context...&quot;)<br><br>asyncio.run(main())</pre><p>In this example, we define an AsyncContextManager class that implements the async context manager protocol by defining the __aenter__ and __aexit__ methods.</p><p>When the async with block is executed, the __aenter__ method is called, which in this case simply prints a message to indicate that the async context has been entered. The code inside the async with block is then executed, which in this example just prints another message.</p><p>When the async with block is exited, the __aexit__ method is called, which also prints a message to indicate that the async context has been exited.</p><p>If an exception occurs inside the async with block, the __aexit__ method is called with the details of the exception, allowing the context manager to handle the exception if necessary.</p><h4>Making Concurrent Web Requests with Aiohttp</h4><p>Now that we know about non-blocking libraries and resource handling with asynchronous context managers, it’s finally time to make some requests.</p><p>Non-blocking requests. Concurrently. Of course.</p><p>But, before we do any of that. Let’s do it the old-fashioned way first — synchronously.</p><pre>import time<br>import requests<br><br><br>def fetch_status(url: str) -&gt; int:<br>    response = requests.get(url)<br>    return response.status_code<br><br><br>def main() -&gt; None:<br>    start = time.time()<br><br>    urls = [&#39;http://python.org&#39; for _ in range(10)]<br>    results = [fetch_status(url) for url in urls]<br>    print(results)<br><br>    end = time.time()<br>    print(f&quot;Total time: {end-start:.4f} seconds&quot;)<br><br><br>main()</pre><p>In this example, we make use of the requests library, which is blocking by default. We simply execute 10 requests sequentially and fetch the status code.</p><p>Running this code takes about 1.4 seconds.</p><p>Now, let’s do the same thing again. However this time we make use of Aiohttp.</p><pre>import time<br>import aiohttp<br>import asyncio<br><br>from aiohttp import ClientSession<br><br><br>async def fetch_status(session: ClientSession, url: str) -&gt; int:<br>    # Use ClientSession to make a GET Request<br>    async with session.get(url) as response:<br>        return response.status<br><br><br>async def main() -&gt; None:<br>    start = time.time()<br><br>    # Acquire new ClientSession<br>    async with aiohttp.ClientSession() as session:<br>        urls = [&#39;http://python.org&#39; for _ in range(10)]<br>        requests = [fetch_status(session, url) for url in urls]<br><br>        results = await asyncio.gather(*requests)<br>        print(results)<br><br>    end = time.time()<br>    print(f&quot;Total time: {end-start:.4f} seconds&quot;)<br><br><br>loop = asyncio.get_event_loop()<br>loop.run_until_complete(main())</pre><p>Running the code above takes only about 0.2 seconds.</p><p>7x faster than before. This is the power of concurrency.</p><p>So how does this work?</p><p>In order to make web requests Aiohttp relies on the concept of sessions, where one session can have multiple connections open. This is known as “Connection pooling” a technique for managing a pool of reusable network connections to a server [5]. This allows us to avoid the overhead of creating a new connection for each request.</p><p>Once we obtained a session, we can make our GET requests. We utilize our helper coroutine fetch_status to create multiple requests and schedule them on the event loop by using asyncio.gather.</p><h4>Things will fail. They simply do.</h4><p>A lot of things can go wrong when making a network request. Unreliable connections. Bad requests. Data errors. All of these issues can cause our request to run indefinitely. Thus, we need a way to time out.</p><p>Luckily for us, we can make use of Aiohttp’s ClientTimeout data structure.</p><pre>import aiohttp<br>import asyncio<br><br>from aiohttp import ClientSession<br><br><br>async def fetch_status(session: ClientSession, url: str) -&gt; int:<br>    # Apply a timeout at request level<br>    request_timeout = aiohttp.ClientTimeout(total=0.2)<br>    async with session.get(url, timeout=request_timeout) as response:<br>        return response.status<br><br><br>async def main() -&gt; None:<br>    # Apply a timeout at session level<br>    session_timeout = aiohttp.ClientTimeout(total=1.0, connect=0.2)<br>    async with aiohttp.ClientSession(timeout=session_timeout) as session:<br>        result = await fetch_status(session, &#39;http://python.org&#39;)<br>        print(result)<br><br><br>loop = asyncio.get_event_loop()<br>loop.run_until_complete(main())</pre><p>In the example above, we simply specify two timeouts. One at the session and the other at the request level. If our request, for example, takes too long an asyncio.TimeoutError will be raised.</p><p>But what if a single request fails? What about exception handling?</p><p>Unfortunately, exception handling when running multiple requests with asyncio.gather is a bit clunky. However, we can make use of the parameter return_exceptions=True which will include all exceptions raised in the result list. This allows us to handle the exceptions accordingly.</p><pre>import aiohttp<br>import asyncio<br><br>from aiohttp import ClientSession<br><br><br>async def fetch_status(session: ClientSession, url: str) -&gt; int:<br>    async with session.get(url) as response:<br>        return response.status<br><br><br>async def main() -&gt; None:<br>    async with aiohttp.ClientSession() as session:<br>        urls = [&#39;http://python.org&#39;, &#39;invalid://address.org&#39;]<br>        requests = [fetch_status(session, url) for url in urls]<br>        <br>        # Include raised exceptions in result list<br>        results = await asyncio.gather(*requests, return_exceptions=True)<br>        # Outputs: [200, AssertionError()]        <br>        print(results)<br>        <br><br>loop = asyncio.get_event_loop()<br>loop.run_until_complete(main())</pre><h4>Just slightly more control. Please.</h4><p>Using asyncio.gather is convenient. But it has its drawbacks.</p><p>Exception handling is somewhat clunky and additionally, we have to wait. We have to wait until all requests are completed before we can proceed to work with any of the results. So if there is just one bad request, that takes forever — we’ll most likely end up waiting forever.</p><p>Fortunately, there is another way.</p><p>We can make use of asyncio.wait which takes a list of awaitables and returns two sets. A set of tasks that are finished, and a set of tasks that are pending.</p><pre>import aiohttp<br>import asyncio<br><br>from aiohttp import ClientSession<br><br><br>async def fetch_status(session: ClientSession, url: str, delay: int) -&gt; int:<br>    await asyncio.sleep(delay)<br>    async with session.get(url) as response:<br>        return response.status<br><br><br>async def main() -&gt; None:<br>    async with aiohttp.ClientSession() as session:<br>        fetchers = [<br>            asyncio.create_task(fetch_status(session, &#39;http://python.org&#39;, 1)),<br>            asyncio.create_task(fetch_status(session, &#39;http://python.org&#39;, 1)),<br>        ]<br><br>        # Wait for all tasks to be completed<br>        done, pending = await asyncio.wait(fetchers)<br><br>        for done_task in done:<br>            result = await done_task<br>            print(result)<br><br><br>loop = asyncio.get_event_loop()<br>loop.run_until_complete(main())</pre><p>In the example above, we get the same effect as if we’d use asyncio.gather. We run our requests concurrently and wait until all tasks are completed.</p><p>However, with asyncio.wait we can specify a return_when parameter.</p><p>Let’s slightly modify the example and include long-running requests. We also want to make sure to set return_when=FIRST_COMPLETED to return the result of whatever task finishes first.</p><p>We loop over a set of pending tasks and call async.wait on that set with each iteration. Once we have a result, we update done and pending and print out any results as soon as possible.</p><pre>import aiohttp<br>import asyncio<br><br>from aiohttp import ClientSession<br><br><br>async def fetch_status(session: ClientSession, url: str, delay: int) -&gt; int:<br>    await asyncio.sleep(delay)<br>    async with session.get(url) as response:<br>        return response.status<br><br><br>async def main() -&gt; None:<br>    async with aiohttp.ClientSession() as session:<br>        # Create a set of pending tasks with different delays<br>        pending = [<br>            asyncio.create_task(fetch_status(session, &#39;http://python.org&#39;, 3)),<br>            asyncio.create_task(fetch_status(session, &#39;http://python.org&#39;, 1)),<br>            asyncio.create_task(fetch_status(session, &#39;http://python.org&#39;, 2)),<br>        ]<br><br>        # Loop over the set as long as tasks are pending<br>        while pending:<br>            # Update both sets<br>            done, pending = await asyncio.wait(<br>                pending,<br>                return_when=asyncio.FIRST_COMPLETED,<br>            )<br><br>            print(f&quot;Tasks done: {len(done)}&quot;)<br>            print(f&quot;Tasks pending: {len(pending)}&quot;)<br>            <br>            # Print results that are already done<br>            for done_task in done:<br>                result = await done_task<br>                print(result)<br><br><br>loop = asyncio.get_event_loop()<br>loop.run_until_complete(main())<br><br># Output:<br># Tasks done: 1<br># Tasks pending: 2<br># 200<br># Tasks done: 1<br># Tasks pending: 1<br># 200<br># Tasks done: 1<br># Tasks pending: 0<br># 200</pre><p>While this approach is definitely less convenient than the use of asyncio.gather and more verbose it allows for more fine-grained control.</p><p>As soon as one task is completed we can proceed to work with its result. Moreover, we get the ability to handle each task individually, which also includes exception handling or the cancellation of a task.</p><h4>Conclusion</h4><p>Aiohttp provides a solution to the issue of blocking libraries and allows for concurrent web requests and efficient acquisition and closure of HTTP sessions. This leads to improved performance and a more pythonic way of working.</p><p>However, it is important to note that while Aiohttp offers a significant improvement in performance, it may not be the best solution for every scenario. Additionally, it’s important to handle exceptions and timeouts appropriately when using Aiohttp and Asyncio in general.</p><p>This blog post only scratches the surface of what can be accomplished with non-blocking libraries, and there are many more to discover.</p><p><em>If you enjoyed the read, make sure to hit ‘follow’ for more on Python concurrency and advanced techniques to take your programming skills to the next level.</em></p><p><em>Consider becoming a </em><a href="https://medium.com/@marvinlanhenke/membership"><em>Medium member</em></a><em> and continue learning with no limits. I’ll receive a portion of your membership fee if you use the following link, at no extra cost to you.</em></p><p><a href="https://medium.com/@marvinlanhenke/membership">Join Medium with my referral link - Marvin Lanhenke</a></p><p><strong>References / Further Material:</strong></p><ul><li>[1] <a href="https://peps.python.org/pep-0020/">The Zen of Python</a></li><li>[2] <a href="https://towardsdatascience.com/why-you-should-use-context-managers-in-python-4f10fe231206">Why You Should Use Context Managers in Python</a></li><li>[3] <a href="https://www.geeksforgeeks.org/context-manager-in-python/">https://www.geeksforgeeks.org/context-manager-in-python/</a></li><li>[4] <a href="https://peps.python.org/pep-0492/">https://peps.python.org/pep-0492/</a></li><li>[5] <a href="https://www.cockroachlabs.com/blog/what-is-connection-pooling/">https://www.cockroachlabs.com/blog/what-is-connection-pooling/</a></li><li>Fowler, Matthew. (2022). Python Concurrency with Asyncio. Manning Publications.</li></ul><h3>Level Up Coding</h3><p>Thanks for being a part of our community! Before you go:</p><ul><li>👏 Clap for the story and follow the author 👉</li><li>📰 View more content in the <a href="https://levelup.gitconnected.com/?utm_source=pub&amp;utm_medium=post">Level Up Coding publication</a></li><li>💰 Free coding interview course ⇒ <a href="https://skilled.dev/?utm_source=luc&amp;utm_medium=article">View Course</a></li><li>🔔 Follow us: <a href="https://twitter.com/gitconnected">Twitter</a> | <a href="https://www.linkedin.com/company/gitconnected">LinkedIn</a> | <a href="https://newsletter.levelup.dev">Newsletter</a></li></ul><p>🚀👉 <a href="https://jobs.levelup.dev/talent/welcome?referral=true"><strong>Join the Level Up talent collective and find an amazing job</strong></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9b4adada2a07" width="1" height="1" alt=""><hr><p><a href="https://levelup.gitconnected.com/concurrent-web-requests-with-aiohttp-get-more-done-in-less-time-9b4adada2a07">Concurrent Web Requests with Aiohttp: Get More Done in Less Time</a> was originally published in <a href="https://levelup.gitconnected.com">Level Up Coding</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Beginner’s Guide to Asyncio in Python: A Deeper Dive into Coroutines and Tasks]]></title>
            <link>https://levelup.gitconnected.com/the-beginners-guide-to-asyncio-in-python-a-deeper-dive-into-coroutines-and-tasks-9a289e061b88?source=rss-1ea0548a5421------2</link>
            <guid isPermaLink="false">https://medium.com/p/9a289e061b88</guid>
            <category><![CDATA[python-programming]]></category>
            <category><![CDATA[asyncio]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[asynchronous-programming]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Marvin Lanhenke]]></dc:creator>
            <pubDate>Wed, 08 Mar 2023 03:40:58 GMT</pubDate>
            <atom:updated>2023-03-08T03:40:58.388Z</atom:updated>
            <content:encoded><![CDATA[<h4>Python Concurrency</h4><h4>Harness the Power of Coroutines, Tasks, and Futures</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*gMa1T51tsRAO1NgB" /><figcaption>Photo by <a href="https://unsplash.com/@laurieannerobert?utm_source=medium&amp;utm_medium=referral">Laurie-Anne Robert</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Are you tired of writing slow and unresponsive code? Do you want to build applications that are fast, scalable, and responsive?</p><p>If your answer is a resounding yes, then it’s time to dive into the world of asynchronous programming with Python’s Asyncio library!</p><p>Asynchronous programming has become a critical skill for modern developers, particularly in building scalable and responsive applications. Python’s Asyncio library offers a powerful and efficient way to write asynchronous code using coroutines, tasks, and futures.</p><blockquote>However, it can be daunting for beginners to dive into this new paradigm of programming.</blockquote><p>That’s why in the following sections, we’ll take a deeper dive into coroutines and tasks, and explore the power of concurrency through Asyncio.</p><p>So, if you’re ready to unlock the full potential of Asyncio in Python, look no further and fire up your IDE.</p><h4>Functions Unleashed: Coroutines</h4><p>They’re at the heart of Asyncio. The foundation.</p><p><strong>Coroutines</strong> are functions that have the ability to pause their execution [1] at specific points in time and resume later, allowing other code to run in the meantime. This “superpower” enables concurrency in our program, making it possible to write highly efficient and responsive code.</p><p>When a coroutine encounters a potentially long-running operation, it can pause its execution and allow other coroutines to run while it waits for the operation to complete without blocking the main thread of execution. Once the operation is finished, the coroutine can wake up and continue where it left off.</p><p>To <strong>create a coroutine </strong>and utilize its “superpowers”, we simply mark a function with the async keyword. Now, we have a coroutine instead of a simple function.</p><pre>import asyncio<br><br># Create a coroutine with the `async` keyword<br>async def print_message() -&gt; None:<br>  print(&#39;Hello, Asyncio&#39;)<br><br># Calling the coroutine returns an coroutine object<br>print(type(print_message()))<br><br># Output:<br># &lt;class &#39;coroutine&#39;&gt;</pre><p>Note that coroutines aren’t executed when we call them, but rather they create a coroutine object that can be run later. To actually run a coroutine, we need to use an event loop.</p><p><a href="https://medium.com/geekculture/unlock-the-power-of-python-a-beginners-guide-to-concurrency-c80b6f2aef3a">Unlock the Power of Python: A Beginner’s Guide to Concurrency</a></p><p>In order to <strong>run the coroutine</strong> and get the actual result, we have to make use of an event loop. Luckily, the Asyncio library provides a convenience function for us: asyncio.run() resembles the main entry point to an Asyncio application.</p><p>It creates a new event loop, runs the coroutine, and returns the result.</p><pre>import asyncio<br><br># Create a coroutine with the `async` keyword<br>async def print_message() -&gt; None:<br>  print(&#39;Hello, Asyncio&#39;)<br><br># Create and run the event-loop<br>asyncio.run(print_message())<br><br># Output:<br># Hello, Asyncio</pre><p>But what if we want to run <strong>multiple coroutines</strong>?</p><p>To run a coroutine we have to await it by using the await keyword. This causes the coroutine to be run and pauses its execution until it returns a value.</p><pre>import asyncio<br><br># Create a coroutine with the `async` keyword<br>async def print_message(message: str, delay: int) -&gt; None:<br>    # Simulate a long running task by introducing a delay<br>    await asyncio.sleep(delay)<br>    print(message)<br><br># Create a main coroutine that can be called by asyncio run<br># This enables us to run multiple coroutines inside the `main` coroutine<br>async def main():<br>    await print_message(&#39;Hello, World&#39;, 1)<br>    await print_message(&#39;Hello, Asyncio&#39;, 1)<br><br># Create and run the event-loop<br>asyncio.run(main())</pre><p>If we run the code above, we get Hello, World and Hello, Asyncio as an output. However, the program returns every result one-by-one each after a second. It runs in sequence. Not concurrently.</p><p>When we use the await keyword with a coroutine, it causes the coroutine to be run while also pausing the execution of the parent coroutine until it returns a value. This is known as “blocking behavior.”</p><p>To achieve true concurrency, we need something else.</p><h4>Unlocking true Concurrency with Tasks</h4><p><strong>Tasks </strong>are an essential component of Asyncio in Python.</p><p>They’re wrappers around coroutines that schedule them to run on the event loop as soon as possible, in a non-blocking manner [2]. This allows us to roughly execute multiple coroutines at the same time, which is crucial for achieving concurrency.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/584/1*IGpTgcgpUFk5WYM-ni_dOA.png" /><figcaption>Running task concurrently [Image by Author]</figcaption></figure><p>Once we create a task, we can run and execute other code. This is possible because tasks run in the background and don’t block the main thread of execution.</p><p>We can create a task using the asyncio.create_task() function, which returns a task object immediately. We can then use the await keyword to get the result of the task.</p><pre>import asyncio<br><br><br>async def print_message(message: str, delay: int) -&gt; None:<br>    await asyncio.sleep(delay)<br>    print(message)<br><br><br>async def main():<br>    # Create task objects<br>    task1 = asyncio.create_task(print_message(&#39;Hello, World&#39;, 1))<br>    task2 = asyncio.create_task(print_message(&#39;Hello, Asyncio&#39;, 1))<br>    <br>    # Await tasks to get the results<br>    await task1<br>    await task2<br><br>asyncio.run(main())</pre><p>If we run this code now, we retrieve both messages concurrently. The program takes roughly a second to complete instead of two as in the previous example.</p><blockquote><strong>Note</strong>: It’s important to note that if we don’t await a task, it will be scheduled, but almost immediately stopped and ‘cleaned up’. Leaving us with no result to retrieve.</blockquote><p>Now, we can run multiple tasks concurrently.</p><p>While this is awesome, it is also quite cumbersome to create each task manually and await it one by one. This will become verbose and messy rather quickly.</p><p>Luckily for us, we can make use of another convenience function from the Asyncio library asyncio.gather().</p><p>This function takes in a sequence of awaitables (more on that in the next section) and runs them concurrently. It is important to note, that gather will wrap a coroutine automatically with a task, so we don’t have to manually create each task by hand.</p><pre>import asyncio<br><br>async def print_message(message: str, delay: int) -&gt; None:<br>    await asyncio.sleep(delay)<br>    print(message)<br><br>async def main():<br>    coroutines = [<br>        print_message(&#39;Hello, Asyncio&#39;, 1),<br>        print_message(&#39;Hello, World&#39;, 1),<br>    ]<br><br>    # Use gather to wrap coroutines in tasks<br>    # and await to run them concurrently<br>    await asyncio.gather(*coroutines)<br><br>asyncio.run(main())</pre><blockquote><strong>Note</strong>: Since Python 3.11 we can also use <a href="https://docs.python.org/3/library/asyncio-task.html#task-groups">Task Groups</a>. Tasks can be added to a group by calling create_task() and by using an asynchronous context manager that awaits all tasks when exited.</blockquote><h4>But I don’t want to wait for Forever</h4><p>Asyncio tasks are nice.</p><p>However, they can run indefinitely which will cause us to wait forever. Thus, we need some kind of way to cancel a task.</p><p>Once again the Asyncio library has got us covered. We can <strong>cancel a task</strong> using the cancel() method, which raises a CancelledError if the task is awaited.</p><pre>import asyncio<br>from asyncio import CancelledError<br><br># Simulate a long running task<br>async def delay(seconds: int) -&gt; None:<br>    await asyncio.sleep(seconds)<br><br>async def main() -&gt; None:<br>    long_task = asyncio.create_task(delay(10))<br><br>    seconds_waited = 0<br>    # Check each second if task is done<br>    while not long_task.done():<br>        print(&#39;Task not done. Waiting for another second.&#39;)<br>        await asyncio.sleep(1)<br><br>        seconds_waited += 1<br>        # If task takes to long cancel it<br>        if seconds_waited == 5:<br>            print(&#39;Task takes to long. Cancelling.&#39;)<br>            long_task.cancel()<br><br>    try:<br>        await long_task<br>    except CancelledError:<br>        print(&#39;Task was cancelled&#39;)<br><br>asyncio.run(main())</pre><p>We can also make use of the asyncio.wait_for() function to set a timeout for a task and raise a TimeoutError if the task takes too long to complete.</p><pre>import asyncio<br>from asyncio.exceptions import TimeoutError<br><br># Simulate a long running task<br>async def delay(seconds: int) -&gt; None:<br>    await asyncio.sleep(seconds)<br><br>async def main() -&gt; None:<br>    long_task = asyncio.create_task(delay(5))<br><br>    try:<br>        # Wait for task to complete, raise TimeoutError after 1 second<br>        await asyncio.wait_for(long_task, timeout=1.0)<br>    except TimeoutError:<br>        print(&#39;Task took too long. TimeoutError&#39;)<br>        print(f&quot;Task has been cancelled: {long_task.cancelled()}&quot;)<br><br>asyncio.run(main())</pre><blockquote><strong>Note</strong>: Additionally, you can use the shield() function to prevent a task from being canceled, which can be useful in some situations where you need to guarantee that a task completes, even if other tasks are canceled.</blockquote><h4>Back to the Future</h4><p>So far we learned about coroutines and tasks.</p><p>This is awesome because those objects allow us to write programs that run concurrently and more efficiently.</p><p>But where is all of this coming from?</p><p>In Asyncio, a <strong>future </strong>is an object that represents the result of an asynchronous operation [3]. It’s a placeholder for a value that hasn’t been computed yet but will be at some point in time. You can think of “Promises” if you are familiar with Javascript. Awaiting a future will cause the execution of a program to be paused until the result is set.</p><p>A<strong> task </strong>can be thought of as a combination of a coroutine and a future. When a task is created we also create an empty future. Once the coroutine runs and finishes, the result is set. The future is now no longer empty and we can retrieve the result.</p><p>So what do all of those objects have in common? What is it that connects futures, tasks, and coroutines?</p><p>The common thread between those three classes is the Awaitable abstract base class. And this seems reasonable since coroutines, tasks, and futures can all be used with an await expression. There is an even stronger relationship between tasks and futures. Tasks directly inherit from and extend futures, which can be seen in the inheritance diagram below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/553/1*Dgu1OQazDBNnez7UmkHq8w.png" /><figcaption>Inheritance diagram [Image by Author]</figcaption></figure><h4>Common Pitfalls</h4><p>Once we know the basics, it’s tempting to use Asyncio everywhere by simply tagging each function with async/await syntax. Unfortunately, it’s not that simple and there are at least two common pitfalls we should be aware of.</p><p>One of the main pitfalls is trying to run <strong>CPU-bound</strong> code in tasks and coroutines <strong>without using multiprocessing</strong>. The GIL (Global Interpreter Lock) in Python will block the concurrent execution of code. So if we try to run CPU-bound tasks in Asyncio without using multiprocessing, we may find that our program doesn’t run as fast as we’d like.</p><p>Another common pitfall is using <strong>blocking I/O libraries</strong> or APIs without multithreading. When we use a blocking I/O library or API, it can block the main thread of execution, which in turn can block the event loop itself. This means that everything will run in sequence, and our program won’t be able to take advantage of the asynchronous nature of Asyncio.</p><p>It‘s also important to be aware of the limitations of Asyncio and to use it in situations where it makes sense. Asyncio is best suited for I/O-bound tasks that involve waiting for network or disk I/O, rather than CPU-bound tasks that involve intensive computation.</p><h4>Conclusion</h4><p>Asyncio is a powerful Python library that enables us to write asynchronous, non-blocking code for I/O-bound tasks. With its coroutines, tasks, and futures, Asyncio provides a flexible and efficient way to manage concurrent tasks in your programs.</p><p>When working with Asyncio however, there are a few common pitfalls to be aware of, such as running CPU-bound code in tasks without using multiprocessing or using blocking I/O libraries without multithreading. By avoiding these pitfalls and using Asyncio in the right situations, we can write more efficient, non-blocking, and responsive code.</p><p>While this blog post has covered just a few of the basics of Asyncio, there is much more to explore. With advanced features such as semaphores, queues, and streams, we can create even more powerful and sophisticated asynchronous programs.</p><p><em>If you enjoyed the read, make sure to hit ‘follow’ for more on Python concurrency and advanced techniques to take your programming skills to the next level.</em></p><p><em>Become a </em><a href="https://medium.com/@marvinlanhenke/membership"><em>Medium member</em></a><em> and continue learning with no limits. I’ll receive a portion of your membership fee if you use the following link, at no extra cost to you.</em></p><p><a href="https://medium.com/@marvinlanhenke/membership">Join Medium with my referral link - Marvin Lanhenke</a></p><p><strong>References / Further Material:</strong></p><ul><li>[1] <a href="https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/">https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/</a></li><li>[2] <a href="https://docs.python.org/3/library/asyncio-task.html#creating-tasks">https://docs.python.org/3/library/asyncio-task.html#creating-tasks</a></li><li>[3] <a href="https://docs.python.org/3/library/asyncio-future.html#future-object">https://docs.python.org/3/library/asyncio-future.html#future-object</a></li><li>Fowler, Matthew. (2022). Python Concurrency with Asyncio. Manning Publications.</li></ul><h3>Level Up Coding</h3><p>Thanks for being a part of our community! Before you go:</p><ul><li>👏 Clap for the story and follow the author 👉</li><li>📰 View more content in the <a href="https://levelup.gitconnected.com/?utm_source=pub&amp;utm_medium=post">Level Up Coding publication</a></li><li>💰 Free coding interview course ⇒ <a href="https://skilled.dev/?utm_source=luc&amp;utm_medium=article">View Course</a></li><li>🔔 Follow us: <a href="https://twitter.com/gitconnected">Twitter</a> | <a href="https://www.linkedin.com/company/gitconnected">LinkedIn</a> | <a href="https://newsletter.levelup.dev">Newsletter</a></li></ul><p>🚀👉 <a href="https://jobs.levelup.dev/talent/welcome?referral=true"><strong>Join the Level Up talent collective and find an amazing job</strong></a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9a289e061b88" width="1" height="1" alt=""><hr><p><a href="https://levelup.gitconnected.com/the-beginners-guide-to-asyncio-in-python-a-deeper-dive-into-coroutines-and-tasks-9a289e061b88">The Beginner’s Guide to Asyncio in Python: A Deeper Dive into Coroutines and Tasks</a> was originally published in <a href="https://levelup.gitconnected.com">Level Up Coding</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Unlock the Power of Python: A Beginner’s Guide to Concurrency]]></title>
            <link>https://medium.com/geekculture/unlock-the-power-of-python-a-beginners-guide-to-concurrency-c80b6f2aef3a?source=rss-1ea0548a5421------2</link>
            <guid isPermaLink="false">https://medium.com/p/c80b6f2aef3a</guid>
            <category><![CDATA[asyncio]]></category>
            <category><![CDATA[python-programming]]></category>
            <category><![CDATA[concurrency]]></category>
            <category><![CDATA[asynchronous-programming]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Marvin Lanhenke]]></dc:creator>
            <pubDate>Fri, 03 Mar 2023 18:01:18 GMT</pubDate>
            <atom:updated>2023-03-03T18:01:18.845Z</atom:updated>
            <content:encoded><![CDATA[<h4>Python Concurrency</h4><h4>Learn the Basic Concepts of Concurrency and Upgrade Your Python Skills today!</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*EWbUXbCplXZDbj1u" /><figcaption>Photo by <a href="https://unsplash.com/@chuttersnap?utm_source=medium&amp;utm_medium=referral">CHUTTERSNAP</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Python is popular. It’s versatile. And it’s widely used for everything from web development to scientific computing [1].</p><p>However, many of today&#39;s applications rely heavily on the use of input/output (I/O) operations, which can have a severe and compounding impact on performance. Luckily for us, Python offers a range of tools and techniques to tackle this issue. Most importantly concurrency.</p><blockquote>But with many options to choose from, it can become quite a challenge, especially for beginners, to figure out where to start.</blockquote><p>In the following sections, we will break down the key and basic concepts for Python concurrency. Covering everything from multithreading and multiprocessing to the Global Interpreter Lock and single-threaded concurrency.</p><p>Whether you are looking to optimize your application’s performance or simply want to improve your programming skills, this guide has got you covered.</p><h4>Concurrency, Parallelism, and Multitasking</h4><p>Let’s begin our journey by covering some basic terminology first.</p><p><strong>Concurrency </strong>is the concept of allowing more than one task to be handled at the same time, out of or in partial order [2]. This can be incredibly useful for improving the performance of applications.</p><p>In Python, concurrency can be achieved in several ways, including threading, multiprocessing, and asynchronous programming using libraries such as <a href="https://docs.python.org/3/library/asyncio.html">asyncio</a>. One important aspect of concurrency is that it is achieved through switching between tasks, which means that only one task is actively being executed at a time, even if multiple tasks are in progress. Thus, concurrency does not imply running multiple tasks in parallel.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/698/1*cYarZczsnrphzH_b0rCr3A.png" /><figcaption>Example of concurrency [Image by Author]</figcaption></figure><p><strong>Parallelism</strong>, on the other hand, is the concept of actively doing more than one task at the same time. Multiple calculations or processes are carried out simultaneously [3]. This is different from concurrency and multiple CPU cores are required to be able to execute various tasks simultaneously.</p><p>In Python, parallelism can be achieved using the <a href="https://docs.python.org/3/library/multiprocessing.html">multiprocessing module</a>, which allows multiple processes to be created and run in parallel. Unlike concurrency, parallelism can significantly improve the performance of CPU-bound tasks.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/682/1*EUuMQsJyNuQhKIUe4tqukw.png" /><figcaption>Example of parallelism [Image by Author]</figcaption></figure><blockquote><strong>Note</strong>: I/O-bound operations in Python are tasks that spend most of the time waiting for input/output operations to complete, such as network requests, file I/O, or user input. CPU-bound operations are tasks that require heavy computations and do not rely on I/O operations for their executions.</blockquote><p><strong>Multitasking </strong>allows for numerous tasks to be executed concurrently in an interleaved manner by sharing resources between them [4]. It can be achieved in two ways: Preemptive and cooperative multitasking.</p><p><strong>Preemptive multitasking</strong> is managed by the operating system, which decides when to switch between tasks. It does so by using an algorithm such as <a href="https://en.wikipedia.org/wiki/Preemption_(computing)#Time_slice">time-slicing</a>. In this approach, each task is allocated a time slice or quantum, during which it can run on the CPU. When the time slice expires, the operating system interrupts and switches to another task. The switching between tasks is resource-intensive and may lead to poor performance if not managed properly.</p><p><strong>Cooperative multitasking</strong>, on the other hand, relies on the developer to provide specific code instructions that allow tasks to yield control to other tasks voluntarily. This approach can be beneficial since it is less resource-intensive and provides more granularity and control.</p><p>In Python, cooperative multitasking is often achieved using coroutines and the async/await syntax. The asyncio module is a good example of cooperative multitasking, where coroutines are run in a single-threaded event loop, and the developer can control when to switch between them using await statements.</p><h4>Processes, Threads, Multithreading, and Multiprocessing</h4><p>Now, that we know and understand some of the basic concepts, we can start to dig a little deeper.</p><p>In Python, a <strong>process</strong> is an instance of a program that is being executed by the operating system. A process is equipped with its own separate memory space. Thus, each process has its own set of resources, such as CPU time and memory, and does not share those with other processes or the parent process that launched it. Additionally, a process can execute multiple threads to perform different tasks concurrently.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/357/1*amBs7sAnuEUE43qsnIuMmw.png" /><figcaption>Example of a process [Image by Author]</figcaption></figure><p><strong>Threads</strong> are a way to achieve multitasking by dividing a program into smaller, independent parts that can be executed concurrently. In simpler terms, threads can be thought of as a “light-weight-process” that can run at the same time with other threads in the same process.</p><p>In contrast to processes, threads share the same memory space as the parent process, which allows accessing the same variables and data structures, making communication between threads easier. However, this also introduces another layer of complexity and problems like “race conditions” to be at least aware of. There will at least be one thread in the parent process: The main thread.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/592/1*enAH-Q8QSHA4nxdLqRwXGA.png" /><figcaption>Example of threads in a process [Image by Author]</figcaption></figure><p>Threads are particularly useful for I/O-bound tasks. By running these tasks in separate threads, a Python program can continue executing other tasks while waiting for I/O operations to complete. This allows for a faster, more efficient, and more responsive application.</p><p>In Python, threads can be created using the <a href="https://www.google.de/search?q=threading+module+python&amp;sxsrf=AJOqlzV9qTkBnBlN8uXSsqM7xo6ZGWGe8w%3A1677213623759&amp;source=hp&amp;ei=tz_4Y_ewLJbBxc8PlcKe6AM&amp;iflsig=AK50M_UAAAAAY_hNxz1TIykY8wFNozLxNmfDV_YD3pmV&amp;oq=threading+m&amp;gs_lcp=Cgdnd3Mtd2l6EAMYADIFCAAQkQIyBQgAEIAEMgUIABCABDIKCAAQgAQQFBCHAjIFCAAQgAQyBQgAEIAEMgUIABCABDIFCAAQgAQyBQgAEIAEMgUIABCABDoKCC4QxwEQ0QMQJzoECCMQJzoECAAQQzoHCC4Q1AIQQzoLCC4QgAQQxwEQ0QM6CAguEIAEENQCUABYoglgzxFoAHAAeACAAV2IAYIGkgECMTGYAQCgAQE&amp;sclient=gws-wiz">threading module</a>, which provides a Thread class that can be used to create and manage new threads.</p><pre>import os<br>import threading<br><br># Get the parent process id<br>process_id = os.getpid()<br># Get the total number of active threads<br>num_active_threads = threading.active_count()<br># Get the current thread&#39;s name<br>curr_thread_name = threading.current_thread().name<br><br>print(f&quot;Python process running with id: {process_id} \<br>with {num_active_threads} active threads running. \<br>Current thread: {curr_thread_name}&quot;)<br><br># Output: <br># Python process running with id: 2300 with 1 active threads running. <br># Current thread: MainThread</pre><p>But how can we improve the application’s performance by utilizing the concepts of processes and threads?</p><p><strong>Multithreading </strong>is a way to achieve concurrency by running multiple threads simultaneously in the same process [5]. In other words, we can execute more than one task at the same time within a single program. As stated earlier, communication between threads is simple, since they share the same memory space. Thus, allowing access to the same variables and data structures.</p><p>The concept of multithreading is especially useful for I/O-bound tasks, such as network requests and file I/O, where waiting for external events is a bottleneck in program performance. By running these tasks in separate threads, we can continue executing other tasks concurrently, which can improve the performance and efficiency of the application.</p><p>However, it is important to note that multithreading has its limitations in Python, due to the Global Interpreter Lock (GIL), which ensures that only one thread can execute Python bytecode at a time.</p><pre>import threading<br><br>def print_numbers():<br>    for i in range(1, 11):<br>        print(i)<br><br>def print_letters():<br>    for letter in [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;, &#39;d&#39;, &#39;e&#39;]:<br>        print(letter)<br><br># Create two threads<br>t1 = threading.Thread(target=print_numbers)<br>t2 = threading.Thread(target=print_letters)<br><br># Start the threads<br>t1.start()<br>t2.start()<br><br># Wait for the threads to finish<br>t1.join()<br>t2.join()<br><br># When we run this program, <br># we&#39;ll see output that interleaves the numbers and letters <br># printed by the two threads. This is because the two threads <br># are executing concurrently, or in parallel</pre><p><strong>Multiprocessing</strong>, on the other hand, is a way to achieve parallelism by running multiple processes simultaneously on different CPU cores. In other words, it allows us to execute more than one task at the same time by distributing them across multiple processes. Each process has its own memory space and system resources.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/549/1*giv-mztffntPRqmXJdKIgA.png" /><figcaption>Example of multiprocessing [Image by Author]</figcaption></figure><p>Multiprocessing and parallelism are particularly useful for CPU-bound tasks, such as heavy computation or data processing. By running these tasks in separate processes, a Python program can execute multiple tasks simultaneously, utilizing all available CPU cores. This can result in a significant speedup in program execution and improve program efficiency.</p><p>In Python, multiprocessing can be achieved using the multiprocessing module, which provides a Process class that can be used to create and manage new processes. Unlike multithreading, multiprocessing does not have the same limitations imposed by the Global Interpreter Lock (GIL), as each process has its own interpreter and can execute Python bytecode independently.</p><pre>import os<br>import multiprocessing<br><br><br>def hello_from_process():<br>    print(f&quot;Hello from child process {os.getpid()}&quot;)<br><br>if __name__ == &#39;__main__&#39;:<br>    # Create new process<br>    child_process = multiprocessing.Process(target=hello_from_process)<br>    # Start new process<br>    child_process.start()<br><br>    print(f&quot;Hello from parent process {os.getpid()}&quot;)<br><br>    # Wait for child process to finish<br>    child_process.join()<br><br>    # Output:<br>    # Hello from parent process 3128<br>    # Hello from child process 2248</pre><p>However, it’s important to note that multiprocessing has some overhead in terms of memory usage and interprocess communication, so it’s not always the best approach for every situation.</p><h4>The Global Interpreter Lock</h4><p>In the previous section, we hinted at the fact that multithreading in Python has its limitations due to the Global Interpreter Lock. Now, let’s take a closer look at what the Global Interpreter Lock (GIL) actually is.</p><p><strong>The Global Interpreter Lock (GIL)</strong> is a mechanism in Python that ensures that only one thread can execute Python bytecode at a time, even in a multithreaded program [6]. The purpose of the GIL is to prevent multiple threads from accessing shared data simultaneously and causing data inconsistencies, which can result in hard-to-debug errors.</p><p>The GIL works by locking the interpreter, which prevents other threads from acquiring the lock and executing Python bytecode. This means while one thread is executing bytecode, all other threads are blocked and have to wait. This can limit the performance of multithreaded programs, especially in situations where the program is CPU-bound and requires heavy computation.</p><p>So why does the GIL even exist in the first place?</p><p>While the GIL can be a limitation for some programs, it’s also a feature that simplifies Python’s <a href="https://docs.python.org/3/c-api/memory.html">memory management</a>. Python uses a reference-counting model for memory management, which is not thread-safe. The GIL ensures that only one thread can modify the reference counts of Python objects at any given time, preventing memory corruption and crashes.</p><p>Probably the most important thing to note is that the GIL only applies to threads that execute Python bytecode.</p><blockquote>I/O-bound tasks, such as network requests and file I/O, can release the GIL and allow other threads to execute Python bytecode while they wait for I/O operations to complete.</blockquote><p>This means that multithreading is still a useful tool for I/O-bound tasks, even in a GIL-constrained environment.</p><h4>Single-threaded Concurrency</h4><p><strong>Single-threaded concurrency</strong> can be achieved by running multiple tasks concurrently within a single thread, without creating additional threads or processes. This approach is especially useful for I/O-bound tasks since those release the GIL and allow other Python bytecode to be executed while waiting for I/O operations to be completed. Moreover, single-threaded concurrency can be more efficient due to the saving of the overhead cost of creating multiple threads or processes.</p><p>In Python, single-threaded concurrency can be achieved using non-blocking sockets and the OS event notification system, such as <a href="https://en.wikipedia.org/wiki/Kqueue">kqueue</a>, <a href="https://en.wikipedia.org/wiki/Epoll">epoll</a>, or <a href="https://en.wikipedia.org/wiki/Input/output_completion_port">IOCP</a>. When data is ready, the system sends a notification, and the coroutine can return the result. This approach allows a single thread to handle multiple I/O operations concurrently, without blocking and waiting for each operation to complete.</p><p>The asyncio module in Python is a good example of single-threaded concurrency in action. The module provides an event loop that can handle multiple coroutines concurrently, using non-blocking sockets and the event notification system to achieve concurrency.</p><h4>The Event-Loop</h4><p>In asyncio Python, an <strong>event loop</strong> is a central part of the asynchronous programming model, allowing coroutines to be scheduled and executed in a non-blocking manner.</p><p>The event loop is responsible for managing tasks and coroutines, and for determining which task or coroutine to run next. It’s essentially a loop that continuously waits for events to occur, and then dispatches tasks or coroutines to handle those events [7].</p><p>The following code example shows an implementation of the most basic event loop.</p><pre>import queue<br><br># Create a queue to store events<br>message_queue = queue.Queue()<br><br># Fill queue with dummy events<br>for i in range(5):<br>    message_queue.put(f&quot;event_{i}&quot;)<br><br>def process_message(message):<br>    print(f&quot;Processing message: {message}&quot;)<br><br><br># Run event-loop forever<br>while True:<br>    try:<br>        if message_queue:<br>            message = message_queue.get(timeout=1)<br>            process_message(message)<br>    except queue.Empty:<br>        # Continue the loop and wait for new events<br>        continue<br>    except KeyboardInterrupt:<br>        # Exiting the program with Ctrl+C<br>        break<br><br># Output:<br># Processing message: event_0<br># Processing message: event_1<br># Processing message: event_2<br># Processing message: event_3<br># Processing message: event_4</pre><p>When an asyncio program starts, it creates an event loop, which is used to schedule and execute coroutines. Each coroutine is a task that represents a unit of work to be done, such as making a network request or reading from a file. The event loop manages these tasks and decides which one to run next based on which task is ready to run, such as one that has data available to read or one that has completed an I/O operation.</p><p>The event loop is also responsible for handling exceptions and errors that may occur during the execution of a coroutine. If an exception occurs, the event loop can catch the exception and decide whether to continue running the coroutine or stop it and move on to the next task.</p><h4>Conclusion</h4><p>Understanding concurrency in Python is an essential skill for any developer who wants to create efficient and responsive programs.</p><p>The concept of asynchronous programming helps to achieve concurrency by allowing programs to handle multiple I/O-bound tasks concurrently and free up system resources. Resulting in greater efficiency, better performance, and resource usage.</p><p>However, there are some drawbacks as well. Asynchronous code can be harder to read and write than synchronous code, especially for beginners. It also requires a different programming mindset. And additionally, some tasks, such as heavy computation or CPU-bound tasks, may not be well-suited for asynchronous programming.</p><p>While asynchronous programming can provide significant performance gains it is unfortunately not a ‘silver bullet’ for everything.</p><p><em>If you enjoyed the read, make sure to hit ‘follow’ for more on Python concurrency and advanced techniques to take your programming skills to the next level.</em></p><p><em>Consider becoming a </em><a href="https://medium.com/@marvinlanhenke/membership"><em>Medium member</em></a><em> and continue learning with no limits. I’ll receive a portion of your membership fee if you use the following link, at no extra cost to you.</em></p><p><a href="https://medium.com/@marvinlanhenke/membership">Join Medium with my referral link - Marvin Lanhenke</a></p><p><strong>References / Further Material:</strong></p><ul><li>[1] <a href="https://www.statista.com/statistics/1338409/python-use-cases/">https://www.statista.com/statistics/1338409/python-use-cases/</a></li><li>[2] <a href="https://en.wikipedia.org/wiki/Concurrency_(computer_science)">https://en.wikipedia.org/wiki/Concurrency_(computer_science)</a></li><li>[3] <a href="https://en.wikipedia.org/wiki/Parallel_computing">https://en.wikipedia.org/wiki/Parallel_computing</a></li><li>[4] <a href="https://en.wikipedia.org/wiki/Computer_multitasking">https://en.wikipedia.org/wiki/Computer_multitasking</a></li><li>[5] <a href="https://en.wikipedia.org/wiki/Multithreading_(computer_architecture)">https://en.wikipedia.org/wiki/Multithreading_(computer_architecture)</a></li><li>[6] <a href="https://wiki.python.org/moin/GlobalInterpreterLock">https://wiki.python.org/moin/GlobalInterpreterLock</a></li><li>[7] <a href="https://en.wikipedia.org/wiki/Event_loop">https://en.wikipedia.org/wiki/Event_loop</a></li><li>Fowler, Matthew. (2022). Python Concurrency with Asyncio. Manning Publications.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c80b6f2aef3a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/geekculture/unlock-the-power-of-python-a-beginners-guide-to-concurrency-c80b6f2aef3a">Unlock the Power of Python: A Beginner’s Guide to Concurrency</a> was originally published in <a href="https://medium.com/geekculture">Geek Culture</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Create a Serverless Authentication Service With AWS CDK, Cognito, and API Gateway]]></title>
            <link>https://medium.com/better-programming/create-a-serverless-authentication-service-with-aws-cdk-cognito-and-api-gateway-ffbd8da6a659?source=rss-1ea0548a5421------2</link>
            <guid isPermaLink="false">https://medium.com/p/ffbd8da6a659</guid>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[aws-lambda]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Marvin Lanhenke]]></dc:creator>
            <pubDate>Wed, 12 Oct 2022 15:45:40 GMT</pubDate>
            <atom:updated>2022-10-20T06:45:19.480Z</atom:updated>
            <content:encoded><![CDATA[<h4>AWS Solutions</h4><h3>How to Create a Serverless Authentication Service With AWS CDK, Cognito, and API Gateway</h3><h4>A backend service using TypeScript, JWT, and HttpOnly cookies</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*GOfcH6KpweR3QrGi" /><figcaption>Photo by <a href="https://unsplash.com/@flyd2069?utm_source=medium&amp;utm_medium=referral">FLY:D</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>I used it. You did, too.</p><p>It’s ubiquitous. It’s everywhere. It’s essential.</p><p>You know what I’m talking about: authentication.</p><blockquote>Authentication describes the act of proving an assertion, such as your identity to a computer system [1].</blockquote><p>Or simply put — you tell the system who you are.</p><p>And since authentication is required in nearly every modern application we use, it might be a good idea to build an authentication service and solve this requirement once and for all.</p><p>In the following sections, you will create a serverless backend service using <a href="https://docs.aws.amazon.com/cognito/index.html">Amazon Cognito</a>, <a href="https://docs.aws.amazon.com/apigateway/">API Gateway</a>, and <a href="https://docs.aws.amazon.com/lambda/">AWS Lambda</a>.</p><p>By making use of the AWS Cloud Development Kit (CDK), you will be able to provide Infrastructure as Code (IaC) — making it very easy to spin up or shut down the backend service with just a simple command line statement.</p><p>However, before diving headfirst into the implementation details, let’s take a step back and briefly talk about the high-level design.</p><h3>High-Level Overview</h3><p>Prior to writing any code at all, it’s always useful to envision the complete picture first. Making sure we know exactly what it is we’re trying to achieve.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/831/1*7ixeijCGt4OX63ECgVs8IA.png" /><figcaption>AWS serverless authentication flow [Image by Author]</figcaption></figure><p>So, our overall goal is to create a serverless backend system that will handle authentication for us. But what does this actually mean?</p><p>Let’s quickly step through the flow above:</p><ol><li>The user either tries to create a new account or to sign in by providing some form of credentials (e.g., username and password)</li><li>The user receives a response. In case of a successful login, it will be an <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies">HttpOnly Cookie</a> with a <a href="https://jwt.io/introduction">JSON Web Token</a> inside.</li><li>Equipped with the cookie, the user tries to access a protected resource via another API Gateway. A Lambda authorizer will parse the cookie that is included in the request header and verify the JWT. If the verification is successful, the authorizer returns a policy document to the user, making it possible to access the protected resource.</li></ol><p>And this is already it.</p><p>Now, let’s begin by firing up our favorite IDE and creating a new project.</p><blockquote><strong>Note</strong>: All of the used services are free-tier eligible so no additional costs should occur. However, it’s still advisable to check your AWS Account and shut down any unused services.</blockquote><h3>Implementing a Serverless Authentication Service</h3><p>First of all, make sure you have <a href="https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html">AWS CDK installed and bootstrapped</a>. The following code can help:</p><pre>// Install AWS CDK<br>npm install -g aws-cdk</pre><pre>// Bootstrap AWS CDK<br>cdk bootstrap aws://ACCOUNT-NUMBER/REGION</pre><p>Now that we’re equipped with the right tools, we can start our project by simply creating a new folder and initializing the CDK via the command line interface.</p><pre>// Create a new folder<br>mkdir aws-serverless-auth<br>cd aws-serverless-auth</pre><pre>// Init CDK<br>cdk init --language typescript</pre><p>Once the installation process has finished, we can finally open our code editor and get to work.</p><h4>Creating an Amazon Cognito user pool</h4><p>First things first.</p><p>In order to build a proper authentication service, we have to create some form of a user database first. For this purpose, we make use of Amazon Cognito which luckily provides us with all the desired features.</p><p>Inside the lib folder, create a new file called user-pool.ts.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/12041719f45990901560541692e5ab66/href">https://medium.com/media/12041719f45990901560541692e5ab66/href</a></iframe><p>In the code above, we export a class called CognitoUserPool.</p><p>Inside the class constructor, we basically create a new user pool and attach an application client to it. While instantiating a new user pool, we also make sure to pass the required configuration parameters as well.</p><p>Note that we expose two read-only class fields for further reference, namely the userPoolId and userPoolClientId.</p><h4>Building the auth API</h4><p>Now, let’s proceed to the nuts and bolts — the authentication API.</p><p>Once again, inside the lib folder create a new file called auth-api.ts.</p><p>Let’s start nice and easy by constructing a RestAPI first and by making sure our class constructor receives the correct properties.</p><p>Next, we add a new resource and attach several lambda functions to it. To make our lives much easier, we will use a private helper method called addRoute().</p><blockquote><strong>Note</strong>: We will create all the necessary Lambda functions in the next section.</blockquote><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2c57e565c3186c787e0a38fe1a1e67e0/href">https://medium.com/media/2c57e565c3186c787e0a38fe1a1e67e0/href</a></iframe><p>Each Lambda function corresponds with a separate route and a specific user action.</p><p>By making use of the helper method, we can not only reduce code duplication but also provide each function with the mandatory environment variables as well as the correct policies (e.g., allowing access to the Cognito user pool).</p><h4>Implementing the auth Lambda functions</h4><p>So far, so good.</p><p>We already created a Cognito user pool and a RestAPI, allowing us to expose our authentication logic to the outside world.</p><p>However, we have yet to implement such logic.</p><p>Inside the root project folder, we create a new directory called lambda. We open the new directory and create a subfolder with the name auth inside.</p><blockquote><strong>Note</strong>: In order to install the type definitions for AWS Lambda enter <em>npm install @types/aws-lambda</em> in your terminal.</blockquote><h4>Signup</h4><p>Let’s begin with the signup function and create a new file, signup.ts.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7ceea5c3b73e0413a50c1ac85635c5ad/href">https://medium.com/media/7ceea5c3b73e0413a50c1ac85635c5ad/href</a></iframe><p>Once we made sure we received a proper event body (the user’s credentials), we simply call the signUp() method on the instance of the CognitoIdentityServiceProvider and return a response to the user.</p><p>If the signup was successful, the user will face a challenge to verify the provided email address and confirm the signup by entering a verification code.</p><h4>Confirm signup</h4><p>Well, you know the drill.</p><p>Create a new file called confirm-signup.ts.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5fdfc14b4df0d2aad25bb0c6d4cae7c7/href">https://medium.com/media/5fdfc14b4df0d2aad25bb0c6d4cae7c7/href</a></iframe><p>Nothing fancy here.</p><p>We simply receive a username and the confirmation code, which we pass to the confirmSignup() method of the CognitoIdentityServiceProvider. In the end, we return an appropriate response to the user.</p><h4>Sign in</h4><p>Now, that we’re able to create a new user we can start working on the sign-in function. Still inside the auth folder, create a new file with the name signin.ts.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8830222b71f9bfabc34b41532d4026c5/href">https://medium.com/media/8830222b71f9bfabc34b41532d4026c5/href</a></iframe><p>Inside the sign-in function, we collect the username and password in order to invoke the initiateAuth() method. If the given credentials are correct, we extract the IdToken from the AuthenticationResult and set a Secure and HttpOnly cookie inside the response header with the token as payload.</p><h4>Sign out</h4><p>The sign-out function is super basic.</p><p>We simply create a new file signout.ts and “delete” the cookie by setting its expiration date to the past.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a9570c15b25469aa7b4c19eb1a5e2850/href">https://medium.com/media/a9570c15b25469aa7b4c19eb1a5e2850/href</a></iframe><h3>Create a Protected API and Lamba Authorizer</h3><p>We’re approaching the finishing line.</p><p>Now, that we already implemented the authentication API and all the necessary Lambda functions, we can start working on the final missing pieces: The protected RestAPI and the Lambda authorizer.</p><h4>Build the protected API</h4><p>Let’s get started with the supposedly easy part.</p><p>Inside the folder lib, we create a new file called protected-api.ts.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cbf85af18f196eaeb2ea1161bb8a57e4/href">https://medium.com/media/cbf85af18f196eaeb2ea1161bb8a57e4/href</a></iframe><p>In the code above, we define a simple RestAPI, two lambda functions, and their integrations.</p><p>The protectedFn returns just a message, allowing us to simulate some protected resources. We create a new file inside the lambda folder with the name of protected.ts.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2592ec5246f80cc9de85a939817b1355/href">https://medium.com/media/2592ec5246f80cc9de85a939817b1355/href</a></iframe><p>By providing a RequestAuthorizer and setting our Lambda authorizer as the handler, we make sure the route is protected.</p><h4>Implementing the Lambda Authorizer</h4><p>The Lambda Authorizer does two things:</p><ol><li>It parses the cookie, provided in the request header, and verifies the JWT.</li><li>It returns a policy document, either denying or allowing access to the resource.</li></ol><p>Simple enough, right?</p><p>Let’s go ahead and create a new file authorizer.ts inside the lambda/auth folder.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/69f8d3f73e861c91c4fb5c8566ba08f8/href">https://medium.com/media/69f8d3f73e861c91c4fb5c8566ba08f8/href</a></iframe><p>Inside the authorizer, we make use of three helper functions: parseCookies(), verifyToken() and createPolicy().</p><p>Let’s cover those next.</p><p>But first, create a new file utils.ts inside the folder lambda which will house all of those three helper functions. Here’s what it looks like:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4d6d2c50a3bcc0ffb8d0535f8bee8ee/href">https://medium.com/media/b4d6d2c50a3bcc0ffb8d0535f8bee8ee/href</a></iframe><p>Our first helper function does what the name suggests — it parses the cookies inside the request header. Basically looping through the headers.Cookie object and creating a cookieMap with the cookie name and value.</p><p>Once we retrieve the cookie with our token, we need to verify it.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6de840f74c11f03f1a8ec24daa3a7473/href">https://medium.com/media/6de840f74c11f03f1a8ec24daa3a7473/href</a></iframe><p>The next helper function, verifyToken, relies on three external libraries so make sure to npm install axios jsonwebtoken jwk-to-pem.</p><p>We retrieve the JSON web key for our Cognito user pool by requesting the provided URL. Next, we convert the key with the help of the external library jwk-to-pem. Once we have converted the key we can verify the token and return the result.</p><p>Based on this result, we create a policy document either allowing or denying access to the protected resource. For this purpose, we create our last helper function, createPolicy().</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5c170fe1b5fdcae83cac7785448f3421/href">https://medium.com/media/5c170fe1b5fdcae83cac7785448f3421/href</a></iframe><h3>Putting It All Together</h3><p>Phew — that was a lot of work.</p><p>Now, there is only one thing left to do. We have to put it all together in our final stack.</p><p>Inside the folder, lib, open the file with the name aws-serverless-auth-stack.ts.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8b5f391d0bf44db9fea5c4ecad1b3c50/href">https://medium.com/media/8b5f391d0bf44db9fea5c4ecad1b3c50/href</a></iframe><p>Here, we simply instantiate all of the other classes we created before. Our Cognito user pool and both of the RestAPIs. Note that we pass userPoolId and userPoolClientId as properties to both APIs.</p><p>We assembled all the pieces. Good job.</p><p>Now, it’s time to deploy the stack by typing cdk deploy inside your terminal.</p><h3>Testing the Flow With Postman</h3><p>Once our stack has been completely deployed, we can finally test the overall communication flow by making use of <a href="https://www.postman.com/">Postman</a>.</p><p>However, before we can start testing we need to obtain both the URL of our authentication and the protected API. Therefore, head over to your AWS console, navigate to API Gateway, select each API, select stages, and copy the URL.</p><p>Let’s get moving by creating a new user and signing up.</p><p>Inside Postman, we create a new POST request with the URL of the authentication API we copied earlier. Our JSON request body simply contains a username, an email, and a password.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/615/1*aDdEyamlgBndImFmM1qwFg.png" /><figcaption>Authentication testing signup [Screenshot by Author]</figcaption></figure><blockquote><strong>Note</strong>: Please make sure to enter a valid email address since we will receive a confirmation code in order to confirm our signup request.</blockquote><p>Next, we have to confirm our signup request by entering a verification code we should have received by mail. Create a new POST request inside Postman with a username and code in the body.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/646/1*anEGWlVh6xdAx9kv8rU26A.png" /><figcaption>Authentication testing confirmation [Screenshot by Author]</figcaption></figure><p>The received response should be stating that we have successfully confirmed the user signup.</p><p>Now, we can test the sign-in function.</p><p>We create yet another POST request, providing a username and password inside the body.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/615/1*SUubmklgHe_lk_N40WeSTQ.png" /><figcaption>Authentication testing sign-in [Screenshot by Author]</figcaption></figure><p>Within the response, we should have received a cookie with a JSON web token inside. We can verify this by inspecting our cookies inside Postman.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_3tVwbomws5RuD-lBZbNGw.png" /><figcaption>HttpOnly cookie set by AWS Lambda [Screenshot by Author]</figcaption></figure><p>We’re successfully logged in. Great.</p><p>Now, let’s try to access our protected resources.</p><p>Create a GET request inside Postman and hit the protected route. Also, make sure to <a href="https://learning.postman.com/docs/sending-requests/cookies/#using-the-cookie-manager">include a cookie</a> token=&lt;replace-with-jwt&gt; within your request.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/594/1*ActFhyJY_modjPYDi8gqAw.png" /><figcaption>Accessing the protected route [Screenshot by Author]</figcaption></figure><p>As we can tell from the response body — it worked. We received a super secret.</p><p>And this is finally it.</p><p>We finished our serverless authentication service.</p><blockquote><strong>Note</strong>: After we’re done testing, we can tear down the infrastructure by typing <em>cdk destroy</em> inside our terminal.</blockquote><h3>Conclusion</h3><p>In this article, we created a serverless authentication service by utilizing Amazon Cognito and API Gateway. We also made use of the CDK, creating our Infrastructure as Code, which allowed us to easily spin up and tear down the complete stack.</p><p>Since authentication is used in almost every application, having a backend service right at our fingertips might prove useful in the future.</p><p>However, there is still room left for some improvements. We only have the capability to signup with a username and password. This could be further enhanced by integrating federated identity providers such as Google, LinkedIn, etc.</p><p>Thanks for reading.</p><p>You can find the full code on my <a href="https://github.com/marvinlanhenke/aws-serverless-auth">GitHub</a>.</p><h3>References</h3><ul><li>[1] <a href="https://en.wikipedia.org/wiki/Authentication">https://en.wikipedia.org/wiki/Authentication</a></li><li><a href="https://dev.to/gkoniaris/how-to-securely-store-jwt-tokens-51cf">https://dev.to/gkoniaris/how-to-securely-store-jwt-tokens-51cf</a></li><li><a href="https://stackoverflow.com/questions/27067251/where-to-store-jwt-in-browser-how-to-protect-against-csrf">https://stackoverflow.com/questions/27067251/where-to-store-jwt-in-browser-how-to-protect-against-csrf</a></li><li><a href="https://docs.aws.amazon.com/cdk/v2/guide/home.html">https://docs.aws.amazon.com/cdk/v2/guide/home.html</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ffbd8da6a659" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/create-a-serverless-authentication-service-with-aws-cdk-cognito-and-api-gateway-ffbd8da6a659">Create a Serverless Authentication Service With AWS CDK, Cognito, and API Gateway</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Design Better DAGs in Apache Airflow]]></title>
            <link>https://medium.com/data-science/how-to-design-better-dags-in-apache-airflow-494f5cb0c9ab?source=rss-1ea0548a5421------2</link>
            <guid isPermaLink="false">https://medium.com/p/494f5cb0c9ab</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[workflow]]></category>
            <category><![CDATA[data-engineering]]></category>
            <category><![CDATA[apache-airflow]]></category>
            <category><![CDATA[airflow]]></category>
            <dc:creator><![CDATA[Marvin Lanhenke]]></dc:creator>
            <pubDate>Thu, 09 Jun 2022 15:15:09 GMT</pubDate>
            <atom:updated>2022-06-09T15:15:09.513Z</atom:updated>
            <content:encoded><![CDATA[<h4>Data Engineering</h4><h4>The two most important properties you need to know when designing a workflow</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Qv9Imt-nkaFpb_Cf" /><figcaption>Photo by <a href="https://unsplash.com/@campaign_creators?utm_source=medium&amp;utm_medium=referral">Campaign Creators</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p><a href="https://towardsdatascience.com/setting-up-apache-airflow-with-docker-compose-in-5-minutes-56a1110f4122"><strong>Last week</strong></a>, we learned how to quickly spin up a development environment for Apache Airflow.</p><p>This is awesome!</p><p>However, we have yet to learn <strong>how to design an efficient workflow</strong>. Simply having a great tool at our fingertips won’t cut the deal alone — unfortunately.</p><p>Although Apache Airflow does a pretty good job at doing most of the heavy lifting for us, we still need to <strong>ensure certain key properties</strong> for each Airflow task, in order to obtain proper and consistent results.</p><p>Luckily, a lot of best practices exist.</p><p>Today, we begin with two of the most important concepts that apply universally to all workflows.</p><p>Today, we learn about <strong>atomicity </strong>and <strong>idempotency</strong>.</p><h3>All or nothing: Atomicity</h3><p>Often used in the context of database systems, atomicity is one of the <a href="https://en.wikipedia.org/wiki/ACID">ACID </a>properties and is considered an indivisible, irreducible series of operations such that <strong>either all occur or nothing at all</strong>¹. It is either performed entirely or not performed at all².</p><p>In terms of Apache Airflow, that means a task should be defined in a way that <strong>allows for success </strong>with a proper result <strong>or complete failure</strong> without affecting the state of the system.</p><p>Let’s imagine, that we have to extract data from a CSV file, apply some transformations to it, and write the result to a database.</p><p>Simple enough, right?</p><p>A bad, <strong>non-atomic approach</strong> would be the following.</p><p>We extract the data line-by-line, apply the transformation right away, and upload the result immediately to the database. <strong>All within the same task</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/727/1*hddzFzwBnQqtxJRkwAxGRg.png" /><figcaption>A non-atomic approach [Image by Author]</figcaption></figure><p>Now, if some lines are corrupt and the task fails halfway through, we’re left with only a fragment of the desired results. Some lines are processed and already inserted — some simply non-existent. Debugging and rerunning this task while avoiding duplication would be a nightmare.</p><p>An improved, <strong>atomic workflow</strong> might be defined like this.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/704/1*bh8Sc3Rf5fmdnmWiQpq5ZA.png" /><figcaption>A better approach with atomic tasks [Image by Author]</figcaption></figure><p>So a general rule of thumb to keep in mind is to <strong>split up the operations</strong> into different tasks. One operation equals a single task — think <a href="https://en.wikipedia.org/wiki/Single-responsibility_principle">Single-responsibility principle</a>.</p><blockquote>Unfortunately, this simple rule cannot be applied every time.</blockquote><p>Some operations are so <strong>tightly coupled</strong>, that it’s best to <strong>keep them in a single</strong> <strong>coherent unit of work</strong>. For example, authenticating to an API before executing the request.</p><p>Luckily for us, most <strong>Airflow operators are designed in an atomic fashion</strong> and can be used straight off the shelf. With the more flexible types of operators like the Bash or Python operator, however, we have to be more cautious and mindful when designing our workflow.</p><p>Creating atomic Airflow tasks allows for the <strong>ability to recover</strong> from failure and rerun only the failed and downstream tasks. Atomicity provides easier maintainable and <strong>transparent workflows</strong> without hidden dependencies and side effects.</p><h3>Start, Stop, Rewind: Idempotency</h3><p>The concept of idempotency goes hand-in-hand with the idea of atomicity and describes a property of certain operations in mathematics and computer science. So the operations can be <strong>applied multiple times without changing the result</strong> beyond the initial application³.</p><p>Think of pressing the “on-button” on a control panel as an operation. Pressing this button multiple times has the same effect as just pressing it once.</p><blockquote>So what does this mean in the context of Apache Airflow?</blockquote><p>Calling the same task multiple times with the same input has no additional effect. In other words, if <strong>rerunning a task without changing the input</strong> <strong>yields the</strong> <strong>same output</strong> it can be considered idempotent.</p><p>Idempotency allows for <strong>decreased recovery time</strong> from failure and <strong>reduces data loss</strong>.</p><p>Now, let’s imagine our job is to fetch data from a database for a specific day and write the results to a CSV file. Rerunning this task for the same day should overwrite the existing file and <strong>produce the same output</strong> every time it is executed.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/763/1*X0Oy0735iOArATNYUo_lAw.png" /><figcaption>An idempotent task producing the same output every time [Image by Author]</figcaption></figure><p>Suppose, we design our task in a different way that with each rerun we simply append the records to an existing file.</p><p>Now, we <strong>violate the concept of</strong> <strong>idempotency</strong>. Every rerun of the task produces a different result with duplicate records.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/658/1*l9FS-rUpwxDjDULjb8XA9w.png" /><figcaption>Non-idempotent task producing duplicate results [Image by Author]</figcaption></figure><p>In general, tasks that write should <strong>check for existing records</strong>, <strong>overwrite </strong>or use <strong>UPSERT</strong> operations to conform to the rules of idempotency.</p><p>For more general applications we have to, however, think carefully of all possible side effects.</p><h3>Conclusion:</h3><p>In this article, we covered two of the most important principles when designing DAGs in Apache Airflow: <strong>atomicity </strong>and <strong>idempotency</strong>.</p><p>Committing those concepts to memory enables us to <strong>create better workflows</strong> that are recoverable, rerunnable, fault-tolerant, consistent, maintainable, transparent, and easier to understand.</p><p>However, there are a lot more <a href="https://www.astronomer.io/guides/dag-best-practices/">best practices</a> to adhere to and consider when coding and creating the next workflow.</p><p>But this is a topic for another day …</p><p><a href="https://towardsdatascience.com/setting-up-apache-airflow-with-docker-compose-in-5-minutes-56a1110f4122">Setting Up Apache Airflow with Docker-Compose in 5 Minutes</a></p><p><em>Enjoyed the article? Become a </em><a href="https://medium.com/@marvinlanhenke/membership"><em>Medium member</em></a><em> and continue learning with no limits. I’ll receive a portion of your membership fee if you use the following link, at no extra cost to you.</em></p><p><a href="https://medium.com/@marvinlanhenke/membership">Join Medium with my referral link - Marvin Lanhenke</a></p><p><strong>References / Further Material:</strong></p><ul><li>[1] <a href="https://en.wikipedia.org/wiki/Atomicity_(database_systems)">https://en.wikipedia.org/wiki/Atomicity_(database_systems)</a></li><li>[2] <a href="https://www.webopedia.com/definitions/atomic-operation/">https://www.webopedia.com/definitions/atomic-operation/</a></li><li>[3] <a href="https://en.wikipedia.org/wiki/Idempotence">https://en.wikipedia.org/wiki/Idempotence</a></li><li><a href="https://en.wikipedia.org/wiki/ACID">https://en.wikipedia.org/wiki/ACID</a></li><li><a href="https://en.wikipedia.org/wiki/Single-responsibility_principle">https://en.wikipedia.org/wiki/Single-responsibility_principle</a></li><li><a href="https://www.astronomer.io/guides/dag-best-practices/">https://www.astronomer.io/guides/dag-best-practices/</a></li><li>Bas Harenslak, Julian de Ruiter. Data Pipelines with Apache Airflow. New York: Manning, 2021.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=494f5cb0c9ab" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/how-to-design-better-dags-in-apache-airflow-494f5cb0c9ab">How to Design Better DAGs in Apache Airflow</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Setting Up Apache Airflow with Docker-Compose in 5 Minutes]]></title>
            <link>https://medium.com/data-science/setting-up-apache-airflow-with-docker-compose-in-5-minutes-56a1110f4122?source=rss-1ea0548a5421------2</link>
            <guid isPermaLink="false">https://medium.com/p/56a1110f4122</guid>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[airflow]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[docker-compose]]></category>
            <category><![CDATA[data-engineering]]></category>
            <dc:creator><![CDATA[Marvin Lanhenke]]></dc:creator>
            <pubDate>Tue, 31 May 2022 20:11:31 GMT</pubDate>
            <atom:updated>2022-06-21T16:20:49.866Z</atom:updated>
            <content:encoded><![CDATA[<h4>Data Engineering</h4><h4>Create a development environment and start building DAGs</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*HEOlsCSDjFWmGbPZ" /><figcaption>Photo by <a href="https://unsplash.com/@fabiolog?utm_source=medium&amp;utm_medium=referral">Fabio Ballasina</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Although being pretty late to the party (Airflow became an Apache Top-Level Project in 2019), I still had trouble finding an easy-to-understand, up-to-date, and lightweight solution to installing Airflow.</p><p>Today, we’re about to change all that.</p><p>In the following sections, we will create a lightweight, standalone, and easily deployed Apache Airflow development environment in just a few minutes.</p><p>Docker-Compose will be our close companion, allowing us to create a smooth development workflow with quick iteration cycles. Simply spin up a few docker containers and we can start to create our own workflows.</p><blockquote><strong>Note</strong>: The following setup will not be suitable for any production purposes and is intended to be used in a development environment only.</blockquote><h3>Why Airflow?</h3><p>Apache Airflow is a <strong>batch-oriented framework</strong> that allows us to easily build scheduled data pipelines in Python. Think of “workflow as code” capable of executing any operation we can implement in Python.</p><p>Airflow is not a data processing tool itself. It’s an <strong>orchestration software</strong>. We can imagine Airflow as some kind of spider in a web. Sitting in the middle, pulling all the strings and coordinating the workload of our data pipelines.</p><p>A data pipeline typically consists of several tasks or actions that need to be executed in a specific order. Apache Airflow models such a <strong>pipeline as a DAG</strong> (directed acyclic graph). A graph with directed edges or tasks without any loops or cycles.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/669/1*XyVoHEKGgflVhFDuJaRaTQ.png" /><figcaption>A simple example DAG [Image by Author]</figcaption></figure><p>This approach allows us to run independent tasks in parallel, saving time and money. Moreover, we can split a data pipeline into several smaller tasks. If a job fails, we can only rerun the failed and the downstream tasks, instead of executing the complete workflow all over again.</p><p><strong>Airflow is composed of three main components</strong>:</p><ol><li>Airflow <strong>Scheduler </strong>— the “heart” of Airflow, that parses the DAGs, checks the scheduled intervals, and passes the tasks over to the workers.</li><li>Airflow <strong>Worker </strong>— picks up the tasks and actually performs the work.</li><li>Airflow <strong>Webserver </strong>— provides the main user interface to visualize and monitor the DAGs and their results.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*z3MNHDV9eTTLGikvucGEKw.png" /><figcaption>A high-level overview of Airflow components [Image by Author]</figcaption></figure><h3>Step-By-Step Installation</h3><p>Now that we shortly introduced Apache Airflow, it’s time to get started.</p><h4>Step 0: Prerequisites</h4><p>Since we will use docker-compose to get Airflow up and running, we have to install Docker first. Simply head over to the <a href="https://docs.docker.com/get-docker/">official Docker site</a> and download the appropriate installation file for your OS.</p><h4>Step 1: Create a new folder</h4><p>We start nice and slow by simply creating a new folder for Airflow.</p><p>Just navigate via your preferred terminal to a directory, create a new folder, and change into it by running:</p><pre>mkdir airflow<br>cd airflow</pre><h4>Step 2: Create a docker-compose file</h4><p>Next, we need to get our hands on a docker-compose file that specifies the required services or docker containers.</p><p>Via the terminal, we can run the following command inside the newly created Airflow folder</p><pre>curl <a href="https://raw.githubusercontent.com/marvinlanhenke/Airflow/main/01GettingStarted/docker-compose.yml">https://raw.githubusercontent.com/marvinlanhenke/Airflow/main/01GettingStarted/docker-compose.yml</a> -o docker-compose.yml</pre><p>or simply create a new file named docker-compose.yml and copy the below content.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/73eba75161167381a12e5e3edc491bfd/href">https://medium.com/media/73eba75161167381a12e5e3edc491bfd/href</a></iframe><p>The above docker-compose file simply specifies the required services we need to get Airflow up and running. Most importantly the scheduler, the webserver, the metadatabase (postgreSQL), and the airflow-init job initializing the database.</p><p>At the top of the file, we make use of some local variables that are commonly used in every docker container or service.</p><h4>Step 3: Environment variables</h4><p>We successfully created a docker-compose file with the mandatory services inside. However, to complete the installation process and configure Airflow properly, we need to provide some environment variables.</p><p>Still, inside your Airflow folder create a .env file with the following content:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/28012d93c64d8386d63db70b46f005ff/href">https://medium.com/media/28012d93c64d8386d63db70b46f005ff/href</a></iframe><p>The above variables set the database credentials, the airflow user, and some further configurations.</p><p>Most importantly, the kind of executor Airflow we will utilize. In our case, we make use of the LocalExecutor.</p><blockquote><strong>Note</strong>: More information on the different kinds of executors can be found <a href="https://airflow.apache.org/docs/apache-airflow/stable/executor/index.html">here</a>.</blockquote><h4>Step 4: Run docker-compose</h4><p>And this is already it!</p><p>Just head over to the terminal and spin up all the necessary containers by running</p><pre>docker compose up -d</pre><p>After a short period of time, we can check the results and the Airflow Web UI by visiting http://localhost:8080. Once we sign in with our credentials (airflow: airflow) we gain access to the user interface.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*x8wEdNe3zHIK1iABY_dNwA.png" /><figcaption>Airflow 2 Web UI [Screenshot by Author]</figcaption></figure><h3>A Quick Test</h3><p>With a working Airflow environment, we can now create a simple DAG for testing purposes.</p><p>First of all, make sure to run pip install apache-airflow to install the required Python modules.</p><p>Now, inside your Airflow folder, navigate to dags and create a new file called sample_dag.py.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/78a0718e8a9a2ed0d119c47b123f4bf3/href">https://medium.com/media/78a0718e8a9a2ed0d119c47b123f4bf3/href</a></iframe><p>We define a new DAG and some pretty simple tasks.</p><p>The EmptyOperator serves no real purpose other than to create a mockup task inside the Web UI. By utilizing the BashOperator, we create a somewhat creative output of “HelloWorld!”. This allows us to visually confirm a proper running Airflow setup.</p><p>Save the file and head over to the Web UI. We can now start the DAG by manually triggering it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/192/1*5fWDEmN-iJyf2k2lNcRIMA.png" /><figcaption>Manually triggering a DAG [Screenshot by Author]</figcaption></figure><blockquote><strong>Note</strong>: It may take a while before your DAG appears in the UI. We can speed things up by running the following command in our terminal docker exec -it --user airflow airflow-scheduler bash -c &quot;airflow dags list&quot;</blockquote><p>Running the DAG shouldn’t take any longer than a couple of seconds.</p><p>Once finished, we can navigate to XComs and inspect the output.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/246/1*jPlxUBYRy1ZItVTzsihbcQ.png" /><figcaption>Navigating to Airflow XComs [Screenshot by Author]</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/903/1*qDe8tJGk6izASaK0Csbpeg.png" /><figcaption>Inspecting the output [Screenshot by Author]</figcaption></figure><p>And this is it!</p><p>We successfully installed Airflow with docker-compose and gave it a quick test ride.</p><blockquote><strong>Note</strong>: We can stop the running containers by simply executing docker compose down.</blockquote><p><a href="https://towardsdatascience.com/how-to-design-better-dags-in-apache-airflow-494f5cb0c9ab">How to Design Better DAGs in Apache Airflow</a></p><h3>Conclusion</h3><p>Airflow is a batch-oriented framework that allows us to create complex data pipelines in Python.</p><p>In this article, we created a simple and easy-to-use environment to quickly iterate and develop new workflows in Apache Airflow. By leveraging docker-compose we can get straight to work and code new workflows.</p><p>However, such an environment should only be used for development purposes and is not suitable for any production environment that requires a more sophisticated and distributed setup of Apache Airflow.</p><p>You can find the full code here on my <a href="https://github.com/marvinlanhenke/Airflow/tree/main/01GettingStarted">GitHub</a>.</p><p><em>Enjoyed the article? Become a </em><a href="https://medium.com/@marvinlanhenke/membership"><em>Medium member</em></a><em> and continue learning with no limits. I’ll receive a portion of your membership fee if you use the following link, at no extra cost to you.</em></p><p><a href="https://medium.com/@marvinlanhenke/membership">Join Medium with my referral link - Marvin Lanhenke</a></p><p><strong>References / Further Material:</strong></p><ul><li><a href="https://airflow.apache.org/docs/apache-airflow/stable/start/docker.html">Airflow Documentation</a></li><li>Bas Harenslak, Julian de Ruiter. Data Pipelines with Apache Airflow. New York: Manning, 2021.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=56a1110f4122" width="1" height="1" alt=""><hr><p><a href="https://medium.com/data-science/setting-up-apache-airflow-with-docker-compose-in-5-minutes-56a1110f4122">Setting Up Apache Airflow with Docker-Compose in 5 Minutes</a> was originally published in <a href="https://medium.com/data-science">TDS Archive</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[NLP-Day 30: A Bag-Of-Resources For Your NLP Learning Adventure]]></title>
            <link>https://medium.com/@marvinlanhenke/nlp-day-30-a-bag-of-resources-for-your-nlp-learning-adventure-ebd38f5cb01a?source=rss-1ea0548a5421------2</link>
            <guid isPermaLink="false">https://medium.com/p/ebd38f5cb01a</guid>
            <category><![CDATA[resources]]></category>
            <category><![CDATA[naturallanguageprocessing]]></category>
            <category><![CDATA[ml-so-good]]></category>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[deep-learning]]></category>
            <dc:creator><![CDATA[Marvin Lanhenke]]></dc:creator>
            <pubDate>Fri, 06 May 2022 16:58:30 GMT</pubDate>
            <atom:updated>2022-05-06T18:09:53.391Z</atom:updated>
            <content:encoded><![CDATA[<h4>#30DaysOfNLP</h4><h4>Gathering resources to continue learning and improving</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/491/1*qMx8KrP6WWr--LtJXG5x0A.png" /><figcaption>Wrapping up the series #30DaysOfNLP [Image by Author]</figcaption></figure><p><a href="https://medium.com/mlearning-ai/nlp-day-29-how-to-manipulate-and-preprocess-string-with-regular-expressions-846fd5dac7e2"><strong>In the last episode</strong></a>, we took a small detour and learned the basics of regular expressions, allowing us to match and modify strings in a way that suits our Natural Language Processing needs.</p><p>Looking back, we have covered a variety of topics in this series. Things like Bag-Of-Words, TF-IDF vectors, convolutional neural networks, and Transformers should all sound familiar by now.</p><p>However, there is still much left to learn in the vast field of Natural Language Processing.</p><p>In the following sections, we’re going to wrap up this series. We will create a non-comprehensive list of resources that hopefully enables us to continue our joyful learning adventure in the world of Natural Language Processing.</p><p>So for the last time, take a seat, don’t go anywhere, and make sure to follow <strong>#30DaysOfNLP: </strong>A Bag-Of-Resources For Your NLP Learning Adventure</p><h3>Online Courses</h3><h4>NLP with Deep Learning (Winter 2017) — Stanford</h4><p>Starting off with a free lecture series provided by Stanford University.</p><p>This series provides an introduction to cutting-edge research in deep learning applied to the field of Natural Language Processing. It contains 18 one-hour long lectures, covering topics like word vectors, recurrent neural networks, attention mechanisms, and transformer-based architectures.</p><p>[<a href="https://www.youtube.com/playlist?list=PL3FW7Lu3i5Jsnh1rnUwq_TcylNr7EkRe6">Link</a>]</p><h4>NLP Specialisation Course — Coursera</h4><p>Provided by DeepLearning.AI this specialization course is created to get you ready to design various NLP applications. Applications like question-answering, sentiment analysis, tools that translate languages and summarize text, and even chatbots.</p><p>However, keep in mind this course is not for free, once the 7-Day-Trial has passed.</p><p>[<a href="https://www.coursera.org/specializations/natural-language-processing">Link</a>]</p><h4>Introduction to Natural Language Processing in Python — Datacamp</h4><p>Designed to teach the NLP basics, this course covers topics like identifying and separating words, extracting topics, or how to build a fake news classifier. It also highlights the use of basic libraries such as NLTK as well as deep learning frameworks.</p><p>This course is also not free.</p><p>[<a href="https://www.datacamp.com/courses/introduction-to-natural-language-processing-in-python">Link</a>]</p><h4>Keras.io (Examples)</h4><p>Although not a real online course, Keras.io provided a plethora of examples we can learn from.</p><p>I’d highly recommend going through each example and coding along. One of the best ways to build a skill is still by performing it and working on projects.</p><p>[<a href="https://keras.io/examples/nlp/">Link</a>]</p><h3>Books</h3><h4>Deep Learning — Ian Goodfellow</h4><p>One of the best books for machine and deep learning in general. Especially the first few chapters get us up to speed, covering and revising all the necessary prerequisites in terms of linear algebra and statistics.</p><p>It also covers all the different neural network architectures in great detail. And the best thing — it’s completely free.</p><p>So get yourself ready for a deep and intense learning experience since this book comes with a high degree of information density.</p><p>[<a href="https://www.deeplearningbook.org/">Link</a>]</p><h4>Deep Learning with Python — F.Chollet</h4><p>Written by Keras creator and Google AI researcher François Chollet, this book introduces the field of deep learning with Python and the Keras library.</p><p>It covers the general principles of deep learning as well as the most common neural networks architectures. All done through intuitive explanations and practical examples.</p><p>[<a href="https://www.amazon.de/Deep-Learning-Python-Francois-Chollet/dp/1617294438/ref=sr_1_1?crid=2RMH928R1E1PR&amp;keywords=deep+learning+with+python&amp;qid=1648098383&amp;sprefix=deep+le%2Caps%2C79&amp;sr=8-1">Link</a>]</p><h4>Natural Language Processing in Action: Understanding, analyzing, and generating text with Python</h4><p>Providing a comprehensive overview of the complete field of Natural Language Processing, this book comes with a lot of practical examples and an easy-to-understand language.</p><p>Since this book covers a wide range of topics including tokenization, bag-of-words, word vectors, and deep learning, it’s the perfect starting point in the world of NLP.</p><p>[<a href="https://www.amazon.de/Natural-Language-Processing-Action-Understanding/dp/1617294632/ref=sr_1_1?crid=M9ZMCFW5RSOK&amp;keywords=natural+language+processing+in+action&amp;qid=1648098248&amp;sprefix=natural+lang%2Caps%2C118&amp;sr=8-1">Link</a>]</p><h4>Natural Language Processing Projects: Build Next-Generation NLP Applications Using AI Techniques</h4><p>The best way to learn is to build stuff.</p><p>This book starts with a general overview of NLP and artificial intelligence, before diving straight into several Natural Language Processing end-to-end projects. Applications like sentiment analysis, topic extraction, resume parsing, building a chatbot, or even generating novel text.</p><p>[<a href="https://www.amazon.de/gp/product/1484273850/ref=ppx_yo_dt_b_asin_image_o01_s00?ie=UTF8&amp;psc=1">Link</a>]</p><h3>Papers</h3><h4>Attention Is All You Need</h4><p>Nothing left to say. A must-read covering the Transformer architecture and self-attention.</p><p>[<a href="https://arxiv.org/abs/1706.03762">Link</a>]</p><h4>BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding</h4><p>Explaining the inner workings of BERT and the benefits of pre-training, this paper is also a must-read.</p><p>[<a href="https://arxiv.org/abs/1810.04805">Link</a>]</p><h4>A Neural Conversational Model</h4><p>In this paper, a simple approach for conversational modeling is presented. The model makes use of a sequence-to-sequence framework and converses by predicting the next sentence given the previous sentence or sentences in a conversation</p><p>[<a href="https://arxiv.org/abs/1506.05869">Link</a>]</p><h4>Improving Language Understanding by Generative Pre-Training</h4><p>Published by OpenAI, this paper addresses the scarcity of labeled data and how to overcome this challenge. By making use of generative pre-training and discriminative fine-tuning on each specific task, the authors demonstrate large gains and improvements.</p><p>[<a href="https://s3-us-west-2.amazonaws.com/openai-assets/research-covers/language-unsupervised/language_understanding_paper.pdf">Link</a>]</p><h3>Conclusion</h3><p>In this article, we wrapped up the series by providing a list of resources, allowing us to continue our learning adventure in the land of Natural Language Processing.</p><blockquote>There is not much more to say but thank you.</blockquote><p>Thank you for reading. Thank you for following this series and thank you for your support. It’s been an incredibly intensive, interesting, fun, but also exhausting <strong>#30DaysOfNLP</strong>.</p><p>Feel free to link and share the complete series.</p><p><a href="https://medium.com/@marvinlanhenke/list/3974a0c731d6">#30DaysOfNLP</a></p><p><em>Enjoyed the article? Become a </em><a href="https://medium.com/@marvinlanhenke/membership"><em>Medium member</em></a><em> and continue learning with no limits. I’ll receive a portion of your membership fee if you use the following link, at no extra cost to you.</em></p><ul><li><a href="https://medium.com/@marvinlanhenke/membership">Join Medium with my referral link - Marvin Lanhenke</a></li><li><a href="https://medium.com/mlearning-ai/mlearning-ai-submission-suggestions-b51e2b130bfb">Mlearning.ai Submission Suggestions</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ebd38f5cb01a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[NLP-Day 29: How To Manipulate And Preprocess Strings With Regular Expressions]]></title>
            <link>https://medium.com/@marvinlanhenke/nlp-day-29-how-to-manipulate-and-preprocess-string-with-regular-expressions-846fd5dac7e2?source=rss-1ea0548a5421------2</link>
            <guid isPermaLink="false">https://medium.com/p/846fd5dac7e2</guid>
            <category><![CDATA[nlp]]></category>
            <category><![CDATA[text-preprocessing]]></category>
            <category><![CDATA[ml-so-good]]></category>
            <category><![CDATA[regex]]></category>
            <category><![CDATA[naturallanguageprocessing]]></category>
            <dc:creator><![CDATA[Marvin Lanhenke]]></dc:creator>
            <pubDate>Thu, 05 May 2022 18:14:20 GMT</pubDate>
            <atom:updated>2022-05-05T18:21:44.304Z</atom:updated>
            <content:encoded><![CDATA[<h4>#30DaysOfNLP</h4><h4>Just express yourself with regular expressions</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/491/1*xuW2bol3CmI5YIyrAKXb9A.png" /><figcaption>Express yourself with regular expressions #30DaysOfNLP [Image by Author]</figcaption></figure><p><a href="https://medium.com/mlearning-ai/nlp-day-28-how-to-approach-and-choose-a-deep-learning-architecture-4ef8fff42f45"><strong>In the last episode</strong></a>, we reviewed the key architectures in the field of deep learning and highlighted the importance of a general workflow. We also stated that most of the challenges lie not in the designing or modeling aspect but in the preparation and preprocessing of the data.</p><p>Now, it’s time to take a small detour and learn about regular expressions.</p><p>In the following sections, we’re going to cover the basics of regular expressions, allowing us to preprocess and modify strings in a way that helps us to solve the NLP task at hand.</p><p>So take a seat, don’t go anywhere, and make sure to follow <strong>#30DaysOfNLP: </strong>How To Manipulate And Preprocess String With Regular Expressions</p><h3>Introducing regular expressions</h3><p>Regular expressions (regex) can also be viewed as a tiny, highly specialized programming language embedded inside Python that is made available through the re module.</p><p>Although embedded, regular expressions are actually compiled into a series of bytecodes and executed by a matching engine written in C.</p><p>Regular expressions allow us to specify rules for a set of possible strings we want to match e.g. English sentences, e-mail addresses, specific characters, etc. However, we can not only match certain patterns but also modify or even split strings.</p><p>Despite regex being quite powerful, it also can get complicated pretty quickly. Thus, more sophisticated preprocessing steps should not be done completely with regex alone but rather with or in combination with Python.</p><h3>Matching operations</h3><p>Let’s begin with probably the most common task. Matching characters.</p><pre>import re</pre><pre>text = &quot;Natural Language processing is so awesome, isn&#39;t it?&quot;<br>pattern = re.compile(r&#39;\?&#39;)<br>matches = pattern.findall(text)</pre><pre>&gt;&gt;&gt; [&#39;?&#39;]</pre><p>However, before we can do anything at all, we have to import the re module and specify or compile a pattern. In our simple case, we define a pattern to match a question mark.</p><p>After defining the matching pattern, we can apply several built-in functions.</p><pre>1. match()- determines if RE matches at the beginning of the String<br>2. search()- scans through a string, looks for any matching location<br>3. findall()- finds all matching substrings, returns a list<br>4. finditer() - finds all matching substrings, returns an iterator</pre><p>We make use of the findall() function that matches all substrings and returns them in a list. In our example, we retrieve the question mark.</p><p>Pretty straightforward so far. But what about more sophisticated patterns? What about metacharacters?</p><h4>Metacharacters</h4><p>Most letters and characters simply match themselves.</p><p>With metacharacters, however, this is a completely different story. We can use metacharacters to signal that some out-of-the-ordinary thing should be matched.</p><p>Let’s consider the square brackets &#39;[]&#39; for example which can be used to specify a set of characters.</p><pre>import re</pre><pre>text = &quot;Natural Language processing is so awesome, isn&#39;t it?&quot;</pre><pre>pattern = re.compile(r&#39;[a-c]&#39;)<br>matches = pattern.findall(text)</pre><pre>&gt;&gt;&gt;<br>[&#39;a&#39;, &#39;a&#39;, &#39;a&#39;, &#39;a&#39;, &#39;c&#39;, &#39;a&#39;]</pre><p>Picking up our simple example from before, we specify a set of characters [a-c] and try to find all occurrences in our string.</p><p>Other metacharacters to consider are the caret ’^’ and the dollar sign &#39;$&#39; which can be used to either check if a string starts or ends with a certain character.</p><pre>import re</pre><pre>text = &quot;Natural Language processing is so awesome, isn&#39;t it?&quot;</pre><pre>pattern = re.compile(r&#39;\?$&#39;)<br>matches = pattern.search(text)</pre><pre>if matches:<br>  print(True)<br>else:<br>  print(False)</pre><pre>&gt;&gt;&gt;<br>True</pre><p>By making use of the dollar sign, we are able to verify that the string ends with a question mark.</p><p>So far so good.</p><p>However, things start to get more interesting once we account for the number of occurrences. Using metacharacters like &#39;*&#39; &#39;+&#39; &#39;?&#39; we can specify the number of times a character has to appear in a given string.</p><pre>import re</pre><pre>text = &quot;abcd&quot;</pre><pre>pattern = re.compile(r&#39;[e-z]+&#39;)<br>matches = pattern.search(text)</pre><pre>if matches:<br>  print(True)<br>else:<br>  print(False)</pre><pre>&gt;&gt;&gt;<br>False</pre><p>By using the &#39;+&#39; character, we try to match the characters in the range [e-z] that appear at least once. Since our string doesn’t contain any of those characters we’re unable to match the pattern.</p><p>For a complete list of metacharacters, you can refer to the table provided by <a href="https://www.w3schools.com/python/gloss_python_regex_metacharacters.asp">w3schools.com.</a></p><h4>Special Sequences</h4><p>Using the backslash character, we can access several special sequences. For example, \w which matches any alphanumeric character.</p><p>Or imagine we want to extract all digits from a sequence.</p><pre>import re</pre><pre>text = &quot;I am 32 years old.&quot;</pre><pre>pattern = re.compile(r&#39;\d&#39;)<br>matches = pattern.findall(text)</pre><pre>&gt;&gt;&gt;<br>[&#39;3&#39;, &#39;2&#39;]</pre><p>In this example, we make use of the \d sequence to extract all single digits.</p><p>For a list of all special sequences, we can once again refer to <a href="https://www.w3schools.com/python/gloss_python_regex_sequences.asp">w3schools.com</a>.</p><h3>String modifications</h3><p>With regular expressions, we can do more than just matching operations. We can also split and modify strings as well.</p><pre>import re</pre><pre>text = &quot;Natural Language Processing is so awesome!&quot;</pre><pre>pattern = re.compile(r&#39;\W+&#39;)<br>result = pattern.split(text)</pre><pre>&gt;&gt;&gt;<br>[&#39;Natural&#39;, &#39;Language&#39;, &#39;Processing&#39;, &#39;is&#39;, &#39;so&#39;, &#39;awesome&#39;, &#39;&#39;]</pre><p>Relatively straightforward. We simply make use of the split() function to split a string. In this example, based on all non-alphanumerical characters.</p><p>By making use of the sub() function we can even modify a string by substituting characters based on a certain pattern. Let’s assume, we want to replace all whitespace characters with a hyphen.</p><pre>import re</pre><pre>text = &quot;Natural Language Processing is so awesome!&quot;</pre><pre>pattern = re.compile(r&#39;\s&#39;)<br>result = re.sub(pattern, &#39;-&#39;, text)</pre><pre>&gt;&gt;&gt;<br>&#39;Natural-Language-Processing-is-so-awesome!&#39;</pre><h3>Useful expressions</h3><p>Now that we covered most of the basics, let’s finish this article with some more useful examples.</p><p><strong>Finding e-mail addresses</strong></p><pre>import re</pre><pre>text = &quot;Here are some mail addresses \<br>alice-b@googlemail.com peter@yahoo.com&quot;</pre><pre>pattern = re.compile(r&#39;[-\w]+@[\w]+&#39;)<br>matches = pattern.findall(text)</pre><pre>&gt;&gt;&gt;<br>[&#39;alice-b@googlemail&#39;, &#39;peter@yahoo&#39;]</pre><p><strong>Extracting phone numbers</strong></p><pre>import re</pre><pre>text = &quot;Here are my phone numbers (555) 555-1234, (555) 555-5678&quot;</pre><pre>pattern = re.compile(r&#39;\([-)\d\s]+&#39;)<br>matches = pattern.findall(text)</pre><pre>&gt;&gt;&gt;<br>[&#39;(555) 555-1234&#39;, &#39;(555) 555-5678&#39;]</pre><p><strong>Working with numbers (including separators)</strong></p><pre>import re</pre><pre>text = &quot;The numbers are 21.40453, 2,245.43, and 4,506.&quot;</pre><pre>pattern = re.compile(r&#39;\b[\d.,]+\b&#39;)<br>matches = pattern.findall(text)</pre><pre>&gt;&gt;&gt;<br>[&#39;21.40453&#39;, &#39;2,245.43&#39;, &#39;4,506&#39;]</pre><p><strong>Extracting dates</strong></p><pre>import re</pre><pre>text = &quot;Here are some timestamps \<br>2013-02-20T17:24:33Z, 2016-03-23T11:19:33Z&quot;</pre><pre>pattern = re.compile(r&#39;[-\d]+\d{2}&#39;)<br>matches = pattern.findall(text)</pre><pre>&gt;&gt;&gt;<br>[&#39;2013-02-20&#39;, &#39;2016-03-23&#39;]</pre><h3>Conclusion</h3><p>In this article, we took a small detour and learned the basics of regular expressions. And such basic knowledge might come in handy when we have to preprocess and modify certain strings to fit into our Natural Language Processing pipeline.</p><p>Now, it’s time to wrap up the complete series by looking back, reviewing the work we have done, and providing some useful resources in the last episode.</p><p>So take a seat, don’t go anywhere, make sure to follow, and never miss a single day of the ongoing series <strong>#30DaysOfNLP.</strong></p><p><a href="https://medium.com/@marvinlanhenke/list/3974a0c731d6">#30DaysOfNLP</a></p><p><em>Enjoyed the article? Become a </em><a href="https://medium.com/@marvinlanhenke/membership"><em>Medium member</em></a><em> and continue learning with no limits. I’ll receive a portion of your membership fee if you use the following link, at no extra cost to you.</em></p><p><a href="https://medium.com/@marvinlanhenke/membership">Join Medium with my referral link - Marvin Lanhenke</a></p><p><strong>References / Further Material:</strong></p><ul><li><a href="https://docs.python.org/3/howto/regex.html">https://docs.python.org/3/howto/regex.html</a></li><li><a href="https://www.w3schools.com/python/python_regex.asp">https://www.w3schools.com/python/python_regex.asp</a></li><li><a href="https://developers.google.com/edu/python/regular-expressions">https://developers.google.com/edu/python/regular-expressions</a></li></ul><p><a href="https://medium.com/mlearning-ai/mlearning-ai-submission-suggestions-b51e2b130bfb">Mlearning.ai Submission Suggestions</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=846fd5dac7e2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[NLP-Day 28: How To Approach And Choose A Deep Learning Architecture]]></title>
            <link>https://medium.com/@marvinlanhenke/nlp-day-28-how-to-approach-and-choose-a-deep-learning-architecture-4ef8fff42f45?source=rss-1ea0548a5421------2</link>
            <guid isPermaLink="false">https://medium.com/p/4ef8fff42f45</guid>
            <category><![CDATA[deep-learning]]></category>
            <category><![CDATA[ml-so-good]]></category>
            <category><![CDATA[workflow]]></category>
            <category><![CDATA[naturallanguageprocessing]]></category>
            <category><![CDATA[nlp]]></category>
            <dc:creator><![CDATA[Marvin Lanhenke]]></dc:creator>
            <pubDate>Wed, 04 May 2022 17:21:59 GMT</pubDate>
            <atom:updated>2022-05-04T20:18:20.401Z</atom:updated>
            <content:encoded><![CDATA[<h4>#30DaysOfNLP</h4><h4>A general workflow and some key network architectures</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/491/1*h_3pnMi0WF2rd9Ci4GNfGw.png" /><figcaption>General Workflow &amp; key architectures #30DaysOfNLP [Image by Author]</figcaption></figure><p><a href="https://medium.com/mlearning-ai/nlp-day-27-how-to-visualize-word-embeddings-with-tensorboard-e69f39707d64"><strong>In the last episode</strong></a>, we gently introduced Tensorboard. A tool that allows us to gain insights and a deeper understanding of the various models we implemented.</p><p>Considering the fact that this series is slowly approaching the finish line, it’s time to take a step back.</p><p>In the following sections, we’re going to take a look at the rearview mirror. Not only highlighting the importance of a general workflow but also revising the key network architectures we already encountered and implemented for ourselves.</p><p>So take a seat, don’t go anywhere, and make sure to follow <strong>#30DaysOfNLP: </strong>How To Approach And Choose A Deep Learning Architecture</p><h3>A simple tool</h3><p>Within only a few years, deep learning has achieved tremendous breakthroughs. Especially in the field of machine perception, working with unstructured data like images, videos, sound, or text.</p><p>Given enough training data neural networks are capable of extracting nearly the same amount and quality of information from the data as a human could.</p><p>However, deep learning is just a tool.</p><p>And simply having a tool at our disposal won’t suffice.</p><p>In order to solve problems, we need a general workflow. We need to understand the key network architectures, enabling us to choose the right tool for a well-defined task.</p><h3>A general workflow</h3><p>It’s not building a model.</p><p>The difficult part is everything that lies before designing and training a model. Understanding the problem domain or knowing how to measure success, unfortunately, isn’t something TensorFlow or Keras can help us with.</p><p>There simply isn’t a plug-and-play function, thus we need a workflow:</p><ol><li>We need to define the problem and know what data is available. What are we trying to predict? Do we need to collect more or manually label the data?</li><li>How can we reliably measure success on our goal? Perhaps a simple metric like accuracy is sufficient or do we need to define a custom, domain-specific metric?</li><li>We need to prepare the validation process to evaluate our model. This means we should define a training, validation, and test dataset.</li><li>Vectorize the data. We need to shape and preprocess (e.g. normalization) the data into a form that makes our model happy and smile.</li><li>Create a first baseline that beats a common-sense approach. This way we ensure that the network can learn anything at all.</li><li>Refine our architecture gradually. We can tune hyperparameters and add regularization to improve the model’s performance and generalization ability.</li><li>We can deploy our final model in production and keep monitoring and refining it.</li></ol><h3>Key network architectures</h3><p>The key architectures can be divided into 4 different categories. Densely connected, convolutional, recurrent networks, and Transformers.</p><p>Each architecture has individual needs in terms of input data and makes different assumptions. Data, underlying assumptions, and the architecture must match in order for the model to be able to learn.</p><p>Image data, for example, can be processed by 2-dimensional convolutional neural networks, whereas sequential data is rather processed by recurrent neural networks.</p><h4>Densely connected networks</h4><p>Contains stacks of dense layers that are meant to process vector data. Dense networks assume no specific structure in the input features. They’re called densely connected because each unit is connected to every other unit. Thus, creating a dense net of connections.</p><p>The dense network attempts to map relationships between any two input features and can be mostly used for categorical data or as a final layer for a classification or regression task.</p><h4>Convolutional neural networks</h4><p>Convnets look at spatially local patterns by applying the same transformation to different patches of the input tensor. The results are translation invariant, making convnets highly data-efficient and parallelizable.</p><p>Convolutional neural networks can be either 1, 2, or 3-dimensional. We can process sequences (e.g. words in a sentence) with a 1-dimensional network. 2-dimensional networks are best suited for image data.</p><p>The network consists of several stacks of convolution and max-pooling layers.</p><h4>Recurrent neural networks</h4><p>RNNs process sequences one timestep at a time while maintaining a state throughout. For sequential data, they should be preferred over 1-dimensional convnets, especially if the data contains temporal order e.g. time-series, words in a sentence.</p><p>We can rely on the Keras API to provide us with several implementations: SimpleRNN, GRU, and LSTM.</p><h4>Transformers</h4><p>Transformers leverage an attention mechanism to transform each input vector (e.g. a word) into a representation that is aware of the context. We can also use positional encoding to make the Transformer aware of both the global context and the order.</p><p>Transformers are more effective than RNNs or 1-dimensional convnets and they especially excel at sequence-to-sequence-related problems.</p><p>Transformers are made up of two parts: The TransformerEncoder and the TransformerDecoder. The encoder transforms an input into a representation that is aware of the context and the order, whereas the decoder takes the encoder’s output and a target sequence and tries to predict the next element in the target sequence.</p><h3>Conclusion</h3><p>In this article, we took a step back and quickly reviewed the key architectures in deep learning. We also established a general workflow, enabling us to approach a problem in a structured, efficient way.</p><p>In the next article, we take a slight detour before finishing the complete series and learn about the basics of regular expressions in Python.</p><p>So take a seat, don’t go anywhere, make sure to follow, and never miss a single day of the ongoing series <strong>#30DaysOfNLP.</strong></p><p><a href="https://medium.com/@marvinlanhenke/list/3974a0c731d6">#30DaysOfNLP</a></p><p><em>Enjoyed the article? Become a </em><a href="https://medium.com/@marvinlanhenke/membership"><em>Medium member</em></a><em> and continue learning with no limits. I’ll receive a portion of your membership fee if you use the following link, at no extra cost to you.</em></p><p><a href="https://medium.com/@marvinlanhenke/membership">Join Medium with my referral link - Marvin Lanhenke</a></p><p><strong>References / Further Material:</strong></p><ul><li>Francois Chollet: Deep Learning with Python. New York: Manning, 2021.</li></ul><p><a href="https://medium.com/mlearning-ai/mlearning-ai-submission-suggestions-b51e2b130bfb">Mlearning.ai Submission Suggestions</a></p><p>ML</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4ef8fff42f45" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>