From 942cb7a4cfb7cc448c2e3a17e15865324627445a Mon Sep 17 00:00:00 2001 From: <> Date: Sun, 3 Sep 2023 16:00:10 +0000 Subject: [PATCH] Update documentation --- .buildinfo | 4 + .nojekyll | 0 _sources/about.rst.txt | 44 + _sources/contributors.rst.txt | 15 + _sources/index.rst.txt | 15 + _sources/modules.rst.txt | 10 + _sources/newsletters/2022/WEEK_1.rst.txt | 66 ++ _sources/newsletters/2023/WEEK_25.rst.txt | 733 +++++++++++++++++ _sources/newsletters/2023/WEEK_26.rst.txt | 676 ++++++++++++++++ _sources/newsletters/2023/WEEK_27.md.txt | 686 ++++++++++++++++ _sources/newsletters/2023/WEEK_28.md.txt | 371 +++++++++ _sources/newsletters/2023/WEEK_29.md.txt | 221 ++++++ _sources/newsletters/2023/WEEK_30.md.txt | 397 ++++++++++ _sources/newsletters/2023/WEEK_31.md.txt | 128 +++ _sources/newsletters/2023/WEEK_32.md.txt | 223 ++++++ _sources/newsletters/2023/WEEK_33.md.txt | 265 +++++++ _sources/newsletters/2023/WEEK_34.md.txt | 230 ++++++ _sources/newsletters/2023/WEEK_35.md.txt | 261 ++++++ _sources/newsletters/index.2022.rst.txt | 6 + _sources/newsletters/index.2023.rst.txt | 17 + _sources/upcoming_events.md.txt | 20 + _static/basic.css | 925 ++++++++++++++++++++++ _static/check-solid.svg | 4 + _static/clipboard.min.js | 7 + _static/copy-button.svg | 5 + _static/copybutton.css | 94 +++ _static/copybutton.js | 248 ++++++ _static/copybutton_funcs.js | 73 ++ _static/debug.css | 69 ++ _static/doctools.js | 156 ++++ _static/documentation_options.js | 13 + _static/file.png | Bin 0 -> 286 bytes _static/language_data.js | 199 +++++ _static/minus.png | Bin 0 -> 90 bytes _static/plus.png | Bin 0 -> 90 bytes _static/pygments.css | 258 ++++++ _static/scripts/furo-extensions.js | 0 _static/scripts/furo.js | 3 + _static/scripts/furo.js.LICENSE.txt | 7 + _static/scripts/furo.js.map | 1 + _static/searchtools.js | 574 ++++++++++++++ _static/skeleton.css | 296 +++++++ _static/sphinx_contributors.css | 32 + _static/sphinx_highlight.js | 154 ++++ _static/styles/furo-extensions.css | 2 + _static/styles/furo-extensions.css.map | 1 + _static/styles/furo.css | 2 + _static/styles/furo.css.map | 1 + about.html | 311 ++++++++ contributors.html | 296 +++++++ genindex.html | 256 ++++++ index.html | 290 +++++++ modules.html | 429 ++++++++++ newsletters/2022/WEEK_1.html | 341 ++++++++ newsletters/2023/WEEK_25.html | 919 +++++++++++++++++++++ newsletters/2023/WEEK_26.html | 778 ++++++++++++++++++ newsletters/2023/WEEK_27.html | 892 +++++++++++++++++++++ newsletters/2023/WEEK_28.html | 592 ++++++++++++++ newsletters/2023/WEEK_29.html | 477 +++++++++++ newsletters/2023/WEEK_30.html | 621 +++++++++++++++ newsletters/2023/WEEK_31.html | 407 ++++++++++ newsletters/2023/WEEK_32.html | 482 +++++++++++ newsletters/2023/WEEK_33.html | 489 ++++++++++++ newsletters/2023/WEEK_34.html | 482 +++++++++++ newsletters/2023/WEEK_35.html | 485 ++++++++++++ newsletters/index.2022.html | 287 +++++++ newsletters/index.2023.html | 421 ++++++++++ objects.inv | Bin 0 -> 504 bytes search.html | 264 ++++++ searchindex.js | 1 + upcoming_events.html | 309 ++++++++ 71 files changed, 17341 insertions(+) create mode 100644 .buildinfo create mode 100644 .nojekyll create mode 100644 _sources/about.rst.txt create mode 100644 _sources/contributors.rst.txt create mode 100644 _sources/index.rst.txt create mode 100644 _sources/modules.rst.txt create mode 100644 _sources/newsletters/2022/WEEK_1.rst.txt create mode 100644 _sources/newsletters/2023/WEEK_25.rst.txt create mode 100644 _sources/newsletters/2023/WEEK_26.rst.txt create mode 100644 _sources/newsletters/2023/WEEK_27.md.txt create mode 100644 _sources/newsletters/2023/WEEK_28.md.txt create mode 100644 _sources/newsletters/2023/WEEK_29.md.txt create mode 100644 _sources/newsletters/2023/WEEK_30.md.txt create mode 100644 _sources/newsletters/2023/WEEK_31.md.txt create mode 100644 _sources/newsletters/2023/WEEK_32.md.txt create mode 100644 _sources/newsletters/2023/WEEK_33.md.txt create mode 100644 _sources/newsletters/2023/WEEK_34.md.txt create mode 100644 _sources/newsletters/2023/WEEK_35.md.txt create mode 100644 _sources/newsletters/index.2022.rst.txt create mode 100644 _sources/newsletters/index.2023.rst.txt create mode 100644 _sources/upcoming_events.md.txt create mode 100644 _static/basic.css create mode 100644 _static/check-solid.svg create mode 100644 _static/clipboard.min.js create mode 100644 _static/copy-button.svg create mode 100644 _static/copybutton.css create mode 100644 _static/copybutton.js create mode 100644 _static/copybutton_funcs.js create mode 100644 _static/debug.css create mode 100644 _static/doctools.js create mode 100644 _static/documentation_options.js create mode 100644 _static/file.png create mode 100644 _static/language_data.js create mode 100644 _static/minus.png create mode 100644 _static/plus.png create mode 100644 _static/pygments.css create mode 100644 _static/scripts/furo-extensions.js create mode 100644 _static/scripts/furo.js create mode 100644 _static/scripts/furo.js.LICENSE.txt create mode 100644 _static/scripts/furo.js.map create mode 100644 _static/searchtools.js create mode 100644 _static/skeleton.css create mode 100644 _static/sphinx_contributors.css create mode 100644 _static/sphinx_highlight.js create mode 100644 _static/styles/furo-extensions.css create mode 100644 _static/styles/furo-extensions.css.map create mode 100644 _static/styles/furo.css create mode 100644 _static/styles/furo.css.map create mode 100644 about.html create mode 100644 contributors.html create mode 100644 genindex.html create mode 100644 index.html create mode 100644 modules.html create mode 100644 newsletters/2022/WEEK_1.html create mode 100644 newsletters/2023/WEEK_25.html create mode 100644 newsletters/2023/WEEK_26.html create mode 100644 newsletters/2023/WEEK_27.html create mode 100644 newsletters/2023/WEEK_28.html create mode 100644 newsletters/2023/WEEK_29.html create mode 100644 newsletters/2023/WEEK_30.html create mode 100644 newsletters/2023/WEEK_31.html create mode 100644 newsletters/2023/WEEK_32.html create mode 100644 newsletters/2023/WEEK_33.html create mode 100644 newsletters/2023/WEEK_34.html create mode 100644 newsletters/2023/WEEK_35.html create mode 100644 newsletters/index.2022.html create mode 100644 newsletters/index.2023.html create mode 100644 objects.inv create mode 100644 search.html create mode 100644 searchindex.js create mode 100644 upcoming_events.html diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 0000000..48486f9 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 69617432d90a9a8ffddc216042bd4086 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/_sources/about.rst.txt b/_sources/about.rst.txt new file mode 100644 index 0000000..ae8c244 --- /dev/null +++ b/_sources/about.rst.txt @@ -0,0 +1,44 @@ +About Us +======== + + +**Welcome to Python-World! 🐍🌎🎉** + +Python-World is a vibrant community dedicated to all things Python! We are passionate about promoting Python as a versatile and powerful programming language, and our mission is to support and inspire Python enthusiasts worldwide. 🚀💻🌟 + +**Who We Are** + +At Python-World, we are a diverse group of Python enthusiasts, ranging from beginners to experienced developers. Our community is a melting pot of creativity, curiosity, and collaboration. We believe in fostering a welcoming and inclusive environment where everyone can learn, share, and grow. 🌈🤝🌱 + +**What We Do** + +Python-World hosts a range of exciting activities and resources, including: + + - **Informative Blog Posts:** Stay up-to-date with the latest Python trends, tips, and tricks through our regularly updated blog. 📰🔍💡 + - **Interactive Workshops:** Join our live workshops and webinars, where Python experts share their knowledge and experience. 🎓🗣️🎥 + - **Code Challenges:** Participate in fun and challenging coding contests to test your Python skills and win exciting prizes. 🏆🚀💥 + - **Engaging Community:** Connect with like-minded Pythonistas, ask questions, and engage in discussions on our online platforms. 💬🌐🗨️ + +**Active Projects** + +Here are some of our active projects that you can contribute to: + + - **S-Tool**: is a utility module that provides helpful methods for interacting with Selenium WebDriver in Python. `Read More... `_ 🌐🎉🎤 + - **Python Handbook:** Coming soooon. 📖📚👶 + +**Get Involved** + +We believe in the power of community, and we'd love to have you on board! Here's how you can get involved: + + - **Follow us on github** Join our vibrant community on `Github `_ 📱🌐📢 + - **Contribute to our Newsletter:** Share your Python insights and experiences by writing for our Newsletter `Here `_ 📝✨🎙️ + - **Attend our events:** Join our live workshops and webinars to expand your Python knowledge. 📆🎓🌟 + - **Participate in discussions:** Engage with fellow Pythonistas in our forums and online groups `Discussion Area `_. 🗣️💬🌐 + +**Contact Us** + +If you have any questions, suggestions, or just want to say hi, feel free to reach out to us at pythonworldhelp@gmail.com. 📧📬👋 + +We look forward to growing and learning together in the exciting world of Python! 🌱🚀💻 + +**Happy Coding!** 🎉🐍💻 diff --git a/_sources/contributors.rst.txt b/_sources/contributors.rst.txt new file mode 100644 index 0000000..2fef78f --- /dev/null +++ b/_sources/contributors.rst.txt @@ -0,0 +1,15 @@ +Contributors +============ + +We would like to express our sincere gratitude to the following +contributors who have made valuable contributions + + +.. contributors:: python-world/Newsletter + :avatars: + :contributions: + :order: DESC + +Thank you for your dedication and for enriching the Python community +with your valuable insights, code snippets, and contributions! Happy +coding! 🐍✨ \ No newline at end of file diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 0000000..3cb931f --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,15 @@ +.. Python-world documentation master file, created by + sphinx-quickstart on Sat Jun 24 16:06:21 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Python-world Newsletter +======================================== + +.. toctree:: + :maxdepth: 2 + + modules + + + diff --git a/_sources/modules.rst.txt b/_sources/modules.rst.txt new file mode 100644 index 0000000..8431c4a --- /dev/null +++ b/_sources/modules.rst.txt @@ -0,0 +1,10 @@ + +.. toctree:: + :maxdepth: 4 + + about + contributors + upcoming_events + newsletters/index.2023 + newsletters/index.2022 + diff --git a/_sources/newsletters/2022/WEEK_1.rst.txt b/_sources/newsletters/2022/WEEK_1.rst.txt new file mode 100644 index 0000000..f502ff1 --- /dev/null +++ b/_sources/newsletters/2022/WEEK_1.rst.txt @@ -0,0 +1,66 @@ +Week 1 - Jan 2022 +################# + +Introduction and Highlights +--------------------------- + +Welcome to the latest edition of the Python-World Newsletter! We bring +you the most exciting updates and insightful content from the Python +community. In this week’s edition, we have some incredible code +snippets, community spotlights, upcoming events, useful resources, and +more. Let’s dive in! + +Introduction +------------- + +Welcome to the Python-World Newsletter, your ultimate source for the latest updates, insights, and code snippets from the Python programming universe! 🌍✨ + +At Python-World, we are passionate about promoting knowledge sharing, collaboration, and continuous learning within the Python community. Our newsletter aims to provide you with valuable content, including Python code examples, insightful articles, and industry news that will keep you at the forefront of Python development. + +🔬 Each edition of our newsletter features carefully curated Python code snippets, showcasing the versatility and power of this beloved programming language. From beginner-friendly scripts to advanced algorithms, our code examples cover a wide range of topics, including data analysis, web development, machine learning, and much more. Whether you're an aspiring Pythonista or a seasoned developer, you'll find something valuable in every edition. + +📚 In addition to code snippets, we bring you engaging articles written by Python experts and enthusiasts. Explore tutorials that delve into specific Python libraries or frameworks, discover best practices for efficient coding, and stay updated with the latest trends and developments in the Python ecosystem. Our goal is to provide you with practical knowledge and actionable insights that you can apply in your own Python projects. + +🌟 Python-World Newsletter is more than just a one-way communication channel. We encourage active participation from our readers! Share your own Python code snippets, tips, and tricks, or suggest topics you would like us to cover. Join our vibrant community and engage in discussions with fellow Python enthusiasts on our dedicated GitHub repository, where you can collaborate, provide feedback, and contribute to the newsletter's growth. + + + +Useful Resources +---------------- + +Here are some valuable resources to enhance your Python programming +skills and stay updated with the latest trends: + + +- **Discussions**:join the discussion on our + `GitHub Discussions board `__. + +- **Website**: `Python + World `__. + Engage with fellow developers, share knowledge, and contribute to the + Python ecosystem. + +Stay curious, keep learning, and make the most of these resources to +enhance your Python journey! + +Contact +------- + +If you have any questions or need further assistance, feel free to reach +out to us at [pythonworldhelp@gmail.com] or join the discussion on our +`GitHub +Discussions `__ +board. + +Contributors +------------ + +We would like to express our sincere gratitude to the following +contributors who have made valuable contributions to this edition of the +Python-World Newsletter: + +- `Ravishankar Chavare `__ + +Thank you for your dedication and for enriching the Python community +with your valuable insights, code snippets, and contributions! Happy +coding! 🐍✨ diff --git a/_sources/newsletters/2023/WEEK_25.rst.txt b/_sources/newsletters/2023/WEEK_25.rst.txt new file mode 100644 index 0000000..9ca5d2e --- /dev/null +++ b/_sources/newsletters/2023/WEEK_25.rst.txt @@ -0,0 +1,733 @@ +Week 25 - June 2023 +################### + + +1. 🐍 Power of the “repr” function in python. +********************************************* + +🐍 Python developers, let’s dive into the fascinating world of the +“repr” function! 🎉 + +⚡️ Have you ever encountered a situation where “print” gives you a +different result than “repr”? Let me show you the magic of “repr” in +such scenarios. + +🔍 Imagine you have a complex object with intricate internal state. When +you use “print” to display it, you might get an output that doesn’t +reveal the full picture. But fear not! “repr” comes to the rescue. + +💡 The “repr” function provides a detailed and unambiguous string +representation of an object, perfect for debugging and understanding its +internals. + +🌟 Let’s consider a complex example: + +.. code:: python + + import datetime + + now = datetime.datetime.now() + + print("Printing using print:") + print(now) # Output: 2023-06-22 15:30:45.123456 + + print("\nPrinting using repr:") + print(repr(now)) # Output: datetime.datetime(2023, 6, 22, 15, 30, 45, 123456) + +🔎 Notice the difference? While “print” gives us a more human-readable +representation, “repr” provides an exact and detailed view of the +object. + +🎯 “repr” is a valuable tool when debugging complex data structures or +objects with custom implementations. It helps us uncover hidden details +and understand the inner workings of our code. + +💡 So, remember to leverage the power of “repr” whenever you encounter +complex objects that need thorough inspection! + +✨ Let’s embrace “repr” and unlock new levels of debugging and +understanding in our Python projects. Happy coding, everyone! 💻 + + +2. 🌈 Enhancing console output with ANSI color codes. +***************************************************** + +Do you want to add some pizzazz to your Python console output? 💥 Say +hello to ANSI escape codes! 🎨✨ + +ANSI escape codes are special character sequences that allow you to +change the color and formatting of text in the console or terminal. +Let’s take a look at how you can use them in Python to bring your +console output to life! 🚀 + +.. code:: python + + # ANSI color codes + RESET = "\033[0m" + RED = "\033[31m" + GREEN = "\033[32m" + YELLOW = "\033[33m" + BLUE = "\033[34m" + MAGENTA = "\033[35m" + CYAN = "\033[36m" + WHITE = "\033[37m" + + # Example usage + print(RED + "This text is red." + RESET) + print(GREEN + "This text is green." + RESET) + print(YELLOW + "This text is yellow." + RESET) + print(BLUE + "This text is blue." + RESET) + print(MAGENTA + "This text is magenta." + RESET) + print(CYAN + "This text is cyan." + RESET) + print(WHITE + "This text is white." + RESET) + +With just a few lines of code, you can transform your console output +into a vibrant display of colors. 🌈✨ Imagine the possibilities! 🎉 + +However, keep in mind that ANSI escape codes may not work on all +terminals or console emulators, especially on Windows. But don’t worry, +Windows users can achieve similar effects by using the ``colorama`` +library. Simply install it with ``pip install colorama`` and modify the +code as follows: + +.. code:: python + + from colorama import Fore, Style + + # Example usage + print(Fore.RED + "This text is red." + Style.RESET_ALL) + print(Fore.GREEN + "This text is green." + Style.RESET_ALL) + print(Fore.YELLOW + "This text is yellow." + Style.RESET_ALL) + print(Fore.BLUE + "This text is blue." + Style.RESET_ALL) + print(Fore.MAGENTA + "This text is magenta." + Style.RESET_ALL) + print(Fore.CYAN + "This text is cyan." + Style.RESET_ALL) + print(Fore.WHITE + "This text is white." + Style.RESET_ALL) + +Now you can enjoy colorful console output on any platform! 🎉🎉 + +So go ahead and make your Python console output stand out from the +crowd. Impress your colleagues and friends with eye-catching displays of +information. 🌟✨ + +Have fun experimenting with ANSI color codes in Python, and let your +imagination run wild! 🚀🐍 + +3. 🔀 Partial method in python +******************************* + +Partial methods are a fascinating feature in Python that allow you to +create new methods from existing ones by pre-filling some of the +arguments. This enables you to customize and specialize functions +without the need for repetitive code. Let’s explore this concept +further! 💡🔧 + +Imagine you have a function with multiple arguments, and you frequently +call it with some fixed values for some of the parameters. Instead of +passing those values every time you call the function, you can use +partial methods to create a new function with those arguments already +filled in. This can make your code more concise and easier to read. 🧩✨ + +In Python, you can utilize the ``functools`` module to create partial +methods. The ``functools`` module provides the ``partial`` function, +which takes a callable and any number of arguments or keyword arguments. +It returns a new partial object, which can be called as a regular +function. 💻🔍 + +Here’s an example to illustrate the concept: 📝 + +.. code:: python + + from functools import partial + + def greet(name, greeting): + print(f"{greeting}, {name}!") + + say_hello = partial(greet, greeting="Hello") + say_hello("Alice") # Output: Hello, Alice! + say_hello("Bob") # Output: Hello, Bob! + +In the example above, we defined a ``greet`` function that takes two +arguments: ``name`` and ``greeting``. By using ``partial``, we created a +new function called ``say_hello``, where the ``greeting`` argument is +already set to “Hello”. Now, whenever we call ``say_hello``, we only +need to provide the ``name`` argument. 👋👋 + +Partial methods are particularly useful when working with libraries or +frameworks that require callback functions with predefined arguments. +Instead of writing separate functions for each callback, you can use +partial methods to create specialized callbacks without duplicating +code. 📚🧪 + +Remember that partial methods are not limited to positional arguments +only; you can also use them with keyword arguments. Additionally, you +can modify or override any of the pre-filled arguments when calling the +partial method, providing you with even more flexibility. 🎛️🔀 + +4. 🔑 Encoding and Decoding using Base64 +***************************************** + +Base64 encoding is commonly used when you need to represent binary data, +such as images or files, as ASCII text. It converts binary data into a +string of characters that are safe to transmit or store, ensuring +compatibility across different systems and protocols. Python provides a +convenient way to perform base64 encoding and decoding using the +built-in ``base64`` module. 📚🔡 + +Let’s take a look at an example to see how base64 encoding and decoding +work: 🖥️🔑 + +.. code:: python + + import base64 + + # Encoding binary data to base64 + data = b"Python-World" + encoded_data = base64.b64encode(data) + print(encoded_data) # Output: b'UHl0aG9uLVdvcmxk' + + # Decoding base64 data to binary + decoded_data = base64.b64decode(encoded_data) + print(decoded_data) # Output: b'Python-World' + +In the example above, we imported the ``base64`` module and encoded the +binary data “Python-World” using ``base64.b64encode()``. The result, +``encoded_data``, is a byte string representing the base64 encoded +version of the data. When printing ``encoded_data``, the ``b`` prefix +indicates that it is a byte string. 📜🔢 + +To decode the base64 data back into its original binary form, we used +``base64.b64decode()`` and assigned the result to ``decoded_data``. +Printing ``decoded_data`` shows that it successfully restored the +original binary data. 🔄🔤 + +Base64 encoding and decoding have various applications, including data +transmission over email, storage of binary data in JSON or XML formats, +and handling binary data in network protocols. Understanding base64 +encoding is essential when working with data that needs to be safely +represented as text. 📡📦 + +Python’s ``base64`` module provides additional functionalities, such as +handling URL-safe base64 encoding and decoding, as well as encoding and +decoding using different character sets. Exploring these options can +further enhance your encoding and decoding capabilities. 🌐🔠 + +5. 🎣 Understanding hooks in requests +************************************* + +Hooks in the ``requests`` module provide a mechanism to intercept and +modify the request and response flow. They enable you to inject your own +code at various stages of the request lifecycle, empowering you to +perform custom processing, logging, authentication, error handling, and +much more. Hooks are a powerful tool to enhance and tailor your HTTP +requests according to your specific needs. 💡🔌 + +.. raw:: html + +

+ +Types of Hooks: + +.. raw:: html + +

+ +1. Response Hooks: + + - ``response``: Executed after a response is received, regardless of + the response status. + +These hooks provide immense flexibility and control over your requests, +enabling you to customize and augment the behavior as per your +requirements. ⚙️🎛️ + +.. raw:: html + +

+ +🎯 Use Cases for Hooks: + +.. raw:: html + +

+ +Hooks can be employed in various scenarios to enhance the functionality +of your requests. Some common use cases include: + +1. Logging: Add logging statements to track the request and response + details for debugging or analysis purposes. +2. Authentication: Implement custom authentication mechanisms or token + management. +3. Error Handling: Define actions to be taken when specific exceptions + occur during the request. +4. Retry Strategies: Implement custom retry logic based on response + status codes or error conditions. + +.. raw:: html + +

+ +🌟 Registering Hooks with Specific Requests: + +.. raw:: html + +

+ +In addition to registering global hooks, you can also specify hooks as +keyword parameters when making individual requests using methods like +``get``, ``post``, ``put``, etc. This allows you to define hooks +specifically for a particular request and control their execution on a +per-request basis. Here’s an example: + +.. code:: python + + import requests + + def first_hook(response, *args, **kwargs): + # Perform custom logic for the first hook + print("First hook executed") + response.first_hook='you can pass any value or object' + + def second_hook(response, *args, **kwargs): + # Perform custom logic for the second hook + print("Second hook executed") + + # Make a request with multiple hooks + response = requests.get('https://www.example.com', hooks={'response': [first_hook, second_hook]}) + + # Retrive first_hook parameters + print(response.first_hook) + +In this case, both ``first_hook`` and ``second_hook`` will be executed +for the single request made in the ``get`` method. + +.. raw:: html + +

+ +🚀 Registering Hooks with a Session: + +.. raw:: html + +

+ +If you need to maintain state or persist hooks across multiple requests, +you can use a ``requests.Session`` object. Here’s an example: + +.. code:: python + + import requests + + def custom_hook(response, *args, **kwargs): + # Perform custom logic + print("Custom hook executed") + + # Also U can set custom attibute to response object + response.custom_hook_called=True + + + # Create a session and register the custom hook + session = requests.Session() + session.hooks['response'].append(custom_hook) + + # Make requests using the session + response1 = session.get('https://www.example.com') + + ## Status of custom hooks + print("parameter from custom hooks:",response1.custom_hook_called) + +In this example, the ``custom_hook`` will be executed for both +``response1`` and ``response2``, as they are made using the same +session. + +6. 📢 LinkExtractor in Scrapy +***************************** + +🔗 The LinkExtractor class in Scrapy is a powerful tool for extracting +links from web pages. It provides a convenient way to crawl and scrape +websites efficiently. Let’s dive into some simple code examples to +understand how it works. + +📝 Code Example: + +.. code:: python + + + import scrapy + from scrapy.crawler import CrawlerProcess + from scrapy.linkextractors import LinkExtractor + + class MySpider(scrapy.Spider): + name = 'example' + start_urls = ['http://www.example.com'] + + def parse(self, response): + # Instantiate a LinkExtractor object + link_extractor = LinkExtractor() + + # Extract links from the response + links = link_extractor.extract_links(response) + + # Process the extracted links + for link in links: + # Access the URL, text, and other attributes of the link + yield { + 'url': link.url, + 'text': link.text + } + + + + # Create a CrawlerProcess instance + process = CrawlerProcess() + + # Add your spider to the CrawlerProcess + process.crawl(MySpider) + + # Start the crawling process + process.start() + + + # Output: > {'url': 'https://www.iana.org/domains/example', 'text': 'More information...'} + +🔍 In this example, we create a Scrapy spider named ``MySpider``. The +``LinkExtractor`` class is imported from ``scrapy.linkextractors`` to +handle link extraction. + +🕸️ Inside the ``parse`` method, we instantiate a ``LinkExtractor`` +object. Then, we use the ``extract_links`` method to extract all links +from the ``response`` object. + +🔗 We iterate over the extracted links and access their URL and text +using the ``url`` and ``text`` attributes. You can perform further +processing or extraction based on your specific needs. + +📥 Finally, we yield a dictionary containing the URL and text of each +extracted link. You can modify this code to process the links +differently or store them in any desired format. + +💡 The LinkExtractor class provides many additional options and +parameters to customize link extraction based on patterns, tags, +attributes, and more. Make sure to explore the Scrapy documentation for +more advanced usage. + +7. 📝 itemgetter in python +*************************** + +🔍 The ``itemgetter`` function in Python is a convenient tool for +extracting specific elements from iterable objects. It provides a simple +and efficient way to access and manipulate data. Let’s explore its +functionality with some simple code examples, including its usage in +``sorted`` with lists, dictionaries, and lists of dictionaries. + +In general, if you need to access specific elements from iterable +objects and performance is a concern, using ``itemgetter`` is a +recommended approach. It offers better performance compared to using a +lambda function, particularly when dealing with large datasets or +performing operations that need to be executed multiple times. + +📝 Code Example: + +.. code:: python + + from operator import itemgetter + + # Example 1: Accessing Elements from a List + my_list = ['apple', 'banana', 'cherry', 'date'] + get_second_element = itemgetter(1) + print(get_second_element(my_list)) # Output: 'banana' + + # Example 2: Accessing Elements from a Dictionary + my_dict = {'name': 'John', 'age': 30, 'city': 'New York'} + get_age = itemgetter('age') + print(get_age(my_dict)) # Output: 30 + + # Example 3: Accessing Multiple Elements from a Tuple + my_tuple = ('apple', 'banana', 'cherry', 'date') + get_first_and_last = itemgetter(0, -1) + print(get_first_and_last(my_tuple)) # Output: ('apple', 'date') + + # Example 4: Using itemgetter in sorted with a List of Tuples + students = [('John', 20, 'A'),('Alice', 19, 'B'),('Bob', 21, 'A-'),('Carol', 18, 'B+')] + sorted_students = sorted(students, key=itemgetter(2)) + print(sorted_students) + + # Example 5: Using itemgetter in sorted with a Dictionary + student_grades = {'John': 'A','Alice': 'B','Bob': 'A-','Carol': 'B+'} + sorted_grades = sorted(student_grades.items(), key=itemgetter(1)) + print(sorted_grades) + + # Example 6: Using itemgetter in sorted with a List of Dictionaries + people = [ + {'name': 'John', 'age': 25}, + {'name': 'Alice', 'age': 30}, + {'name': 'Bob', 'age': 20}, + {'name': 'Carol', 'age': 35} + ] + sorted_people = sorted(people, key=itemgetter('age')) + print(sorted_people) + +🔢 In these examples, we import the ``itemgetter`` function from the +``operator`` module to extract specific elements from iterable objects. + +📌 Examples 1, 2, and 3 demonstrate how ``itemgetter`` can be used to +access elements from a list, dictionary, and tuple, respectively. + +📌 Example 4 showcases the usage of ``itemgetter`` in the ``sorted`` +function with a list of tuples. By specifying ``key=itemgetter(2)``, the +``sorted`` function sorts the students based on their grade (the third +element in each tuple). + +📌 Example 5 illustrates how ``itemgetter`` can be used with the +``sorted`` function with a dictionary. We convert the dictionary items +into a list of tuples using the ``items()`` method and then sort the +tuples based on the grades using ``key=itemgetter(1)``. + +📌 Example 6 demonstrates the usage of ``itemgetter`` in the ``sorted`` +function with a list of dictionaries. By specifying +``key=itemgetter('age')``, the ``sorted`` function sorts the list of +dictionaries based on the ‘age’ key, resulting in a sorted list of + +people based on their age. + +💡 The ``itemgetter`` function is a powerful tool for accessing and +sorting specific elements from iterable objects. It offers flexibility +and performance benefits, making it a valuable addition to your Python +toolkit. + +8. 🖥️ Fetching device name using platform +****************************************** + +🔍 The ``platform`` module in Python provides a convenient way to access +information about the platform your code is running on. Let’s dive into +how you can use this module to fetch the device name: + +🔦 **Fetching Device Name:** The ``platform`` module’s ``node()`` +function returns the device’s network name, which is often the device’s +hostname. This can provide a reasonable approximation of the device name +in many cases. + +.. code:: python + + import platform + + device_name = platform.node() + print(f"Device Name: {device_name}") + +🖥️💻📱 The ``device_name`` variable will contain the name of the device +your code is running on, which could be the hostname of the machine or a +recognizable identifier for the device. + +🚀 **Platform Information:** The ``platform`` module can provide more +than just the device name. You can also access other platform-related +information, such as the operating system, Python implementation, and +architecture. + +.. code:: python + + import platform + + # Get the operating system name + os_name = platform.system() + print(f"Operating System: {os_name}") + + # Get the release version of the operating system + os_release = platform.release() + print(f"Release Version: {os_release}") + + # Get the version of the operating system + os_version = platform.version() + print(f"OS Version: {os_version}") + + # Get the machine type + machine_type = platform.machine() + print(f"Machine Type: {machine_type}") + + # Get the processor name or identifier + processor = platform.processor() + print(f"Processor: {processor}") + + # Get the version of Python + python_version = platform.python_version() + print(f"Python Version: {python_version}") + + # Get the name of the Python implementation + python_implementation = platform.python_implementation() + print(f"Python Implementation: {python_implementation}") + + # Get the compiler used to build Python + python_compiler = platform.python_compiler() + print(f"Python Compiler: {python_compiler}") + + # Get the architecture and bitness of the operating system + architecture, bitness = platform.architecture() + print(f"Architecture: {architecture}") + print(f"Bitness: {bitness}") + + # Get the network name of the device + device_name = platform.node() + print(f"Device Name: {device_name}") + + ''' + Output: + Operating System: Windows + Release Version: 11 + OS Version: 12.0.22334621 + Machine Type: AMD64 + Processor: AMD64 Family 21 Model 111 Stepping 2, AuthenticAMD + Python Version: 3.11.3 + Python Implementation: CPython + Python Compiler: MSC v.1934 64 bit (AMD64) + Architecture: 64bit + Bitness: WindowsPE + Device Name: DESKTOP-KJK + ''' + +📡 **Platform-Specific Functionality:** By knowing the device name or +platform, you can tailor your code’s behavior to provide +platform-specific functionality. For example, you can enable or disable +certain features, adjust settings, or optimize code paths based on the +device or platform your code is running on. + +💡 Fetching the device name using the ``platform`` module allows you to +adapt your code to different devices and platforms. It opens up +possibilities for building cross-platform applications, device-specific +optimizations, and customized user experiences. + +9. 🌟 Package your app into a executable file using zipapp +********************************************************** + + +🔍 **What is Zipapp?** Zipapp is a module introduced in Python 3.5 that +enables you to package your Python application along with its +dependencies into a single executable ZIP file. This ZIP file can be +executed directly, making it convenient to distribute your application +as a standalone package. + +🚀 **Key Features and Benefits:** + +- Simplified Distribution: + With Zipapp, you can distribute your Python application as a single file, + eliminating the need for users to install dependencies separately. + +- Easy Execution: + The executable ZIP file can be run directly without + the need for a Python interpreter or additional setup. + +- Cross-Platform Compatibility: + Zipapp packages are portable and can + be executed on different operating systems, making it easier to + distribute your application across platforms. + +🔧 **Creating a Zipapp:** + +Creating a zipapp is straightforward.Here’s a +simple example create ``app.py`` file in ``myapp``: + +.. code:: python + + # myapp/app.py + def main(): + print("Hello, Python-World!") + + if __name__ == '__main__': + main() + +To create the zipapp + +.. code:: bash + + python -m zipapp myapp -m "app:main" + +In this example, we’re creating a zipapp named ``myapp.pyz``, specifying +the ``app.py`` file as the entry point . + +🚀 **Executing the Zipapp:** + +Once you have the zipapp file, you can run +it like any other executable. Here’s an example: + +.. code:: bash + + $ python myapp.pyz + Hello, Python-World! + +💡 **Use Cases and Considerations:** + +- *Distribution of Command-Line Tools:* + Zipapp is an excellent choice for packaging and distributing + command-line tools written in Python. +- *Portable Applications:* + If you want to create a portable Python application that can be run on + different systems without requiring installation, zipapp can be a great + solution. +- *Version Management:* + Zipapp can help in managing and + distributing specific versions of your application, ensuring consistent + execution across environments. + +Zipapp is a powerful tool for simplifying the distribution and execution +of your Python applications. It provides a convenient way to package +your code and dependencies into a single executable file, making it +easier for users to run your application without complex setup +processes. + +10. 🔧 Secure User Input with getpass() +**************************************** + +🔎 When it comes to handling sensitive user input, such as passwords or other confidential information, security is of utmost importance.the `getpass()` method, which provides a secure way to accept user input without displaying it on the screen. + +**What is getpass()?** + +The `getpass()` method is a part of the Python `getpass` module. It is used to prompt the user for input, such as a password, and securely captures the input without echoing it back to the screen. + +🚀 **Key Features and Benefits:** + +- **Secure User Input:** The `getpass()` method ensures that sensitive user input, like passwords, remains hidden, reducing the risk of unauthorized access. +- **Cross-Platform Compatibility:** The method works consistently across different operating systems, providing a reliable solution for secure user input. +- **Simplicity of Use:** With a single function call, you can prompt the user for input and retrieve it securely, without the need for complex code. + +🔧 **Using getpass():** + +Using the `getpass()` method is straightforward. Here's a simple example: + +.. code:: python + + import getpass + + password = getpass.getpass("Enter your password: ") + print("Password entered:", password) + + +When you run this code, the `getpass()` method will prompt the user to enter a password, and the input will be securely captured without being displayed on the screen. + +💡 **Best Practices:** + +- **Avoid Storing Passwords in Plain Text:** Remember, the `getpass()` method only helps in securely capturing user input. It's essential to handle the captured passwords carefully and avoid storing them in plain text. Consider using secure password storage mechanisms like hashing and salting. +- **Ensure Proper Error Handling:** Handle exceptions raised by the `getpass()` method to provide meaningful error messages and gracefully handle any issues encountered during user input. + +The `getpass()` method is a valuable tool for securely capturing sensitive user input, making it an ideal choice when handling passwords or other confidential information. By using this method, you can enhance the security of your applications and protect user privacy. + +.. raw:: html + + + + +Useful Resources +---------------- + +Here are some valuable resources to enhance your Python programming +skills and stay updated with the latest trends: + + +- **Online Course**: Take your Python web development skills to the + next level with the “Mastering Flask” course on + `Pluralsight `__. + +- **Forum**: Join the vibrant Python community discussions and seek + help at `Python + Forums `__. + Engage with fellow developers, share knowledge, and contribute to the + Python ecosystem. + +Stay curious, keep learning, and make the most of these resources to +enhance your Python journey! diff --git a/_sources/newsletters/2023/WEEK_26.rst.txt b/_sources/newsletters/2023/WEEK_26.rst.txt new file mode 100644 index 0000000..649ed06 --- /dev/null +++ b/_sources/newsletters/2023/WEEK_26.rst.txt @@ -0,0 +1,676 @@ +Week 26 - Jully 2023 +#################### + + + +1. 🐍 Exploring the Power of \\r in Python's Print Statement +************************************************************* + +🖋️ The '\\r' character allows you to return the cursor to the beginning of the current line without advancing to the next line. This simple yet powerful escape sequence enables dynamic and interactive output. + +💡 Use Cases and Examples: + +1️⃣ Basic Usage: Update the content on the same line. For example: + +.. code:: python + + import time + + for i in range(10): + print('Count:', i, end='\r') + time.sleep(1) + + +2️⃣ Progress Bar Effect: Create captivating progress bars or indicators using '\\r' to update the value dynamically. For example: + +.. code:: python + + import time + + for i in range(10): + progress = '#' * i + '-' * (10 - i) + print('Progress:', progress, end='\r') + time.sleep(1) + + +3️⃣ Overwriting Lines: Master the art of using multiple '\\r' characters to overwrite previous lines, perfect for real-time updates or interactive outputs. For example: + +.. code:: python + + import time + + print('Starting process...') + time.sleep(2) + print('Processing Step 1...', end='\r') + time.sleep(2) + print('Processing Step 2...', end='\r') + time.sleep(2) + print('Processing Step 3... Complete!',end='\r') + + +**🌟 Enhancing User Experience:** + +By leveraging the power of '\\r', you can create engaging and interactive command-line applications that provide real-time feedback and a captivating user experience. + +**🏢 Industry Examples:** + +**1️⃣ Software Development:** Use '\\r' to display progress and status updates during long-running tasks, such as building and compiling large codebases. + +**2️⃣ Data Analysis:** Employ '\\r' to provide live updates on data processing and analysis tasks, enhancing the transparency and interactivity of the process. + +**3️⃣ File Conversion Tools:** Utilize '\r' to show progress while converting files from one format to another, giving users real-time feedback on the conversion process. + +**4️⃣ Network Monitoring:** Implement '\\r' to display real-time network statistics, such as data transfer rates and network latency, helping network administrators monitor network performance. + +**⚠️ Compatibility Considerations:** + +Remember that the behavior of '\\r' may vary depending on the platform or console being used. While it typically works well in terminal environments, it may not function as expected in certain IDEs or text editors. + + + +2. 📚 Unleashing the Power of OrderedDict +***************************************** + +**📰 What is OrderedDict?** + +OrderedDict is a specialized dictionary available in Python's collections module that maintains the order of its items based on their insertion. While regular dictionaries don't guarantee any specific order, OrderedDict offers a solution by providing a predictable sequence of elements. + +**✨ Features and Benefits:** + +**1️⃣ Order Preservation:** The primary advantage of OrderedDict is that it retains the order of elements, allowing you to iterate over them in the exact order they were added. This characteristic is valuable when working with scenarios where order matters. + +**2️⃣ Predictable Iteration:** Since OrderedDict maintains the order of elements, it guarantees predictable iteration behavior. This property makes it useful when you need to perform operations that rely on a specific sequence, such as sorting, serialization, or generating reports. + +**3️⃣ Element Reordering:** With OrderedDict, you can easily rearrange the elements by inserting or moving items at specific positions. This feature is particularly handy when you want to change the order dynamically based on certain conditions or requirements. + +**4️⃣ JSON-like Structure:** The structure of OrderedDict resembles JSON objects, making it convenient when working with JSON data. It ensures that the order of elements is preserved when serializing or deserializing JSON data, maintaining consistency across operations. + +**5️⃣ Frequency Counting:** By combining OrderedDict with other data structures like defaultdict, you can create powerful tools for counting the frequency or popularity of elements in a collection. This can be helpful in various scenarios, such as analyzing user interactions or tracking word occurrences in a text corpus. + +**🌟 Real-World Examples:** + +**1️⃣ Maintaining Configuration Order:** + +OrderedDict can be used to store configuration settings, ensuring they are applied in a specific order. For instance, consider a scenario where you have multiple settings with dependencies, and the order of their application matters. OrderedDict guarantees that the settings are processed in the desired sequence. + +Example: + +.. code:: python + + from collections import OrderedDict + + config = OrderedDict() + config['database'] = 'localhost' + config['username'] = 'admin' + config['password'] = 'secretpassword' + config['port'] = 5432 + + for key, value in config.items(): + print(f'{key}: {value}') + +**2️⃣ Logging Request Order:** + +When building a web application, you might want to log incoming requests in the order they are received. By utilizing an OrderedDict, you can store request information and maintain the exact order of requests, allowing you to analyze the traffic patterns effectively. + +Example: + +.. code:: python + + from collections import OrderedDict + + request_log = OrderedDict() + + def log_request(request): + request_log[request['timestamp']] = request['url'] + + # Log requests + log_request(dict(timestamp='2023-06-23 10:30:00', url='http://example.com')) + log_request(dict(timestamp='2023-06-23 10:35:00', url='http://example.com/about')) + log_request(dict(timestamp='2023-06-23 10:40:00', url='http://example.com/contact')) + + # Print request log + for timestamp, url in request_log.items(): + print(f'{timestamp}: {url}') + +**💡 Conclusion:** + +OrderedDict in Python provides a valuable tool for scenarios where preserving the order of elements is crucial. By using OrderedDict, you can maintain order, achieve predictable iteration, and handle various real-world use cases effectively. Its flexibility and simplicity make it a powerful addition to your Python toolkit. + + + + +3. 🌿 Exploring the Magic of stem in pathlib +********************************************* + +**📜 Overview:** + +The `pathlib` module is a powerful tool for working with file system paths in a platform-independent manner. One of its notable features is the `stem` attribute, which allows you to extract the file name without the file extension from a given path. + +**💡 Understanding `stem`:** + +The `stem` attribute provides a convenient way to extract the base name of a file from its path. It essentially removes the file extension and returns the file name only. This can be extremely useful when you need to work with file names or perform operations that require manipulating file names separately. + +**🔧 Example Usage:** + +Let's see an example to better understand the usage of `stem`. Suppose we have a file path like "/path/to/myfile.txt" and we want to extract just the file name "myfile" without the ".txt" extension. We can achieve this using the `stem` attribute as follows: + +.. code:: python + + from pathlib import Path + + path = Path("/path/to/myfile.txt") + file_stem = path.stem + + print(file_stem) # Output: "myfile" + +As you can see, by accessing the `stem` attribute of the `Path` object, we obtain the desired file name without the extension. + +**🌱 Benefits of Using `stem`:** + + - Simplifies file name extraction: With `stem`, you can easily extract the base name of a file without manually manipulating the string or using other techniques. + - Platform-independent: The `pathlib` module ensures consistent behavior across different operating systems, making your code more portable. + +**🌿 Industry Examples:** + + - Renaming files: You can use `stem` to extract the base name of files and perform renaming operations based on specific criteria. + - File manipulation: By working with the extracted file names, you can perform targeted operations on files within a directory. + + + +4. 🌐 Demystifying URL Parsing with `urlparse` +********************************************** + + +**🔗 What is `urlparse`?** + +`urlparse` is a Python module that provides a convenient way to parse, manipulate, and extract information from URLs. It allows you to break down a URL into its various components, such as the scheme, network location, path, query parameters, and more. + +**🚀 Why Use `urlparse`?** + + - URL Parsing: Extract individual components from a URL, such as the domain, path, or query parameters. + - URL Construction: Assemble a URL by combining its components into a valid URL string. + - URL Manipulation: Modify specific parts of a URL, such as adding or updating query parameters. + - URL Validation: Check the validity and integrity of a URL. + - URL Normalization: Convert relative URLs to absolute URLs for consistency and usability. + +**🔧 How to Use `urlparse`:** + +Using `urlparse` is straightforward. Let's look at an example: + +.. code:: python + + from urllib.parse import urlparse + + url = "https://www.python.org/docs/?q=python&page=1#intro" + parsed_url = urlparse(url) + + print(parsed_url.scheme) # Output: "https" + print(parsed_url.netloc) # Output: "www.python.org" + print(parsed_url.path) # Output: "/docs/" + print(parsed_url.query) # Output: "q=python&page=1" + print(parsed_url.fragment) # Output: "intro" + +**💡 Pro Tip:** + +Remember to handle exceptions when parsing URLs, as not all URLs may conform to the expected format. Additionally, consider URL encoding and decoding for special characters in query parameters. + +**🌟 Industry Use Cases:** + + - Web Scraping: Extracting specific information from URLs. + - URL Routing: Building web applications with URL routing and parameter extraction. + - API Integration: Parsing URLs to interact with RESTful APIs. + - URL Canonicalization: Standardizing and normalizing URLs for data analysis or comparison. + + + + +5. 🧪 Exploring Type Creation and Modification using `type` +*********************************************************** + + +**🔍 Understanding Types and Metaclasses:** + +In Python, everything is an object, including types themselves. The `type` function not only allows us to check the type of an object but also grants us the ability to dynamically create and modify types. This opens up exciting possibilities for metaprogramming and advanced customization of our Python programs. + +**💡 Introspection with `type`:** + + - Checking the type of an object: `type(obj)` + - Obtaining the base classes of a type: `type.__bases__` + - Retrieving the name of a type: `type.__name__` + - Accessing the module in which a type is defined: `type.__module__` + +**✨ Creating New Types:** + +You can create a new class by invoking type with three arguments: the class name, the base classes (as a tuple), and a dictionary containing the class attributes and methods. + +Using `type` dynamically, we can create new types on the fly. For example: + +.. code:: python + + MyClass = type("MyClass", (object,), {}) + +🔹 Adding attributes to a dynamically created class: + +.. code:: python + + MyClass.attr = value + +🔹 Defining methods for a dynamically created class: + +.. code:: python + + def my_method(self): + ... + MyClass.my_method = my_method + + +**💡 Practical Examples:** + +**1️ Dynamic Class Generation:** + +Generating classes dynamically based on runtime conditions or configuration parameters. + +.. code:: python + + def create_dynamic_class(name, bases, attrs): + return type(name, bases, attrs) + + value=5 + DynamicClass = create_dynamic_class("DynamicClass", (object,), {"attr": value}) + print(DynamicClass.attr) # Output 5 + +**2 Create a new class by inheriting properties from a parent class** + +.. code:: python + + # Define the parent class + class ParentClass: + def parent_method(self): + print("This is a parent method.") + + # Define a dictionary to specify the attributes of the new class + class_attrs = { + 'child_attr': 42, + 'child_method': lambda self: print("This is a child method.") + } + + # Create a new class dynamically by inheriting from the parent class + ChildClass = type('ChildClass', (ParentClass,), class_attrs) + + # Create an instance of the child class + child = ChildClass() + + # Call methods from both parent and child classes + child.parent_method() # Output: This is a parent method. + child.child_method() # Output: This is a child method. + + # Access attributes from the child class + print(child.child_attr) # Output: 42 + + +6. 🏝️ Uses of Ellipses in Python +********************************* + +In Python, the Ellipsis or Ellipsis literal `...` (three dots) serves various purposes and provides useful functionalities in different contexts. This article explores the different uses of ellipses in Python and how they can enhance your code. + +**🌟 Placeholder for Unwritten Code** + +One use of the ellipsis literal is as a placeholder for unwritten code. Instead of using the `pass` keyword, you can use the ellipsis `...` to indicate that certain parts of your code, such as functions or classes, are yet to be implemented. + + +.. code:: python + + def unfinished_function(): + ... + + +Using the ellipsis as a placeholder signifies that the function body will be filled in later. + +**📚 Type Hinting with Ellipsis** + +Since Python 3.5, ellipses have been utilized in type hinting. Type hints allow you to declare and use specific data types for variables, parameters, and return values. The ellipsis can be part of a type hint, indicating that only part of the type is specified. + + +.. code:: python + + from typing import Tuple + + def process_data(data: Tuple[int, ...]): + # Process the data + + +In the above example, the ellipsis `...` is used within a tuple type hint to indicate that all items in the tuple should be of the same integer type, but the length of the tuple can be arbitrary. + +**🔪 Slicing in NumPy** + +NumPy, a popular library for scientific computing, makes extensive use of ellipses for slicing multidimensional arrays. Slicing allows you to extract specific portions of arrays based on indexes. Ellipsis provides a concise way to handle multidimensional arrays in NumPy. + + +.. code:: python + + import numpy as np + + arr = np.array([[5, 3, 2, 3], [4, 8, 2, 6], [8, 2, 3, 0]]) + + # Extracting elements using ellipsis literal + print(arr[..., 1]) + + # Extracting elements using general slice notation + print(arr[:, 1]) + + # Extracting elements using ellipsis object + print(arr[Ellipsis, 1]) + + +In the above code, the ellipsis `...` represents all the dimensions of the array. By using ellipsis, you can conveniently extract elements from specific indexes across different dimensions of the array. + +**💡 Conclusion** + +Ellipses in Python serve multiple purposes, ranging from placeholders for unwritten code to enabling advanced slicing in libraries like NumPy. Understanding and utilizing ellipses can enhance your code readability and make complex operations more manageable. By harnessing the power of ellipses, you can unlock new possibilities in your Python programming journey. + + +7. 🧩 Understanding `*args` and `**kwargs` in Python +***************************************************** + +In Python, \*args and \**kwargs are special syntaxes used to pass a variable number of arguments to functions. These notations provide flexibility when working with functions that can accept an arbitrary number of arguments. +This article dives into the details of \*args and \**kwargs and explores their uses and benefits. + +**🌟 *args: Variable-Length Arguments** + +The \*args syntax allows a function to accept a variable number of non-keyword arguments. It collects the arguments passed to the function into a tuple, enabling the function to handle any number of positional arguments. + + +.. code:: python + + def sum_numbers(*args): + total = 0 + for num in args: + total += num + return total + + print(sum_numbers(1, 2, 3)) # Output: 6 + print(sum_numbers(4, 5, 6, 7)) # Output: 22 + + +In the above code, the function `sum_numbers` accepts any number of arguments. The `*args` notation allows passing multiple values, which are then treated as a tuple within the function. This way, you can perform operations on an arbitrary number of arguments. + +**🔧 kwargs : Variable-Length Keyword Arguments** + +The \**kwargs syntax, on the other hand, enables a function to accept a variable number of keyword arguments. It collects the keyword arguments passed to the function into a dictionary, allowing the function to handle a flexible set of named arguments. + + +.. code:: python + + def print_details(**kwargs): + for key, value in kwargs.items(): + print(f"{key}: {value}") + + print_details(name="John", age=25) # Output: name: John, age: 25 + print_details(city="London", country="UK", occupation="Engineer") # Output: city: London, country: UK, occupation: Engineer + + +In the above code, the function `print_details` accepts any number of keyword arguments. The `\**kwargs` notation collects the key-value pairs and treats them as a dictionary within the function. This allows for flexible handling of named arguments without explicitly defining them. + +**🎯 Combining *args and **kwargs** + +You can also use \*args and \**kwargs together in a function declaration to handle both positional and keyword arguments simultaneously. This allows for maximum flexibility when designing functions that can accept different types of inputs. + + +.. code:: python + + def process_data(*args, **kwargs): + for arg in args: + print(f"Positional Argument: {arg}") + for key, value in kwargs.items(): + print(f"Keyword Argument - {key}: {value}") + + process_data(1, 2, 3, name="John", age=25) + + +In the above code, the function `process_data` can handle both positional and keyword arguments. The `*args` notation captures any number of positional arguments, while the `**kwargs` notation captures any number of keyword arguments. This allows for ultimate flexibility in function parameter handling. + +**💡 Conclusion** + +Understanding \*args and \**kwargs in Python empowers you to write more flexible and versatile functions. \*args enables you to handle an arbitrary number of positional arguments, while \**kwargs allows you to handle a variable number of keyword arguments. By combining both notations, you can create functions that are capable of accepting and processing different types of inputs. Utilizing \*args and \**kwargs expands the capabilities of your code and enables you to build more dynamic and adaptable solutions. + + +8. 🔍 Exploring Context Managers in Python with `with` +****************************************************** + +**🔗 What are Context Managers?** + +Context managers in Python are objects that help manage resources and define the behavior that should occur when entering and exiting a specific context. They are primarily used to simplify the management of resources like files, database connections, and network connections, ensuring that they are properly initialized and cleaned up after use. + +**🚀 Using Context Managers for File Handling** + +One common use case for context managers is file handling. By using a context manager, you can automatically handle the opening and closing of files, ensuring that resources are properly released, even in the presence of exceptions. + +Here's an example of using a context manager with the `with` statement for file handling: + + +.. code:: python + + with open("example.txt", "r") as file: + content = file.read() + print(content) + + +In the above code, the `open` function is used to open the file "example.txt" in read mode. The `with` statement ensures that the file is automatically closed when the block is exited, even if an exception occurs. This eliminates the need to manually close the file and reduces the risk of resource leaks. + +**🔧 Creating Custom Context Managers** + +Python allows you to create your own context managers by defining classes that implement the `__enter__` and `__exit__` methods. The `__enter__` method sets up the context, and the `__exit__` method defines the actions to be taken when leaving the context. + +Here's an example of creating a custom context manager using the `contextlib` module: + + +.. code:: python + + from contextlib import contextmanager + + @contextmanager + def my_context_manager(): + # Code to be executed on entering the context + print("Entering the context") + + # Yielding control back to the caller + yield + + # Code to be executed on exiting the context + print("Exiting the context") + + # Using the custom context manager + with my_context_manager(): + print("Inside the context") + + +In the above code, the `@contextmanager` decorator from the `contextlib` module is used to define a generator-based context manager. The code before the `yield` statement is executed on entering the context, and the code after the `yield` statement is executed on exiting the context. + +**💡 Utility of Context Managers** + +Context managers offer several benefits: + +1. **Resource Management**: Context managers ensure that resources are properly initialized and cleaned up, even in the presence of exceptions. This helps prevent resource leaks and improves the reliability of your code. + +2. **Readability**: Using context managers with the `with` statement improves the readability of your code by clearly delineating the start and end of a context. It also eliminates the need for explicit cleanup code. + +3. **Simplification**: Context managers simplify the usage of resources by encapsulating the setup and teardown operations within the context manager object. This makes your code more concise and easier to maintain. + +4. **Error Handling**: Context managers allow you to handle exceptions gracefully. The `__exit__` method can be used to catch and handle exceptions that occur within the context, providing a centralized place for error handling. + +**🌟 Conclusion** + +Context managers in Python, used with the `with` statement, provide a convenient and reliable way to manage resources and handle context-specific behaviors. They simplify the management of resources like files and database connections, ensuring proper initialization and cleanup. By creating custom context managers, you can encapsulate specific context-related functionality and improve the readability and maintainability of your code. Understanding and utilizing context managers is a valuable skill for writing robust and efficient Python code. + + +9. 🧨 Harnessing the Power of Generators in Python with `yield` +*************************************************************** + +**🔗 What are Generators?** + +Generators in Python are a type of iterable that allows you to iterate over a potentially infinite sequence of values without storing them all in memory at once. They are defined using the `yield` keyword and offer a more memory-efficient and lazy evaluation approach compared to traditional lists or iterators. + +**🚀 Working with Generator Functions** + +Generator functions are special functions that use the `yield` keyword to produce a sequence of values. When called, a generator function returns a generator object that can be iterated over using a `for` loop or by using the `next()` function. + +Here's an example of a generator function that generates a sequence of Fibonacci numbers: + + +.. code:: python + + def fibonacci(): + a, b = 0, 1 + while True: + yield a + a, b = b, a + b + + # Using the generator function + fib_gen = fibonacci() + for _ in range(10): + print(next(fib_gen)) + + +In the above code, the `fibonacci()` function is a generator function that yields the Fibonacci sequence. The `yield` keyword suspends the function's execution and returns a value, allowing the generator to produce the sequence incrementally. The `for` loop and `next()` function are used to retrieve values from the generator. + +**🔧 Benefits of Generators** + +Generators offer several advantages: + +1. **Memory Efficiency**: Generators produce values on the fly, allowing you to work with large or infinite sequences without needing to store all the values in memory. This makes generators memory-efficient and suitable for processing large datasets. + +2. **Lazy Evaluation**: Generators use lazy evaluation, meaning they compute values only when needed. This results in improved performance by avoiding unnecessary computations and reducing memory consumption. + +3. **Simplified Code**: Generators simplify code by encapsulating complex logic within a single function. They enable you to express iterative algorithms more concisely and intuitively. + +4. **Infinite Sequences**: Generators are ideal for generating infinite sequences or handling situations where the exact number of values is unknown in advance. They allow you to work with sequences that would otherwise be impractical to generate or store in memory. + +**💡 Creating Generator Expressions** + +In addition to generator functions, Python also provides generator expressions, which are similar to list comprehensions but generate values on-the-fly. Generator expressions are enclosed in parentheses instead of brackets and offer a concise way to create generators. + +Here's an example of a generator expression that yields squares of numbers: + + +.. code:: python + + squares_gen = (x ** 2 for x in range(10)) + for num in squares_gen: + print(num) + + +In the above code, the generator expression `(x ** 2 for x in range(10))` generates squares of numbers from 0 to 9. The resulting generator can be iterated over to retrieve the squared values. + +**🌟 Conclusion** + +Generators in Python, implemented using the `yield` keyword, provide a powerful mechanism for working with sequences of values in a memory-efficient and lazy manner. They allow you to generate values on-demand, handle infinite or large sequences, and simplify code by encapsulating complex logic. Whether through generator functions or generator expressions, harnessing the power of generators is invaluable for writing efficient, concise, and flexible code in Python. + + + +10.⚡️ Understanding Async and Await in Python: Concurrency Made Easy +********************************************************************** + +**🔗 What is Async and Await?** + +Async and await are keywords in Python that enable asynchronous programming, also known as concurrency. Asynchronous programming allows you to write code that can perform multiple tasks concurrently, without blocking the execution of other code. + +**🚀 Concurrency Made Easy** + +Traditionally, Python executes code sequentially, meaning one line at a time. However, certain tasks, such as network requests or file operations, can take a significant amount of time to complete. During this time, the program would be idle, waiting for the task to finish, which can lead to inefficiencies. + +With async and await, you can define asynchronous functions that allow other parts of your program to continue executing while waiting for a task to complete. This concurrency model enables you to make efficient use of system resources and greatly improves the performance of certain operations. + +**💡 Understanding Concurrency with Real-World Examples** + +To better understand the concept of concurrency, let's consider a couple of real-world examples: + +**1. Downloading Multiple Files Concurrently** + +Imagine you need to download multiple large files from the internet. Using synchronous programming, you would have to download one file at a time, waiting for each download to complete before starting the next one. This would result in significant waiting time. + +However, with async and await, you can write an asynchronous function for downloading a single file and call it multiple times concurrently. This allows the downloads to happen simultaneously, significantly reducing the overall time required to download all the files. + +**2. Web Scraping with Multiple Requests** + +Web scraping often involves sending multiple HTTP requests to fetch data from different pages. In synchronous programming, you would need to wait for each request to complete before making the next one, leading to slower scraping times. + +Using async and await, you can write asynchronous functions for making HTTP requests and parse the responses. By executing these functions concurrently, you can send multiple requests simultaneously, speeding up the web scraping process. + +**🔧 How to Use Async and Await** + +To utilize async and await, you need to follow a few key steps: + +1. Define an asynchronous function using the `async def` syntax. +2. Use the `await` keyword to indicate points where the function can await the completion of tasks without blocking other code execution. +3. Call the asynchronous function using an event loop, which manages the execution of multiple asynchronous tasks. + +Here's a simple example: + + +.. code:: python + + import asyncio + + async def greet(name): + print(f"Hello, {name}!") + await asyncio.sleep(1) + print(f"Goodbye, {name}!") + + async def main(): + await asyncio.gather(greet("Alice"), greet("Bob")) + + asyncio.run(main()) + + +In the above code, the `greet` function is defined as an asynchronous function. It prints a greeting and awaits for 1 second using `asyncio.sleep()` before printing a farewell. The `main` function is also defined as an asynchronous function and uses `asyncio.gather()` to concurrently execute the `greet` function with different names. + +**🌟 Benefits of Async and Await** + +Async and await provide several advantages: + +1. **Improved Performance**: By allowing tasks to execute concurrently, async and await enable better utilization of system resources, resulting in improved performance and responsiveness. + +2. **Simplified Code**: Asynchronous programming simplifies code by eliminating the need for complex threading and callback mechanisms. It offers a more straightforward way to write concurrent code. + +3. **Enhanced Scalability**: With async and await, you can handle large numbers of concurrent tasks efficiently, making it suitable for building scalable systems. + +4. **Responsive Applications**: By avoiding blocking operations and leveraging + + non-blocking I/O, async and await help create responsive applications that can handle multiple tasks simultaneously. + + + +**🚀 Unlocking the Power of Async and Await** + +Async and await have revolutionized the way Python handles concurrency, making it easier to write efficient and scalable code. By understanding and leveraging async and await, you can build responsive applications that effectively handle multiple tasks concurrently. + +Whether you're downloading files, scraping websites, or managing resources, async and await provide the tools to optimize your code and unleash the power of concurrency. + +Keep exploring the vast possibilities of async and await, and embrace the world of concurrent programming in Python! + + + + +Useful Resources +---------------- + +Here are some valuable resources to enhance your Python programming +skills and stay updated with the latest trends: + +- **Online Course**:Interested in remote sensing, geospatial technology, and its applications using python. + `IIRS `__. + +- **Python Version Update**: This is a beta preview of Python 3.12 + `Python 3.12.0b3 `__. + +- **Python-World Github Repo**: Python-World Github Repository + `Python-World `__. + +- **Forum**: Join the vibrant Python community discussions and seek + help at `Python + Forums `__. + Engage with fellow developers, share knowledge, and contribute to the + Python ecosystem. + +Stay curious, keep learning, and make the most of these resources to +enhance your Python journey! \ No newline at end of file diff --git a/_sources/newsletters/2023/WEEK_27.md.txt b/_sources/newsletters/2023/WEEK_27.md.txt new file mode 100644 index 0000000..b5cf5c4 --- /dev/null +++ b/_sources/newsletters/2023/WEEK_27.md.txt @@ -0,0 +1,686 @@ + +# Week 27 - Jully 2023 + + + + +## 1. 🔄 Mastering the 'else' Statement in Looping + +In Python, the 'else' statement in loops is often overlooked but can bring some valuable functionality to your code. It allows you to specify a block of code that will execute only if the loop completes normally, without any 'break' statements being encountered. + +Here's an example to illustrate its usage: + +```python +numbers = [1, 2, 3, 4, 5] +for num in numbers: + if num == 0: + break +else: + print("All numbers processed successfully!") +``` + +In this code snippet, the 'else' block will execute only if the loop completes without encountering a 'break' statement. It acts as a way to provide additional actions or notifications when the loop finishes its iteration. + +**Practical Applications ✨** + +The 'else' statement in loops can be handy in various scenarios. Here are a few practical use cases: + +1️⃣ **Searching for an item**: You can use a 'for' loop to search for an element in a list and execute specific code in the 'else' block if the item is not found. + +2️⃣ **Validating conditions**: If you have a loop that checks multiple conditions, you can use the 'else' block to perform additional checks or execute code when all conditions are satisfied. + +3️⃣ **Looping with 'try-except'**: When working with exceptions, you can combine a loop with a 'try-except' statement. In this case, the 'else' block will run if no exceptions are raised within the loop. + +Keep in mind that the 'else' block in loops is entirely optional and not mandatory. You can choose to use it when it provides clarity and improves the readability of your code. + + + + + +## 2. 🌐 Track the history of a request using the "requests.history" + +**Understanding Requests History 📜** + +The "requests" library is widely used for making HTTP requests in Python. One of its notable features is the ability to track the history of a request using the "requests.history" attribute. This attribute provides access to a list of response objects that contains the entire redirect history of the original request. + +**Why is Requests History Useful? 🤔** + +1️⃣ **Redirect Tracing**: By accessing the requests history, you can trace the path of your request, including any redirections that occurred. This is particularly helpful when dealing with APIs or web scraping tasks that involve following redirects. + +2️⃣ **Error Diagnosis**: If a request encounters an error, examining the history can provide insights into the intermediate steps and the responses received. This can aid in diagnosing and troubleshooting issues during the request lifecycle. + +3️⃣ **Verification**: Verifying the request history allows you to ensure that the expected redirections or intermediary responses are occurring correctly. This can be crucial for compliance with specific APIs or handling authentication flows. + +**Example Usage 🚀** + +Let's look at a simple example to demonstrate how to access the requests history: + +```python + +import requests + +response = requests.get('https://example.com') + +if response.history: + print(f"Request was redirected {len(response.history)} times.") + + for resp in response.history: + print(f"Redirected to: {resp.url}") +else: + print("Request was not redirected.") +``` + +In this code snippet, we make a GET request to "https://example.com". If the request encountered any redirects, we print the number of times it was redirected and display the URLs of the intermediate responses. + +**Start Tracing Your Requests! 🔎** + +Now that you're aware of the "requests.history" feature, start incorporating it into your projects. It enables you to track the journey of your requests, diagnose errors, and ensure the proper flow of interactions with APIs and websites. + + +## 3. 🆔 Understanding UUIDs + +A UUID, which stands for Universally Unique Identifier, is a 128-bit identifier that ensures uniqueness across all devices and platforms. It provides a reliable way to generate unique identifiers without the need for central coordination. Python offers a built-in module called "uuid" that allows you to work with UUIDs effortlessly. + +**Why are UUIDs Useful? 🤔** + +1️⃣ **Uniqueness**: UUIDs are designed to be highly unique, reducing the chances of collisions when generating identifiers. This makes them ideal for scenarios where unique identification is essential, such as database records, distributed systems, or data synchronization. + +2️⃣ **Global Uniqueness**: UUIDs generated using different machines and at different times will still maintain their uniqueness, making them suitable for distributed systems or scenarios involving multiple devices. + +3️⃣ **Persistence**: UUIDs can be serialized and stored in databases or used as identifiers in various contexts. They can be easily converted to strings, making them compatible with different data formats. + +**Generating UUIDs in Python 🚀** + +The "uuid" module in Python provides several functions to generate different types of UUIDs. Let's take a look at a simple example: + +```python + +import uuid + +# Generate a random UUID +random_uuid = uuid.uuid4() +print("Random UUID:", random_uuid) + +# Generate a UUID based on a host ID and current time +time_based_uuid = uuid.uuid1() +print("Time-based UUID:", time_based_uuid) +``` + +In this code snippet, we use the `uuid.uuid4()` function to generate a random UUID and `uuid.uuid1()` to generate a time-based UUID. You can explore other functions in the "uuid" module to generate UUIDs based on different criteria. + +If security is the primary concern in your application, using uuid4 is recommended due to its reliance on cryptographic randomness. However, if high uniqueness is the primary requirement and the potential predictability of MAC addresses is not a concern in your specific use case, uuid1 can still be a viable option. + +**Embrace the Power of Uniqueness! 🔐** + +By incorporating UUIDs into your Python projects, you can ensure global uniqueness, mitigate the risk of collisions, and simplify identification and data synchronization. The "uuid" module provides the necessary tools to generate and work with UUIDs effortlessly. + + + +## 4. ⚠️ Understanding Errno and Error Handling + +In Python, "errno" refers to the system error codes that are provided by the underlying operating system. These codes represent various types of errors encountered during program execution. Python's "errno" module provides a convenient way to access and handle these error codes. + +**Why is Error Handling Important? 🤔** + +1️⃣ **Graceful Program Flow**: Effective error handling allows your program to gracefully handle unexpected scenarios and prevent crashes or undesirable outcomes. It enables you to anticipate potential issues and take appropriate actions, ensuring a smoother user experience. + +2️⃣ **Debugging and Troubleshooting**: Error handling provides valuable insights into the cause of errors and helps with debugging and troubleshooting. By capturing and logging error messages and associated error codes, you can quickly identify the root cause and address the underlying issues. + +3️⃣ **Robustness and Resilience**: Proper error handling makes your code more robust and resilient. It allows you to handle exceptions, recover from errors, and implement fallback mechanisms, ensuring that your program continues to run smoothly even in challenging conditions. + +**Utilizing "Errno" in Python 🚀** + +Let's take a look at an example that demonstrates the usage of "errno" in Python: + +```python + +import errno + +try: + file = open("nonexistent_file.txt", "r") +except IOError as e: + if e.errno == errno.ENOENT: + print("File not found!") + elif e.errno == errno.EACCES: + print("Permission denied!") + else: + print("An error occurred:", e) +``` + +In this code snippet, we attempt to open a file that doesn't exist. If an `IOError` exception occurs, we check the associated `errno` attribute to determine the specific error code and provide a customized error message based on the code. + +Here are some commonly used `errno` attributes along with their meanings: + + - `errno.EACCES`: Permission denied. Indicates that the operation was not permitted due to insufficient access rights or privileges. + + - `errno.EEXIST`: File or directory already exists. Occurs when attempting to create a file or directory that already exists. + + - `errno.ENOENT`: No such file or directory. Indicates that the specified file or directory does not exist. + + - `errno.EIO`: Input/output error. Indicates an error occurred during an input/output operation, such as reading from or writing to a file. + + - `errno.ERANGE`: Result too large. Occurs when the result of an operation exceeds the maximum representable value. + + - `errno.EAGAIN`: Resource temporarily unavailable. Indicates that a resource required for the operation is temporarily unavailable and the operation should be retried. + + - `errno.EPIPE`: Broken pipe. Occurs when a process tries to write to a pipe that has been closed by the reader. + + - `errno.ENOTEMPTY`: Directory not empty. Indicates that a directory being operated on is not empty and cannot be removed or overwritten. + + - `errno.EINVAL`: Invalid argument. Occurs when an invalid argument is passed to a function or system call. + +These are just a few examples of `errno` attributes. Python's `errno` module provides a wide range of error codes that cover various scenarios. You can refer to the Python documentation for a comprehensive list of `errno` attributes and their meanings: https://docs.python.org/3/library/errno.html + +Understanding these error codes can help you diagnose and handle specific error conditions in your Python programs more effectively. + +**Handle Errors like a Pro! 💪** + +By mastering the art of error handling and utilizing the "errno" module, you can ensure your Python programs handle errors gracefully and respond intelligently to unexpected situations. Error handling empowers you to build robust and reliable applications. + + +## 5. 🐞 Dive into Debugging with PDB + +**⚙️ Understanding PDB and its Importance ⚙️** + +PDB, the Python Debugger, is an invaluable tool for debugging and troubleshooting Python programs. It allows you to pause the execution of your code, inspect variables, step through statements, and gain insights into the flow of your program. PDB comes in handy when you encounter unexpected behavior, errors, or when you simply want to understand the inner workings of your code. + +**Why is PDB Useful? 🤔** + +1️⃣ **Precise Bug Identification**: PDB enables you to set breakpoints in your code and examine variables at specific points. This helps identify the exact location of bugs and understand the values that lead to unexpected outcomes. + +2️⃣ **Program Flow Exploration**: By stepping through your code, line by line, you gain a deeper understanding of how your program executes. PDB allows you to see the sequence of statements and the values of variables at each step, making it easier to spot errors or inefficiencies. + +3️⃣ **Interactive Troubleshooting**: PDB provides an interactive environment within your program, allowing you to experiment with statements, modify variables, and test hypotheses on-the-fly. This interactive approach makes the debugging process more dynamic and efficient. + +**Unleash the Power of PDB 🚀** + +Let's take a glimpse at how to use PDB in your Python projects: + +1. Import the `pdb` module: + +```python + +import pdb +``` + +2. Set a breakpoint in your code: + +```python + +pdb.set_trace() +``` + +3. Run your program and start debugging! + +Once the program hits the breakpoint, you can use PDB's commands to navigate, inspect variables, execute statements, and step through your code. + +**Level up Your Debugging Skills! 💪** + +By embracing PDB, you unlock a whole new level of debugging proficiency. Understanding PDB's capabilities and integrating it into your development workflow can save you time and frustration when troubleshooting issues. + + + +## 6. 🔥 Unleash the power of Python Function Decorators + +In Python, functions are first-class objects, which means they can be passed as arguments to other functions. +This property is known as "higher-order functions" or "first-class functions." + +For example, I want to add some logging statements, before and after function execution, and log how much time the function took to execute. + +```python +import time + + +def my_func(): + # dummy function + print("Executing my function") + for i in range(3): + print(1000000*100000*1000000*23456423) + time.sleep(0.5) + +def log_time(func): + print("Before executing function") + start = time.time() + func() + end = time.time() + print("Finished function execution") + print(f"Function exitted in {(end-start):.2f}s") + + +log_time(my_func) + +``` + +In this above example, you see that `log_time` function executes `my_func`, +but also adds extra functionality. +For external users or other modules of my library, +if I dont want to expose `log_time` and `my_func`, +I can simply, create a new function, calling which would have the same effect +as doing `log_time(my_func)` + +```python +def log_time_decorator(func): + print("Printing from decorator ...") + def log_time_wrapper(): + print("Printing from wrapper function ...") + print("Before executing function") + start = time.time() + func() + end = time.time() + print("Finished function execution") + print(f"Function exitted in {(end-start):.2f}s") + return log_time_wrapper +``` + +- We can see that the `wrapper` function wraps the original function, and adds additional features to it. +- The `decorator` function returns the `wrapper` function. + +Now, I can enhance and convert `my_func` to a more powerful function. + +```python +print("Creating decorated function ...") +my_func_t = log_time_decorator(my_func) +print("\n\nCalling decorated function ...") +my_func_t() +``` + +We may choose not to create a seperate `my_func_t` object, and just modify the existing `my_func` function object, by decorating it. + +```python +my_func = log_time_decorator(my_func) +``` + +The above line has same effect, as using the `@decorator` shortcut in python while defining a function. + +```python +@log_time_decorator +def my_func(): + # dummy function + ... + # business logic + +``` + +This decorator, can be re-used very easily to enhance any other function, by logging its execution time. + +In the newsletter of upcoming weeks, we will go through decorators in much more depth. We will discuss decorators that can take arguments and also class decorators. +We will also discuss about various open source libraries where this decorator patter is commonly used. +Stay tuned. + + +## 7. ✅ Using `assert` in Python + +The usage of `assert` is very simple. You simply write `assert` and then the condition, you want to check. + +If the condition you have put, turns out to be true, then the program flow continues as intended. Otherwise the program exits at that point, throwing an assertion error. + +You may choose to handle that error, and do something specific, depending on your business logic. + +```python +x = 10 +assert x == 10 +assert x > 0 +assert x < 0 +``` + +If you run this piece of code, the first two `assert` statements would pass, but the third would fail. + +```shell +Traceback (most recent call last): + File "/home/aahnik/Projects/Writing/newsletter/test.py", line 4, in + assert x < 0 + ^^^^^ +AssertionError +``` + +We can also show a custom error message, when this assertion fails. + +```python +assert x < 0, "x is not negative" +``` + +This would output + +```shell +AssertionError: x is not negative +``` + +### Industry use cases of `assert` + +The `assert` statement in Python is a powerful tool that allows programmers to perform runtime assertions in their code. It helps verify assumptions and catch potential bugs during development and testing. While its primary purpose is to validate conditions, `assert` can be used in a variety of real-life scenarios, ranging from simple to advanced applications. In this article, we explore some practical use cases of `assert` in Python. + +#### 1. Input Validation + +One common use of `assert` is to validate input parameters or function arguments. By using `assert` statements, you can ensure that the provided inputs meet the expected criteria or constraints. Let's consider an example: + +```python +def calculate_discount(price, discount): + assert price > 0, "Price should be greater than 0" + assert 0 <= discount <= 100, "Discount should be between 0 and 100" + + discounted_price = price - (price * discount / 100) + return discounted_price +``` + +In this scenario, the `assert` statements ensure that the `price` is positive and the `discount` is within the valid range. If any of these conditions are not met, an `AssertionError` is raised with a custom error message, providing valuable feedback to the developer. + +#### 2. Debugging and Testing + +When debugging code or writing test cases, `assert` statements can be incredibly useful. They help to check intermediate values, test assumptions, and ensure that the code is behaving as expected. Let's consider a simple debugging example: + +```python +def divide_numbers(a, b): + assert b != 0, "Division by zero is not allowed" + + result = a / b + return result + +# Debugging +a = 10 +b = 0 +assert b != 0, "b should not be zero" # Assertion for debugging +result = divide_numbers(a, b) +print(result) +``` + +In this case, the `assert` statement is used as a debugging tool to validate the assumption that `b` should not be zero before executing the `divide_numbers` function. If the condition fails, the program halts, and an error message is displayed, aiding in identifying the issue. + +#### 3. Program Invariants + +Program invariants are conditions that are expected to be true at specific points during the program's execution. Using `assert` statements to check these invariants can help detect logical errors. For instance, consider a banking application where account balances should always be positive: + +```python +class BankAccount: + def __init__(self, balance): + assert balance >= 0, "Balance should be non-negative" + self.balance = balance + + def withdraw(self, amount): + assert amount > 0, "Withdraw amount should be positive" + assert amount <= self.balance, "Insufficient funds" + + self.balance -= amount + return self.balance +``` + +Here, the `assert` statements ensure that the `balance` is non-negative and that the withdrawal amount is both positive and within the available funds. Violating these conditions would indicate a logical error, allowing developers to identify and fix issues early. + +#### 4. Unit Testing + +In unit testing, `assert` statements are frequently used to verify the correctness of code by comparing expected and actual values. By comparing these values, `assert` statements help validate that functions and classes produce the expected output. Let's look at a simple example: + +```python +def add_numbers(a, b): + return a + b + +# Unit Test +result = add_numbers(2, 3) +assert result == 5, f"Expected 5, but got {result}" +``` + +In this case, the `assert` statement ensures that the `add_numbers` function correctly adds two numbers by comparing the expected result (5) with the actual result. If they don't match, an `AssertionError` is raised, indicating a potential bug or unexpected behavior. + +### Conclusion + +The `assert` statement in Python is a versatile tool that serves various purposes in real-life scenarios. From input validation to debugging, testing, enforcing invariants, and unit testing, `assert` statements provide a means to verify conditions and catch potential issues early. By leveraging `assert`, programmers can improve code quality, ensure correct behavior, and reduce the likelihood of bugs in their applications. + +Remember that `assert` statements are primarily intended for debugging and testing purposes and can be disabled in a production environment. Therefore, it's crucial to use them judiciously and consider appropriate error handling mechanisms for production code. + + +## 8. 🌀 Exploiting Parallelism with Python: Multiprocessing vs Multithreading vs Co-routines + +When executing code synchronously or sequentially, each task is processed one after the other. This approach can be limiting, especially when dealing with computationally intensive or time-consuming tasks. To overcome this limitation, parallelism comes into play, allowing multiple tasks to be executed simultaneously. In Python, there are three main techniques for exploiting parallelism: Multiprocessing, Multithreading, and Co-routines. In this article, we'll explore these techniques, understand their differences, and determine their best use cases. + +### Synchronous/Sequential Execution + +Before diving into parallelism, let's briefly discuss synchronous or sequential execution. In this mode, code is executed line by line, and each task must complete before moving on to the next. This approach works well for simple programs, but it becomes inefficient when dealing with time-consuming operations or tasks that can benefit from concurrent processing. + +### IO-Bound Tasks vs CPU-Bound Tasks + +Before we explore parallelism techniques, it's important to understand the distinction between IO-bound and CPU-bound tasks. + +- **IO-Bound Tasks**: These tasks spend most of their time waiting for input/output operations to complete. Examples include reading from/writing to files, making network requests, or interacting with a database. IO-bound tasks can benefit from parallelism as they can be scheduled concurrently, utilizing idle time during IO operations. + +- **CPU-Bound Tasks**: These tasks heavily rely on the CPU for processing and computation. Examples include complex mathematical calculations, image/video processing, or simulations. CPU-bound tasks can benefit from parallelism by utilizing multiple CPU cores to speed up execution. + +### Multiprocessing for CPU-Bound Tasks + +Multiprocessing is a technique that involves running multiple processes simultaneously to achieve parallel execution. Each process has its own memory space, allowing them to execute independently. This technique is well-suited for CPU-bound tasks, as it takes advantage of multiple CPU cores. + +```python +import multiprocessing + +def process_data(data): + # Perform CPU-intensive computations + pass + +if __name__ == "__main__": + data = [...] # Data to be processed + + # Create a pool of processes + with multiprocessing.Pool() as pool: + # Distribute the data across processes + results = pool.map(process_data, data) +``` + +By utilizing the `multiprocessing` module, we can spin up multiple processes to distribute CPU-bound tasks across available CPU cores. This allows us to scale the execution speed and improve overall performance. + +### Multithreading for IO-Bound Tasks + +Multithreading is a technique where multiple threads of execution run concurrently within a single process. Each thread shares the same memory space, making data sharing easier. While multithreading can provide parallelism, it's not always suitable for CPU-bound tasks due to Python's Global Interpreter Lock (GIL), which prevents true parallelism for CPU-bound tasks. However, it can be used effectively for IO-bound tasks. + +```python +import threading + +def process_data(data): + # Perform IO-bound operations + pass + +if __name__ == "__main__": + data = [...] # Data to be processed + + # Create multiple threads + threads = [] + for d in data: + thread = threading.Thread(target=process_data, args=(d,)) + threads.append(thread) + thread.start() + + # Wait for all threads to complete + for thread in threads: + thread.join() +``` + +In this example, multiple threads are created to process IO-bound tasks concurrently. Each thread performs its operations independently, taking advantage of idle time during IO operations. + +### Co-routines for Efficient Asynchronous Tasks + +Co-routines are lightweight, independent units of execution that can be scheduled and paused during their execution. They allow for efficient concurrency without the overhead associated with operating system threads or processes. Co-routines enable us to exploit parallelism by efficiently handling both IO-bound and CPU-bound tasks. + +In Python, co-routines are implemented using the `asyncio` module, which provides an event loop for managing the execution of co-routines. + +```python +import asyncio + +async def process_data(data): + # Perform async operations + pass + +if __name__ == "__main__": + data = [...] # Data to be processed + + # Create an event loop + loop = asyncio.get_event_loop() + + # Schedule the execution of co-routines + tasks = [process_data(d) for d in data] + loop.run_until_complete(asyncio.gather(*tasks)) + + # Close the event loop + loop.close() +``` + +Using `asyncio`, we can define co-routines with the `async` keyword, enabling them to be paused and resumed during execution. Co-routines provide a more efficient approach to parallelism, allowing for both IO-bound and CPU-bound tasks to be handled effectively. + +### Simplifying Co-routine Development with `async`/`await` + +Python 3.5 introduced the `async` and `await` keywords, simplifying the development of co-routines. `async` is used to define a co-routine, and `await` is used to suspend execution until the awaited operation completes. + +`async`/`await` syntax simplifies the handling of asynchronous tasks, making the code more readable and reducing complexity. It allows for straightforward development of highly concurrent and efficient programs. + +### Comparison and Use Cases + +To summarize the different techniques: + +- Multiprocessing is best suited for CPU-bound tasks that can benefit from parallel execution across multiple CPU cores. +- Multithreading is useful for IO-bound tasks, leveraging concurrent execution to maximize performance during IO operations. +- Co-routines, with the help of `asyncio`, are effective for both IO-bound and CPU-bound tasks, providing efficient concurrency and allowing developers to write highly concurrent code. + +When choosing a parallelism technique, consider the nature of the task (IO-bound or CPU-bound) and the specific requirements of your application. Each technique has its strengths and use cases, allowing you to exploit parallelism in Python effectively. + +By leveraging parallelism, Python developers can significantly improve the performance and efficiency of their applications, enabling faster execution and better resource utilization. + +Remember, each technique has its own considerations and trade-offs, so choose the approach that best suits your specific requirements and use cases. Happy parallel programming in Python! + + +### Video Resources + +- [Making multiple HTTP requests using Python (synchronous, multiprocessing, multithreading, asyncio)](https://youtu.be/R4Oz8JUuM4s) by [Indian Pythonista](https://www.youtube.com/@IndianPythonista) +- [Python Multiprocessing Tutorial: Run Code in Parallel Using the Multiprocessing Module](https://youtu.be/fKl2JW_qrso) by [Corey Schafer](https://www.youtube.com/@coreyms) +- [Python Threading Tutorial: Run Code Concurrently Using the Threading Module](https://youtu.be/IEEhzQoKtQU) by [Corey Schafer](https://www.youtube.com/@coreyms) + + +## 9. 🤸 Understanding Python Typing and Type Hints with MyPy and VS Code Support + +Python is a dynamically typed language, meaning variables do not require explicit type declarations. However, with the introduction of type hints in Python 3.5 and the support of tools like MyPy and VS Code, developers can now add static type information to their code, enhancing readability, maintainability, and catching potential type-related bugs. + +### Python Typing and Type Hints + +Type hints in Python allow developers to provide information about the expected types of variables, function arguments, and return values. While type hints are optional and do not affect the runtime behavior of the code, they serve as documentation and enable static type checking tools to analyze the code for potential type-related errors. + +Consider the following example: + +```python +def add_numbers(a: int, b: int) -> int: + return a + b +``` + +In this code snippet, type hints are used to indicate that both `a` and `b` should be of type `int`, and the function should return an `int`. These type hints provide clarity about the expected types, making the code more self-explanatory and enabling static analysis tools to detect type inconsistencies. + +### MyPy - Static Type Checker for Python + +MyPy is a popular static type checker for Python that can analyze code with type hints and detect potential type errors without running the program. It helps catch common mistakes and allows developers to find type-related bugs early in the development process. + +Using MyPy is straightforward. After installing it, you can run MyPy on your codebase: + +```bash +mypy your_code.py +``` + +If there are any type errors or inconsistencies in your code, MyPy will provide detailed error messages and suggestions for fixing them. This helps ensure that your code aligns with the specified types, improving code quality and reliability. + +### VS Code Support for Type Hints + +Visual Studio Code (VS Code) is a popular code editor with excellent support for Python and type hints. With the help of various extensions, such as the Python extension and the Pyright extension, developers can take advantage of advanced type checking features directly within the editor. + +Once you have the necessary extensions installed, VS Code will automatically analyze your code for type errors and provide real-time feedback. It highlights potential type mismatches, missing type hints, and other issues, helping you write more robust and maintainable code. + +Additionally, VS Code offers features like autocompletion and hover information based on type hints, enabling you to write code faster and with fewer errors. It improves the development experience and encourages best practices in using type hints. + +### Benefits of Type Hints and Tooling Support + +The use of type hints and tools like MyPy and VS Code support offer several advantages: + +1. **Improved Readability**: Type hints make code more self-documenting by providing explicit information about the expected types, leading to enhanced code comprehension and maintainability. + +2. **Early Bug Detection**: Static type checkers like MyPy can catch potential type errors before the code is executed, reducing the chances of runtime errors and improving overall code quality. + +3. **Enhanced Tooling**: IDEs and code editors, such as VS Code, leverage type hints to provide better autocompletion, code navigation, and error checking, leading to a more efficient and error-free development experience. + +4. **Collaboration and Documentation**: Type hints serve as documentation for developers, making it easier to understand and collaborate on codebases. They can also generate API documentation automatically, providing clear guidance for users of your code. + +### Embrace the Power of Type Hints + +Type hints and the associated tooling support, such as MyPy and VS Code, offer powerful ways to enhance your Python code. By providing static type information, you can improve code quality, catch bugs early, and enjoy a more productive development experience. Embrace type hints and the available tooling to write more robust and maintainable Python code. + + + +### Resources + +- [Python Types Intro](https://fastapi.tiangolo.com/python-types/) from [FastAPI docs](https://fastapi.tiangolo.com/) + + +## 10. 🗄️ Python Persistence: Pickling and SQLite + +Python provides several mechanisms for data persistence, allowing you to store and retrieve data across different program executions. Two commonly used methods for persistence are pickling and SQLite. Let's explore these techniques briefly. + +### Pickling + +Pickling is a Python-specific way of serializing and deserializing Python objects. It allows you to convert complex objects into a byte stream that can be stored in a file or transferred over a network. The `pickle` module in Python provides the necessary functions for pickling and unpickling objects. + +To persist data using pickling, you can use the following code: + +```python +import pickle + +# Data to be pickled +data = [1, 2, 3, 4, 5] + +# Pickle the data to a file +with open('data.pickle', 'wb') as file: + pickle.dump(data, file) + +# Unpickle the data from the file +with open('data.pickle', 'rb') as file: + unpickled_data = pickle.load(file) +``` + +Here, the `pickle.dump()` function is used to pickle the `data` list and store it in a file named `data.pickle`. The `pickle.load()` function is then used to retrieve the pickled data from the file and store it in the `unpickled_data` variable. + +Pickling is a convenient way to store and retrieve Python objects, but it's important to note that it can have security implications if used with untrusted data. Therefore, it's recommended to use pickling only in trusted environments. + +### SQLite + +SQLite is a lightweight and embedded relational database engine that is widely used in Python applications. It provides a simple and efficient way to store and query structured data without requiring a separate database server. Python includes the `sqlite3` module, which allows seamless interaction with SQLite databases. + +To use SQLite for data persistence, you can use the following code: + +```python +import sqlite3 + +# Connect to the database +conn = sqlite3.connect('database.db') + +# Create a cursor object +cursor = conn.cursor() + +# Create a table +cursor.execute('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)') + +# Insert data into the table +cursor.execute('INSERT INTO users (name, age) VALUES (?, ?)', ('Alice', 25)) + +# Retrieve data from the table +cursor.execute('SELECT * FROM users') +data = cursor.fetchall() + +# Commit the changes +conn.commit() + +# Close the connection +conn.close() +``` + +In this code, we first connect to the SQLite database file named `database.db`. We then create a cursor object to execute SQL statements. The code demonstrates creating a table, inserting data, and retrieving data from the table. Finally, we commit the changes and close the connection to the database. + +SQLite provides a SQL-based interface, making it easy to work with structured data. It supports a wide range of SQL operations, including creating tables, inserting data, querying, and more. It's a reliable and efficient choice for many small to medium-sized applications. + +### Choosing the Right Persistence Method + +When deciding between pickling and SQLite for data persistence, consider the nature of your data and the requirements of your application. Pickling is suitable for storing and retrieving Python objects with their full state intact, while SQLite is better suited for structured data and complex querying needs. + +If you need to store and retrieve complex Python objects without the need for advanced querying capabilities, pickling can be a straightforward and efficient solution. On the other hand, if you require structured data storage and advanced querying capabilities, SQLite offers a more robust solution. + +Pickling and SQLite are just two of the many options available for data persistence in Python. Depending on your specific needs, you may explore other alternatives, such as using file systems, NoSQL databases, or ORM frameworks. + +Remember to evaluate the trade-offs of each persistence method, considering factors like performance, scalability, security, and the complexity of your data model. By choosing the right persistence technique, you can ensure that your data is stored effectively and can be retrieved efficiently in your Python applications. diff --git a/_sources/newsletters/2023/WEEK_28.md.txt b/_sources/newsletters/2023/WEEK_28.md.txt new file mode 100644 index 0000000..cb15517 --- /dev/null +++ b/_sources/newsletters/2023/WEEK_28.md.txt @@ -0,0 +1,371 @@ + +# Week 28 - July 2023 + + + +## 1. ⚖️ Unleashing the Potential of Counter + +The `Counter` class is a built-in container in Python's `collections` module that provides an easy way to count the occurrences of elements in an iterable. Whether it's counting elements in a list, characters in a string, or words in a text document, the `Counter` has got you covered. + +Here's an example to get you started: + +```python +from collections import Counter + +# Count the occurrences of elements in a list +my_list = [1, 2, 3, 1, 2, 1, 3, 4, 5, 2, 1] +counter = Counter(my_list) +print(counter) + +# Output: Counter({1: 4, 2: 3, 3: 2, 4: 1, 5: 1}) +``` + +🔢 Count with Confidence +With the `Counter` class, you can effortlessly count and keep track of elements. It offers various methods such as `most_common()`, which returns the most frequently occurring elements, and arithmetic operations like addition and subtraction between counters. + +Here's an example showcasing the `most_common()` method: + +```python +from collections import Counter + +# Count the occurrences of characters in a string +my_string = "pythonworld" +counter = Counter(my_string) +print(counter.most_common(2)) + +# Output: [('o', 2), ('t', 1)] +``` + +**📊 Real-Life Applications** + +The applications of `Counter` are diverse. You can use it for tasks like analyzing text data, tracking user actions, monitoring website traffic, and much more. The simplicity and efficiency of `Counter` make it a must-have tool in your Python toolkit. + +💡 Tips and Tricks +To help you make the most of `Counter`, we've compiled some useful tips and tricks. Here are a few: + +1. Initialize a counter using different approaches: +```python +counter1 = Counter() # Empty counter +counter2 = Counter([1, 2, 2, 3, 3, 3]) # Counter from a list +counter3 = Counter({'a': 2, 'b': 3}) # Counter from a dictionary +``` + +2. Update a counter with new elements: +```python +counter = Counter() +counter.update([1, 2, 1, 3, 2, 1]) +print(counter) # Output: Counter({1: 3, 2: 2, 3: 1}) +``` + +3. Perform arithmetic operations between counters: +```python +counter1 = Counter([1, 2, 3]) +counter2 = Counter([2, 3, 4]) +counter = counter1 + counter2 +print(counter) # Output: Counter({2: 2, 3: 2, 1: 1, 4: 1}) +``` + + +- Official Python Documentation: [Counter](https://docs.python.org/3/library/collections.html#collections.Counter) + + +## 2. ⚙️ Exploring Python's Temporary File Handling + +The `tempfile` module provides a convenient way to work with temporary files and directories. It handles the creation, management, and cleanup of temporary files, making it an essential tool for various scenarios. + +**📄 Creating Temporary Files** + +Creating temporary files is a breeze with `tempfile`. Here's an example that generates a unique temporary file name and writes some data into it: + +```python +import tempfile + +with tempfile.NamedTemporaryFile() as temp_file: + temp_file.write(b"Hello, Python-world!") + temp_file.seek(0) + print(temp_file.read().decode()) + +# Output: Hello, Python-world! +``` + +**📁 Creating Temporary Directories** + +Sometimes, you need temporary directories to store intermediate files or perform operations. The `tempfile` module makes it easy. Here's an example: + +```python +import tempfile +import os + +with tempfile.TemporaryDirectory() as temp_dir: + temp_file_path = os.path.join(temp_dir, 'example.txt') + with open(temp_file_path, 'w') as temp_file: + temp_file.write('Hello, Python-world!') + + # Perform operations in the temporary directory + +# The temporary directory and its contents are automatically cleaned up +``` + +⚠️ Safe and Secure Handling +`tempfile` ensures that temporary files and directories are created securely. It uses appropriate permissions, and by default, the created files are deleted when closed or when the program terminates. + +**🌟 Examples** + +To give you a taste of the possibilities, here are a couple of unique examples showcasing the versatility of `tempfile`: + +1️⃣ Generating Temporary CSV Files: +```python +import tempfile +import csv + +with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp_file: + csv_writer = csv.writer(temp_file) + csv_writer.writerow(['Name', 'Age']) + csv_writer.writerow(['John Doe', '30']) + csv_writer.writerow(['Jane Smith', '25']) + + print(f"Temporary CSV file created: {temp_file.name}") +``` + +2️⃣ Creating Secure Temporary Files: +```python +import tempfile + +with tempfile.NamedTemporaryFile(delete=False, mode='w', prefix='secret_', dir='/secure_dir') as temp_file: + temp_file.write('This is a secure temporary file.') + print(f"Secure temporary file created: {temp_file.name}") +``` + +💡 Explore Further +To dive deeper into the `tempfile` module, here are some recommended resources: + +- Official Python Documentation: [`tempfile`](https://docs.python.org/3/library/tempfile.html) + + +## 3. 🔍 Discovering the Magic of String Prefix Matching with startswith + +The `startswith` method is a built-in string method in Python that allows you to check whether a string starts with a specific substring. It provides a straightforward and efficient way to perform prefix-based searches and comparisons. + +**✅ Basic Usage** + +To use the `startswith` method, simply call it on a string object and provide the desired prefix as the argument. The method returns a boolean value indicating whether the string starts with the specified prefix. + +Here's a Python-World example to get you started: + +```python +text = "Hello, Python-World! 🐍" +if text.startswith("Hello"): + print("The string starts with 'Hello'") +else: + print("The string does not start with 'Hello'") + +# Output: The string starts with 'Hello' +``` + +**🎯 Advanced Usage** + +The `startswith` method is versatile and offers various possibilities for text manipulation and filtering. You can combine it with other string methods, such as `strip` or `split`, to extract specific substrings or filter out unwanted content. + +Here's a Python-World example showcasing advanced usage: + +```python +text = "Welcome to the Python-World Newsletter 🌍" + +# Extracting words that start with a specific letter +words = text.split() +filtered_words = [word for word in words if word.startswith("P")] +print(filtered_words) + +# Output: ['Python-World'] + +``` + +**💡 Tips and Tricks** + +To help you make the most of the `startswith` method, here are a few tips and tricks: + +1️⃣ Case-insensitive Matching: +You can perform case-insensitive matching by converting the strings to lowercase or uppercase before using `startswith`. + +2️⃣ Multiple Prefixes: +The `startswith` method accepts a tuple of prefixes. It returns `True` if the string starts with any of the specified prefixes. + +3️⃣ Unicode Support: +The `startswith` method handles Unicode characters properly. You can search for prefixes containing emojis or special characters without any issues. + +**🌟 Examples** + +To showcase the versatility of `startswith`, here are a couple of Python-World examples: + +1️⃣ Checking File Extensions: +```python +filename = "document.pdf" +if filename.startswith(("doc", "txt", "pdf")): + print("The file has a supported extension") +else: + print("The file extension is not supported") +``` + +2️⃣ Filtering Email Addresses: +```python +emails = ["info@example.com", "john@example.com", "jane@gmail.com"] +filtered_emails = [email for email in emails if email.startswith("john")] +print(filtered_emails) +``` + +**💡 Explore Further** +To dive deeper into the `startswith` method and string manipulation in Python, here are some recommended resources: + +- Official Python Documentation: [str.startswith](https://docs.python.org/3/library/stdtypes.html#str.startswith) + + +## 4. 🌐 Simplify file path matching and retrieval using `glob` + +The `glob` module provides a convenient way to search for files using pattern matching rules similar to those used in the Unix shell. It allows you to retrieve a list of file paths that match a specified pattern, making it a go-to tool for various file-related operations. + +**✅ Basic Usage** +To use the `glob` module, simply import it and utilize the `glob` function. Provide a pattern as an argument to the function, and it will return a list of file paths that match the pattern. + +Here's an example to get you started: + +```python +import glob + +# Get a list of all Python files in the current directory +python_files = glob.glob("*.py") +print(python_files) +``` + +**🌟 Unleashing the Potential** + +To showcase the versatility of `glob`, let's explore some interesting scenarios where it can be immensely useful: + +**1️⃣ Retrieving All Files in a Directory:** + +```python +import glob + +# Get a list of all files (including subdirectories) in a directory +all_files = glob.glob("**/*", recursive=True) +print(all_files) +``` + +**2️⃣ Finding Specific File Types:** + +```python +import glob + +# Get a list of all image files in a directory +image_files = glob.glob("path/to/images/*.jpg") +print(image_files) +``` + +**💡 Tips and Tricks** + +To help you make the most of the `glob` module, here are a few tips and tricks: + +1️⃣ Recursive Matching: +The `recursive=True` parameter allows you to perform recursive matching, enabling the retrieval of files from subdirectories. + +2️⃣ Wildcard Usage: +Utilize wildcards (`*` and `?`) in the pattern to match multiple characters or a single character, respectively. + +3️⃣ Character Ranges: +Square brackets (`[]`) can be used to specify character ranges for pattern matching. + +**🌐 Real-Life Applications** + +The `glob` module finds applications in various scenarios, such as data processing, file manipulation, and directory traversing. It simplifies tasks that involve working with multiple files and allows for efficient file discovery. + +**💡 Explore Further** + +To dive deeper into the `glob` module, here are some recommended resources: + +- Official Python Documentation: [`glob`](https://docs.python.org/3/library/glob.html) + +## 5. 🔤 Unveiling the Secrets of the string Module + +The `string` module provides a rich set of constants and functions for working with strings. It offers convenient ways to access and manipulate various character sets, including numbers, ASCII characters, and special symbols. + +**🔢 Working with Numbers** + +The `string` module provides a constant called `digits`, which contains all the decimal digits (0-9). You can use this constant to easily check if a string contains numeric characters or extract numerical values from a string. + +Here's a simplified example showcasing the usage of `digits`: + +```python +import string + +print("Digits:", string.digits) +``` + +Output: +``` +Digits: 0123456789 +``` + +**💻 ASCII Characters at Your Fingertips** + +The `string` module also provides constants for different sets of ASCII characters. For example, `ascii_letters` contains all the ASCII letters (both lowercase and uppercase), while `punctuation` contains all the ASCII punctuation symbols. + +Let's see a simplified example that utilizes these constants: + +```python +import string + +print("ASCII Letters:", string.ascii_letters) +print("Punctuation Symbols:", string.punctuation) +``` + +Output: +``` +ASCII Letters: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ +Punctuation Symbols: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ +``` + +**💫 Special Symbols Made Easy** + +The `string` module also offers a constant called `printable`, which contains all the printable ASCII characters. This includes letters, digits, punctuation, and whitespace. + +Here's a simplified example that demonstrates the usage of `printable`: + +```python +import string + +print("Printable ASCII Characters:", string.printable) +``` + +Output: +``` +Printable ASCII Characters: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ +``` + +**🌟 Examples** + +To further showcase the versatility of the `string` module, here are a couple of unique examples: + +1️⃣ Generating Random Passwords: +```python +import string +import random + +password_length = 8 +password_characters = string.ascii_letters + string.digits + string.punctuation +password = ''.join(random.choice(password_characters) for _ in range(password_length)) +print("Random Password:", password) +``` + +2️⃣ Removing Punctuation from a String: +```python +import string + +text = "Hello, Python-World!" +clean_text = ''.join(char for char in text if char not in string.punctuation) +print("Cleaned Text:", clean_text) +``` + +**🔍 Explore Further** + +To dive deeper into the `string` module and its functionalities, here are some recommended resources: + +- Official Python Documentation: [`string`](https://docs.python.org/3/library/string.html) \ No newline at end of file diff --git a/_sources/newsletters/2023/WEEK_29.md.txt b/_sources/newsletters/2023/WEEK_29.md.txt new file mode 100644 index 0000000..7f0f72d --- /dev/null +++ b/_sources/newsletters/2023/WEEK_29.md.txt @@ -0,0 +1,221 @@ + +# Week 29 - July 2023 + + + +## 1. 💼 Simplifying Python Classes with Dataclasses. + +Python dataclasses are a fantastic addition to the Python standard library that simplifies the creation of classes for storing data. 🏰💾 With just a few lines of code, you can define classes that come with built-in methods for easy attribute initialization, comparison, and more. 🎁💻 + +**🌟 Key Features of Python Dataclasses:** + +✅ Automatic attribute initialization - Say goodbye to writing tedious `__init__` methods! + +✅ Concise and clean class definitions using decorators. + +✅ Built-in methods for equality comparison, representation, and more. + +✅ Customizable field defaults and ordering. + +✅ Full integration with Python's standard library. + + +**📝 Simple Examples to Get Started:** + +```python +from dataclasses import dataclass + +@dataclass +class Point: + x: int + y: int + +# Creating instances +p1 = Point(1, 2) +p2 = Point(3, 4) + +# Accessing attributes +print(f"Point 1: x={p1.x}, y={p1.y}") +print(f"Point 2: x={p2.x}, y={p2.y}") +``` + +**🚀 Use Cases for Python Dataclasses:** +1. **Configuration Objects**: Use dataclasses to create clean and straightforward configuration objects for your applications. 🛠️🔧 +2. **Data Containers**: Dataclasses are perfect for holding and manipulating structured data, such as CSV records or JSON data. 📂🗃️ +3. **API Responses**: Parse API responses into dataclass instances, making the data easy to work with and comprehend. 📡💼 +4. **Immutable Objects**: By default, dataclasses are immutable, making them suitable for representing read-only data. 🔒🔏 +5. **Testing**: Dataclasses can be incredibly useful when writing test cases with complex input data. 🧪🧾 + +**💡 Pro Tip:** +Don't forget to explore advanced dataclass features like default values, ordering, and post-init processing. Dataclasses are highly customizable to fit your specific needs! 🔍🔧 + + + +## 2. 🔦 Exploring Python's Hidden Secrets: The inspect Module + +The `inspect` module is your backstage pass to Python's internals. 🎭🎫 It provides functions for introspecting live objects like modules, classes, functions, and more. With `inspect`, you can retrieve information about code objects, inspect callables' signatures, and even perform source code analysis. 🕵️‍♀️📝 + +**🔍 Key Features of `inspect`:** + +✅ Retrieving information about objects and modules at runtime. + +✅ Accessing source code and analyzing the structure of Python functions and classes. + +✅ Inspecting and manipulating the call signature of functions. + +✅ Extracting and examining documentation strings. + +✅ Finding the source file and line number of code objects. + +**📝 Examples to Get Started:** +```python +import inspect + +# Get the source code of a function +def greet(name,age): + return f"Hello, {name}!" + +source_code = inspect.getsource(greet) +print('Source code:\n',source_code) + +# Inspect a function's signature +signature = inspect.signature(greet) +print("Signature:",signature) + + + +''' +Output +Source code: + def greet(name,age): + return f"Hello, {name}!" + +Signature: (name, age) +''' + +``` + +**🚀 Use Cases for `inspect`:** +1. **Debugging and Logging**: Use `inspect` to gather information about the calling frames for powerful debugging and logging capabilities. 🐞📝 +2. **Documentation Generation**: Automate the generation of documentation by extracting docstrings and function signatures with `inspect`. 📚📜 +3. **Dynamic Function Invocation**: Dynamically call functions with the correct arguments using `inspect` to inspect their signatures. 📞🔧 +4. **Code Analysis**: Perform static code analysis and gather insights about your codebase using `inspect` functions. 📊📋 +5. **Custom Decorators**: Create custom decorators that can automatically introspect and modify the behavior of decorated functions. 🎭🔮 + +**💡 Pro Tip:** +Experiment with `inspect` interactively in a Python REPL to get a better feel for its capabilities. 🔄🔬 + + + + +## 3. 🔧 The Python Package Manager: Unleashing the Power of pip + + +`pip` is your go-to tool for installing, managing, and distributing Python packages. 🛠️📚 With a vast repository of open-source libraries available on the Python Package Index (PyPI), `pip` makes it a breeze to enhance your Python projects with external functionality. 🌟📦 + +**🌟 Key Features of `pip`:** + +✅ Installing packages with a simple command - `pip install package-name`. + +✅ Managing package versions and dependencies effortlessly. + +✅ Listing installed packages - `pip list`. + +✅ Upgrading packages to the latest versions - `pip install --upgrade package-name`. + +✅ Creating virtual environments for isolated package installations. + +**📝 Examples to Get Started:** + +```python +# Install a package +# Example: Installing the popular requests library +# pip install requests + +# Listing installed packages +# pip list + +# Upgrading a package +# Example: Upgrading requests to the latest version +# pip install --upgrade requests +``` + +**🚀 Use Cases for `pip`:** + +1. **Library Installation**: Use `pip` to easily install third-party libraries like NumPy, pandas, Django, and more, saving you time and effort. 📚💡 +2. **Package Management**: Keep your project's dependencies organized and up to date with `pip`, ensuring smooth collaboration. 📦🔄 +3. **Virtual Environments**: Create virtual environments to isolate package installations and avoid version conflicts. 🌐🧪 +4. **Continuous Integration**: Utilize `pip` to install project dependencies in CI/CD pipelines for automated testing and deployment. 🚀🔧 +5. **Package Distribution**: Share your Python projects with others by distributing them as packages on PyPI using `pip`. 🚀🌟 + +**💡 Pro Tip:** +Explore `pip`'s extensive options like installing packages from version control repositories or installing specific package versions to suit your project's requirements. 🔄🎛️ + + +## 4. 🎁 Unpacking the Magic of Tuples + +Tuples are one of Python's versatile data structures, and unpacking allows you to extract individual elements from a tuple effortlessly. 🎩🌟 It's like unwrapping a present and discovering the treasures within! 🎁💎 + +**🧳 Packing Tuples for Efficiency:** +Packing, on the other hand, involves creating tuples by grouping elements together. 🎒🌐 It's like packing your essentials for a journey, ensuring everything stays organized and accessible. 🧳🗺️ + +**📝 Examples to Get Started:** +```python +# Tuple Unpacking +point = (10, 20) +x, y = point +print(f"x: {x}, y: {y}") + +# Extended Unpacking +numbers = (1, 2, 3, 4, 5) +first, *middle, last = numbers +print(f"First: {first}, Middle: {middle}, Last: {last}") + +# Tuple Packing +person = "John", 30, "Engineer" +print(person) +``` + +**🚀 Use Cases for Tuple Unpacking and Packing:** +1. **Multiple Return Values**: Unpack tuples to receive multiple return values from functions in one assignment. 📤🔁 +2. **Swapping Values**: Swap variable values without using a temporary variable using tuple unpacking. ↔️🔄 +3. **Iterating over Multiple Elements**: Unpack tuples while iterating over lists or other iterable data structures. 🔄📑 +4. **Function Arguments**: Use tuple packing to pass multiple arguments to a function in a single parameter. 📋🎛️ +5. **Namedtuples**: Unpack namedtuples for concise access to individual attributes. 🏷️🗃️ + +**💡 Pro Tip:** +Remember that both tuple unpacking and packing support extended syntax, allowing you to handle multiple elements at once! 🎯🎯 + + + +## 5. ➗ Divmod - Dividing and Modulo Together + +`divmod` is a Python built-in function that takes two numbers and returns a pair of values - the result of integer division and the remainder (modulo). 🧮🔢 It's like getting two for the price of one! 💲🎁 + +**📝 Examples to Get Started:** +```python +# Using divmod +result = divmod(20, 3) +print(f"Result of divmod(20, 3): {result}") + +# Using divmod in a loop +quotients = [] +remainders = [] +for num in range(1, 6): + q, r = divmod(20, num) + quotients.append(q) + remainders.append(r) +print(f"Quotients: {quotients}") +print(f"Remainders: {remainders}") +``` + +**🚀 Use Cases for `divmod`:** +1. **Time Conversion**: Convert seconds into hours, minutes, and remaining seconds using `divmod`. 🕐🕒 +2. **Loop Iterations**: Use `divmod` in loops to efficiently calculate quotients and remainders for a range of numbers. 🔃🔢 +3. **Formatting**: Combine `divmod` results to display complex numerical data elegantly. 🎨🔠 +4. **Resource Allocation**: Distribute resources evenly among a given number of entities using `divmod`. 💻📊 +5. **Currency Handling**: Calculate the number of denominations needed when making change with `divmod`. 💲💸 + +**💡 Pro Tip:** +Remember that `divmod` can be a powerful tool to optimize certain mathematical operations and simplify your code. 🧮🚀 + diff --git a/_sources/newsletters/2023/WEEK_30.md.txt b/_sources/newsletters/2023/WEEK_30.md.txt new file mode 100644 index 0000000..736ffb6 --- /dev/null +++ b/_sources/newsletters/2023/WEEK_30.md.txt @@ -0,0 +1,397 @@ + +# Week 30 - July 2023 + +## 1. 💻 Understanding `__name__` and `__main__`. + +In Python, `__name__` and `__main__` are special variables that provide essential information about the execution context of a script. These variables play a crucial role in controlling how code behaves when it is run directly or imported as a module. + +**Example 1: The `__name__` Variable** + +```python +# filename: greetings.py + +def say_hello(): + return "Hello, Python-World! 🐍🌍" + +if __name__ == "__main__": + # This block of code will execute when the script is run directly. + print(say_hello()) +``` + +```python +# filename: main.py +import greetings + +print("Importing greetings module...") +print(greetings.say_hello()) +``` + +In this example, when running `greetings.py` directly, the `__name__` variable will be set to `"__main__"`, and the `say_hello()` function will be executed, displaying the greeting message. However, if `greetings.py` is imported as a module into `main.py`, the `__name__` variable will be set to `"greetings"`, and the `say_hello()` function won't be executed. + +**Example 2: Using `__name__` for Modular Code** + +```python +# filename: math_operations.py + +def add(a, b): + return a + b + +def subtract(a, b): + return a - b + +def multiply(a, b): + return a * b + +def divide(a, b): + return a / b + +if __name__ == "__main__": + print("Running math_operations.py directly...") + print("Addition:", add(5, 3)) + print("Subtraction:", subtract(10, 4)) +``` + +In this example, we have a `math_operations.py` script containing various math functions. When executed directly, the script will perform addition and subtraction operations. However, when imported into another script, only the function definitions will be available, and the addition and subtraction won't be executed. + +**Conclusion:** + +Understanding the role of `__name__` and `__main__` is crucial for writing modular and reusable Python code. Utilizing these special variables enables you to control the execution of code based on whether a script is run directly or imported as a module. + + + +## 2. 🚀 Exploring `getattr()` and `setattr()` in python. + +In Python, `getattr()` and `setattr()` are built-in functions that allow us to access and modify object attributes dynamically. These functions provide a versatile way to interact with objects, especially when the attribute names are determined at runtime. + +**Example 1: Dynamic Attribute Access with `getattr()`** + +```python +# Dynamic configuration example + +class Configuration: + def __init__(self): + self.host = "localhost" + self.port = 8000 + self.debug = False + +config = Configuration() + +# Let's retrieve the value of 'port' dynamically +attribute_name = "port" +port_value = getattr(config, attribute_name) + +print(f"The port value is: {port_value}") +``` + +In this example, `getattr()` dynamically retrieves the value of the attribute specified by the `attribute_name`. This allows us to access the `port` attribute without knowing it beforehand. + +**Example 2: Dynamic Attribute Setting with `setattr()`** + +```python +# Dynamic attribute setting example + +class User: + def __init__(self, username): + self.username = username + +user = User("john_doe") + +# Let's set the 'age' attribute dynamically +attribute_name = "age" +attribute_value = 30 + +setattr(user, attribute_name, attribute_value) + +print(f"{user.username}'s age is: {user.age}") +``` + +In this example, `setattr()` dynamically sets the `age` attribute with the given value. This allows us to add new attributes to objects programmatically. + +**Example 3: Handling Attribute Existence** + +```python +# Handling attribute existence with getattr() + +class Person: + def __init__(self, name): + self.name = name + +person = Person("Alice") + +# Let's check if the 'age' attribute exists, and set a default value if not found +attribute_name = "age" +default_age = 25 + +age = getattr(person, attribute_name, default_age) + +print(f"{person.name}'s age is: {age}") +``` + +In this example, `getattr()` checks if the `age` attribute exists in the `person` object. If it doesn't exist, the `default_age` value is returned, avoiding attribute errors. + +**Conclusion:** + +The `getattr()` and `setattr()` functions provide dynamic attribute access and modification capabilities in Python. These powerful tools enable developers to write more flexible and adaptable code, making them valuable additions to any Python programmer's toolkit. + + + +## 3. 📦 Parsing, installing, verifying requirements using `pip._internal` in python. + +`pip` is the go-to package installer for Python developers, but did you know that it comes with a powerful internal module called `pip._internal`? This hidden gem provides advanced functionalities for package management, including the ability to parse requirements with precision. + +**Example 1: Installing Packages using `pip`** + +```python +# requirements.txt +beautifulsoup4==4.9.3 +requests>=2.26.0 +pandas +``` + +In this example, the `requirements.txt` file specifies three packages: `beautifulsoup4` with version 4.9.3, `requests` with version 2.26.0 or higher, and `pandas` without specifying a version. + +**Example 2: Parsing Requirements using `pip._internal`** + +```python +from pip._internal.req import parse_requirements +import sys + +def parse_requirements_file(requirements_file): + requirements = parse_requirements(requirements_file, session="hack") # Create a dummy session + return [str(req.requirement) for req in requirements] + +requirements_file = "requirements.txt" +parsed_requirements = parse_requirements_file(requirements_file) + +print("Parsed requirements:") +for requirement in parsed_requirements: + print(requirement) +``` + +In this example, we use `pip._internal.req.parse_requirements` to parse the `requirements.txt` file. The result is a list of required packages, each represented as a string in the format `package_name==version` or `package_name>=version`. + +**Example 3: Installing Packages from Requirements** + +```python +from pip._internal import main as pip_main + +def install_requirements(requirements_file): + try: + pip_main(["install", "-r", requirements_file]) + print("All requirements installed successfully!") + except Exception as e: + print(f"Error: Failed to install requirements - {e}") + +install_requirements("requirements.txt") +``` + +In this example, we use `pip._internal.main` to run the `pip install` command on the `requirements.txt` file, installing all the specified packages and their versions. + +**Consolidated Example** + +fetch installed requirements , install requirements if not installed already + +```python +from pip._internal.req import parse_requirements +from pip._internal.operations.freeze import freeze +from pip._internal import main as pip_main +import re + + +def parse_requirements_file(requirements_file): + """ + Parse the requirements file and return a list of required packages. + + Args: + requirements_file (str): The path to the requirements file. + + Returns: + list: A list of strings, each representing a required package in the format 'package_name==version'. + """ + requirements = parse_requirements(requirements_file, session="hack") # Create a dummy session + return [str(req.requirement) for req in requirements] + +def is_package_installed(package_name): + """ + Check if a package is already installed. + + Args: + package_name (str): The name of the package to check. + + Returns: + bool: True if the package is installed, False otherwise. + """ + installed_packages = [ele.split('==')[0] for ele in freeze()] + package_sep = ['>=', '<=', '=='] + + clean_package_name = re.split('|'.join(package_sep),package_name)[0] + return clean_package_name.lower() in installed_packages + +def install_requirements(requirements_file): + """ + Install the required packages specified in the requirements file if they are not already installed. + + Args: + requirements_file (str): The path to the requirements file. + """ + try: + for requirement in parse_requirements_file(requirements_file): + package_name, _, version = requirement.partition('==') + package_name = package_name.strip() + if not is_package_installed(package_name): + pip_main(["install", requirement]) + print(f"Package '{package_name}' installed successfully!") + else: + print(f"Package '{package_name}' is already installed.") + print("All requirements satisfied!") + except Exception as e: + print(f"Error: Failed to install requirements - {e}") + +requirements_file = "requirements.txt" +install_requirements(requirements_file) + +``` + +**Conclusion:** + +Exploring `pip._internal` brings a new dimension to requirements management in Python. Its powerful capabilities enable us to parse and install packages with ease, empowering developers to create robust and scalable applications. + + + + + +## 4. 🔧 Exploring `dis` - Disassembler for python bytecode. + +Python bytecode is the low-level representation of Python code, generated by the Python interpreter before execution. The `dis` module allows us to disassemble Python bytecode, revealing the underlying instructions and how Python interprets our code. + +**Example 1: Disassembling Python Code** + +```python +import dis + +def greet(name): + message = f"Hello, {name}!" + print(message) + +dis.dis(greet) + +''' +OUTPUT + 3 0 RESUME 0 + + 4 2 LOAD_CONST 1 ('Hello, ') + 4 LOAD_FAST 0 (name) + 6 FORMAT_VALUE 0 + 8 LOAD_CONST 2 ('!') + 10 BUILD_STRING 3 + 12 STORE_FAST 1 (message) + + 5 14 LOAD_GLOBAL 1 (NULL + print) + 26 LOAD_FAST 1 (message) + 28 PRECALL 1 + 32 CALL 1 + 42 POP_TOP + 44 LOAD_CONST 0 (None) + 46 RETURN_VALUE +''' +``` + +In this example, we define a simple function `greet` that takes a `name` argument, formats a greeting message, and prints it. By using `dis.dis(greet)`, we disassemble the bytecode of the `greet` function, revealing the Python instructions. + +**Example 2: Inspecting Loops and Conditions** + +```python +import dis + +def count_to_n(n): + for i in range(n): + if i % 2 == 0: + print(f"{i} is even.") + else: + print(f"{i} is odd.") + +dis.dis(count_to_n) +``` + +In this example, we have a function `count_to_n` that counts from 0 to `n-1`, printing whether each number is even or odd. By disassembling this function, we gain insights into how Python handles loops and conditions at the bytecode level. + +**Example 3: Understanding List Comprehensions** + +```python +import dis + +def squares(n): + return [x**2 for x in range(n) if x % 2 == 0] + +dis.dis(squares) +``` + +In this example, we define a function `squares` that utilizes a list comprehension to create a list of squares of even numbers up to `n-1`. By disassembling the `squares` function, we can explore how Python handles list comprehensions at the bytecode level. + +**Industry UseCases** + +1. **Performance Optimization:** `dis` is handy when developers want to understand how their code is translated into bytecode. By inspecting the bytecode, they can identify performance bottlenecks, unnecessary operations, or areas for optimization, leading to more efficient code. + +2. **Understanding Third-Party Libraries:** When using third-party libraries, especially in security-critical applications, it's essential to know what operations are being performed under the hood. `dis` allows developers to inspect the bytecode of these libraries to ensure there are no hidden surprises. + +3. **Educational Purposes:** In educational settings, `dis` can be used to demonstrate the internal workings of Python code. It helps students understand how the Python interpreter executes their code step by step. + +4. **Code Analysis and Debugging:** In certain debugging scenarios, `dis` can be useful to inspect the bytecode of specific functions or modules to find the cause of unexpected behavior or bugs. + +5. **Reverse Engineering and Security Auditing:** In cybersecurity and reverse engineering contexts, analysts may use `dis` to inspect the bytecode of potentially malicious Python scripts or modules to better understand their functionality and potential security risks. + +6. **Code Obfuscation and Protection:** Some organizations use `dis` to analyze their code and identify vulnerabilities or areas where code obfuscation might be needed to protect intellectual property. + +7. **Code Quality and Compliance:** In industries with strict coding standards or compliance requirements, using `dis` can help ensure that code adheres to specific guidelines and does not include prohibited operations. + +8. **Dynamic Code Generation:** Some applications require dynamic code generation, where Python code is generated on the fly. `dis` can help developers understand and verify the generated bytecode. + + +**Conclusion:** + +`dis` is a powerful tool for understanding Python bytecode and the inner workings of Python code. It allows developers to inspect low-level instructions, enabling them to optimize and gain deeper insights into Python's execution. + + + +## 5. 🔒 Generating cryptographically secure tokens, passwords using `secrets`. + + +The `secrets` module is part of the Python Standard Library and provides functions for generating secure random numbers, strings, and managing cryptographic operations. It is designed specifically for creating cryptographically secure tokens, passwords, and other secrets. + +**Example: Generating Secure Tokens 🔑** + +```python +import secrets + +# Generate a secure random token for authentication +token = secrets.token_hex(32) + +print(f"Authentication Token: {token}") +``` + +In this example, we use `secrets.token_hex(32)` to generate a 32-byte (64-character) hexadecimal token. This token is suitable for authentication purposes, such as token-based authentication in web applications. + +**Example: Secure Password Generation** + +```python +import secrets +import string + +def generate_password(length=12): + # Define character set for password generation + characters = string.ascii_letters + string.digits + string.punctuation + + # Generate a secure random password + password = ''.join(secrets.choice(characters) for _ in range(length)) + + return password + +secure_password = generate_password() +print(f"Secure Password: {secure_password}") +``` + +In this example, we create a function `generate_password` that generates a random password of a specified length using uppercase and lowercase letters, digits, and special characters. The `secrets.choice()` function helps ensure that the password is cryptographically secure. + +**Conclusion:** + +The `secrets` module is a valuable asset in Python for generating cryptographically secure tokens, passwords, and managing secrets in a secure manner. By leveraging this module, developers can fortify their applications against security vulnerabilities related to sensitive information. diff --git a/_sources/newsletters/2023/WEEK_31.md.txt b/_sources/newsletters/2023/WEEK_31.md.txt new file mode 100644 index 0000000..6df94fa --- /dev/null +++ b/_sources/newsletters/2023/WEEK_31.md.txt @@ -0,0 +1,128 @@ +# Week 31 - Augest 2023 + + +## 1. 🔢 Python Match Statement + +Introduced in Python 3.10, the match statement is a powerful tool for pattern matching. It allows you to simplify complex if-elif-else chains by providing a concise and readable syntax. Here's an example: + +```python +def get_day_of_week(day_number): + match day_number: + case 1: + return "Monday" + case 2: + return "Tuesday" + case 3: + return "Wednesday" + case 4: + return "Thursday" + case 5: + return "Friday" + case 6: + return "Saturday" + case 7: + return "Sunday" + case _: + return "Invalid day number" +``` + +The match statement evaluates the input expression (`day_number` in this case) and compares it against different patterns (`case` statements). If a match is found, the corresponding block of code is executed. The `_` represents a wildcard pattern that matches anything. + +## 2. 🌪️ Decorators that Take Arguments + +Building upon the [previous article](https://python-world.github.io/newsletter/newsletters/2023/WEEK_27.html#unleash-the-power-of-python-function-decorators) on decorators, we can enhance their functionality by allowing them to accept arguments. This provides greater flexibility and customization. + +Here's an example: + +```python +def repeat(n): + def decorator(func): + def wrapper(*args, **kwargs): + for _ in range(n): + result = func(*args, **kwargs) + return result + return wrapper + return decorator + +@repeat(3) +def greet(name): + print(f"Hello, {name}!") + +greet("John") +``` + +In this example, the `repeat` decorator takes an argument `n` and returns a decorator function. This decorator function, in turn, takes the original function as an argument and returns the modified function (`wrapper`). The modified function is then executed multiple times, as specified by the `repeat` argument. + +## 3. 🛫 Map and Filter Functions + +Python provides two built-in functions, `map` and `filter`, that are widely used to process iterables. + +The `map` function applies a given function to each item in an iterable and returns an iterator with the results. Here's an example: + +```python +numbers = [1, 2, 3, 4, 5] + +squared_numbers = map(lambda x: x ** 2, numbers) +print(list(squared_numbers)) # Output: [1, 4, 9, 16, 25] +``` + +The `filter` function applies a given function to each item in an iterable and returns an iterator with the items for which the function returns `True`. Here's an example: + +```python +numbers = [1, 2, 3, 4, 5] + +even_numbers = filter(lambda x: x % 2 == 0, numbers) +print(list(even_numbers)) # Output: [2, 4] +``` + +## 4. 🍁 Global and Nonlocal Variables + +In Python, the `global` and `nonlocal` keywords allow you to modify variables outside the current scope. + +The `global` keyword is used to indicate that a variable within a function should refer to the global variable with the same name. Here's an example: + +```python +count = 0 + +def increment(): + global count + count += 1 + +increment() +print(count) # Output: 1 +``` + +The `nonlocal` keyword is used to indicate that a variable within a nested function should refer to a variable from its outer scope. Here's an example: + +```python +def outer(): + x = 1 + + def inner(): + nonlocal x + x += 1 + print(x) + + inner() + +outer() # Output: 2 +``` + +## 5. 🫙 Closures + +A closure is a function object that remembers values in its enclosing scope, even if they are not present in memory. This allows the function to access and manipulate variables from the outer function, even after the outer function has finished executing. Here's an example: + +```python +def outer_function(x): + def inner_function(y): + return x + y + return inner_function + +add_5 = outer_function(5) +print(add_5(3)) # Output: 8 +``` + +In this example, `outer_function` returns `inner_function`, which remembers the value of `x` even after `outer_function` has completed. The returned `inner_function` can be called later, providing the remembered value `x` and accepting an additional parameter `y` to perform the desired computation. + +These are just a few examples of Python's powerful features. Each of these concepts has its own unique applications and can greatly enhance your Python programming skills. Experiment with these concepts and explore their potential to unlock even more possibilities in your projects! + diff --git a/_sources/newsletters/2023/WEEK_32.md.txt b/_sources/newsletters/2023/WEEK_32.md.txt new file mode 100644 index 0000000..1a8d85f --- /dev/null +++ b/_sources/newsletters/2023/WEEK_32.md.txt @@ -0,0 +1,223 @@ + +# Week 32 - Augest 2023 + + +## 1. 👫 Strict separation of settings from code with Decouple + +*Decouple* is a tool that assists in managing settings, allowing you to adjust parameters without the need to redeploy your application. It offers several benefits, such as the ability to store parameters in files like *ini* or *.env*, set up default values, ensure proper data type conversions, and centralize all configurations into a single module. Initially created for Django, it has evolved into a versatile tool for separating settings from the core code, making it applicable to various projects beyond Django. + +**How to use in your project ?** + +1. Install the package into your project environment. +```shell +pip install python-decouple +``` +2. In your `config.py` + +```python +from decouple import config + +SECRET_KEY = config('SECRET_KEY') +DEBUG = config('DEBUG', default=False, cast=bool) +EMAIL_HOST = config('EMAIL_HOST', default='localhost') +EMAIL_PORT = config('EMAIL_PORT', default=25, cast=int) +PAYMENT_GATEWAY_API_KEY = config('PAYMENT_GATEWAY_API_KEY', cast=str) +``` + +3. Now in your other modules where you need to use these values + +```python +import config as cfg +print(cfg.EMAIL_HOST) + +``` + +## 2. 🐍 Empower yourself with Pydantic + +Pydantic is a powerful data validation library for Python that has gained significant popularity among developers due to its extensive features and ease of use. + +1. Typehints powering schema validation: Pydantic leverages Python type hints to define the structure of data models. + +2. Performance: The core is written in rust, making it superfast. + +3. Serialization: Pydantic provides seamless serialization and deserialization of data models to and from JSON, dictionaries, and other data formats. + +4. JSON Schema: JSON Schema is a standard for describing the structure of JSON data. Pydantic can automatically generate JSON Schema representations of your data models. This allows you to validate your data against a defined JSON Schema or share the schema with other systems. + +5. Strict mode and data coercion: Pydantic supports a strict mode that enforces the data model's field types strictly. It also provides data coercion, allowing you to automatically convert data to the specified types whenever possible. This feature makes handling user inputs and external data sources more robust. + + +Let's consider a simple use case of using Pydantic to validate and serialize data for a User model: + +```python +from pydantic import BaseModel + +class User(BaseModel): + username: str + email: str + age: int + +# Valid data +user_data = { + "username": "john_doe", + "email": "john@example.com", + "age": 30 +} + +# Creating a User instance from the data +user = User(**user_data) + +# The instance is automatically validated and parsed +print(user.username) # Output: john_doe +print(user.email) # Output: john@example.com +print(user.age) # Output: 30 + +# Invalid data - missing 'email' field +invalid_user_data = { + "username": "jane_doe", + "age": 25 +} + +# Trying to create a User instance with invalid data +try: + invalid_user = User(**invalid_user_data) +except Exception as e: + print(f"Error: {e}") + # Output: Error: 1 validation error for User + # email + # field required (type=value_error.missing) +``` + +In this example, we define a simple User model using Pydantic's BaseModel class and specify the expected types for each field (username, email, and age). When we create an instance of the User model with valid data, Pydantic automatically validates and parses the input. If the data is invalid, Pydantic raises an error with a helpful validation message. + +Overall, Pydantic simplifies data validation, serialization, and parsing tasks, making it an invaluable tool for Python developers, especially in the context of web APIs, data processing, and data validation tasks. Its performance, type hint-based approach, and seamless integration with other libraries contribute to its widespread adoption in the Python ecosystem. + + +## 3. 🐃 Stable Diffusion WebUI + +This project offers a browser interface based on Gradio library for Stable Diffusion. + +Gradio is a library offered by HuggingFace to quickly develop web interfaces for ml apps. + +In simple words you can generate images based on a prompt. + +Refer to the [GitHub repository](https://github.com/AUTOMATIC1111/stable-diffusion-webui) to learn about installation and usage. + +## 4. ✍️ Text Generation WebUI + +This is a sibling project of the previous one. A gradio web UI for running Large Language Models like LLaMA, llama.cpp, GPT-J, OPT, and GALACTICA. + +As per its documentation the key features are: + +- 3 interface modes: default, notebook, and chat +- Multiple model backends: tranformers, llama.cpp, AutoGPTQ, GPTQ-for-LLaMa, ExLlama, RWKV, FlexGen +- Dropdown menu for quickly switching between different models + +Refer to the [github repository](https://github.com/oobabooga/text-generation-webui) for learning how to install and use. + + +## 5. 🥇 Streamlit: A Python Library for Interactive Data Apps + +In recent years, data visualization and interactive web applications have become essential tools for data scientists and developers. Creating intuitive and interactive data apps used to require extensive web development skills, but now, with the Streamlit Python library, anyone with basic Python knowledge can build powerful data-driven applications effortlessly. + +**What is Streamlit?** + +Streamlit is an open-source Python library that allows developers to create web applications for data exploration and visualization with minimal effort. It was designed to simplify the process of turning data scripts into shareable web apps, making it an excellent tool for showcasing data insights and machine learning models. + +**Getting Started** + +To use Streamlit, you need to have Python installed on your system. You can install Streamlit using pip: + +``` +pip install streamlit +``` + +**Creating Your First Streamlit App** + +Once Streamlit is installed, creating a basic app is as simple as writing a few lines of Python code. Let's start by importing Streamlit and creating a simple app that displays a line chart: + +```python +# app.py +import streamlit as st +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt + +# Create a sample dataframe +data = pd.DataFrame({ + 'x': np.arange(10), + 'y': np.random.randn(10) +}) + +# Add a title to the app +st.title('My First Streamlit App') + +# Display the dataframe +st.dataframe(data) + +# Create a line chart +plt.plot(data['x'], data['y']) +st.pyplot(plt) +``` + +Save this code in a file named `app.py`, and then run the app using the following command: + +``` +streamlit run app.py +``` + +A new tab will open in your default web browser, displaying your first Streamlit app. The app will show a DataFrame and a simple line chart based on the data provided. + +**Customizing Your App** + +One of the greatest strengths of Streamlit is its ability to respond to user inputs and generate dynamic visualizations accordingly. For instance, you can add widgets like sliders, buttons, and text inputs to allow users to interact with your app. + +Here's an example of how to add a slider to control the number of data points displayed in the chart: + +```python +# app.py +import streamlit as st +import pandas as pd +import numpy as np +import matplotlib.pyplot as plt + +# Create a sample dataframe +data = pd.DataFrame({ + 'x': np.arange(100), + 'y': np.random.randn(100) +}) + +# Add a title to the app +st.title('Dynamic Chart with Streamlit') + +# Add a slider for the number of data points +num_points = st.slider('Number of Data Points', 10, 100, 50) + +# Display the selected number of data points in the DataFrame +st.dataframe(data.head(num_points)) + +# Create a dynamic line chart +plt.plot(data['x'][:num_points], data['y'][:num_points]) +st.pyplot(plt) +``` + +Now, when you run the app again, you will see a slider that allows you to control the number of data points displayed in the line chart. + +**Sharing Your App** + +Once you've created your Streamlit app and are satisfied with its functionality, you can easily share it with others. You can deploy it on various platforms, including Streamlit Sharing, Heroku, or your own web server. + +For example, if you want to deploy it on Streamlit Sharing, you can follow these steps: + +1. Create a GitHub repository with your Streamlit app code. +2. Sign in to Streamlit Sharing (https://streamlit.io/sharing) using your GitHub account. +3. Connect your GitHub repository to Streamlit Sharing and set up the deployment settings. +4. Once connected, Streamlit Sharing will automatically build and deploy your app. + +Now, you have a publicly accessible web link for your Streamlit app that you can share with anyone. + +**Conclusion** + +Streamlit is a game-changer in the world of data visualization and app development. Its simplicity and ease of use allow data scientists and developers to focus on presenting their data insights and models interactively without getting bogged down in web development complexities. With its increasing popularity and active community, Streamlit is likely to remain a popular choice for building interactive data applications in Python. So, if you want to create stunning and interactive data apps quickly, give Streamlit a try! + + diff --git a/_sources/newsletters/2023/WEEK_33.md.txt b/_sources/newsletters/2023/WEEK_33.md.txt new file mode 100644 index 0000000..3e02554 --- /dev/null +++ b/_sources/newsletters/2023/WEEK_33.md.txt @@ -0,0 +1,265 @@ + +# Week 33 - Augest 2023 + + +## 1. 📊 Understanding Underscores in Numeric Literals + +Python 3.6 introduced a delightful feature that allows you to use underscores in numeric literals. This feature has a simple yet impactful purpose: to enhance the readability of large numbers. By strategically placing underscores, you can visually separate the digits into more manageable groups, making the code easier to comprehend at a glance. + +Underscores in numeric literals might seem like a small feature, but they pack a punch when it comes to code readability. They help you avoid errors, reduce confusion, and make your codebase more maintainable. So go ahead, use underscores in your numeric literals and write code that's not just functional, but also elegant and clear! + +**Why Use Underscores?** + +🔍 **Clarity:** When dealing with numbers spanning several digits, like monetary values, scientific constants, or long identifiers, underscores serve as virtual separators, preventing potential confusion among the digits. + +🌟 **Readability:** Complex numbers, such as binary or hexadecimal literals, can quickly become unwieldy. By introducing underscores, you can break down these numbers into meaningful sections, making them much more readable. + +**Examples in Action** + +```python +# Without underscores (hard to read) +large_number = 1000000000 + +# With underscores (much clearer) +large_number = 1_000_000_000 + +# Binary literal without underscores +binary_num = 0b110100101101 + +# Binary literal with underscores +binary_num = 0b1101_0010_1101 + +# Hexadecimal literal without underscores +hex_num = 0x1A3B5F2D + +# Hexadecimal literal with underscores +hex_num = 0x1A_3B_5F_2D +``` + +**Best Practices** + +📝 **Consistency:** Be consistent with your use of underscores. If you choose to use them, stick to the pattern throughout your codebase. + +💡 **Placement:** Place underscores only between digits, not at the beginning or end of a number, and not in locations where the syntax would be invalid. + + + +## 2. 📜 Text Wrapping with Python's Textwrap Module + +Have you ever struggled with making your text fit within a specific width while preserving readability? The `textwrap` module comes to the rescue! It allows you to effortlessly format paragraphs, strings, or even code snippets to fit a certain width, all while ensuring proper line breaks and alignment. + +The `textwrap` module empowers you to present your text content in a professional and visually appealing manner. Whether you're working on documentation, emails, or any text-based communication, this module streamlines your formatting process and enhances the overall readability of your content. +**Why Choose Text Wrapping?** + +📚 **Clean Documentation:** When writing documentation or docstrings, it's crucial to keep your content well-organized and easily digestible. Text wrapping makes sure your text doesn't spill beyond its designated space, maintaining a neat and professional appearance. + +📝 **Formatted Output:** Whether you're generating reports or crafting emails, the `textwrap` module lets you create structured and visually appealing text layouts. No more manual line breaks or awkward formatting! + +**Examples** + +```python +import textwrap + +text = "Python-World Newsletter brings you the latest Python tips and tricks to elevate your coding game. Stay tuned for tutorials, deep dives, and more!" + +# Wrap the text to fit within 40 characters +wrapped_text = textwrap.fill(text, width=40) + +print(wrapped_text) +'''OUTPUT + +Python-World Newsletter brings you the +latest Python tips and tricks to elevate +your coding game. Stay tuned for +tutorials, deep dives, and more! +''' + + +print(text) +'''OUTPUT +Python-World Newsletter brings you the latest Python tips and tricks to elevate your coding game. Stay tuned for tutorials, deep dives, and more! +''' +``` + +**Additional Use Cases** + +📜 **Code Formatting:** When you're dealing with code snippets in your documentation, you can ensure that your code maintains its indentation and structure, even when it's wrapped to fit a specific width. + +💌 **Email Composition:** Need to send well-formatted emails programmatically? Text wrapping ensures your email content appears neat and is easily readable on various devices and email clients. + +**Best Practices** + +🔍 **Choose the Right Width:** Be mindful of the width you choose. A width that's too narrow might create excessive line breaks, while a width that's too wide could defeat the purpose of wrapping. + +💡 **Preserve Intent:** Use the `replace_whitespace` parameter to control how whitespace is handled in wrapped lines, preserving the intended formatting of your text. + + + + +## 3. 🌐 Exploring IP Address Manipulation with Python's `ipaddress` Module + +IP addresses are the backbone of the internet, enabling devices to communicate across the digital landscape. The `ipaddress` module provides an elegant and efficient way to handle IP addresses, both IPv4 and IPv6, within your Python code. + +The `ipaddress` module elevates your IP address manipulation game by providing a seamless and intuitive way to work with both IPv4 and IPv6 addresses. Whether you're configuring networks, building security features, or exploring the depths of the internet, this module is your trusted companion. + + +**Why `ipaddress` Module?** + +🌐 **Precision Handling:** The `ipaddress` module ensures accurate handling of IP addresses, allowing you to perform various operations like validation, comparison, and network calculations with ease. + +🔍 **Readability:** No more manual conversions between dot-decimal notation and integers. The module lets you work with IP addresses directly in a human-readable format. + +**Examples** + +```python +import ipaddress + +# Creating IPv4 and IPv6 objects +ipv4_address = ipaddress.IPv4Address('192.168.0.1') +ipv6_address = ipaddress.IPv6Address('2001:0db8:85a3:0000:0000:8a2e:0370:7334') + +# Check if an address is private +print(ipv4_address.is_private) # True +print(ipv6_address.is_private) # False + +# Perform subnet calculations +network = ipaddress.ip_network('192.168.0.0/24') +print(network.network_address) # 192.168.0.0 +print(network.broadcast_address) # 192.168.0.255 +``` + +**Use Cases** + +🖥️ **Network Operations:** The `ipaddress` module shines when working with network configurations, such as subnetting, network classification, and routing. + +🛡️ **Security and Access Control:** IP address validation and classification are crucial for access control, firewalls, and security-related tasks. + +**Best Practices** + +🔐 **Validation:** Always validate user inputs that involve IP addresses to prevent unexpected behavior and security vulnerabilities. + +💡 **Performance:** While the `ipaddress` module is powerful, remember that parsing large lists of IP addresses might impact performance. Optimize your code for efficiency. + + + +## 4. 🏁 Graceful Script Exit with `atexit` in Python + +Have you ever wished for a way to ensure that certain cleanup tasks or actions are performed reliably when your Python script ends, regardless of whether it finishes normally or encounters an exception? The `atexit` module is here to grant your wish! + +The `atexit` module adds a touch of finesse to your script exits by allowing you to perform crucial cleanup operations without fuss. Whether you're closing files, ending network connections, or tidying up other resources, `atexit` has your back. + + +**Why Embrace `atexit`?** + +🧹 **Effortless Cleanup:** With `atexit`, you can register functions to be executed when your script terminates, allowing you to release resources, close files, or perform other essential cleanup tasks without cluttering your code. + +🏁 **Exit Handling:** No more forgetting to close open files or connections before your script exits unexpectedly. `atexit` ensures that you always leave your script's environment in a tidy state. + +**Examples** + +```python +import atexit + +def exit_handler(): + print("Script is exiting. Performing cleanup tasks...") + +# Register the exit_handler function +atexit.register(exit_handler) + +# Your script code here + +# The exit_handler function will be called when the script exits +``` + +**More Examples** + +1. **Closing Files:** + +```python +import atexit + +def close_files(): + file1.close() + file2.close() + +file1 = open("file1.txt", "w") +file2 = open("file2.txt", "w") + +# Register the close_files function +atexit.register(close_files) + +# Your script code here +``` + +2. **Network Connections:** + +```python +import atexit +import socket + +def close_connections(): + socket1.close() + socket2.close() + +socket1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +socket2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + +# Register the close_connections function +atexit.register(close_connections) + +# Your script code here +``` + +**Use Cases** + +📦 **File Management:** Use `atexit` to close open files, ensuring that data is properly saved and resources are freed. + +🌐 **Network Connections:** Close network sockets or database connections gracefully, preventing potential resource leaks. + +**Best Practices** + +🕊️ **Keep It Simple:** Register only essential cleanup functions with `atexit`. Complex logic or time-consuming tasks might not be suitable. + +💡 **Exception Handling:** Remember that `atexit` functions are also called in case of exceptions. Make sure your cleanup code handles exceptions gracefully. + + +## 5. 🔒 Safeguarding Data with Python's `hashlib` Module + +In a digital world teeming with sensitive information, ensuring the integrity and security of data is paramount. Python's `hashlib` module equips you with cryptographic hash functions to create unique fingerprints of data, aiding in verification and protection against tampering. + + +The `hashlib` module empowers you to guard data against tampering and verify its authenticity. Whether you're securing user passwords, ensuring file integrity, or validating data integrity, `hashlib` is your shield against data vulnerabilities. + + +**Why Embrace `hashlib`?** + +🔐 **Data Security:** Hash functions enable you to securely store passwords, check file integrity, and verify data authenticity without exposing the original content. + +🔍 **Data Integrity:** Hashing allows you to detect even the slightest alterations in data. If the hash doesn't match, you know something has changed. + +**Examples** + +```python +import hashlib + +data = b'Hello, Python-World Newsletter readers!' +hash_object = hashlib.sha256(data) +hash_value = hash_object.hexdigest() + +print("Original Data:", data) +print("SHA-256 Hash:", hash_value) +``` + +**Use Cases** + +🔑 **Password Storage:** Hash passwords before storing them in databases. When users log in, their input can be hashed and compared to the stored hash. + +📂 **File Verification:** Calculate the hash of a file before and after transmission. If the hashes match, the file is intact. + +**Best Practices** + +🔐 **Salting:** When hashing passwords, use a unique "salt" value for each user to prevent attackers from using precomputed hash tables (rainbow tables). + +💡 **Secure Algorithms:** Choose strong hash algorithms like SHA-256 for robust security. + + diff --git a/_sources/newsletters/2023/WEEK_34.md.txt b/_sources/newsletters/2023/WEEK_34.md.txt new file mode 100644 index 0000000..48dce53 --- /dev/null +++ b/_sources/newsletters/2023/WEEK_34.md.txt @@ -0,0 +1,230 @@ + +# Week 34 - Augest 2023 + + +## 1. 📰 The 'Title()' Method and Its Nuances(Limitations) + +The `title()` method is a versatile tool for enhancing text formatting. However, it's essential to recognize its limitations. The title() method isn't just about capitalizing; it also handles special characters and maintains proper casing rules. + + + Let's explore some insights: + +🔍 **Use Case 1: Letters Only** +```python +text = "pycon 2023" +formatted_text = text.title() +print(formatted_text) # Output: Pycon 2023 +``` + +📋 **Use Case 2: Alphanumeric Exploration** +```python +text = "python3.10_release" +formatted_text = text.title() +print(formatted_text) # Output: Python3.10_Release +``` + +📄 **Use Case 3: Punctuation Perspective** +```python +text = "don't STOP believin'!" +formatted_text = text.title() +print(formatted_text) # Output: Don'T Stop Believin'! +``` + + +## 2. 📚 Run A Python Module Directly With `__main__.py` + +The `__main__.py` file empowers you to seamlessly convert your Python module into an executable script, enhancing usability and efficiency. + +Have you ever wished to run a Python module directly as if it were a script? The `__main__.py` file holds the key! Let's explore a captivating use case that showcases its remarkable potential: + +🔍 **Use Case: Transforming Modules into Scripts** +``` +my_module/ +|-- __main__.py +|-- helper_functions.py +``` + +```python +# file : helper_functions.py + +def some_function(): + return "Python World" +``` + +Imagine you have a module named `my_module` containing useful functions. By adding the following code to `__main__.py`, you can make your module directly executable: +```python +from helper_functions import * + +if __name__ == "__main__": + # Your script logic goes here + result = some_function() + print("Result:", result) +``` + +Now, executing `python my_module` directly from the command line runs the script within the `__main__.py` file, providing instant access to your module's functionality without additional steps. + + +## 3. 🔍 Unraveling Dictionary Literals and Constructors + +Dictionaries are fundamental for storing and accessing key-value data, and Python's versatility in creating them caters to various scenarios. + +Dictionaries are essential data structures in Python, and the methods to create them are fascinating. Let's explore these methods along with some captivating use cases: + +🔍 **Use Case 1: Dictionary Literals** +```python +employee = {'name': 'Alice', 'age': 30, 'department': 'HR'} +``` +Dictionary literals allow you to create dictionaries directly by listing key-value pairs enclosed in curly braces. Perfect for concise and readable code. + +📋 **Use Case 2: Dictionary Constructor** +```python +colors = dict(red='#FF0000', green='#00FF00', blue='#0000FF') +``` +The dictionary constructor lets you create dictionaries using the built-in `dict()` function. Ideal for dynamically constructing dictionaries from various sources. + +📄 **Use Case 3: Merging Dictionaries** +```python +defaults = {'theme': 'light', 'font_size': 12} +user_preferences = {'font_size': 14, 'language': 'en'} +merged_preferences = {**defaults, **user_preferences} +``` +Combine dictionaries effortlessly by unpacking them into a new dictionary. A powerful technique for overriding default values. + +🔗 **Use Case 4: Creating Dynamic Dictionaries** +```python +keys = ['a', 'b', 'c'] +values = [1, 2, 3] +dynamic_dict = dict(zip(keys, values)) +``` +Construct dictionaries dynamically by using the `zip()` function to combine lists of keys and values. + + +**🚀 In a Nutshell:** + +Choose dictionary literals for straightforward dictionary creation, and opt for the dictionary constructor when you need more flexibility, such as when dynamically constructing dictionaries or merging multiple dictionaries. + + +## 4. 🧹 Discovering isort: The Art of Import Arrangement + +Import statements – a gateway to the world of functionality. But arranging them can be a challenge. Enter `isort`, the Python package that effortlessly arranges your imports for clarity and readability. Behold how easily you can get started: + +1. **Installation of isort** via pip: + + ``` + pip install isort + ``` + +2. **Running isort** on your Python file: + + ``` + isort your_file.py + ``` + + 🌟 Witness the transformation as your imports fall into a harmonious order! + +🌐 **A Symphony of Imports: Prioritize with Precision** + +Picture this: system packages, third-party modules, and local imports, all in symphonic order. `isort` brings this vision to life: + +```python +# Before isort +from flask import Flask, render_template +import os, sys +from datetime import datetime, timedelta +from my_local_module import my_function + +# After isort +import os +import sys +from datetime import datetime, timedelta + +from flask import Flask, render_template + +from my_local_module import my_function +``` + +🧩 **Custom Choreography: Tailoring isort to Your Tunes** + +`isort` is not just a one-size-fits-all solution; it dances to your rhythm. Craft your own dance steps with configuration options in `pyproject.toml`: + +```toml +# pyproject.toml +[tool.isort] +profile = "black" +line_length = 88 +known_third_party = ["flask", "requests"] +``` + +🔍 **isort in Your Workflow Ensemble: Effortless Integration** + +Seamlessly blend `isort` into your development routine. Set it as a pre-commit hook using tools like `pre-commit`. Feel the magic as your imports gracefully align with every commit. + + + +## 5. 🕰️ Master Time with Python's Scheduler! + +In the chaotic world of programming, managing tasks at specific intervals is a breeze with Python's built-in `sched` module. Let's jump into a simple example to showcase its power: + +```python +import sched +import time + +def greet(name): + print(f"Hello, {name}! It's time to shine.") + +# Create a scheduler instance +scheduler = sched.scheduler(time.time, time.sleep) + +# Schedule the greeting +scheduler.enter(5, 1, greet, ("Python-world",)) + +# Run the scheduler +scheduler.run() +``` + +🚀 **Automation at Your Fingertips: Scheduled Tasks** + +Python's scheduler isn't limited to just delaying functions. You can schedule tasks at specific times, automate backups, and more. Here's a snippet to illustrate scheduling a task every hour: + +```python +import sched +import time + +def hourly_task(): + print("Performing hourly task!") + +scheduler = sched.scheduler(time.time, time.sleep) + +# Schedule the task every hour +interval = 3600 # 1 hour in seconds +while True: + scheduler.enter(interval, 1, hourly_task, ()) + scheduler.run() +``` + +⏳ **Cron-like Scheduling: `schedule` Library** + +For more advanced scheduling, the `schedule` library offers a cron-like syntax. Check out how easy it is to schedule tasks with this powerful tool: + +```python +import schedule +import time + +def daily_task(): + print("Daily task is due!") + +# Schedule a task to run daily at 3:30 PM +schedule.every().day.at("15:30").do(daily_task) + +while True: + schedule.run_pending() + time.sleep(1) +``` + +🔍 **Tailoring to Your Time Needs: Versatility of Scheduling** + +Whether it's running regular maintenance scripts, sending automated emails, or managing data backups, Python's scheduler empowers you to automate tasks according to your precise timing needs. + +🌐 **Unlock the Potential of Python's Scheduler!** + +Say goodbye to manual task management and hello to efficient automation with Python's scheduler. From simple delays to intricate cron-like scheduling, Python has your back when it comes to managing time and tasks. \ No newline at end of file diff --git a/_sources/newsletters/2023/WEEK_35.md.txt b/_sources/newsletters/2023/WEEK_35.md.txt new file mode 100644 index 0000000..ad1fd1c --- /dev/null +++ b/_sources/newsletters/2023/WEEK_35.md.txt @@ -0,0 +1,261 @@ + +# Week 35 - Septmember 2023 + + +## 1. 📦 Unveiling Descriptors: The Hidden Gems of Python + + +Descriptors are a fascinating, albeit less-explored aspect of Python. They allow you to customize attribute access on classes, providing a level of control and abstraction that can significantly enhance your code. + +A descriptor is a class that implements the special methods `__get__()`, `__set__()`, or `__delete__()` and is assigned to a class attribute. This magic trio opens the door to attribute management, validation, and computation. + +Descriptors and property decorators are not merely advanced Python features; they are powerful tools for crafting clean, maintainable, and robust code. Whether you're building a complex framework, a user-friendly API, or simply want to add some magic to your classes, these tools are worth mastering. + + +Let's dive deeper: + +```python +class DescriptorExample: + def __get__(self, instance, owner): + # Custom attribute retrieval logic + return instance._value + + def __set__(self, instance, value): + # Custom attribute assignment logic + instance._value = value + 10 # Add 10 + +class MyClass: + descriptor_attr = DescriptorExample() + +# Usage +obj = MyClass() +obj.descriptor_attr = 42 +print(obj.descriptor_attr) # 52 +``` + +**Elevating Control with Property Decorators 🚀** + +Property decorators, often seen as the friendlier face of descriptors, provide an elegant way to manage class attributes without exposing the underlying descriptor machinery. + +By decorating a method with `@property`, you can define a computed attribute that appears as a regular attribute but allows you to add custom logic to its access. Similarly, `@property_name.setter` and `@property_name.deleter` decorators enable control over assignment and deletion. + +```python +class MyClass: + def __init__(self): + self._value = None + + @property + def value(self): + # Custom attribute retrieval logic + return self._value + + @value.setter + def value(self, new_value): + # Custom attribute assignment logic + self._value = new_value + +# Usage +obj = MyClass() +obj.value = 42 +print(obj.value) # 42 +``` + +**Practical Applications 🛠️** + +Understanding descriptors and property decorators opens doors to a plethora of practical applications, including: + +- **Data Validation**: Ensure attribute values meet specific criteria or constraints. +- **Computed Properties**: Create attributes with dynamic values calculated on the fly. +- **Access Control**: Implement read-only or write-only attributes. +- **Legacy Code Integration**: Retrofit descriptor behavior into existing codebases. + + + + +## 2. 🧙‍♂️ Dynamic Code Generation: Crafting Code on the Fly + +Dynamic code generation is the art of creating and manipulating Python code at runtime. This opens up a world of possibilities, enabling you to generate classes, functions, and code structures dynamically to solve complex problems and automate repetitive tasks. + +Imagine building a code generator that tailors Python scripts based on user input or generating data models from configuration files. With dynamic code generation, the possibilities are limitless. + +```python +# Dynamic function creation +def generate_multiply_function(factor): + def multiply(x): + return x * factor + return multiply + +# Usage +double = generate_multiply_function(2) +print(double(5)) # Output: 10 +``` + +**Metaprogramming: Code that Writes Code 🖋️** + +Metaprogramming takes dynamic code generation to the next level. It's about writing code that writes or manipulates other code. This powerful technique is often used in frameworks, libraries, and code generators to simplify complex tasks. + +Metaclasses, decorators, and class factories are common tools in the metaprogrammer's toolbox. They allow you to control class creation, customize attribute access, and modify the behavior of functions and methods. + +```python +# Metaclass example +class MyMeta(type): + def __new__(cls, name, bases, dct): + # Customize class creation + dct['custom_attribute'] = 42 + return super().__new__(cls, name, bases, dct) + +class MyClass(metaclass=MyMeta): + pass + +# Usage +obj = MyClass() +print(obj.custom_attribute) # Output: 42 +``` + +**Practical Applications: Where the Magic Happens 🌟** + +Dynamic code generation and metaprogramming aren't just theoretical concepts; they have practical applications across various domains: + +- **Code Generators**: Automate code generation for repetitive tasks and template-based code. + +- **Configuration-driven Development**: Create dynamic configurations that generate code based on user-defined parameters. + +- **Domain-Specific Languages (DSLs)**: Build custom languages tailored to specific tasks or industries. + +- **Framework and Library Development**: Simplify complex APIs and extend framework functionality. + + + +## 3. 🐍 Avoid These 5 Common Mistakes When Writing Python Programs + + +Python is a fantastic language for its simplicity and readability, but that doesn't mean it's immune to errors. By avoiding these common mistakes and adopting best practices, you'll become a more proficient Python programmer and create more reliable and maintainable software. + +**Mistake 1: Neglecting Indentation 🧐** + +Python's use of indentation for code blocks is one of its distinctive features. However, it's also a common source of errors. Failing to maintain consistent and correct indentation levels can lead to syntax errors and unexpected program behavior. + +**Tip:** Use a reliable code editor or IDE that automatically handles indentation, and be consistent with your style. + +**Mistake 2: Ignoring Error Handling 🚨** + +Errors and exceptions are a natural part of software development. Neglecting to handle exceptions or using overly broad `try...except` blocks can make it challenging to diagnose and fix issues in your code. + +**Tip:** Always include proper error handling in your code to gracefully handle exceptions and provide meaningful error messages. + +**Mistake 3: Not Using Virtual Environments 🌐** + +Failing to use virtual environments for your Python projects can lead to version conflicts and dependencies issues. Mixing packages from different projects can result in headaches when trying to maintain or distribute your code. + +**Tip:** Create and use virtual environments for each Python project to isolate dependencies and ensure a clean environment. + +**Mistake 4: Poor Documentation 📖** + +Insufficient or outdated documentation can make your code difficult for others (and even yourself) to understand. Neglecting docstrings, inline comments, and clear variable/function names can hinder collaboration and future maintenance. + +**Tip:** Practice good documentation habits by adding docstrings to your functions, documenting your code's purpose, and maintaining up-to-date README files. + +**Mistake 5: Not Testing Code 🧪** + +Failure to test your code thoroughly can lead to undiscovered bugs and regressions. Relying solely on manual testing or skipping testing altogether can result in unreliable software. + +**Tip:** Implement automated testing using tools like `unittest`, `pytest`, or `doctest` to ensure your code behaves as expected and remains stable as it evolves. + + +## 4. 🛡️ Test Cases: Your Safety Net in Code Development + +Test cases are the safety net that ensures your Python code works as intended. They help you catch bugs early in the development process, provide documentation for your code's behavior, and facilitate collaboration among developers. + +Writing effective test cases is not just a practice; it's an investment in the quality and reliability of your Python software. By following these tips and incorporating testing into your development workflow, you'll catch issues early, save time, and build more robust applications. + + +Writing effective test cases is a skill every Python programmer should master. Let's explore some best practices: + +**Tip 1: Be Clear on What You Want to Test 🎯** + +Before writing a test case, have a clear understanding of the specific functionality or behavior you want to test. Define your test's scope, inputs, and expected outputs. + +```python +def test_addition(): + result = add(3, 5) + assert result == 8 +``` + +**Tip 2: Cover Edge Cases and Boundaries 🌉** + +Don't just test for typical scenarios. Ensure your test suite includes edge cases, boundary conditions, and scenarios where unexpected inputs might be provided. + +```python +def test_division_by_zero(): + with pytest.raises(ZeroDivisionError): + divide(10, 0) +``` + +**Tip 3: Keep Your Tests Isolated 🧩** + +Tests should be independent of each other. Avoid test cases that rely on the state or results of previous tests. Isolation ensures that each test provides clear and unambiguous results. + +```python +def test_multiply(): + result = multiply(4, 5) + assert result == 20 +``` + +**Tip 4: Use Descriptive Test Names 📝** + +Choose descriptive names for your test functions so that failures are easy to understand. A clear test name should indicate the purpose and context of the test. + +```python +def test_user_registration_valid_data(): + # ... +``` + +**Tip 5: Automate Your Tests 🤖** + +Automate the execution of your test suite. Tools like `unittest`, `pytest` make running tests and reporting results straightforward. + +```bash +$ pytest test_my_module.py +``` + + +## 5. 🔄 How to Check if a Generator is Empty + + +Generators are a valuable tool in Python, but knowing how to manage them effectively is equally important. With these techniques to check if a generator is empty, you can write more robust and efficient code while working with iterators. + +Generators in Python are a versatile way to create iterators without the need to build an entire class. They allow you to generate values on-the-fly, making them particularly useful for dealing with large data sets or when you don't want to store all values in memory. + +Let's explore the methods to check if a generator is empty: + +**Method 1: Iterate and Check 🚶** + +One common approach to check if a generator is empty is to attempt to iterate over it and catch the `StopIteration` exception that occurs when the generator is exhausted. + +```python +def is_generator_empty(generator): + try: + next(generator) + return False # Generator is not empty + except StopIteration: + return True # Generator is empty +``` + +**Method 2: Use `itertools.tee()` 🍐** + +The `itertools` module provides a helpful `tee()` function that can create multiple independent iterators from a single iterable, including generators. By using `tee()` to create a second iterator, you can check if the first one is empty without consuming its values. + +```python +from itertools import tee + +def is_generator_empty(generator): + # Create two independent iterators + gen1, gen2 = tee(generator) + + try: + next(gen2) # Attempt to advance the second iterator + return False # Generator is not empty + except StopIteration: + return True # Generator is empty +``` +**Note**: element will be exausted from generator, so please use it carefully. \ No newline at end of file diff --git a/_sources/newsletters/index.2022.rst.txt b/_sources/newsletters/index.2022.rst.txt new file mode 100644 index 0000000..d5feac2 --- /dev/null +++ b/_sources/newsletters/index.2022.rst.txt @@ -0,0 +1,6 @@ +2022 +========== +.. toctree:: + :maxdepth: 4 + + 2022 Jan - Week 1 <2022/WEEK_1.rst> \ No newline at end of file diff --git a/_sources/newsletters/index.2023.rst.txt b/_sources/newsletters/index.2023.rst.txt new file mode 100644 index 0000000..61cd707 --- /dev/null +++ b/_sources/newsletters/index.2023.rst.txt @@ -0,0 +1,17 @@ +2023 +========== +.. toctree:: + :maxdepth: 4 + + September - Week 35 <2023/WEEK_35.md> + Augest - Week 34 <2023/WEEK_34.md> + Augest - Week 33 <2023/WEEK_33.md> + Augest - Week 32 <2023/WEEK_32.md> + Augest - Week 31 <2023/WEEK_31.md> + July - Week 30 <2023/WEEK_30.md> + July - Week 29 <2023/WEEK_29.md> + July - Week 28 <2023/WEEK_28.md> + July - Week 27 <2023/WEEK_27.md> + June - Week 26 <2023/WEEK_26.rst> + June - Week 25 <2023/WEEK_25.rst> + \ No newline at end of file diff --git a/_sources/upcoming_events.md.txt b/_sources/upcoming_events.md.txt new file mode 100644 index 0000000..03e774d --- /dev/null +++ b/_sources/upcoming_events.md.txt @@ -0,0 +1,20 @@ +# Upcoming Events + +Hello, Python Enthusiasts! 🎉 Check out these exciting Python events that are just around the corner. Whether you're a seasoned coder or a newbie, these gatherings are perfect opportunities to learn, network, and celebrate the Python community. 🚀 + +| Event Name | Date | Location | Status | URL | +|---------------------|--------------------|------------|----------|------------------------------------------| +| PyCon 2023 | Sept 29 - Oct 2 | Hyderabad | ![Active](https://img.shields.io/badge/Active-Green) | [Website](https://in.pycon.org/2023/) | +| PyDelhi Conference 2023 | Aug 19 & 20 | Delhi | Completed | [Website](https://conference.pydelhi.org/) | + +Mark your calendars and get ready to dive into the world of Python with fellow enthusiasts. These events offer a blend of talks, workshops, and networking opportunities that promise to enrich your Python journey. + +Stay connected and keep coding! +The Python-World Newsletter Team 🐍📆 + + + +Stay updated with the latest events and conferences in the Python +community. Mark your calendars and don’t miss out on these exciting +opportunities to learn, network, and engage with fellow Python +enthusiasts! diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000..30fee9d --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/check-solid.svg b/_static/check-solid.svg new file mode 100644 index 0000000..92fad4b --- /dev/null +++ b/_static/check-solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/_static/clipboard.min.js b/_static/clipboard.min.js new file mode 100644 index 0000000..54b3c46 --- /dev/null +++ b/_static/clipboard.min.js @@ -0,0 +1,7 @@ +/*! + * clipboard.js v2.0.8 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */ +!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return o}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),c=n.n(e);function a(t){try{return document.execCommand(t)}catch(t){return}}var f=function(t){t=c()(t);return a("cut"),t};var l=function(t){var e,n,o,r=1 + + + + diff --git a/_static/copybutton.css b/_static/copybutton.css new file mode 100644 index 0000000..f1916ec --- /dev/null +++ b/_static/copybutton.css @@ -0,0 +1,94 @@ +/* Copy buttons */ +button.copybtn { + position: absolute; + display: flex; + top: .3em; + right: .3em; + width: 1.7em; + height: 1.7em; + opacity: 0; + transition: opacity 0.3s, border .3s, background-color .3s; + user-select: none; + padding: 0; + border: none; + outline: none; + border-radius: 0.4em; + /* The colors that GitHub uses */ + border: #1b1f2426 1px solid; + background-color: #f6f8fa; + color: #57606a; +} + +button.copybtn.success { + border-color: #22863a; + color: #22863a; +} + +button.copybtn svg { + stroke: currentColor; + width: 1.5em; + height: 1.5em; + padding: 0.1em; +} + +div.highlight { + position: relative; +} + +/* Show the copybutton */ +.highlight:hover button.copybtn, button.copybtn.success { + opacity: 1; +} + +.highlight button.copybtn:hover { + background-color: rgb(235, 235, 235); +} + +.highlight button.copybtn:active { + background-color: rgb(187, 187, 187); +} + +/** + * A minimal CSS-only tooltip copied from: + * https://codepen.io/mildrenben/pen/rVBrpK + * + * To use, write HTML like the following: + * + *

Short

+ */ + .o-tooltip--left { + position: relative; + } + + .o-tooltip--left:after { + opacity: 0; + visibility: hidden; + position: absolute; + content: attr(data-tooltip); + padding: .2em; + font-size: .8em; + left: -.2em; + background: grey; + color: white; + white-space: nowrap; + z-index: 2; + border-radius: 2px; + transform: translateX(-102%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); +} + +.o-tooltip--left:hover:after { + display: block; + opacity: 1; + visibility: visible; + transform: translateX(-100%) translateY(0); + transition: opacity 0.2s cubic-bezier(0.64, 0.09, 0.08, 1), transform 0.2s cubic-bezier(0.64, 0.09, 0.08, 1); + transition-delay: .5s; +} + +/* By default the copy button shouldn't show up when printing a page */ +@media print { + button.copybtn { + display: none; + } +} diff --git a/_static/copybutton.js b/_static/copybutton.js new file mode 100644 index 0000000..2ea7ff3 --- /dev/null +++ b/_static/copybutton.js @@ -0,0 +1,248 @@ +// Localization support +const messages = { + 'en': { + 'copy': 'Copy', + 'copy_to_clipboard': 'Copy to clipboard', + 'copy_success': 'Copied!', + 'copy_failure': 'Failed to copy', + }, + 'es' : { + 'copy': 'Copiar', + 'copy_to_clipboard': 'Copiar al portapapeles', + 'copy_success': '¡Copiado!', + 'copy_failure': 'Error al copiar', + }, + 'de' : { + 'copy': 'Kopieren', + 'copy_to_clipboard': 'In die Zwischenablage kopieren', + 'copy_success': 'Kopiert!', + 'copy_failure': 'Fehler beim Kopieren', + }, + 'fr' : { + 'copy': 'Copier', + 'copy_to_clipboard': 'Copier dans le presse-papier', + 'copy_success': 'Copié !', + 'copy_failure': 'Échec de la copie', + }, + 'ru': { + 'copy': 'Скопировать', + 'copy_to_clipboard': 'Скопировать в буфер', + 'copy_success': 'Скопировано!', + 'copy_failure': 'Не удалось скопировать', + }, + 'zh-CN': { + 'copy': '复制', + 'copy_to_clipboard': '复制到剪贴板', + 'copy_success': '复制成功!', + 'copy_failure': '复制失败', + }, + 'it' : { + 'copy': 'Copiare', + 'copy_to_clipboard': 'Copiato negli appunti', + 'copy_success': 'Copiato!', + 'copy_failure': 'Errore durante la copia', + } +} + +let locale = 'en' +if( document.documentElement.lang !== undefined + && messages[document.documentElement.lang] !== undefined ) { + locale = document.documentElement.lang +} + +let doc_url_root = DOCUMENTATION_OPTIONS.URL_ROOT; +if (doc_url_root == '#') { + doc_url_root = ''; +} + +/** + * SVG files for our copy buttons + */ +let iconCheck = ` + ${messages[locale]['copy_success']} + + +` + +// If the user specified their own SVG use that, otherwise use the default +let iconCopy = ``; +if (!iconCopy) { + iconCopy = ` + ${messages[locale]['copy_to_clipboard']} + + + +` +} + +/** + * Set up copy/paste for code blocks + */ + +const runWhenDOMLoaded = cb => { + if (document.readyState != 'loading') { + cb() + } else if (document.addEventListener) { + document.addEventListener('DOMContentLoaded', cb) + } else { + document.attachEvent('onreadystatechange', function() { + if (document.readyState == 'complete') cb() + }) + } +} + +const codeCellId = index => `codecell${index}` + +// Clears selected text since ClipboardJS will select the text when copying +const clearSelection = () => { + if (window.getSelection) { + window.getSelection().removeAllRanges() + } else if (document.selection) { + document.selection.empty() + } +} + +// Changes tooltip text for a moment, then changes it back +// We want the timeout of our `success` class to be a bit shorter than the +// tooltip and icon change, so that we can hide the icon before changing back. +var timeoutIcon = 2000; +var timeoutSuccessClass = 1500; + +const temporarilyChangeTooltip = (el, oldText, newText) => { + el.setAttribute('data-tooltip', newText) + el.classList.add('success') + // Remove success a little bit sooner than we change the tooltip + // So that we can use CSS to hide the copybutton first + setTimeout(() => el.classList.remove('success'), timeoutSuccessClass) + setTimeout(() => el.setAttribute('data-tooltip', oldText), timeoutIcon) +} + +// Changes the copy button icon for two seconds, then changes it back +const temporarilyChangeIcon = (el) => { + el.innerHTML = iconCheck; + setTimeout(() => {el.innerHTML = iconCopy}, timeoutIcon) +} + +const addCopyButtonToCodeCells = () => { + // If ClipboardJS hasn't loaded, wait a bit and try again. This + // happens because we load ClipboardJS asynchronously. + if (window.ClipboardJS === undefined) { + setTimeout(addCopyButtonToCodeCells, 250) + return + } + + // Add copybuttons to all of our code cells + const COPYBUTTON_SELECTOR = 'div.highlight pre'; + const codeCells = document.querySelectorAll(COPYBUTTON_SELECTOR) + codeCells.forEach((codeCell, index) => { + const id = codeCellId(index) + codeCell.setAttribute('id', id) + + const clipboardButton = id => + `` + codeCell.insertAdjacentHTML('afterend', clipboardButton(id)) + }) + +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} + + +var copyTargetText = (trigger) => { + var target = document.querySelector(trigger.attributes['data-clipboard-target'].value); + + // get filtered text + let exclude = '.linenos'; + + let text = filterText(target, exclude); + return formatCopyText(text, '', false, true, true, true, '', '') +} + + // Initialize with a callback so we can modify the text before copy + const clipboard = new ClipboardJS('.copybtn', {text: copyTargetText}) + + // Update UI with error/success messages + clipboard.on('success', event => { + clearSelection() + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_success']) + temporarilyChangeIcon(event.trigger) + }) + + clipboard.on('error', event => { + temporarilyChangeTooltip(event.trigger, messages[locale]['copy'], messages[locale]['copy_failure']) + }) +} + +runWhenDOMLoaded(addCopyButtonToCodeCells) \ No newline at end of file diff --git a/_static/copybutton_funcs.js b/_static/copybutton_funcs.js new file mode 100644 index 0000000..dbe1aaa --- /dev/null +++ b/_static/copybutton_funcs.js @@ -0,0 +1,73 @@ +function escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} + +/** + * Removes excluded text from a Node. + * + * @param {Node} target Node to filter. + * @param {string} exclude CSS selector of nodes to exclude. + * @returns {DOMString} Text from `target` with text removed. + */ +export function filterText(target, exclude) { + const clone = target.cloneNode(true); // clone as to not modify the live DOM + if (exclude) { + // remove excluded nodes + clone.querySelectorAll(exclude).forEach(node => node.remove()); + } + return clone.innerText; +} + +// Callback when a copy button is clicked. Will be passed the node that was clicked +// should then grab the text and replace pieces of text that shouldn't be used in output +export function formatCopyText(textContent, copybuttonPromptText, isRegexp = false, onlyCopyPromptLines = true, removePrompts = true, copyEmptyLines = true, lineContinuationChar = "", hereDocDelim = "") { + var regexp; + var match; + + // Do we check for line continuation characters and "HERE-documents"? + var useLineCont = !!lineContinuationChar + var useHereDoc = !!hereDocDelim + + // create regexp to capture prompt and remaining line + if (isRegexp) { + regexp = new RegExp('^(' + copybuttonPromptText + ')(.*)') + } else { + regexp = new RegExp('^(' + escapeRegExp(copybuttonPromptText) + ')(.*)') + } + + const outputLines = []; + var promptFound = false; + var gotLineCont = false; + var gotHereDoc = false; + const lineGotPrompt = []; + for (const line of textContent.split('\n')) { + match = line.match(regexp) + if (match || gotLineCont || gotHereDoc) { + promptFound = regexp.test(line) + lineGotPrompt.push(promptFound) + if (removePrompts && promptFound) { + outputLines.push(match[2]) + } else { + outputLines.push(line) + } + gotLineCont = line.endsWith(lineContinuationChar) & useLineCont + if (line.includes(hereDocDelim) & useHereDoc) + gotHereDoc = !gotHereDoc + } else if (!onlyCopyPromptLines) { + outputLines.push(line) + } else if (copyEmptyLines && line.trim() === '') { + outputLines.push(line) + } + } + + // If no lines with the prompt were found then just use original lines + if (lineGotPrompt.some(v => v === true)) { + textContent = outputLines.join('\n'); + } + + // Remove a trailing newline to avoid auto-running when pasting + if (textContent.endsWith("\n")) { + textContent = textContent.slice(0, -1) + } + return textContent +} diff --git a/_static/debug.css b/_static/debug.css new file mode 100644 index 0000000..74d4aec --- /dev/null +++ b/_static/debug.css @@ -0,0 +1,69 @@ +/* + This CSS file should be overridden by the theme authors. It's + meant for debugging and developing the skeleton that this theme provides. +*/ +body { + font-family: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji"; + background: lavender; +} +.sb-announcement { + background: rgb(131, 131, 131); +} +.sb-announcement__inner { + background: black; + color: white; +} +.sb-header { + background: lightskyblue; +} +.sb-header__inner { + background: royalblue; + color: white; +} +.sb-header-secondary { + background: lightcyan; +} +.sb-header-secondary__inner { + background: cornflowerblue; + color: white; +} +.sb-sidebar-primary { + background: lightgreen; +} +.sb-main { + background: blanchedalmond; +} +.sb-main__inner { + background: antiquewhite; +} +.sb-header-article { + background: lightsteelblue; +} +.sb-article-container { + background: snow; +} +.sb-article-main { + background: white; +} +.sb-footer-article { + background: lightpink; +} +.sb-sidebar-secondary { + background: lightgoldenrodyellow; +} +.sb-footer-content { + background: plum; +} +.sb-footer-content__inner { + background: palevioletred; +} +.sb-footer { + background: pink; +} +.sb-footer__inner { + background: salmon; +} +.sb-article { + background: white; +} diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000..d06a71d --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 0000000..7e4c114 --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000000000000000000000000000000000000..a858a410e4faa62ce324d814e4b816fff83a6fb3 GIT binary patch literal 286 zcmV+(0pb3MP)s`hMrGg#P~ix$^RISR_I47Y|r1 z_CyJOe}D1){SET-^Amu_i71Lt6eYfZjRyw@I6OQAIXXHDfiX^GbOlHe=Ae4>0m)d(f|Me07*qoM6N<$f}vM^LjV8( literal 0 HcmV?d00001 diff --git a/_static/language_data.js b/_static/language_data.js new file mode 100644 index 0000000..250f566 --- /dev/null +++ b/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, is available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/_static/minus.png b/_static/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..d96755fdaf8bb2214971e0db9c1fd3077d7c419d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu=nj kDsEF_5m^0CR;1wuP-*O&G^0G}KYk!hp00i_>zopr08q^qX#fBK literal 0 HcmV?d00001 diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..7107cec93a979b9a5f64843235a16651d563ce2d GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^+#t*WBp7;*Yy1LIik>cxAr*|t7R?Mi>2?kWtu>-2 m3q%Vub%g%s<8sJhVPMczOq}xhg9DJoz~JfX=d#Wzp$Pyb1r*Kz literal 0 HcmV?d00001 diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 0000000..c2e07c7 --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,258 @@ +.highlight pre { line-height: 125%; } +.highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +.highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #8f5902; font-style: italic } /* Comment */ +.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ +.highlight .g { color: #000000 } /* Generic */ +.highlight .k { color: #204a87; font-weight: bold } /* Keyword */ +.highlight .l { color: #000000 } /* Literal */ +.highlight .n { color: #000000 } /* Name */ +.highlight .o { color: #ce5c00; font-weight: bold } /* Operator */ +.highlight .x { color: #000000 } /* Other */ +.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ +.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #8f5902; font-style: italic } /* Comment.Preproc */ +.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #a40000 } /* Generic.Deleted */ +.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ +.highlight .ges { color: #000000; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +.highlight .gr { color: #ef2929 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #000000; font-style: italic } /* Generic.Output */ +.highlight .gp { color: #8f5902 } /* Generic.Prompt */ +.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ +.highlight .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #204a87; font-weight: bold } /* Keyword.Type */ +.highlight .ld { color: #000000 } /* Literal.Date */ +.highlight .m { color: #0000cf; font-weight: bold } /* Literal.Number */ +.highlight .s { color: #4e9a06 } /* Literal.String */ +.highlight .na { color: #c4a000 } /* Name.Attribute */ +.highlight .nb { color: #204a87 } /* Name.Builtin */ +.highlight .nc { color: #000000 } /* Name.Class */ +.highlight .no { color: #000000 } /* Name.Constant */ +.highlight .nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */ +.highlight .ni { color: #ce5c00 } /* Name.Entity */ +.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #000000 } /* Name.Function */ +.highlight .nl { color: #f57900 } /* Name.Label */ +.highlight .nn { color: #000000 } /* Name.Namespace */ +.highlight .nx { color: #000000 } /* Name.Other */ +.highlight .py { color: #000000 } /* Name.Property */ +.highlight .nt { color: #204a87; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #000000 } /* Name.Variable */ +.highlight .ow { color: #204a87; font-weight: bold } /* Operator.Word */ +.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ +.highlight .w { color: #f8f8f8 } /* Text.Whitespace */ +.highlight .mb { color: #0000cf; font-weight: bold } /* Literal.Number.Bin */ +.highlight .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */ +.highlight .mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */ +.highlight .mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */ +.highlight .mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */ +.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ +.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ +.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ +.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ +.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ +.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ +.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ +.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ +.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ +.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ +.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ +.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ +.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #000000 } /* Name.Function.Magic */ +.highlight .vc { color: #000000 } /* Name.Variable.Class */ +.highlight .vg { color: #000000 } /* Name.Variable.Global */ +.highlight .vi { color: #000000 } /* Name.Variable.Instance */ +.highlight .vm { color: #000000 } /* Name.Variable.Magic */ +.highlight .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */ +@media not print { +body[data-theme="dark"] .highlight pre { line-height: 125%; } +body[data-theme="dark"] .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body[data-theme="dark"] .highlight .hll { background-color: #404040 } +body[data-theme="dark"] .highlight { background: #202020; color: #d0d0d0 } +body[data-theme="dark"] .highlight .c { color: #ababab; font-style: italic } /* Comment */ +body[data-theme="dark"] .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +body[data-theme="dark"] .highlight .esc { color: #d0d0d0 } /* Escape */ +body[data-theme="dark"] .highlight .g { color: #d0d0d0 } /* Generic */ +body[data-theme="dark"] .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ +body[data-theme="dark"] .highlight .l { color: #d0d0d0 } /* Literal */ +body[data-theme="dark"] .highlight .n { color: #d0d0d0 } /* Name */ +body[data-theme="dark"] .highlight .o { color: #d0d0d0 } /* Operator */ +body[data-theme="dark"] .highlight .x { color: #d0d0d0 } /* Other */ +body[data-theme="dark"] .highlight .p { color: #d0d0d0 } /* Punctuation */ +body[data-theme="dark"] .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ +body[data-theme="dark"] .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ +body[data-theme="dark"] .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ +body[data-theme="dark"] .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ +body[data-theme="dark"] .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ +body[data-theme="dark"] .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body[data-theme="dark"] .highlight .gd { color: #d22323 } /* Generic.Deleted */ +body[data-theme="dark"] .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ +body[data-theme="dark"] .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body[data-theme="dark"] .highlight .gr { color: #d22323 } /* Generic.Error */ +body[data-theme="dark"] .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ +body[data-theme="dark"] .highlight .gi { color: #589819 } /* Generic.Inserted */ +body[data-theme="dark"] .highlight .go { color: #cccccc } /* Generic.Output */ +body[data-theme="dark"] .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ +body[data-theme="dark"] .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ +body[data-theme="dark"] .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ +body[data-theme="dark"] .highlight .gt { color: #d22323 } /* Generic.Traceback */ +body[data-theme="dark"] .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ +body[data-theme="dark"] .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ +body[data-theme="dark"] .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ +body[data-theme="dark"] .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ +body[data-theme="dark"] .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ +body[data-theme="dark"] .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ +body[data-theme="dark"] .highlight .ld { color: #d0d0d0 } /* Literal.Date */ +body[data-theme="dark"] .highlight .m { color: #51b2fd } /* Literal.Number */ +body[data-theme="dark"] .highlight .s { color: #ed9d13 } /* Literal.String */ +body[data-theme="dark"] .highlight .na { color: #bbbbbb } /* Name.Attribute */ +body[data-theme="dark"] .highlight .nb { color: #2fbccd } /* Name.Builtin */ +body[data-theme="dark"] .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ +body[data-theme="dark"] .highlight .no { color: #40ffff } /* Name.Constant */ +body[data-theme="dark"] .highlight .nd { color: #ffa500 } /* Name.Decorator */ +body[data-theme="dark"] .highlight .ni { color: #d0d0d0 } /* Name.Entity */ +body[data-theme="dark"] .highlight .ne { color: #bbbbbb } /* Name.Exception */ +body[data-theme="dark"] .highlight .nf { color: #71adff } /* Name.Function */ +body[data-theme="dark"] .highlight .nl { color: #d0d0d0 } /* Name.Label */ +body[data-theme="dark"] .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ +body[data-theme="dark"] .highlight .nx { color: #d0d0d0 } /* Name.Other */ +body[data-theme="dark"] .highlight .py { color: #d0d0d0 } /* Name.Property */ +body[data-theme="dark"] .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ +body[data-theme="dark"] .highlight .nv { color: #40ffff } /* Name.Variable */ +body[data-theme="dark"] .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ +body[data-theme="dark"] .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ +body[data-theme="dark"] .highlight .w { color: #666666 } /* Text.Whitespace */ +body[data-theme="dark"] .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ +body[data-theme="dark"] .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ +body[data-theme="dark"] .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ +body[data-theme="dark"] .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ +body[data-theme="dark"] .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ +body[data-theme="dark"] .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ +body[data-theme="dark"] .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ +body[data-theme="dark"] .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ +body[data-theme="dark"] .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ +body[data-theme="dark"] .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ +body[data-theme="dark"] .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ +body[data-theme="dark"] .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ +body[data-theme="dark"] .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ +body[data-theme="dark"] .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ +body[data-theme="dark"] .highlight .sx { color: #ffa500 } /* Literal.String.Other */ +body[data-theme="dark"] .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ +body[data-theme="dark"] .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ +body[data-theme="dark"] .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ +body[data-theme="dark"] .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ +body[data-theme="dark"] .highlight .fm { color: #71adff } /* Name.Function.Magic */ +body[data-theme="dark"] .highlight .vc { color: #40ffff } /* Name.Variable.Class */ +body[data-theme="dark"] .highlight .vg { color: #40ffff } /* Name.Variable.Global */ +body[data-theme="dark"] .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ +body[data-theme="dark"] .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ +body[data-theme="dark"] .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ +@media (prefers-color-scheme: dark) { +body:not([data-theme="light"]) .highlight pre { line-height: 125%; } +body:not([data-theme="light"]) .highlight td.linenos .normal { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos { color: #aaaaaa; background-color: transparent; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +body:not([data-theme="light"]) .highlight .hll { background-color: #404040 } +body:not([data-theme="light"]) .highlight { background: #202020; color: #d0d0d0 } +body:not([data-theme="light"]) .highlight .c { color: #ababab; font-style: italic } /* Comment */ +body:not([data-theme="light"]) .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +body:not([data-theme="light"]) .highlight .esc { color: #d0d0d0 } /* Escape */ +body:not([data-theme="light"]) .highlight .g { color: #d0d0d0 } /* Generic */ +body:not([data-theme="light"]) .highlight .k { color: #6ebf26; font-weight: bold } /* Keyword */ +body:not([data-theme="light"]) .highlight .l { color: #d0d0d0 } /* Literal */ +body:not([data-theme="light"]) .highlight .n { color: #d0d0d0 } /* Name */ +body:not([data-theme="light"]) .highlight .o { color: #d0d0d0 } /* Operator */ +body:not([data-theme="light"]) .highlight .x { color: #d0d0d0 } /* Other */ +body:not([data-theme="light"]) .highlight .p { color: #d0d0d0 } /* Punctuation */ +body:not([data-theme="light"]) .highlight .ch { color: #ababab; font-style: italic } /* Comment.Hashbang */ +body:not([data-theme="light"]) .highlight .cm { color: #ababab; font-style: italic } /* Comment.Multiline */ +body:not([data-theme="light"]) .highlight .cp { color: #ff3a3a; font-weight: bold } /* Comment.Preproc */ +body:not([data-theme="light"]) .highlight .cpf { color: #ababab; font-style: italic } /* Comment.PreprocFile */ +body:not([data-theme="light"]) .highlight .c1 { color: #ababab; font-style: italic } /* Comment.Single */ +body:not([data-theme="light"]) .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ +body:not([data-theme="light"]) .highlight .gd { color: #d22323 } /* Generic.Deleted */ +body:not([data-theme="light"]) .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ +body:not([data-theme="light"]) .highlight .ges { color: #d0d0d0; font-weight: bold; font-style: italic } /* Generic.EmphStrong */ +body:not([data-theme="light"]) .highlight .gr { color: #d22323 } /* Generic.Error */ +body:not([data-theme="light"]) .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ +body:not([data-theme="light"]) .highlight .gi { color: #589819 } /* Generic.Inserted */ +body:not([data-theme="light"]) .highlight .go { color: #cccccc } /* Generic.Output */ +body:not([data-theme="light"]) .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ +body:not([data-theme="light"]) .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ +body:not([data-theme="light"]) .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ +body:not([data-theme="light"]) .highlight .gt { color: #d22323 } /* Generic.Traceback */ +body:not([data-theme="light"]) .highlight .kc { color: #6ebf26; font-weight: bold } /* Keyword.Constant */ +body:not([data-theme="light"]) .highlight .kd { color: #6ebf26; font-weight: bold } /* Keyword.Declaration */ +body:not([data-theme="light"]) .highlight .kn { color: #6ebf26; font-weight: bold } /* Keyword.Namespace */ +body:not([data-theme="light"]) .highlight .kp { color: #6ebf26 } /* Keyword.Pseudo */ +body:not([data-theme="light"]) .highlight .kr { color: #6ebf26; font-weight: bold } /* Keyword.Reserved */ +body:not([data-theme="light"]) .highlight .kt { color: #6ebf26; font-weight: bold } /* Keyword.Type */ +body:not([data-theme="light"]) .highlight .ld { color: #d0d0d0 } /* Literal.Date */ +body:not([data-theme="light"]) .highlight .m { color: #51b2fd } /* Literal.Number */ +body:not([data-theme="light"]) .highlight .s { color: #ed9d13 } /* Literal.String */ +body:not([data-theme="light"]) .highlight .na { color: #bbbbbb } /* Name.Attribute */ +body:not([data-theme="light"]) .highlight .nb { color: #2fbccd } /* Name.Builtin */ +body:not([data-theme="light"]) .highlight .nc { color: #71adff; text-decoration: underline } /* Name.Class */ +body:not([data-theme="light"]) .highlight .no { color: #40ffff } /* Name.Constant */ +body:not([data-theme="light"]) .highlight .nd { color: #ffa500 } /* Name.Decorator */ +body:not([data-theme="light"]) .highlight .ni { color: #d0d0d0 } /* Name.Entity */ +body:not([data-theme="light"]) .highlight .ne { color: #bbbbbb } /* Name.Exception */ +body:not([data-theme="light"]) .highlight .nf { color: #71adff } /* Name.Function */ +body:not([data-theme="light"]) .highlight .nl { color: #d0d0d0 } /* Name.Label */ +body:not([data-theme="light"]) .highlight .nn { color: #71adff; text-decoration: underline } /* Name.Namespace */ +body:not([data-theme="light"]) .highlight .nx { color: #d0d0d0 } /* Name.Other */ +body:not([data-theme="light"]) .highlight .py { color: #d0d0d0 } /* Name.Property */ +body:not([data-theme="light"]) .highlight .nt { color: #6ebf26; font-weight: bold } /* Name.Tag */ +body:not([data-theme="light"]) .highlight .nv { color: #40ffff } /* Name.Variable */ +body:not([data-theme="light"]) .highlight .ow { color: #6ebf26; font-weight: bold } /* Operator.Word */ +body:not([data-theme="light"]) .highlight .pm { color: #d0d0d0 } /* Punctuation.Marker */ +body:not([data-theme="light"]) .highlight .w { color: #666666 } /* Text.Whitespace */ +body:not([data-theme="light"]) .highlight .mb { color: #51b2fd } /* Literal.Number.Bin */ +body:not([data-theme="light"]) .highlight .mf { color: #51b2fd } /* Literal.Number.Float */ +body:not([data-theme="light"]) .highlight .mh { color: #51b2fd } /* Literal.Number.Hex */ +body:not([data-theme="light"]) .highlight .mi { color: #51b2fd } /* Literal.Number.Integer */ +body:not([data-theme="light"]) .highlight .mo { color: #51b2fd } /* Literal.Number.Oct */ +body:not([data-theme="light"]) .highlight .sa { color: #ed9d13 } /* Literal.String.Affix */ +body:not([data-theme="light"]) .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ +body:not([data-theme="light"]) .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ +body:not([data-theme="light"]) .highlight .dl { color: #ed9d13 } /* Literal.String.Delimiter */ +body:not([data-theme="light"]) .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ +body:not([data-theme="light"]) .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ +body:not([data-theme="light"]) .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ +body:not([data-theme="light"]) .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ +body:not([data-theme="light"]) .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ +body:not([data-theme="light"]) .highlight .sx { color: #ffa500 } /* Literal.String.Other */ +body:not([data-theme="light"]) .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ +body:not([data-theme="light"]) .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ +body:not([data-theme="light"]) .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ +body:not([data-theme="light"]) .highlight .bp { color: #2fbccd } /* Name.Builtin.Pseudo */ +body:not([data-theme="light"]) .highlight .fm { color: #71adff } /* Name.Function.Magic */ +body:not([data-theme="light"]) .highlight .vc { color: #40ffff } /* Name.Variable.Class */ +body:not([data-theme="light"]) .highlight .vg { color: #40ffff } /* Name.Variable.Global */ +body:not([data-theme="light"]) .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ +body:not([data-theme="light"]) .highlight .vm { color: #40ffff } /* Name.Variable.Magic */ +body:not([data-theme="light"]) .highlight .il { color: #51b2fd } /* Literal.Number.Integer.Long */ +} +} \ No newline at end of file diff --git a/_static/scripts/furo-extensions.js b/_static/scripts/furo-extensions.js new file mode 100644 index 0000000..e69de29 diff --git a/_static/scripts/furo.js b/_static/scripts/furo.js new file mode 100644 index 0000000..32e7c05 --- /dev/null +++ b/_static/scripts/furo.js @@ -0,0 +1,3 @@ +/*! For license information please see furo.js.LICENSE.txt */ +(()=>{var t={212:function(t,e,n){var o,r;r=void 0!==n.g?n.g:"undefined"!=typeof window?window:this,o=function(){return function(t){"use strict";var e={navClass:"active",contentClass:"active",nested:!1,nestedClass:"active",offset:0,reflow:!1,events:!0},n=function(t,e,n){if(n.settings.events){var o=new CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n});e.dispatchEvent(o)}},o=function(t){var e=0;if(t.offsetParent)for(;t;)e+=t.offsetTop,t=t.offsetParent;return e>=0?e:0},r=function(t){t&&t.sort((function(t,e){return o(t.content)=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},l=function(t,e){var n=t[t.length-1];if(function(t,e){return!(!s()||!c(t.content,e,!0))}(n,e))return n;for(var o=t.length-1;o>=0;o--)if(c(t[o].content,e))return t[o]},a=function(t,e){if(e.nested&&t.parentNode){var n=t.parentNode.closest("li");n&&(n.classList.remove(e.nestedClass),a(n,e))}},i=function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.remove(e.navClass),t.content.classList.remove(e.contentClass),a(o,e),n("gumshoeDeactivate",o,{link:t.nav,content:t.content,settings:e}))}},u=function(t,e){if(e.nested){var n=t.parentNode.closest("li");n&&(n.classList.add(e.nestedClass),u(n,e))}};return function(o,c){var s,a,d,f,m,v={setup:function(){s=document.querySelectorAll(o),a=[],Array.prototype.forEach.call(s,(function(t){var e=document.getElementById(decodeURIComponent(t.hash.substr(1)));e&&a.push({nav:t,content:e})})),r(a)},detect:function(){var t=l(a,m);t?d&&t.content===d.content||(i(d,m),function(t,e){if(t){var o=t.nav.closest("li");o&&(o.classList.add(e.navClass),t.content.classList.add(e.contentClass),u(o,e),n("gumshoeActivate",o,{link:t.nav,content:t.content,settings:e}))}}(t,m),d=t):d&&(i(d,m),d=null)}},h=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame(v.detect)},g=function(e){f&&t.cancelAnimationFrame(f),f=t.requestAnimationFrame((function(){r(a),v.detect()}))};return v.destroy=function(){d&&i(d,m),t.removeEventListener("scroll",h,!1),m.reflow&&t.removeEventListener("resize",g,!1),a=null,s=null,d=null,f=null,m=null},m=function(){var t={};return Array.prototype.forEach.call(arguments,(function(e){for(var n in e){if(!e.hasOwnProperty(n))return;t[n]=e[n]}})),t}(e,c||{}),v.setup(),v.detect(),t.addEventListener("scroll",h,!1),m.reflow&&t.addEventListener("resize",g,!1),v}}(r)}.apply(e,[]),void 0===o||(t.exports=o)}},e={};function n(o){var r=e[o];if(void 0!==r)return r.exports;var c=e[o]={exports:{}};return t[o].call(c.exports,c,c.exports,n),c.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var o in e)n.o(e,o)&&!n.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{"use strict";var t=n(212),e=n.n(t),o=null,r=null,c=window.pageYOffset||document.documentElement.scrollTop;const s=64;function l(){const t=localStorage.getItem("theme")||"auto";var e;"light"!==(e=window.matchMedia("(prefers-color-scheme: dark)").matches?"auto"===t?"light":"light"==t?"dark":"auto":"auto"===t?"dark":"dark"==t?"light":"auto")&&"dark"!==e&&"auto"!==e&&(console.error(`Got invalid theme mode: ${e}. Resetting to auto.`),e="auto"),document.body.dataset.theme=e,localStorage.setItem("theme",e),console.log(`Changed to ${e} mode.`)}function a(){!function(){const t=document.getElementsByClassName("theme-toggle");Array.from(t).forEach((t=>{t.addEventListener("click",l)}))}(),function(){let t=0,e=!1;window.addEventListener("scroll",(function(n){t=window.scrollY,e||(window.requestAnimationFrame((function(){var n;n=t,0==Math.floor(r.getBoundingClientRect().top)?r.classList.add("scrolled"):r.classList.remove("scrolled"),function(t){tc&&document.documentElement.classList.remove("show-back-to-top"),c=t}(n),function(t){null!==o&&(0==t?o.scrollTo(0,0):Math.ceil(t)>=Math.floor(document.documentElement.scrollHeight-window.innerHeight)?o.scrollTo(0,o.scrollHeight):document.querySelector(".scroll-current"))}(n),e=!1})),e=!0)})),window.scroll()}(),null!==o&&new(e())(".toc-tree a",{reflow:!0,recursive:!0,navClass:"scroll-current",offset:()=>{let t=parseFloat(getComputedStyle(document.documentElement).fontSize);return r.getBoundingClientRect().height+.5*t+1}})}document.addEventListener("DOMContentLoaded",(function(){document.body.parentNode.classList.remove("no-js"),r=document.querySelector("header"),o=document.querySelector(".toc-scroll"),a()}))})()})(); +//# sourceMappingURL=furo.js.map \ No newline at end of file diff --git a/_static/scripts/furo.js.LICENSE.txt b/_static/scripts/furo.js.LICENSE.txt new file mode 100644 index 0000000..1632189 --- /dev/null +++ b/_static/scripts/furo.js.LICENSE.txt @@ -0,0 +1,7 @@ +/*! + * gumshoejs v5.1.2 (patched by @pradyunsg) + * A simple, framework-agnostic scrollspy script. + * (c) 2019 Chris Ferdinandi + * MIT License + * http://github.com/cferdinandi/gumshoe + */ diff --git a/_static/scripts/furo.js.map b/_static/scripts/furo.js.map new file mode 100644 index 0000000..7b7ddb1 --- /dev/null +++ b/_static/scripts/furo.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/furo.js","mappings":";iCAAA,MAQWA,SAWS,IAAX,EAAAC,EACH,EAAAA,EACkB,oBAAXC,OACPA,OACAC,KAbS,EAAF,WACP,OAaJ,SAAUD,GACR,aAMA,IAAIE,EAAW,CAEbC,SAAU,SACVC,aAAc,SAGdC,QAAQ,EACRC,YAAa,SAGbC,OAAQ,EACRC,QAAQ,EAGRC,QAAQ,GA6BNC,EAAY,SAAUC,EAAMC,EAAMC,GAEpC,GAAKA,EAAOC,SAASL,OAArB,CAGA,IAAIM,EAAQ,IAAIC,YAAYL,EAAM,CAChCM,SAAS,EACTC,YAAY,EACZL,OAAQA,IAIVD,EAAKO,cAAcJ,EAVgB,CAWrC,EAOIK,EAAe,SAAUR,GAC3B,IAAIS,EAAW,EACf,GAAIT,EAAKU,aACP,KAAOV,GACLS,GAAYT,EAAKW,UACjBX,EAAOA,EAAKU,aAGhB,OAAOD,GAAY,EAAIA,EAAW,CACpC,EAMIG,EAAe,SAAUC,GACvBA,GACFA,EAASC,MAAK,SAAUC,EAAOC,GAG7B,OAFcR,EAAaO,EAAME,SACnBT,EAAaQ,EAAMC,UACF,EACxB,CACT,GAEJ,EAwCIC,EAAW,SAAUlB,EAAME,EAAUiB,GACvC,IAAIC,EAASpB,EAAKqB,wBACd1B,EAnCU,SAAUO,GAExB,MAA+B,mBAApBA,EAASP,OACX2B,WAAWpB,EAASP,UAItB2B,WAAWpB,EAASP,OAC7B,CA2Be4B,CAAUrB,GACvB,OAAIiB,EAEAK,SAASJ,EAAOD,OAAQ,KACvB/B,EAAOqC,aAAeC,SAASC,gBAAgBC,cAG7CJ,SAASJ,EAAOS,IAAK,KAAOlC,CACrC,EAMImC,EAAa,WACf,OACEC,KAAKC,KAAK5C,EAAOqC,YAAcrC,EAAO6C,cAnCjCF,KAAKG,IACVR,SAASS,KAAKC,aACdV,SAASC,gBAAgBS,aACzBV,SAASS,KAAKE,aACdX,SAASC,gBAAgBU,aACzBX,SAASS,KAAKP,aACdF,SAASC,gBAAgBC,aAkC7B,EAmBIU,EAAY,SAAUzB,EAAUX,GAClC,IAAIqC,EAAO1B,EAASA,EAAS2B,OAAS,GACtC,GAbgB,SAAUC,EAAMvC,GAChC,SAAI4B,MAAgBZ,EAASuB,EAAKxB,QAASf,GAAU,GAEvD,CAUMwC,CAAYH,EAAMrC,GAAW,OAAOqC,EACxC,IAAK,IAAII,EAAI9B,EAAS2B,OAAS,EAAGG,GAAK,EAAGA,IACxC,GAAIzB,EAASL,EAAS8B,GAAG1B,QAASf,GAAW,OAAOW,EAAS8B,EAEjE,EAOIC,EAAmB,SAAUC,EAAK3C,GAEpC,GAAKA,EAAST,QAAWoD,EAAIC,WAA7B,CAGA,IAAIC,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASR,aAG7BkD,EAAiBG,EAAI7C,GAV0B,CAWjD,EAOIiD,EAAa,SAAUC,EAAOlD,GAEhC,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUC,OAAOhD,EAASX,UAC7B6D,EAAMnC,QAAQgC,UAAUC,OAAOhD,EAASV,cAGxCoD,EAAiBG,EAAI7C,GAGrBJ,EAAU,oBAAqBiD,EAAI,CACjCM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,EAOIoD,EAAiB,SAAUT,EAAK3C,GAElC,GAAKA,EAAST,OAAd,CAGA,IAAIsD,EAAKF,EAAIC,WAAWE,QAAQ,MAC3BD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASR,aAG1B4D,EAAeP,EAAI7C,GAVS,CAW9B,EA6LA,OA1JkB,SAAUsD,EAAUC,GAKpC,IACIC,EAAU7C,EAAU8C,EAASC,EAAS1D,EADtC2D,EAAa,CAUjBA,MAAmB,WAEjBH,EAAWhC,SAASoC,iBAAiBN,GAGrC3C,EAAW,GAGXkD,MAAMC,UAAUC,QAAQC,KAAKR,GAAU,SAAUjB,GAE/C,IAAIxB,EAAUS,SAASyC,eACrBC,mBAAmB3B,EAAK4B,KAAKC,OAAO,KAEjCrD,GAGLJ,EAAS0D,KAAK,CACZ1B,IAAKJ,EACLxB,QAASA,GAEb,IAGAL,EAAaC,EACf,EAKAgD,OAAoB,WAElB,IAAIW,EAASlC,EAAUzB,EAAUX,GAG5BsE,EASDb,GAAWa,EAAOvD,UAAY0C,EAAQ1C,UAG1CkC,EAAWQ,EAASzD,GAzFT,SAAUkD,EAAOlD,GAE9B,GAAKkD,EAAL,CAGA,IAAIL,EAAKK,EAAMP,IAAIG,QAAQ,MACtBD,IAGLA,EAAGE,UAAUM,IAAIrD,EAASX,UAC1B6D,EAAMnC,QAAQgC,UAAUM,IAAIrD,EAASV,cAGrC8D,EAAeP,EAAI7C,GAGnBJ,EAAU,kBAAmBiD,EAAI,CAC/BM,KAAMD,EAAMP,IACZ5B,QAASmC,EAAMnC,QACff,SAAUA,IAjBM,CAmBpB,CAqEIuE,CAASD,EAAQtE,GAGjByD,EAAUa,GAfJb,IACFR,EAAWQ,EAASzD,GACpByD,EAAU,KAchB,GAMIe,EAAgB,SAAUvE,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,sBAAsBf,EAAWgB,OACpD,EAMIC,EAAgB,SAAU3E,GAExByD,GACFxE,EAAOuF,qBAAqBf,GAI9BA,EAAUxE,EAAOwF,uBAAsB,WACrChE,EAAaC,GACbgD,EAAWgB,QACb,GACF,EAkDA,OA7CAhB,EAAWkB,QAAU,WAEfpB,GACFR,EAAWQ,EAASzD,GAItBd,EAAO4F,oBAAoB,SAAUN,GAAe,GAChDxE,EAASN,QACXR,EAAO4F,oBAAoB,SAAUF,GAAe,GAItDjE,EAAW,KACX6C,EAAW,KACXC,EAAU,KACVC,EAAU,KACV1D,EAAW,IACb,EAOEA,EA3XS,WACX,IAAI+E,EAAS,CAAC,EAOd,OANAlB,MAAMC,UAAUC,QAAQC,KAAKgB,WAAW,SAAUC,GAChD,IAAK,IAAIC,KAAOD,EAAK,CACnB,IAAKA,EAAIE,eAAeD,GAAM,OAC9BH,EAAOG,GAAOD,EAAIC,EACpB,CACF,IACOH,CACT,CAkXeK,CAAOhG,EAAUmE,GAAW,CAAC,GAGxCI,EAAW0B,QAGX1B,EAAWgB,SAGXzF,EAAOoG,iBAAiB,SAAUd,GAAe,GAC7CxE,EAASN,QACXR,EAAOoG,iBAAiB,SAAUV,GAAe,GAS9CjB,CACT,CAOF,CArcW4B,CAAQvG,EAChB,UAFM,SAEN,uBCXDwG,EAA2B,CAAC,EAGhC,SAASC,EAAoBC,GAE5B,IAAIC,EAAeH,EAAyBE,GAC5C,QAAqBE,IAAjBD,EACH,OAAOA,EAAaE,QAGrB,IAAIC,EAASN,EAAyBE,GAAY,CAGjDG,QAAS,CAAC,GAOX,OAHAE,EAAoBL,GAAU1B,KAAK8B,EAAOD,QAASC,EAAQA,EAAOD,QAASJ,GAGpEK,EAAOD,OACf,CCrBAJ,EAAoBO,EAAKF,IACxB,IAAIG,EAASH,GAAUA,EAAOI,WAC7B,IAAOJ,EAAiB,QACxB,IAAM,EAEP,OADAL,EAAoBU,EAAEF,EAAQ,CAAEG,EAAGH,IAC5BA,CAAM,ECLdR,EAAoBU,EAAI,CAACN,EAASQ,KACjC,IAAI,IAAInB,KAAOmB,EACXZ,EAAoBa,EAAED,EAAYnB,KAASO,EAAoBa,EAAET,EAASX,IAC5EqB,OAAOC,eAAeX,EAASX,EAAK,CAAEuB,YAAY,EAAMC,IAAKL,EAAWnB,IAE1E,ECNDO,EAAoBxG,EAAI,WACvB,GAA0B,iBAAf0H,WAAyB,OAAOA,WAC3C,IACC,OAAOxH,MAAQ,IAAIyH,SAAS,cAAb,EAChB,CAAE,MAAOC,GACR,GAAsB,iBAAX3H,OAAqB,OAAOA,MACxC,CACA,CAPuB,GCAxBuG,EAAoBa,EAAI,CAACrB,EAAK6B,IAAUP,OAAOzC,UAAUqB,eAAenB,KAAKiB,EAAK6B,4CCK9EC,EAAY,KACZC,EAAS,KACTC,EAAgB/H,OAAO6C,aAAeP,SAASC,gBAAgByF,UACnE,MAAMC,EAAmB,GA2EzB,SAASC,IACP,MAAMC,EAAeC,aAAaC,QAAQ,UAAY,OAZxD,IAAkBC,EACH,WADGA,EAaItI,OAAOuI,WAAW,gCAAgCC,QAI/C,SAAjBL,EACO,QACgB,SAAhBA,EACA,OAEA,OAIU,SAAjBA,EACO,OACgB,QAAhBA,EACA,QAEA,SA9BoB,SAATG,GAA4B,SAATA,IACzCG,QAAQC,MAAM,2BAA2BJ,yBACzCA,EAAO,QAGThG,SAASS,KAAK4F,QAAQC,MAAQN,EAC9BF,aAAaS,QAAQ,QAASP,GAC9BG,QAAQK,IAAI,cAAcR,UA0B5B,CAkDA,SAASnC,KART,WAEE,MAAM4C,EAAUzG,SAAS0G,uBAAuB,gBAChDrE,MAAMsE,KAAKF,GAASlE,SAASqE,IAC3BA,EAAI9C,iBAAiB,QAAS8B,EAAe,GAEjD,CAGEiB,GA9CF,WAEE,IAAIC,EAA6B,EAC7BC,GAAU,EAEdrJ,OAAOoG,iBAAiB,UAAU,SAAUuB,GAC1CyB,EAA6BpJ,OAAOsJ,QAE/BD,IACHrJ,OAAOwF,uBAAsB,WAzDnC,IAAuB+D,IA0DDH,EA9GkC,GAAlDzG,KAAK6G,MAAM1B,EAAO7F,wBAAwBQ,KAC5CqF,EAAOjE,UAAUM,IAAI,YAErB2D,EAAOjE,UAAUC,OAAO,YAI5B,SAAmCyF,GAC7BA,EAAYtB,EACd3F,SAASC,gBAAgBsB,UAAUC,OAAO,oBAEtCyF,EAAYxB,EACdzF,SAASC,gBAAgBsB,UAAUM,IAAI,oBAC9BoF,EAAYxB,GACrBzF,SAASC,gBAAgBsB,UAAUC,OAAO,oBAG9CiE,EAAgBwB,CAClB,CAoCEE,CAA0BF,GAlC5B,SAA6BA,GACT,OAAd1B,IAKa,GAAb0B,EACF1B,EAAU6B,SAAS,EAAG,GAGtB/G,KAAKC,KAAK2G,IACV5G,KAAK6G,MAAMlH,SAASC,gBAAgBS,aAAehD,OAAOqC,aAE1DwF,EAAU6B,SAAS,EAAG7B,EAAU7E,cAGhBV,SAASqH,cAAc,mBAc3C,CAKEC,CAAoBL,GAwDdF,GAAU,CACZ,IAEAA,GAAU,EAEd,IACArJ,OAAO6J,QACT,CA6BEC,GA1BkB,OAAdjC,GAKJ,IAAI,IAAJ,CAAY,cAAe,CACzBrH,QAAQ,EACRuJ,WAAW,EACX5J,SAAU,iBACVI,OAAQ,KACN,IAAIyJ,EAAM9H,WAAW+H,iBAAiB3H,SAASC,iBAAiB2H,UAChE,OAAOpC,EAAO7F,wBAAwBkI,OAAS,GAAMH,EAAM,CAAC,GAiBlE,CAcA1H,SAAS8D,iBAAiB,oBAT1B,WACE9D,SAASS,KAAKW,WAAWG,UAAUC,OAAO,SAE1CgE,EAASxF,SAASqH,cAAc,UAChC9B,EAAYvF,SAASqH,cAAc,eAEnCxD,GACF","sources":["webpack:///./src/furo/assets/scripts/gumshoe-patched.js","webpack:///webpack/bootstrap","webpack:///webpack/runtime/compat get default export","webpack:///webpack/runtime/define property getters","webpack:///webpack/runtime/global","webpack:///webpack/runtime/hasOwnProperty shorthand","webpack:///./src/furo/assets/scripts/furo.js"],"sourcesContent":["/*!\n * gumshoejs v5.1.2 (patched by @pradyunsg)\n * A simple, framework-agnostic scrollspy script.\n * (c) 2019 Chris Ferdinandi\n * MIT License\n * http://github.com/cferdinandi/gumshoe\n */\n\n(function (root, factory) {\n if (typeof define === \"function\" && define.amd) {\n define([], function () {\n return factory(root);\n });\n } else if (typeof exports === \"object\") {\n module.exports = factory(root);\n } else {\n root.Gumshoe = factory(root);\n }\n})(\n typeof global !== \"undefined\"\n ? global\n : typeof window !== \"undefined\"\n ? window\n : this,\n function (window) {\n \"use strict\";\n\n //\n // Defaults\n //\n\n var defaults = {\n // Active classes\n navClass: \"active\",\n contentClass: \"active\",\n\n // Nested navigation\n nested: false,\n nestedClass: \"active\",\n\n // Offset & reflow\n offset: 0,\n reflow: false,\n\n // Event support\n events: true,\n };\n\n //\n // Methods\n //\n\n /**\n * Merge two or more objects together.\n * @param {Object} objects The objects to merge together\n * @returns {Object} Merged values of defaults and options\n */\n var extend = function () {\n var merged = {};\n Array.prototype.forEach.call(arguments, function (obj) {\n for (var key in obj) {\n if (!obj.hasOwnProperty(key)) return;\n merged[key] = obj[key];\n }\n });\n return merged;\n };\n\n /**\n * Emit a custom event\n * @param {String} type The event type\n * @param {Node} elem The element to attach the event to\n * @param {Object} detail Any details to pass along with the event\n */\n var emitEvent = function (type, elem, detail) {\n // Make sure events are enabled\n if (!detail.settings.events) return;\n\n // Create a new event\n var event = new CustomEvent(type, {\n bubbles: true,\n cancelable: true,\n detail: detail,\n });\n\n // Dispatch the event\n elem.dispatchEvent(event);\n };\n\n /**\n * Get an element's distance from the top of the Document.\n * @param {Node} elem The element\n * @return {Number} Distance from the top in pixels\n */\n var getOffsetTop = function (elem) {\n var location = 0;\n if (elem.offsetParent) {\n while (elem) {\n location += elem.offsetTop;\n elem = elem.offsetParent;\n }\n }\n return location >= 0 ? location : 0;\n };\n\n /**\n * Sort content from first to last in the DOM\n * @param {Array} contents The content areas\n */\n var sortContents = function (contents) {\n if (contents) {\n contents.sort(function (item1, item2) {\n var offset1 = getOffsetTop(item1.content);\n var offset2 = getOffsetTop(item2.content);\n if (offset1 < offset2) return -1;\n return 1;\n });\n }\n };\n\n /**\n * Get the offset to use for calculating position\n * @param {Object} settings The settings for this instantiation\n * @return {Float} The number of pixels to offset the calculations\n */\n var getOffset = function (settings) {\n // if the offset is a function run it\n if (typeof settings.offset === \"function\") {\n return parseFloat(settings.offset());\n }\n\n // Otherwise, return it as-is\n return parseFloat(settings.offset);\n };\n\n /**\n * Get the document element's height\n * @private\n * @returns {Number}\n */\n var getDocumentHeight = function () {\n return Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight,\n document.body.offsetHeight,\n document.documentElement.offsetHeight,\n document.body.clientHeight,\n document.documentElement.clientHeight,\n );\n };\n\n /**\n * Determine if an element is in view\n * @param {Node} elem The element\n * @param {Object} settings The settings for this instantiation\n * @param {Boolean} bottom If true, check if element is above bottom of viewport instead\n * @return {Boolean} Returns true if element is in the viewport\n */\n var isInView = function (elem, settings, bottom) {\n var bounds = elem.getBoundingClientRect();\n var offset = getOffset(settings);\n if (bottom) {\n return (\n parseInt(bounds.bottom, 10) <\n (window.innerHeight || document.documentElement.clientHeight)\n );\n }\n return parseInt(bounds.top, 10) <= offset;\n };\n\n /**\n * Check if at the bottom of the viewport\n * @return {Boolean} If true, page is at the bottom of the viewport\n */\n var isAtBottom = function () {\n if (\n Math.ceil(window.innerHeight + window.pageYOffset) >=\n getDocumentHeight()\n )\n return true;\n return false;\n };\n\n /**\n * Check if the last item should be used (even if not at the top of the page)\n * @param {Object} item The last item\n * @param {Object} settings The settings for this instantiation\n * @return {Boolean} If true, use the last item\n */\n var useLastItem = function (item, settings) {\n if (isAtBottom() && isInView(item.content, settings, true)) return true;\n return false;\n };\n\n /**\n * Get the active content\n * @param {Array} contents The content areas\n * @param {Object} settings The settings for this instantiation\n * @return {Object} The content area and matching navigation link\n */\n var getActive = function (contents, settings) {\n var last = contents[contents.length - 1];\n if (useLastItem(last, settings)) return last;\n for (var i = contents.length - 1; i >= 0; i--) {\n if (isInView(contents[i].content, settings)) return contents[i];\n }\n };\n\n /**\n * Deactivate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var deactivateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested || !nav.parentNode) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Remove the active class\n li.classList.remove(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n deactivateNested(li, settings);\n };\n\n /**\n * Deactivate a nav and content area\n * @param {Object} items The nav item and content to deactivate\n * @param {Object} settings The settings for this instantiation\n */\n var deactivate = function (items, settings) {\n // Make sure there are items to deactivate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Remove the active class from the nav and content\n li.classList.remove(settings.navClass);\n items.content.classList.remove(settings.contentClass);\n\n // Deactivate any parent navs in a nested navigation\n deactivateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeDeactivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Activate parent navs in a nested navigation\n * @param {Node} nav The starting navigation element\n * @param {Object} settings The settings for this instantiation\n */\n var activateNested = function (nav, settings) {\n // If nesting isn't activated, bail\n if (!settings.nested) return;\n\n // Get the parent navigation\n var li = nav.parentNode.closest(\"li\");\n if (!li) return;\n\n // Add the active class\n li.classList.add(settings.nestedClass);\n\n // Apply recursively to any parent navigation elements\n activateNested(li, settings);\n };\n\n /**\n * Activate a nav and content area\n * @param {Object} items The nav item and content to activate\n * @param {Object} settings The settings for this instantiation\n */\n var activate = function (items, settings) {\n // Make sure there are items to activate\n if (!items) return;\n\n // Get the parent list item\n var li = items.nav.closest(\"li\");\n if (!li) return;\n\n // Add the active class to the nav and content\n li.classList.add(settings.navClass);\n items.content.classList.add(settings.contentClass);\n\n // Activate any parent navs in a nested navigation\n activateNested(li, settings);\n\n // Emit a custom event\n emitEvent(\"gumshoeActivate\", li, {\n link: items.nav,\n content: items.content,\n settings: settings,\n });\n };\n\n /**\n * Create the Constructor object\n * @param {String} selector The selector to use for navigation items\n * @param {Object} options User options and settings\n */\n var Constructor = function (selector, options) {\n //\n // Variables\n //\n\n var publicAPIs = {};\n var navItems, contents, current, timeout, settings;\n\n //\n // Methods\n //\n\n /**\n * Set variables from DOM elements\n */\n publicAPIs.setup = function () {\n // Get all nav items\n navItems = document.querySelectorAll(selector);\n\n // Create contents array\n contents = [];\n\n // Loop through each item, get it's matching content, and push to the array\n Array.prototype.forEach.call(navItems, function (item) {\n // Get the content for the nav item\n var content = document.getElementById(\n decodeURIComponent(item.hash.substr(1)),\n );\n if (!content) return;\n\n // Push to the contents array\n contents.push({\n nav: item,\n content: content,\n });\n });\n\n // Sort contents by the order they appear in the DOM\n sortContents(contents);\n };\n\n /**\n * Detect which content is currently active\n */\n publicAPIs.detect = function () {\n // Get the active content\n var active = getActive(contents, settings);\n\n // if there's no active content, deactivate and bail\n if (!active) {\n if (current) {\n deactivate(current, settings);\n current = null;\n }\n return;\n }\n\n // If the active content is the one currently active, do nothing\n if (current && active.content === current.content) return;\n\n // Deactivate the current content and activate the new content\n deactivate(current, settings);\n activate(active, settings);\n\n // Update the currently active content\n current = active;\n };\n\n /**\n * Detect the active content on scroll\n * Debounced for performance\n */\n var scrollHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(publicAPIs.detect);\n };\n\n /**\n * Update content sorting on resize\n * Debounced for performance\n */\n var resizeHandler = function (event) {\n // If there's a timer, cancel it\n if (timeout) {\n window.cancelAnimationFrame(timeout);\n }\n\n // Setup debounce callback\n timeout = window.requestAnimationFrame(function () {\n sortContents(contents);\n publicAPIs.detect();\n });\n };\n\n /**\n * Destroy the current instantiation\n */\n publicAPIs.destroy = function () {\n // Undo DOM changes\n if (current) {\n deactivate(current, settings);\n }\n\n // Remove event listeners\n window.removeEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.removeEventListener(\"resize\", resizeHandler, false);\n }\n\n // Reset variables\n contents = null;\n navItems = null;\n current = null;\n timeout = null;\n settings = null;\n };\n\n /**\n * Initialize the current instantiation\n */\n var init = function () {\n // Merge user options into defaults\n settings = extend(defaults, options || {});\n\n // Setup variables based on the current DOM\n publicAPIs.setup();\n\n // Find the currently active content\n publicAPIs.detect();\n\n // Setup event listeners\n window.addEventListener(\"scroll\", scrollHandler, false);\n if (settings.reflow) {\n window.addEventListener(\"resize\", resizeHandler, false);\n }\n };\n\n //\n // Initialize and return the public APIs\n //\n\n init();\n return publicAPIs;\n };\n\n //\n // Return the Constructor\n //\n\n return Constructor;\n },\n);\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// getDefaultExport function for compatibility with non-harmony modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.g = (function() {\n\tif (typeof globalThis === 'object') return globalThis;\n\ttry {\n\t\treturn this || new Function('return this')();\n\t} catch (e) {\n\t\tif (typeof window === 'object') return window;\n\t}\n})();","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","import Gumshoe from \"./gumshoe-patched.js\";\n\n////////////////////////////////////////////////////////////////////////////////\n// Scroll Handling\n////////////////////////////////////////////////////////////////////////////////\nvar tocScroll = null;\nvar header = null;\nvar lastScrollTop = window.pageYOffset || document.documentElement.scrollTop;\nconst GO_TO_TOP_OFFSET = 64;\n\nfunction scrollHandlerForHeader() {\n if (Math.floor(header.getBoundingClientRect().top) == 0) {\n header.classList.add(\"scrolled\");\n } else {\n header.classList.remove(\"scrolled\");\n }\n}\n\nfunction scrollHandlerForBackToTop(positionY) {\n if (positionY < GO_TO_TOP_OFFSET) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n } else {\n if (positionY < lastScrollTop) {\n document.documentElement.classList.add(\"show-back-to-top\");\n } else if (positionY > lastScrollTop) {\n document.documentElement.classList.remove(\"show-back-to-top\");\n }\n }\n lastScrollTop = positionY;\n}\n\nfunction scrollHandlerForTOC(positionY) {\n if (tocScroll === null) {\n return;\n }\n\n // top of page.\n if (positionY == 0) {\n tocScroll.scrollTo(0, 0);\n } else if (\n // bottom of page.\n Math.ceil(positionY) >=\n Math.floor(document.documentElement.scrollHeight - window.innerHeight)\n ) {\n tocScroll.scrollTo(0, tocScroll.scrollHeight);\n } else {\n // somewhere in the middle.\n const current = document.querySelector(\".scroll-current\");\n if (current == null) {\n return;\n }\n\n // https://github.com/pypa/pip/issues/9159 This breaks scroll behaviours.\n // // scroll the currently \"active\" heading in toc, into view.\n // const rect = current.getBoundingClientRect();\n // if (0 > rect.top) {\n // current.scrollIntoView(true); // the argument is \"alignTop\"\n // } else if (rect.bottom > window.innerHeight) {\n // current.scrollIntoView(false);\n // }\n }\n}\n\nfunction scrollHandler(positionY) {\n scrollHandlerForHeader();\n scrollHandlerForBackToTop(positionY);\n scrollHandlerForTOC(positionY);\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Theme Toggle\n////////////////////////////////////////////////////////////////////////////////\nfunction setTheme(mode) {\n if (mode !== \"light\" && mode !== \"dark\" && mode !== \"auto\") {\n console.error(`Got invalid theme mode: ${mode}. Resetting to auto.`);\n mode = \"auto\";\n }\n\n document.body.dataset.theme = mode;\n localStorage.setItem(\"theme\", mode);\n console.log(`Changed to ${mode} mode.`);\n}\n\nfunction cycleThemeOnce() {\n const currentTheme = localStorage.getItem(\"theme\") || \"auto\";\n const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;\n\n if (prefersDark) {\n // Auto (dark) -> Light -> Dark\n if (currentTheme === \"auto\") {\n setTheme(\"light\");\n } else if (currentTheme == \"light\") {\n setTheme(\"dark\");\n } else {\n setTheme(\"auto\");\n }\n } else {\n // Auto (light) -> Dark -> Light\n if (currentTheme === \"auto\") {\n setTheme(\"dark\");\n } else if (currentTheme == \"dark\") {\n setTheme(\"light\");\n } else {\n setTheme(\"auto\");\n }\n }\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Setup\n////////////////////////////////////////////////////////////////////////////////\nfunction setupScrollHandler() {\n // Taken from https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event\n let last_known_scroll_position = 0;\n let ticking = false;\n\n window.addEventListener(\"scroll\", function (e) {\n last_known_scroll_position = window.scrollY;\n\n if (!ticking) {\n window.requestAnimationFrame(function () {\n scrollHandler(last_known_scroll_position);\n ticking = false;\n });\n\n ticking = true;\n }\n });\n window.scroll();\n}\n\nfunction setupScrollSpy() {\n if (tocScroll === null) {\n return;\n }\n\n // Scrollspy -- highlight table on contents, based on scroll\n new Gumshoe(\".toc-tree a\", {\n reflow: true,\n recursive: true,\n navClass: \"scroll-current\",\n offset: () => {\n let rem = parseFloat(getComputedStyle(document.documentElement).fontSize);\n return header.getBoundingClientRect().height + 0.5 * rem + 1;\n },\n });\n}\n\nfunction setupTheme() {\n // Attach event handlers for toggling themes\n const buttons = document.getElementsByClassName(\"theme-toggle\");\n Array.from(buttons).forEach((btn) => {\n btn.addEventListener(\"click\", cycleThemeOnce);\n });\n}\n\nfunction setup() {\n setupTheme();\n setupScrollHandler();\n setupScrollSpy();\n}\n\n////////////////////////////////////////////////////////////////////////////////\n// Main entrypoint\n////////////////////////////////////////////////////////////////////////////////\nfunction main() {\n document.body.parentNode.classList.remove(\"no-js\");\n\n header = document.querySelector(\"header\");\n tocScroll = document.querySelector(\".toc-scroll\");\n\n setup();\n}\n\ndocument.addEventListener(\"DOMContentLoaded\", main);\n"],"names":["root","g","window","this","defaults","navClass","contentClass","nested","nestedClass","offset","reflow","events","emitEvent","type","elem","detail","settings","event","CustomEvent","bubbles","cancelable","dispatchEvent","getOffsetTop","location","offsetParent","offsetTop","sortContents","contents","sort","item1","item2","content","isInView","bottom","bounds","getBoundingClientRect","parseFloat","getOffset","parseInt","innerHeight","document","documentElement","clientHeight","top","isAtBottom","Math","ceil","pageYOffset","max","body","scrollHeight","offsetHeight","getActive","last","length","item","useLastItem","i","deactivateNested","nav","parentNode","li","closest","classList","remove","deactivate","items","link","activateNested","add","selector","options","navItems","current","timeout","publicAPIs","querySelectorAll","Array","prototype","forEach","call","getElementById","decodeURIComponent","hash","substr","push","active","activate","scrollHandler","cancelAnimationFrame","requestAnimationFrame","detect","resizeHandler","destroy","removeEventListener","merged","arguments","obj","key","hasOwnProperty","extend","setup","addEventListener","factory","__webpack_module_cache__","__webpack_require__","moduleId","cachedModule","undefined","exports","module","__webpack_modules__","n","getter","__esModule","d","a","definition","o","Object","defineProperty","enumerable","get","globalThis","Function","e","prop","tocScroll","header","lastScrollTop","scrollTop","GO_TO_TOP_OFFSET","cycleThemeOnce","currentTheme","localStorage","getItem","mode","matchMedia","matches","console","error","dataset","theme","setItem","log","buttons","getElementsByClassName","from","btn","setupTheme","last_known_scroll_position","ticking","scrollY","positionY","floor","scrollHandlerForBackToTop","scrollTo","querySelector","scrollHandlerForTOC","scroll","setupScrollHandler","recursive","rem","getComputedStyle","fontSize","height"],"sourceRoot":""} \ No newline at end of file diff --git a/_static/searchtools.js b/_static/searchtools.js new file mode 100644 index 0000000..7918c3f --- /dev/null +++ b/_static/searchtools.js @@ -0,0 +1,574 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2023 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + `Search finished, found ${resultCount} page(s) matching the search query.` + ); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + htmlElement.querySelectorAll(".headerlink").forEach((el) => { el.remove() }); + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent !== undefined) return docContent.textContent; + console.warn( + "Content block not found. Sphinx search tries to obtain it via '[role=main]'. Could you check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + /** + * execute search (requires search index to be loaded) + */ + query: (query) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + // array of [docname, title, anchor, descr, score, filename] + let results = []; + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + results.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id] of foundEntries) { + let score = Math.round(100 * queryLower.length / entry.length) + results.push([ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // lookup as object + objectTerms.forEach((term) => + results.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + results.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) results.forEach((item) => (item[4] = Scorer.score(item))); + + // now sort the results by score (in opposite order of appearance, since the + // display function below uses pop() to retrieve items) and then + // alphabetically + results.sort((a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; + }); + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + results = results.reverse(); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord) && !terms[word]) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord) && !titleTerms[word]) + arr.push({ files: titleTerms[word], score: Scorer.partialTitle }); + }); + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (fileMap.has(file) && fileMap.get(file).indexOf(word) === -1) + fileMap.get(file).push(word); + else fileMap.set(file, [word]); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords) => { + const text = Search.htmlToText(htmlText); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/_static/skeleton.css b/_static/skeleton.css new file mode 100644 index 0000000..467c878 --- /dev/null +++ b/_static/skeleton.css @@ -0,0 +1,296 @@ +/* Some sane resets. */ +html { + height: 100%; +} + +body { + margin: 0; + min-height: 100%; +} + +/* All the flexbox magic! */ +body, +.sb-announcement, +.sb-content, +.sb-main, +.sb-container, +.sb-container__inner, +.sb-article-container, +.sb-footer-content, +.sb-header, +.sb-header-secondary, +.sb-footer { + display: flex; +} + +/* These order things vertically */ +body, +.sb-main, +.sb-article-container { + flex-direction: column; +} + +/* Put elements in the center */ +.sb-header, +.sb-header-secondary, +.sb-container, +.sb-content, +.sb-footer, +.sb-footer-content { + justify-content: center; +} +/* Put elements at the ends */ +.sb-article-container { + justify-content: space-between; +} + +/* These elements grow. */ +.sb-main, +.sb-content, +.sb-container, +article { + flex-grow: 1; +} + +/* Because padding making this wider is not fun */ +article { + box-sizing: border-box; +} + +/* The announcements element should never be wider than the page. */ +.sb-announcement { + max-width: 100%; +} + +.sb-sidebar-primary, +.sb-sidebar-secondary { + flex-shrink: 0; + width: 17rem; +} + +.sb-announcement__inner { + justify-content: center; + + box-sizing: border-box; + height: 3rem; + + overflow-x: auto; + white-space: nowrap; +} + +/* Sidebars, with checkbox-based toggle */ +.sb-sidebar-primary, +.sb-sidebar-secondary { + position: fixed; + height: 100%; + top: 0; +} + +.sb-sidebar-primary { + left: -17rem; + transition: left 250ms ease-in-out; +} +.sb-sidebar-secondary { + right: -17rem; + transition: right 250ms ease-in-out; +} + +.sb-sidebar-toggle { + display: none; +} +.sb-sidebar-overlay { + position: fixed; + top: 0; + width: 0; + height: 0; + + transition: width 0ms ease 250ms, height 0ms ease 250ms, opacity 250ms ease; + + opacity: 0; + background-color: rgba(0, 0, 0, 0.54); +} + +#sb-sidebar-toggle--primary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--primary"], +#sb-sidebar-toggle--secondary:checked + ~ .sb-sidebar-overlay[for="sb-sidebar-toggle--secondary"] { + width: 100%; + height: 100%; + opacity: 1; + transition: width 0ms ease, height 0ms ease, opacity 250ms ease; +} + +#sb-sidebar-toggle--primary:checked ~ .sb-container .sb-sidebar-primary { + left: 0; +} +#sb-sidebar-toggle--secondary:checked ~ .sb-container .sb-sidebar-secondary { + right: 0; +} + +/* Full-width mode */ +.drop-secondary-sidebar-for-full-width-content + .hide-when-secondary-sidebar-shown { + display: none !important; +} +.drop-secondary-sidebar-for-full-width-content .sb-sidebar-secondary { + display: none !important; +} + +/* Mobile views */ +.sb-page-width { + width: 100%; +} + +.sb-article-container, +.sb-footer-content__inner, +.drop-secondary-sidebar-for-full-width-content .sb-article, +.drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 100vw; +} + +.sb-article, +.match-content-width { + padding: 0 1rem; + box-sizing: border-box; +} + +@media (min-width: 32rem) { + .sb-article, + .match-content-width { + padding: 0 2rem; + } +} + +/* Tablet views */ +@media (min-width: 42rem) { + .sb-article-container { + width: auto; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 42rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 46rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 46rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 50rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 50rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Tablet views */ +@media (min-width: 59rem) { + .sb-sidebar-secondary { + position: static; + } + .hide-when-secondary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} +@media (min-width: 63rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } + .sb-article, + .match-content-width { + width: 46rem; + } +} +@media (min-width: 67rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-article, + .match-content-width { + width: 50rem; + } +} + +/* Desktop views */ +@media (min-width: 76rem) { + .sb-sidebar-primary { + position: static; + } + .hide-when-primary-sidebar-shown { + display: none !important; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 59rem; + } + .sb-article, + .match-content-width { + width: 42rem; + } +} + +/* Full desktop views */ +@media (min-width: 80rem) { + .sb-article, + .match-content-width { + width: 46rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 63rem; + } +} + +@media (min-width: 84rem) { + .sb-article, + .match-content-width { + width: 50rem; + } + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } +} + +@media (min-width: 88rem) { + .sb-footer-content__inner, + .drop-secondary-sidebar-for-full-width-content .sb-article, + .drop-secondary-sidebar-for-full-width-content .match-content-width { + width: 67rem; + } + .sb-page-width { + width: 88rem; + } +} diff --git a/_static/sphinx_contributors.css b/_static/sphinx_contributors.css new file mode 100644 index 0000000..af6b52c --- /dev/null +++ b/_static/sphinx_contributors.css @@ -0,0 +1,32 @@ +.sphinx-contributors_contributor { + display: flex; +} + +.sphinx-contributors_contributor__contributions:before{ + content: "-"; + padding: 0 5px; +} + +.sphinx-contributors--avatars .sphinx-contributors_list { + display: flex; + flex-wrap: wrap; + justify-content: space-around; +} + +.sphinx-contributors--avatars .sphinx-contributors_list__item { + list-style: none; + text-align: center; +} + +.sphinx-contributors--avatars .sphinx-contributors_contributor { + flex-direction: column; +} + +.sphinx-contributors--avatars .sphinx-contributors_contributor__image { + margin-top: 10px; + max-width: 150px; +} + +.sphinx-contributors--avatars .sphinx-contributors_contributor__username { + display: block; +} diff --git a/_static/sphinx_highlight.js b/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/_static/styles/furo-extensions.css b/_static/styles/furo-extensions.css new file mode 100644 index 0000000..bc447f2 --- /dev/null +++ b/_static/styles/furo-extensions.css @@ -0,0 +1,2 @@ +#furo-sidebar-ad-placement{padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)}#furo-sidebar-ad-placement .ethical-sidebar{background:var(--color-background-secondary);border:none;box-shadow:none}#furo-sidebar-ad-placement .ethical-sidebar:hover{background:var(--color-background-hover)}#furo-sidebar-ad-placement .ethical-sidebar a{color:var(--color-foreground-primary)}#furo-sidebar-ad-placement .ethical-callout a{color:var(--color-foreground-secondary)!important}#furo-readthedocs-versions{background:transparent;display:block;position:static;width:100%}#furo-readthedocs-versions .rst-versions{background:#1a1c1e}#furo-readthedocs-versions .rst-current-version{background:var(--color-sidebar-item-background);cursor:unset}#furo-readthedocs-versions .rst-current-version:hover{background:var(--color-sidebar-item-background)}#furo-readthedocs-versions .rst-current-version .fa-book{color:var(--color-foreground-primary)}#furo-readthedocs-versions>.rst-other-versions{padding:0}#furo-readthedocs-versions>.rst-other-versions small{opacity:1}#furo-readthedocs-versions .injected .rst-versions{position:unset}#furo-readthedocs-versions:focus-within,#furo-readthedocs-versions:hover{box-shadow:0 0 0 1px var(--color-sidebar-background-border)}#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:hover .rst-current-version{background:#1a1c1e;font-size:inherit;height:auto;line-height:inherit;padding:12px;text-align:right}#furo-readthedocs-versions:focus-within .rst-current-version .fa-book,#furo-readthedocs-versions:hover .rst-current-version .fa-book{color:#fff;float:left}#furo-readthedocs-versions:focus-within .fa-caret-down,#furo-readthedocs-versions:hover .fa-caret-down{display:none}#furo-readthedocs-versions:focus-within .injected,#furo-readthedocs-versions:focus-within .rst-current-version,#furo-readthedocs-versions:focus-within .rst-other-versions,#furo-readthedocs-versions:hover .injected,#furo-readthedocs-versions:hover .rst-current-version,#furo-readthedocs-versions:hover .rst-other-versions{display:block}#furo-readthedocs-versions:focus-within>.rst-current-version,#furo-readthedocs-versions:hover>.rst-current-version{display:none}.highlight:hover button.copybtn{color:var(--color-code-foreground)}.highlight button.copybtn{align-items:center;background-color:var(--color-code-background);border:none;color:var(--color-background-item);cursor:pointer;height:1.25em;opacity:1;right:.5rem;top:.625rem;transition:color .3s,opacity .3s;width:1.25em}.highlight button.copybtn:hover{background-color:var(--color-code-background);color:var(--color-brand-content)}.highlight button.copybtn:after{background-color:transparent;color:var(--color-code-foreground);display:none}.highlight button.copybtn.success{color:#22863a;transition:color 0ms}.highlight button.copybtn.success:after{display:block}.highlight button.copybtn svg{padding:0}body{--sd-color-primary:var(--color-brand-primary);--sd-color-primary-highlight:var(--color-brand-content);--sd-color-primary-text:var(--color-background-primary);--sd-color-shadow:rgba(0,0,0,.05);--sd-color-card-border:var(--color-card-border);--sd-color-card-border-hover:var(--color-brand-content);--sd-color-card-background:var(--color-card-background);--sd-color-card-text:var(--color-foreground-primary);--sd-color-card-header:var(--color-card-marginals-background);--sd-color-card-footer:var(--color-card-marginals-background);--sd-color-tabs-label-active:var(--color-brand-content);--sd-color-tabs-label-hover:var(--color-foreground-muted);--sd-color-tabs-label-inactive:var(--color-foreground-muted);--sd-color-tabs-underline-active:var(--color-brand-content);--sd-color-tabs-underline-hover:var(--color-foreground-border);--sd-color-tabs-underline-inactive:var(--color-background-border);--sd-color-tabs-overline:var(--color-background-border);--sd-color-tabs-underline:var(--color-background-border)}.sd-tab-content{box-shadow:0 -2px var(--sd-color-tabs-overline),0 1px var(--sd-color-tabs-underline)}.sd-card{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)}.sd-shadow-sm{box-shadow:0 .1rem .25rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-md{box-shadow:0 .3rem .75rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-shadow-lg{box-shadow:0 .6rem 1.5rem var(--sd-color-shadow),0 0 .0625rem rgba(0,0,0,.1)!important}.sd-card-hover:hover{transform:none}.sd-cards-carousel{gap:.25rem;padding:.25rem}body{--tabs--label-text:var(--color-foreground-muted);--tabs--label-text--hover:var(--color-foreground-muted);--tabs--label-text--active:var(--color-brand-content);--tabs--label-text--active--hover:var(--color-brand-content);--tabs--label-background:transparent;--tabs--label-background--hover:transparent;--tabs--label-background--active:transparent;--tabs--label-background--active--hover:transparent;--tabs--padding-x:0.25em;--tabs--margin-x:1em;--tabs--border:var(--color-background-border);--tabs--label-border:transparent;--tabs--label-border--hover:var(--color-foreground-muted);--tabs--label-border--active:var(--color-brand-content);--tabs--label-border--active--hover:var(--color-brand-content)}[role=main] .container{max-width:none;padding-left:0;padding-right:0}.shadow.docutils{border:none;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)!important}.sphinx-bs .card{background-color:var(--color-background-secondary);color:var(--color-foreground)} +/*# sourceMappingURL=furo-extensions.css.map*/ \ No newline at end of file diff --git a/_static/styles/furo-extensions.css.map b/_static/styles/furo-extensions.css.map new file mode 100644 index 0000000..9ba5637 --- /dev/null +++ b/_static/styles/furo-extensions.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo-extensions.css","mappings":"AAGA,2BACE,oFACA,4CAKE,6CAHA,YACA,eAEA,CACA,kDACE,yCAEF,8CACE,sCAEJ,8CACE,kDAEJ,2BAGE,uBACA,cAHA,gBACA,UAEA,CAGA,yCACE,mBAEF,gDAEE,gDADA,YACA,CACA,sDACE,gDACF,yDACE,sCAEJ,+CACE,UACA,qDACE,UAGF,mDACE,eAEJ,yEAEE,4DAEA,mHASE,mBAPA,kBAEA,YADA,oBAGA,aADA,gBAIA,CAEA,qIAEE,WADA,UACA,CAEJ,uGACE,aAEF,iUAGE,cAEF,mHACE,aC1EJ,gCACE,mCAEF,0BAKE,mBAUA,8CACA,YAFA,mCAKA,eAZA,cALA,UASA,YADA,YAYA,iCAdA,YAcA,CAEA,gCAEE,8CADA,gCACA,CAEF,gCAGE,6BADA,mCADA,YAEA,CAEF,kCAEE,cADA,oBACA,CACA,wCACE,cAEJ,8BACE,UC5CN,KAEE,6CAA8C,CAC9C,uDAAwD,CACxD,uDAAwD,CAGxD,iCAAsC,CAGtC,+CAAgD,CAChD,uDAAwD,CACxD,uDAAwD,CACxD,oDAAqD,CACrD,6DAA8D,CAC9D,6DAA8D,CAG9D,uDAAwD,CACxD,yDAA0D,CAC1D,4DAA6D,CAC7D,2DAA4D,CAC5D,8DAA+D,CAC/D,iEAAkE,CAClE,uDAAwD,CACxD,wDAAyD,CAG3D,gBACE,qFAGF,SACE,6EAEF,cACE,uFAEF,cACE,uFAEF,cACE,uFAGF,qBACE,eAEF,mBACE,WACA,eChDF,KACE,gDAAiD,CACjD,uDAAwD,CACxD,qDAAsD,CACtD,4DAA6D,CAC7D,oCAAqC,CACrC,2CAA4C,CAC5C,4CAA6C,CAC7C,mDAAoD,CACpD,wBAAyB,CACzB,oBAAqB,CACrB,6CAA8C,CAC9C,gCAAiC,CACjC,yDAA0D,CAC1D,uDAAwD,CACxD,8DAA+D,CCbjE,uBACE,eACA,eACA,gBAGF,iBACE,YACA,+EAGF,iBACE,mDACA","sources":["webpack:///./src/furo/assets/styles/extensions/_readthedocs.sass","webpack:///./src/furo/assets/styles/extensions/_copybutton.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-design.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-inline-tabs.sass","webpack:///./src/furo/assets/styles/extensions/_sphinx-panels.sass"],"sourcesContent":["// This file contains the styles used for tweaking how ReadTheDoc's embedded\n// contents would show up inside the theme.\n\n#furo-sidebar-ad-placement\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n .ethical-sidebar\n // Remove the border and box-shadow.\n border: none\n box-shadow: none\n // Manage the background colors.\n background: var(--color-background-secondary)\n &:hover\n background: var(--color-background-hover)\n // Ensure the text is legible.\n a\n color: var(--color-foreground-primary)\n\n .ethical-callout a\n color: var(--color-foreground-secondary) !important\n\n#furo-readthedocs-versions\n position: static\n width: 100%\n background: transparent\n display: block\n\n // Make the background color fit with the theme's aesthetic.\n .rst-versions\n background: rgb(26, 28, 30)\n\n .rst-current-version\n cursor: unset\n background: var(--color-sidebar-item-background)\n &:hover\n background: var(--color-sidebar-item-background)\n .fa-book\n color: var(--color-foreground-primary)\n\n > .rst-other-versions\n padding: 0\n small\n opacity: 1\n\n .injected\n .rst-versions\n position: unset\n\n &:hover,\n &:focus-within\n box-shadow: 0 0 0 1px var(--color-sidebar-background-border)\n\n .rst-current-version\n // Undo the tweaks done in RTD's CSS\n font-size: inherit\n line-height: inherit\n height: auto\n text-align: right\n padding: 12px\n\n // Match the rest of the body\n background: #1a1c1e\n\n .fa-book\n float: left\n color: white\n\n .fa-caret-down\n display: none\n\n .rst-current-version,\n .rst-other-versions,\n .injected\n display: block\n\n > .rst-current-version\n display: none\n",".highlight\n &:hover button.copybtn\n color: var(--color-code-foreground)\n\n button.copybtn\n // Make it visible\n opacity: 1\n\n // Align things correctly\n align-items: center\n\n height: 1.25em\n width: 1.25em\n\n top: 0.625rem // $code-spacing-vertical\n right: 0.5rem\n\n // Make it look better\n color: var(--color-background-item)\n background-color: var(--color-code-background)\n border: none\n\n // Change to cursor to make it obvious that you can click on it\n cursor: pointer\n\n // Transition smoothly, for aesthetics\n transition: color 300ms, opacity 300ms\n\n &:hover\n color: var(--color-brand-content)\n background-color: var(--color-code-background)\n\n &::after\n display: none\n color: var(--color-code-foreground)\n background-color: transparent\n\n &.success\n transition: color 0ms\n color: #22863a\n &::after\n display: block\n\n svg\n padding: 0\n","body\n // Colors\n --sd-color-primary: var(--color-brand-primary)\n --sd-color-primary-highlight: var(--color-brand-content)\n --sd-color-primary-text: var(--color-background-primary)\n\n // Shadows\n --sd-color-shadow: rgba(0, 0, 0, 0.05)\n\n // Cards\n --sd-color-card-border: var(--color-card-border)\n --sd-color-card-border-hover: var(--color-brand-content)\n --sd-color-card-background: var(--color-card-background)\n --sd-color-card-text: var(--color-foreground-primary)\n --sd-color-card-header: var(--color-card-marginals-background)\n --sd-color-card-footer: var(--color-card-marginals-background)\n\n // Tabs\n --sd-color-tabs-label-active: var(--color-brand-content)\n --sd-color-tabs-label-hover: var(--color-foreground-muted)\n --sd-color-tabs-label-inactive: var(--color-foreground-muted)\n --sd-color-tabs-underline-active: var(--color-brand-content)\n --sd-color-tabs-underline-hover: var(--color-foreground-border)\n --sd-color-tabs-underline-inactive: var(--color-background-border)\n --sd-color-tabs-overline: var(--color-background-border)\n --sd-color-tabs-underline: var(--color-background-border)\n\n// Tabs\n.sd-tab-content\n box-shadow: 0 -2px var(--sd-color-tabs-overline), 0 1px var(--sd-color-tabs-underline)\n\n// Shadows\n.sd-card // Have a shadow by default\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n.sd-shadow-sm\n box-shadow: 0 0.1rem 0.25rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-md\n box-shadow: 0 0.3rem 0.75rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n.sd-shadow-lg\n box-shadow: 0 0.6rem 1.5rem var(--sd-color-shadow), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Cards\n.sd-card-hover:hover // Don't change scale on hover\n transform: none\n\n.sd-cards-carousel // Have a bit of gap in the carousel by default\n gap: 0.25rem\n padding: 0.25rem\n","// This file contains styles to tweak sphinx-inline-tabs to work well with Furo.\n\nbody\n --tabs--label-text: var(--color-foreground-muted)\n --tabs--label-text--hover: var(--color-foreground-muted)\n --tabs--label-text--active: var(--color-brand-content)\n --tabs--label-text--active--hover: var(--color-brand-content)\n --tabs--label-background: transparent\n --tabs--label-background--hover: transparent\n --tabs--label-background--active: transparent\n --tabs--label-background--active--hover: transparent\n --tabs--padding-x: 0.25em\n --tabs--margin-x: 1em\n --tabs--border: var(--color-background-border)\n --tabs--label-border: transparent\n --tabs--label-border--hover: var(--color-foreground-muted)\n --tabs--label-border--active: var(--color-brand-content)\n --tabs--label-border--active--hover: var(--color-brand-content)\n","// This file contains styles to tweak sphinx-panels to work well with Furo.\n\n// sphinx-panels includes Bootstrap 4, which uses .container which can conflict\n// with docutils' `.. container::` directive.\n[role=\"main\"] .container\n max-width: initial\n padding-left: initial\n padding-right: initial\n\n// Make the panels look nicer!\n.shadow.docutils\n border: none\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1) !important\n\n// Make panel colors respond to dark mode\n.sphinx-bs .card\n background-color: var(--color-background-secondary)\n color: var(--color-foreground)\n"],"names":[],"sourceRoot":""} \ No newline at end of file diff --git a/_static/styles/furo.css b/_static/styles/furo.css new file mode 100644 index 0000000..3d29a21 --- /dev/null +++ b/_static/styles/furo.css @@ -0,0 +1,2 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{-webkit-text-size-adjust:100%;line-height:1.15}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}@media print{.content-icon-container,.headerlink,.mobile-header,.related-pages{display:none!important}.highlight{border:.1pt solid var(--color-foreground-border)}a,blockquote,dl,ol,pre,table,ul{page-break-inside:avoid}caption,figure,h1,h2,h3,h4,h5,h6,img{page-break-after:avoid;page-break-inside:avoid}dl,ol,ul{page-break-before:avoid}}.visually-hidden{clip:rect(0,0,0,0)!important;border:0!important;height:1px!important;margin:-1px!important;overflow:hidden!important;padding:0!important;position:absolute!important;white-space:nowrap!important;width:1px!important}:-moz-focusring{outline:auto}body{--font-stack:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;--font-stack--monospace:"SFMono-Regular",Menlo,Consolas,Monaco,Liberation Mono,Lucida Console,monospace;--font-size--normal:100%;--font-size--small:87.5%;--font-size--small--2:81.25%;--font-size--small--3:75%;--font-size--small--4:62.5%;--sidebar-caption-font-size:var(--font-size--small--2);--sidebar-item-font-size:var(--font-size--small);--sidebar-search-input-font-size:var(--font-size--small);--toc-font-size:var(--font-size--small--3);--toc-font-size--mobile:var(--font-size--normal);--toc-title-font-size:var(--font-size--small--4);--admonition-font-size:0.8125rem;--admonition-title-font-size:0.8125rem;--code-font-size:var(--font-size--small--2);--api-font-size:var(--font-size--small);--header-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*4);--header-padding:0.5rem;--sidebar-tree-space-above:1.5rem;--sidebar-caption-space-above:1rem;--sidebar-item-line-height:1rem;--sidebar-item-spacing-vertical:0.5rem;--sidebar-item-spacing-horizontal:1rem;--sidebar-item-height:calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*2);--sidebar-expander-width:var(--sidebar-item-height);--sidebar-search-space-above:0.5rem;--sidebar-search-input-spacing-vertical:0.5rem;--sidebar-search-input-spacing-horizontal:0.5rem;--sidebar-search-input-height:1rem;--sidebar-search-icon-size:var(--sidebar-search-input-height);--toc-title-padding:0.25rem 0;--toc-spacing-vertical:1.5rem;--toc-spacing-horizontal:1.5rem;--toc-item-spacing-vertical:0.4rem;--toc-item-spacing-horizontal:1rem;--icon-search:url('data:image/svg+xml;charset=utf-8,');--icon-pencil:url('data:image/svg+xml;charset=utf-8,');--icon-abstract:url('data:image/svg+xml;charset=utf-8,');--icon-info:url('data:image/svg+xml;charset=utf-8,');--icon-flame:url('data:image/svg+xml;charset=utf-8,');--icon-question:url('data:image/svg+xml;charset=utf-8,');--icon-warning:url('data:image/svg+xml;charset=utf-8,');--icon-failure:url('data:image/svg+xml;charset=utf-8,');--icon-spark:url('data:image/svg+xml;charset=utf-8,');--color-admonition-title--caution:#ff9100;--color-admonition-title-background--caution:rgba(255,145,0,.2);--color-admonition-title--warning:#ff9100;--color-admonition-title-background--warning:rgba(255,145,0,.2);--color-admonition-title--danger:#ff5252;--color-admonition-title-background--danger:rgba(255,82,82,.2);--color-admonition-title--attention:#ff5252;--color-admonition-title-background--attention:rgba(255,82,82,.2);--color-admonition-title--error:#ff5252;--color-admonition-title-background--error:rgba(255,82,82,.2);--color-admonition-title--hint:#00c852;--color-admonition-title-background--hint:rgba(0,200,82,.2);--color-admonition-title--tip:#00c852;--color-admonition-title-background--tip:rgba(0,200,82,.2);--color-admonition-title--important:#00bfa5;--color-admonition-title-background--important:rgba(0,191,165,.2);--color-admonition-title--note:#00b0ff;--color-admonition-title-background--note:rgba(0,176,255,.2);--color-admonition-title--seealso:#448aff;--color-admonition-title-background--seealso:rgba(68,138,255,.2);--color-admonition-title--admonition-todo:grey;--color-admonition-title-background--admonition-todo:hsla(0,0%,50%,.2);--color-admonition-title:#651fff;--color-admonition-title-background:rgba(101,31,255,.2);--icon-admonition-default:var(--icon-abstract);--color-topic-title:#14b8a6;--color-topic-title-background:rgba(20,184,166,.2);--icon-topic-default:var(--icon-pencil);--color-problematic:#b30000;--color-foreground-primary:#000;--color-foreground-secondary:#5a5c63;--color-foreground-muted:#646776;--color-foreground-border:#878787;--color-background-primary:#fff;--color-background-secondary:#f8f9fb;--color-background-hover:#efeff4;--color-background-hover--transparent:#efeff400;--color-background-border:#eeebee;--color-background-item:#ccc;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2962ff;--color-brand-content:#2a5adf;--color-api-background:var(--color-background-hover--transparent);--color-api-background-hover:var(--color-background-hover);--color-api-overall:var(--color-foreground-secondary);--color-api-name:var(--color-problematic);--color-api-pre-name:var(--color-problematic);--color-api-paren:var(--color-foreground-secondary);--color-api-keyword:var(--color-foreground-primary);--color-highlight-on-target:#ffc;--color-inline-code-background:var(--color-background-secondary);--color-highlighted-background:#def;--color-highlighted-text:var(--color-foreground-primary);--color-guilabel-background:#ddeeff80;--color-guilabel-border:#bedaf580;--color-guilabel-text:var(--color-foreground-primary);--color-admonition-background:transparent;--color-table-header-background:var(--color-background-secondary);--color-table-border:var(--color-background-border);--color-card-border:var(--color-background-secondary);--color-card-background:transparent;--color-card-marginals-background:var(--color-background-secondary);--color-header-background:var(--color-background-primary);--color-header-border:var(--color-background-border);--color-header-text:var(--color-foreground-primary);--color-sidebar-background:var(--color-background-secondary);--color-sidebar-background-border:var(--color-background-border);--color-sidebar-brand-text:var(--color-foreground-primary);--color-sidebar-caption-text:var(--color-foreground-muted);--color-sidebar-link-text:var(--color-foreground-secondary);--color-sidebar-link-text--top-level:var(--color-brand-primary);--color-sidebar-item-background:var(--color-sidebar-background);--color-sidebar-item-background--current:var( --color-sidebar-item-background );--color-sidebar-item-background--hover:linear-gradient(90deg,var(--color-background-hover--transparent) 0%,var(--color-background-hover) var(--sidebar-item-spacing-horizontal),var(--color-background-hover) 100%);--color-sidebar-item-expander-background:transparent;--color-sidebar-item-expander-background--hover:var( --color-background-hover );--color-sidebar-search-text:var(--color-foreground-primary);--color-sidebar-search-background:var(--color-background-secondary);--color-sidebar-search-background--focus:var(--color-background-primary);--color-sidebar-search-border:var(--color-background-border);--color-sidebar-search-icon:var(--color-foreground-muted);--color-toc-background:var(--color-background-primary);--color-toc-title-text:var(--color-foreground-muted);--color-toc-item-text:var(--color-foreground-secondary);--color-toc-item-text--hover:var(--color-foreground-primary);--color-toc-item-text--active:var(--color-brand-primary);--color-content-foreground:var(--color-foreground-primary);--color-content-background:transparent;--color-link:var(--color-brand-content);--color-link--hover:var(--color-brand-content);--color-link-underline:var(--color-background-border);--color-link-underline--hover:var(--color-foreground-border)}.only-light{display:block!important}html body .only-dark{display:none!important}@media not print{body[data-theme=dark]{--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body[data-theme=dark] .only-light{display:none!important}body[data-theme=dark] .only-dark{display:block!important}@media(prefers-color-scheme:dark){body:not([data-theme=light]){--color-problematic:#ee5151;--color-foreground-primary:#ffffffcc;--color-foreground-secondary:#9ca0a5;--color-foreground-muted:#81868d;--color-foreground-border:#666;--color-background-primary:#131416;--color-background-secondary:#1a1c1e;--color-background-hover:#1e2124;--color-background-hover--transparent:#1e212400;--color-background-border:#303335;--color-background-item:#444;--color-announcement-background:#000000dd;--color-announcement-text:#eeebee;--color-brand-primary:#2b8cee;--color-brand-content:#368ce2;--color-highlighted-background:#083563;--color-guilabel-background:#08356380;--color-guilabel-border:#13395f80;--color-api-keyword:var(--color-foreground-secondary);--color-highlight-on-target:#330;--color-admonition-background:#18181a;--color-card-border:var(--color-background-secondary);--color-card-background:#18181a;--color-card-marginals-background:var(--color-background-hover)}html body:not([data-theme=light]) .only-light{display:none!important}body:not([data-theme=light]) .only-dark{display:block!important}}}body[data-theme=auto] .theme-toggle svg.theme-icon-when-auto,body[data-theme=dark] .theme-toggle svg.theme-icon-when-dark,body[data-theme=light] .theme-toggle svg.theme-icon-when-light{display:block}body{font-family:var(--font-stack)}code,kbd,pre,samp{font-family:var(--font-stack--monospace)}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}article{line-height:1.5}h1,h2,h3,h4,h5,h6{border-radius:.5rem;font-weight:700;line-height:1.25;margin:.5rem -.5rem;padding-left:.5rem;padding-right:.5rem}h1+p,h2+p,h3+p,h4+p,h5+p,h6+p{margin-top:0}h1{font-size:2.5em;margin-bottom:1rem}h1,h2{margin-top:1.75rem}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.25em}h5{font-size:1.125em}h6{font-size:1em}small{font-size:80%;opacity:75%}p{margin-bottom:.75rem;margin-top:.5rem}hr.docutils{background-color:var(--color-background-border);border:0;height:1px;margin:2rem 0;padding:0}.centered{text-align:center}a{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}a:hover{color:var(--color-link--hover);text-decoration-color:var(--color-link-underline--hover)}a.muted-link{color:inherit}a.muted-link:hover{color:var(--color-link);text-decoration-color:var(--color-link-underline--hover)}html{overflow-x:hidden;overflow-y:scroll;scroll-behavior:smooth}.sidebar-scroll,.toc-scroll,article[role=main] *{scrollbar-color:var(--color-foreground-border) transparent;scrollbar-width:thin}.sidebar-scroll::-webkit-scrollbar,.toc-scroll::-webkit-scrollbar,article[role=main] ::-webkit-scrollbar{height:.25rem;width:.25rem}.sidebar-scroll::-webkit-scrollbar-thumb,.toc-scroll::-webkit-scrollbar-thumb,article[role=main] ::-webkit-scrollbar-thumb{background-color:var(--color-foreground-border);border-radius:.125rem}body,html{background:var(--color-background-primary);color:var(--color-foreground-primary);height:100%}article{background:var(--color-content-background);color:var(--color-content-foreground);overflow-wrap:break-word}.page{display:flex;min-height:100%}.mobile-header{background-color:var(--color-header-background);border-bottom:1px solid var(--color-header-border);color:var(--color-header-text);display:none;height:var(--header-height);width:100%;z-index:10}.mobile-header.scrolled{border-bottom:none;box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2)}.mobile-header .header-center a{color:var(--color-header-text);text-decoration:none}.main{display:flex;flex:1}.sidebar-drawer{background:var(--color-sidebar-background);border-right:1px solid var(--color-sidebar-background-border);box-sizing:border-box;display:flex;justify-content:flex-end;min-width:15em;width:calc(50% - 26em)}.sidebar-container,.toc-drawer{box-sizing:border-box;width:15em}.toc-drawer{background:var(--color-toc-background);padding-right:1rem}.sidebar-sticky,.toc-sticky{display:flex;flex-direction:column;height:min(100%,100vh);height:100vh;position:sticky;top:0}.sidebar-scroll,.toc-scroll{flex-grow:1;flex-shrink:1;overflow:auto;scroll-behavior:smooth}.content{display:flex;flex-direction:column;justify-content:space-between;padding:0 3em;width:46em}.icon{display:inline-block;height:1rem;width:1rem}.icon svg{height:100%;width:100%}.announcement{align-items:center;background-color:var(--color-announcement-background);color:var(--color-announcement-text);display:flex;height:var(--header-height);overflow-x:auto}.announcement+.page{min-height:calc(100% - var(--header-height))}.announcement-content{box-sizing:border-box;min-width:100%;padding:.5rem;text-align:center;white-space:nowrap}.announcement-content a{color:var(--color-announcement-text);text-decoration-color:var(--color-announcement-text)}.announcement-content a:hover{color:var(--color-announcement-text);text-decoration-color:var(--color-link--hover)}.no-js .theme-toggle-container{display:none}.theme-toggle-container{vertical-align:middle}.theme-toggle{background:transparent;border:none;cursor:pointer;padding:0}.theme-toggle svg{color:var(--color-foreground-primary);display:none;height:1rem;vertical-align:middle;width:1rem}.theme-toggle-header{float:left;padding:1rem .5rem}.nav-overlay-icon,.toc-overlay-icon{cursor:pointer;display:none}.nav-overlay-icon .icon,.toc-overlay-icon .icon{color:var(--color-foreground-secondary);height:1rem;width:1rem}.nav-overlay-icon,.toc-header-icon{align-items:center;justify-content:center}.toc-content-icon{height:1.5rem;width:1.5rem}.content-icon-container{display:flex;float:right;gap:.5rem;margin-bottom:1rem;margin-left:1rem;margin-top:1.5rem}.content-icon-container .edit-this-page svg{color:inherit;height:1rem;width:1rem}.sidebar-toggle{display:none;position:absolute}.sidebar-toggle[name=__toc]{left:20px}.sidebar-toggle:checked{left:40px}.overlay{background-color:rgba(0,0,0,.54);height:0;opacity:0;position:fixed;top:0;transition:width 0ms,height 0ms,opacity .25s ease-out;width:0}.sidebar-overlay{z-index:20}.toc-overlay{z-index:40}.sidebar-drawer{transition:left .25s ease-in-out;z-index:30}.toc-drawer{transition:right .25s ease-in-out;z-index:50}#__navigation:checked~.sidebar-overlay{height:100%;opacity:1;width:100%}#__navigation:checked~.page .sidebar-drawer{left:0;top:0}#__toc:checked~.toc-overlay{height:100%;opacity:1;width:100%}#__toc:checked~.page .toc-drawer{right:0;top:0}.back-to-top{background:var(--color-background-primary);border-radius:1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 1px 0 hsla(220,9%,46%,.502);display:none;font-size:.8125rem;left:0;margin-left:50%;padding:.5rem .75rem .5rem .5rem;position:fixed;text-decoration:none;top:1rem;transform:translateX(-50%);z-index:10}.back-to-top svg{fill:currentColor;display:inline-block;height:1rem;width:1rem}.back-to-top span{margin-left:.25rem}.show-back-to-top .back-to-top{align-items:center;display:flex}@media(min-width:97em){html{font-size:110%}}@media(max-width:82em){.toc-content-icon{display:flex}.toc-drawer{border-left:1px solid var(--color-background-muted);height:100vh;position:fixed;right:-15em;top:0}.toc-tree{border-left:none;font-size:var(--toc-font-size--mobile)}.sidebar-drawer{width:calc(50% - 18.5em)}}@media(max-width:67em){.nav-overlay-icon{display:flex}.sidebar-drawer{height:100vh;left:-15em;position:fixed;top:0;width:15em}.toc-header-icon{display:flex}.theme-toggle-content,.toc-content-icon{display:none}.theme-toggle-header{display:block}.mobile-header{align-items:center;display:flex;justify-content:space-between;position:sticky;top:0}.mobile-header .header-left,.mobile-header .header-right{display:flex;height:var(--header-height);padding:0 var(--header-padding)}.mobile-header .header-left label,.mobile-header .header-right label{height:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.nav-overlay-icon .icon,.theme-toggle svg{height:1.25rem;width:1.25rem}:target{scroll-margin-top:var(--header-height)}.back-to-top{top:calc(var(--header-height) + .5rem)}.page{flex-direction:column;justify-content:center}.content{margin-left:auto;margin-right:auto}}@media(max-width:52em){.content{overflow-x:auto;width:100%}}@media(max-width:46em){.content{padding:0 1em}article aside.sidebar{float:none;margin:1rem 0;width:100%}}.admonition,.topic{background:var(--color-admonition-background);border-radius:.2rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1);font-size:var(--admonition-font-size);margin:1rem auto;overflow:hidden;padding:0 .5rem .5rem;page-break-inside:avoid}.admonition>:nth-child(2),.topic>:nth-child(2){margin-top:0}.admonition>:last-child,.topic>:last-child{margin-bottom:0}.admonition p.admonition-title,p.topic-title{font-size:var(--admonition-title-font-size);font-weight:500;line-height:1.3;margin:0 -.5rem .5rem;padding:.4rem .5rem .4rem 2rem;position:relative}.admonition p.admonition-title:before,p.topic-title:before{content:"";height:1rem;left:.5rem;position:absolute;width:1rem}p.admonition-title{background-color:var(--color-admonition-title-background)}p.admonition-title:before{background-color:var(--color-admonition-title);-webkit-mask-image:var(--icon-admonition-default);mask-image:var(--icon-admonition-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}p.topic-title{background-color:var(--color-topic-title-background)}p.topic-title:before{background-color:var(--color-topic-title);-webkit-mask-image:var(--icon-topic-default);mask-image:var(--icon-topic-default);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.admonition{border-left:.2rem solid var(--color-admonition-title)}.admonition.caution{border-left-color:var(--color-admonition-title--caution)}.admonition.caution>.admonition-title{background-color:var(--color-admonition-title-background--caution)}.admonition.caution>.admonition-title:before{background-color:var(--color-admonition-title--caution);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.warning{border-left-color:var(--color-admonition-title--warning)}.admonition.warning>.admonition-title{background-color:var(--color-admonition-title-background--warning)}.admonition.warning>.admonition-title:before{background-color:var(--color-admonition-title--warning);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.danger{border-left-color:var(--color-admonition-title--danger)}.admonition.danger>.admonition-title{background-color:var(--color-admonition-title-background--danger)}.admonition.danger>.admonition-title:before{background-color:var(--color-admonition-title--danger);-webkit-mask-image:var(--icon-spark);mask-image:var(--icon-spark)}.admonition.attention{border-left-color:var(--color-admonition-title--attention)}.admonition.attention>.admonition-title{background-color:var(--color-admonition-title-background--attention)}.admonition.attention>.admonition-title:before{background-color:var(--color-admonition-title--attention);-webkit-mask-image:var(--icon-warning);mask-image:var(--icon-warning)}.admonition.error{border-left-color:var(--color-admonition-title--error)}.admonition.error>.admonition-title{background-color:var(--color-admonition-title-background--error)}.admonition.error>.admonition-title:before{background-color:var(--color-admonition-title--error);-webkit-mask-image:var(--icon-failure);mask-image:var(--icon-failure)}.admonition.hint{border-left-color:var(--color-admonition-title--hint)}.admonition.hint>.admonition-title{background-color:var(--color-admonition-title-background--hint)}.admonition.hint>.admonition-title:before{background-color:var(--color-admonition-title--hint);-webkit-mask-image:var(--icon-question);mask-image:var(--icon-question)}.admonition.tip{border-left-color:var(--color-admonition-title--tip)}.admonition.tip>.admonition-title{background-color:var(--color-admonition-title-background--tip)}.admonition.tip>.admonition-title:before{background-color:var(--color-admonition-title--tip);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.important{border-left-color:var(--color-admonition-title--important)}.admonition.important>.admonition-title{background-color:var(--color-admonition-title-background--important)}.admonition.important>.admonition-title:before{background-color:var(--color-admonition-title--important);-webkit-mask-image:var(--icon-flame);mask-image:var(--icon-flame)}.admonition.note{border-left-color:var(--color-admonition-title--note)}.admonition.note>.admonition-title{background-color:var(--color-admonition-title-background--note)}.admonition.note>.admonition-title:before{background-color:var(--color-admonition-title--note);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition.seealso{border-left-color:var(--color-admonition-title--seealso)}.admonition.seealso>.admonition-title{background-color:var(--color-admonition-title-background--seealso)}.admonition.seealso>.admonition-title:before{background-color:var(--color-admonition-title--seealso);-webkit-mask-image:var(--icon-info);mask-image:var(--icon-info)}.admonition.admonition-todo{border-left-color:var(--color-admonition-title--admonition-todo)}.admonition.admonition-todo>.admonition-title{background-color:var(--color-admonition-title-background--admonition-todo)}.admonition.admonition-todo>.admonition-title:before{background-color:var(--color-admonition-title--admonition-todo);-webkit-mask-image:var(--icon-pencil);mask-image:var(--icon-pencil)}.admonition-todo>.admonition-title{text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd{margin-left:2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:first-child{margin-top:.125rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list,dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) dd>:last-child{margin-bottom:.75rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list>dt{font-size:var(--font-size--small);text-transform:uppercase}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd:empty{margin-bottom:.5rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul{margin-left:-1.2rem}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p:nth-child(2){margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple) .field-list dd>ul>li>p+p:last-child:empty{margin-bottom:0;margin-top:0}dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt{color:var(--color-api-overall)}.sig:not(.sig-inline){background:var(--color-api-background);border-radius:.25rem;font-family:var(--font-stack--monospace);font-size:var(--api-font-size);font-weight:700;margin-left:-.25rem;margin-right:-.25rem;padding:.25rem .5rem .25rem 3em;text-indent:-2.5em;transition:background .1s ease-out}.sig:not(.sig-inline):hover{background:var(--color-api-background-hover)}.sig:not(.sig-inline) a.reference .viewcode-link{font-weight:400;width:3.5rem}em.property{font-style:normal}em.property:first-child{color:var(--color-api-keyword)}.sig-name{color:var(--color-api-name)}.sig-prename{color:var(--color-api-pre-name);font-weight:400}.sig-paren{color:var(--color-api-paren)}.sig-param{font-style:normal}.versionmodified{font-style:italic}div.deprecated p,div.versionadded p,div.versionchanged p{margin-bottom:.125rem;margin-top:.125rem}.viewcode-back,.viewcode-link{float:right;text-align:right}.line-block{margin-bottom:.75rem;margin-top:.5rem}.line-block .line-block{margin-bottom:0;margin-top:0;padding-left:1rem}.code-block-caption,article p.caption,table>caption{font-size:var(--font-size--small);text-align:center}.toctree-wrapper.compound .caption,.toctree-wrapper.compound :not(.caption)>.caption-text{font-size:var(--font-size--small);margin-bottom:0;text-align:initial;text-transform:uppercase}.toctree-wrapper.compound>ul{margin-bottom:0;margin-top:0}.sig-inline,code.literal{background:var(--color-inline-code-background);border-radius:.2em;font-size:var(--font-size--small--2);padding:.1em .2em}pre.literal-block .sig-inline,pre.literal-block code.literal{font-size:inherit;padding:0}p .sig-inline,p code.literal{border:1px solid var(--color-background-border)}.sig-inline{font-family:var(--font-stack--monospace)}div[class*=" highlight-"],div[class^=highlight-]{display:flex;margin:1em 0}div[class*=" highlight-"] .table-wrapper,div[class^=highlight-] .table-wrapper,pre{margin:0;padding:0}pre{overflow:auto}article[role=main] .highlight pre{line-height:1.5}.highlight pre,pre.literal-block{font-size:var(--code-font-size);padding:.625rem .875rem}pre.literal-block{background-color:var(--color-code-background);border-radius:.2rem;color:var(--color-code-foreground);margin-bottom:1rem;margin-top:1rem}.highlight{border-radius:.2rem;width:100%}.highlight .gp,.highlight span.linenos{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.highlight .hll{display:block;margin-left:-.875rem;margin-right:-.875rem;padding-left:.875rem;padding-right:.875rem}.code-block-caption{background-color:var(--color-code-background);border-bottom:1px solid;border-radius:.25rem;border-bottom-left-radius:0;border-bottom-right-radius:0;border-color:var(--color-background-border);color:var(--color-code-foreground);display:flex;font-weight:300;padding:.625rem .875rem}.code-block-caption+div[class]{margin-top:0}.code-block-caption+div[class] pre{border-top-left-radius:0;border-top-right-radius:0}.highlighttable{display:block;width:100%}.highlighttable tbody{display:block}.highlighttable tr{display:flex}.highlighttable td.linenos{background-color:var(--color-code-background);border-bottom-left-radius:.2rem;border-top-left-radius:.2rem;color:var(--color-code-foreground);padding:.625rem 0 .625rem .875rem}.highlighttable .linenodiv{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;font-size:var(--code-font-size);padding-right:.875rem}.highlighttable td.code{display:block;flex:1;overflow:hidden;padding:0}.highlighttable td.code .highlight{border-bottom-left-radius:0;border-top-left-radius:0}.highlight span.linenos{box-shadow:-.0625rem 0 var(--color-foreground-border) inset;display:inline-block;margin-right:.875rem;padding-left:0;padding-right:.875rem}.footnote-reference{font-size:var(--font-size--small--4);vertical-align:super}dl.footnote.brackets{color:var(--color-foreground-secondary);display:grid;font-size:var(--font-size--small);grid-template-columns:max-content auto}dl.footnote.brackets dt{margin:0}dl.footnote.brackets dt>.fn-backref{margin-left:.25rem}dl.footnote.brackets dt:after{content:":"}dl.footnote.brackets dt .brackets:before{content:"["}dl.footnote.brackets dt .brackets:after{content:"]"}dl.footnote.brackets dd{margin:0;padding:0 1rem}aside.footnote{color:var(--color-foreground-secondary);font-size:var(--font-size--small)}aside.footnote>span,div.citation>span{float:left;font-weight:500;padding-right:.25rem}aside.footnote>p,div.citation>p{margin-left:2rem}img{box-sizing:border-box;height:auto;max-width:100%}article .figure,article figure{border-radius:.2rem;margin:0}article .figure :last-child,article figure :last-child{margin-bottom:0}article .align-left{clear:left;float:left;margin:0 1rem 1rem}article .align-right{clear:right;float:right;margin:0 1rem 1rem}article .align-center,article .align-default{display:block;margin-left:auto;margin-right:auto;text-align:center}article table.align-default{display:table;text-align:initial}.domainindex-jumpbox,.genindex-jumpbox{border-bottom:1px solid var(--color-background-border);border-top:1px solid var(--color-background-border);padding:.25rem}.domainindex-section h2,.genindex-section h2{margin-bottom:.5rem;margin-top:.75rem}.domainindex-section ul,.genindex-section ul{margin-bottom:0;margin-top:0}ol,ul{margin-bottom:1rem;margin-top:1rem;padding-left:1.2rem}ol li>p:first-child,ul li>p:first-child{margin-bottom:.25rem;margin-top:.25rem}ol li>p:last-child,ul li>p:last-child{margin-top:.25rem}ol li>ol,ol li>ul,ul li>ol,ul li>ul{margin-bottom:.5rem;margin-top:.5rem}ol.arabic{list-style:decimal}ol.loweralpha{list-style:lower-alpha}ol.upperalpha{list-style:upper-alpha}ol.lowerroman{list-style:lower-roman}ol.upperroman{list-style:upper-roman}.simple li>ol,.simple li>ul,.toctree-wrapper li>ol,.toctree-wrapper li>ul{margin-bottom:0;margin-top:0}.field-list dt,.option-list dt,dl.footnote dt,dl.glossary dt,dl.simple dt,dl:not([class]) dt{font-weight:500;margin-top:.25rem}.field-list dt+dt,.option-list dt+dt,dl.footnote dt+dt,dl.glossary dt+dt,dl.simple dt+dt,dl:not([class]) dt+dt{margin-top:0}.field-list dt .classifier:before,.option-list dt .classifier:before,dl.footnote dt .classifier:before,dl.glossary dt .classifier:before,dl.simple dt .classifier:before,dl:not([class]) dt .classifier:before{content:":";margin-left:.2rem;margin-right:.2rem}.field-list dd ul,.field-list dd>p:first-child,.option-list dd ul,.option-list dd>p:first-child,dl.footnote dd ul,dl.footnote dd>p:first-child,dl.glossary dd ul,dl.glossary dd>p:first-child,dl.simple dd ul,dl.simple dd>p:first-child,dl:not([class]) dd ul,dl:not([class]) dd>p:first-child{margin-top:.125rem}.field-list dd ul,.option-list dd ul,dl.footnote dd ul,dl.glossary dd ul,dl.simple dd ul,dl:not([class]) dd ul{margin-bottom:.125rem}.math-wrapper{overflow-x:auto;width:100%}div.math{position:relative;text-align:center}div.math .headerlink,div.math:focus .headerlink{display:none}div.math:hover .headerlink{display:inline-block}div.math span.eqno{position:absolute;right:.5rem;top:50%;transform:translateY(-50%);z-index:1}abbr[title]{cursor:help}.problematic{color:var(--color-problematic)}kbd:not(.compound){background-color:var(--color-background-secondary);border:1px solid var(--color-foreground-border);border-radius:.2rem;box-shadow:0 .0625rem 0 rgba(0,0,0,.2),inset 0 0 0 .125rem var(--color-background-primary);color:var(--color-foreground-primary);display:inline-block;font-size:var(--font-size--small--3);margin:0 .2rem;padding:0 .2rem;vertical-align:text-bottom}blockquote{background:var(--color-background-secondary);border-left:4px solid var(--color-background-border);margin-left:0;margin-right:0;padding:.5rem 1rem}blockquote .attribution{font-weight:600;text-align:right}blockquote.highlights,blockquote.pull-quote{font-size:1.25em}blockquote.epigraph,blockquote.pull-quote{border-left-width:0;border-radius:.5rem}blockquote.highlights{background:transparent;border-left-width:0}p .reference img{vertical-align:middle}p.rubric{font-size:1.125em;font-weight:700;line-height:1.25}dd p.rubric{font-size:var(--font-size--small);font-weight:inherit;line-height:inherit;text-transform:uppercase}article .sidebar{background-color:var(--color-background-secondary);border:1px solid var(--color-background-border);border-radius:.2rem;clear:right;float:right;margin-left:1rem;margin-right:0;width:30%}article .sidebar>*{padding-left:1rem;padding-right:1rem}article .sidebar>ol,article .sidebar>ul{padding-left:2.2rem}article .sidebar .sidebar-title{border-bottom:1px solid var(--color-background-border);font-weight:500;margin:0;padding:.5rem 1rem}.table-wrapper{margin-bottom:.5rem;margin-top:1rem;overflow-x:auto;padding:.2rem .2rem .75rem;width:100%}table.docutils{border-collapse:collapse;border-radius:.2rem;border-spacing:0;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .0625rem rgba(0,0,0,.1)}table.docutils th{background:var(--color-table-header-background)}table.docutils td,table.docutils th{border-bottom:1px solid var(--color-table-border);border-left:1px solid var(--color-table-border);border-right:1px solid var(--color-table-border);padding:0 .25rem}table.docutils td p,table.docutils th p{margin:.25rem}table.docutils td:first-child,table.docutils th:first-child{border-left:none}table.docutils td:last-child,table.docutils th:last-child{border-right:none}table.docutils td.text-left,table.docutils th.text-left{text-align:left}table.docutils td.text-right,table.docutils th.text-right{text-align:right}table.docutils td.text-center,table.docutils th.text-center{text-align:center}:target{scroll-margin-top:.5rem}@media(max-width:67em){:target{scroll-margin-top:calc(.5rem + var(--header-height))}section>span:target{scroll-margin-top:calc(.8rem + var(--header-height))}}.headerlink{font-weight:100;-webkit-user-select:none;-moz-user-select:none;user-select:none}.code-block-caption>.headerlink,dl dt>.headerlink,figcaption p>.headerlink,h1>.headerlink,h2>.headerlink,h3>.headerlink,h4>.headerlink,h5>.headerlink,h6>.headerlink,p.caption>.headerlink,table>caption>.headerlink{margin-left:.5rem;visibility:hidden}.code-block-caption:hover>.headerlink,dl dt:hover>.headerlink,figcaption p:hover>.headerlink,h1:hover>.headerlink,h2:hover>.headerlink,h3:hover>.headerlink,h4:hover>.headerlink,h5:hover>.headerlink,h6:hover>.headerlink,p.caption:hover>.headerlink,table>caption:hover>.headerlink{visibility:visible}.code-block-caption>.toc-backref,dl dt>.toc-backref,figcaption p>.toc-backref,h1>.toc-backref,h2>.toc-backref,h3>.toc-backref,h4>.toc-backref,h5>.toc-backref,h6>.toc-backref,p.caption>.toc-backref,table>caption>.toc-backref{color:inherit;text-decoration-line:none}figure:hover>figcaption>p>.headerlink,table:hover>caption>.headerlink{visibility:visible}:target>h1:first-of-type,:target>h2:first-of-type,:target>h3:first-of-type,:target>h4:first-of-type,:target>h5:first-of-type,:target>h6:first-of-type,span:target~h1:first-of-type,span:target~h2:first-of-type,span:target~h3:first-of-type,span:target~h4:first-of-type,span:target~h5:first-of-type,span:target~h6:first-of-type{background-color:var(--color-highlight-on-target)}:target>h1:first-of-type code.literal,:target>h2:first-of-type code.literal,:target>h3:first-of-type code.literal,:target>h4:first-of-type code.literal,:target>h5:first-of-type code.literal,:target>h6:first-of-type code.literal,span:target~h1:first-of-type code.literal,span:target~h2:first-of-type code.literal,span:target~h3:first-of-type code.literal,span:target~h4:first-of-type code.literal,span:target~h5:first-of-type code.literal,span:target~h6:first-of-type code.literal{background-color:transparent}.literal-block-wrapper:target .code-block-caption,.this-will-duplicate-information-and-it-is-still-useful-here li :target,figure:target,table:target>caption{background-color:var(--color-highlight-on-target)}dt:target{background-color:var(--color-highlight-on-target)!important}.footnote-reference:target,.footnote>dt:target+dd{background-color:var(--color-highlight-on-target)}.guilabel{background-color:var(--color-guilabel-background);border:1px solid var(--color-guilabel-border);border-radius:.5em;color:var(--color-guilabel-text);font-size:.9em;padding:0 .3em}footer{display:flex;flex-direction:column;font-size:var(--font-size--small);margin-top:2rem}.bottom-of-page{align-items:center;border-top:1px solid var(--color-background-border);color:var(--color-foreground-secondary);display:flex;justify-content:space-between;line-height:1.5;margin-top:1rem;padding-bottom:1rem;padding-top:1rem}@media(max-width:46em){.bottom-of-page{flex-direction:column-reverse;gap:.25rem;text-align:center}}.bottom-of-page .left-details{font-size:var(--font-size--small)}.bottom-of-page .right-details{display:flex;flex-direction:column;gap:.25rem;text-align:right}.bottom-of-page .icons{display:flex;font-size:1rem;gap:.25rem;justify-content:flex-end}.bottom-of-page .icons a{text-decoration:none}.bottom-of-page .icons img,.bottom-of-page .icons svg{font-size:1.125rem;height:1em;width:1em}.related-pages a{align-items:center;display:flex;text-decoration:none}.related-pages a:hover .page-info .title{color:var(--color-link);text-decoration:underline;text-decoration-color:var(--color-link-underline)}.related-pages a svg.furo-related-icon,.related-pages a svg.furo-related-icon>use{color:var(--color-foreground-border);flex-shrink:0;height:.75rem;margin:0 .5rem;width:.75rem}.related-pages a.next-page{clear:right;float:right;max-width:50%;text-align:right}.related-pages a.prev-page{clear:left;float:left;max-width:50%}.related-pages a.prev-page svg{transform:rotate(180deg)}.page-info{display:flex;flex-direction:column;overflow-wrap:anywhere}.next-page .page-info{align-items:flex-end}.page-info .context{align-items:center;color:var(--color-foreground-muted);display:flex;font-size:var(--font-size--small);padding-bottom:.1rem;text-decoration:none}ul.search{list-style:none;padding-left:0}ul.search li{border-bottom:1px solid var(--color-background-border);padding:1rem 0}[role=main] .highlighted{background-color:var(--color-highlighted-background);color:var(--color-highlighted-text)}.sidebar-brand{display:flex;flex-direction:column;flex-shrink:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none}.sidebar-brand-text{color:var(--color-sidebar-brand-text);font-size:1.5rem;overflow-wrap:break-word}.sidebar-brand-text,.sidebar-logo-container{margin:var(--sidebar-item-spacing-vertical) 0}.sidebar-logo{display:block;margin:0 auto;max-width:100%}.sidebar-search-container{align-items:center;background:var(--color-sidebar-search-background);display:flex;margin-top:var(--sidebar-search-space-above);position:relative}.sidebar-search-container:focus-within,.sidebar-search-container:hover{background:var(--color-sidebar-search-background--focus)}.sidebar-search-container:before{background-color:var(--color-sidebar-search-icon);content:"";height:var(--sidebar-search-icon-size);left:var(--sidebar-item-spacing-horizontal);-webkit-mask-image:var(--icon-search);mask-image:var(--icon-search);position:absolute;width:var(--sidebar-search-icon-size)}.sidebar-search{background:transparent;border:none;border-bottom:1px solid var(--color-sidebar-search-border);border-top:1px solid var(--color-sidebar-search-border);box-sizing:border-box;color:var(--color-sidebar-search-foreground);padding:var(--sidebar-search-input-spacing-vertical) var(--sidebar-search-input-spacing-horizontal) var(--sidebar-search-input-spacing-vertical) calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size));width:100%;z-index:10}.sidebar-search:focus{outline:none}.sidebar-search::-moz-placeholder{font-size:var(--sidebar-search-input-font-size)}.sidebar-search::placeholder{font-size:var(--sidebar-search-input-font-size)}#searchbox .highlight-link{margin:0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0;text-align:center}#searchbox .highlight-link a{color:var(--color-sidebar-search-icon);font-size:var(--font-size--small--2)}.sidebar-tree{font-size:var(--sidebar-item-font-size);margin-bottom:var(--sidebar-item-spacing-vertical);margin-top:var(--sidebar-tree-space-above)}.sidebar-tree ul{display:flex;flex-direction:column;list-style:none;margin-bottom:0;margin-top:0;padding:0}.sidebar-tree li{margin:0;position:relative}.sidebar-tree li>ul{margin-left:var(--sidebar-item-spacing-horizontal)}.sidebar-tree .icon,.sidebar-tree .reference{color:var(--color-sidebar-link-text)}.sidebar-tree .reference{box-sizing:border-box;display:inline-block;height:100%;line-height:var(--sidebar-item-line-height);overflow-wrap:anywhere;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-decoration:none;width:100%}.sidebar-tree .reference:hover{background:var(--color-sidebar-item-background--hover)}.sidebar-tree .reference.external:after{color:var(--color-sidebar-link-text);content:url("data:image/svg+xml;charset=utf-8,%3Csvg width='12' height='12' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' stroke-width='1.5' stroke='%23607D8B' fill='none' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M0 0h24v24H0z' stroke='none'/%3E%3Cpath d='M11 7H6a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2-2v-5M10 14 20 4M15 4h5v5'/%3E%3C/svg%3E");margin:0 .25rem;vertical-align:middle}.sidebar-tree .current-page>.reference{font-weight:700}.sidebar-tree label{align-items:center;cursor:pointer;display:flex;height:var(--sidebar-item-height);justify-content:center;position:absolute;right:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:var(--sidebar-expander-width)}.sidebar-tree .caption,.sidebar-tree :not(.caption)>.caption-text{color:var(--color-sidebar-caption-text);font-size:var(--sidebar-caption-font-size);font-weight:700;margin:var(--sidebar-caption-space-above) 0 0 0;padding:var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal);text-transform:uppercase}.sidebar-tree li.has-children>.reference{padding-right:var(--sidebar-expander-width)}.sidebar-tree .toctree-l1>.reference,.sidebar-tree .toctree-l1>label .icon{color:var(--color-sidebar-link-text--top-level)}.sidebar-tree label{background:var(--color-sidebar-item-expander-background)}.sidebar-tree label:hover{background:var(--color-sidebar-item-expander-background--hover)}.sidebar-tree .current>.reference{background:var(--color-sidebar-item-background--current)}.sidebar-tree .current>.reference:hover{background:var(--color-sidebar-item-background--hover)}.toctree-checkbox{display:none;position:absolute}.toctree-checkbox~ul{display:none}.toctree-checkbox~label .icon svg{transform:rotate(90deg)}.toctree-checkbox:checked~ul{display:block}.toctree-checkbox:checked~label .icon svg{transform:rotate(-90deg)}.toc-title-container{padding:var(--toc-title-padding);padding-top:var(--toc-spacing-vertical)}.toc-title{color:var(--color-toc-title-text);font-size:var(--toc-title-font-size);padding-left:var(--toc-spacing-horizontal);text-transform:uppercase}.no-toc{display:none}.toc-tree-container{padding-bottom:var(--toc-spacing-vertical)}.toc-tree{border-left:1px solid var(--color-background-border);font-size:var(--toc-font-size);line-height:1.3;padding-left:calc(var(--toc-spacing-horizontal) - var(--toc-item-spacing-horizontal))}.toc-tree>ul>li:first-child{padding-top:0}.toc-tree>ul>li:first-child>ul{padding-left:0}.toc-tree>ul>li:first-child>a{display:none}.toc-tree ul{list-style-type:none;margin-bottom:0;margin-top:0;padding-left:var(--toc-item-spacing-horizontal)}.toc-tree li{padding-top:var(--toc-item-spacing-vertical)}.toc-tree li.scroll-current>.reference{color:var(--color-toc-item-text--active);font-weight:700}.toc-tree .reference{color:var(--color-toc-item-text);overflow-wrap:anywhere;text-decoration:none}.toc-scroll{max-height:100vh;overflow-y:scroll}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here){background:rgba(255,0,0,.25);color:var(--color-problematic)}.contents:not(.this-will-duplicate-information-and-it-is-still-useful-here):before{content:"ERROR: Adding a table of contents in Furo-based documentation is unnecessary, and does not work well with existing styling.Add a 'this-will-duplicate-information-and-it-is-still-useful-here' class, if you want an escape hatch."}.text-align\:left>p{text-align:left}.text-align\:center>p{text-align:center}.text-align\:right>p{text-align:right} +/*# sourceMappingURL=furo.css.map*/ \ No newline at end of file diff --git a/_static/styles/furo.css.map b/_static/styles/furo.css.map new file mode 100644 index 0000000..d1dfb10 --- /dev/null +++ b/_static/styles/furo.css.map @@ -0,0 +1 @@ +{"version":3,"file":"styles/furo.css","mappings":"AAAA,2EAA2E,CAU3E,KAEE,6BAA8B,CAD9B,gBAEF,CASA,KACE,QACF,CAMA,KACE,aACF,CAOA,GACE,aAAc,CACd,cACF,CAUA,GACE,sBAAuB,CACvB,QAAS,CACT,gBACF,CAOA,IACE,+BAAiC,CACjC,aACF,CASA,EACE,4BACF,CAOA,YACE,kBAAmB,CACnB,yBAA0B,CAC1B,gCACF,CAMA,SAEE,kBACF,CAOA,cAGE,+BAAiC,CACjC,aACF,CAeA,QAEE,aAAc,CACd,aAAc,CACd,iBAAkB,CAClB,uBACF,CAEA,IACE,aACF,CAEA,IACE,SACF,CASA,IACE,iBACF,CAUA,sCAKE,mBAAoB,CACpB,cAAe,CACf,gBAAiB,CACjB,QACF,CAOA,aAEE,gBACF,CAOA,cAEE,mBACF,CAMA,gDAIE,yBACF,CAMA,wHAIE,iBAAkB,CAClB,SACF,CAMA,4GAIE,6BACF,CAMA,SACE,0BACF,CASA,OACE,qBAAsB,CACtB,aAAc,CACd,aAAc,CACd,cAAe,CACf,SAAU,CACV,kBACF,CAMA,SACE,uBACF,CAMA,SACE,aACF,CAOA,6BAEE,qBAAsB,CACtB,SACF,CAMA,kFAEE,WACF,CAOA,cACE,4BAA6B,CAC7B,mBACF,CAMA,yCACE,uBACF,CAOA,6BACE,yBAA0B,CAC1B,YACF,CASA,QACE,aACF,CAMA,QACE,iBACF,CAiBA,kBACE,YACF,CCvVA,aAcE,kEACE,uBAOF,WACE,iDAMF,gCACE,wBAEF,qCAEE,uBADA,uBACA,CAEF,SACE,wBAtBA,CCpBJ,iBAOE,6BAEA,mBANA,qBAEA,sBACA,0BAFA,oBAHA,4BAOA,6BANA,mBAOA,CAEF,gBACE,aCPF,KCGE,mHAEA,wGAGA,wBAAyB,CACzB,wBAAyB,CACzB,4BAA6B,CAC7B,yBAA0B,CAC1B,2BAA4B,CAG5B,sDAAuD,CACvD,gDAAiD,CACjD,wDAAyD,CAGzD,0CAA2C,CAC3C,gDAAiD,CACjD,gDAAiD,CAKjD,gCAAiC,CACjC,sCAAuC,CAGvC,2CAA4C,CAG5C,uCAAwC,CChCxC,+FAGA,uBAAwB,CAGxB,iCAAkC,CAClC,kCAAmC,CAEnC,+BAAgC,CAChC,sCAAuC,CACvC,sCAAuC,CACvC,qGAIA,mDAAoD,CAEpD,mCAAoC,CACpC,8CAA+C,CAC/C,gDAAiD,CACjD,kCAAmC,CACnC,6DAA8D,CAG9D,6BAA8B,CAC9B,6BAA8B,CAC9B,+BAAgC,CAChC,kCAAmC,CACnC,kCAAmC,CCPjC,ukBCYA,srCAZF,kaCVA,mLAOA,oTAWA,2UAaA,0CACA,gEACA,0CAGA,gEAUA,yCACA,+DAGA,4CACA,CACA,iEAGA,sGACA,uCACA,4DAGA,sCACA,2DAEA,4CACA,kEACA,oGACA,CAEA,0GACA,+CAGA,+MAOA,+EACA,wCAIA,4DACA,sEACA,kEACA,sEACA,gDAGA,+DACA,0CACA,gEACA,gGACA,CAGA,2DACA,qDAGA,0CACA,8CACA,oDACA,oDL7GF,iCAEA,iEAME,oCKyGA,yDAIA,sCACA,kCACA,sDAGA,0CACA,kEACA,oDAEA,sDAGA,oCACA,oEAIA,CAGA,yDAGA,qDACA,oDAGA,6DAIA,iEAGA,2DAEA,2DL9IE,4DAEA,gEAIF,gEKgGA,gFAIA,oNAOA,qDAEA,gFAIA,4DAIA,oEAMA,yEAIA,6DACA,0DAGA,uDAGA,qDAEA,wDLpII,6DAEA,yDACE,2DAMN,uCAIA,yCACE,8CAGF,sDMjDA,6DAKA,oCAIA,4CACA,kBAGF,sBAMA,2BAME,qCAGA,qCAEA,iCAEA,+BAEA,mCAEA,qCAIA,CACA,gCACA,gDAKA,kCAIA,6BAEA,0CAQA,kCAIF,8BAGE,8BACA,uCAGF,sCAKE,kCAEA,sDAGA,iCACE,CACA,2FAGA,gCACE,CACA,+DCzEJ,wCAEA,sBAEF,yDAEE,mCACA,wDAGA,2GAGA,wIACE,gDAMJ,kCAGE,6BACA,0CAGA,gEACA,8BACA,uCAKA,sCAIA,kCACA,sDACA,iCACA,sCAOA,sDAKE,gGAIE,+CAGN,sBAEE,yCAMA,0BAMA,yLAMA,aACA,MAEF,6BACE,2DAIF,wCAIE,kCAGA,SACA,kCAKA,mBAGA,CAJA,eACA,CAHF,gBAEE,CAWA,mBACA,mBACA,mDAGA,YACA,CACA,kBACA,CAEE,kBAKJ,OAPE,kBAQA,CADF,GACE,iCACA,wCAEA,wBACA,aACA,CAFA,WAEA,GACA,oBACA,CAFA,gBAEA,aACE,+CAIF,UAJE,kCAIF,WACA,iBACA,GAGA,uBACE,CAJF,yBAGA,CACE,iDACA,uCAEA,yDACE,cACA,wDAKN,yDAIE,uBAEF,kBACE,uBAEA,kDAIA,0DAGA,CAHA,oBAGA,0GAYA,aAEA,CAHA,YAGA,4HAKF,+CAGE,sBAEF,WAKE,0CAEA,CALA,qCAGA,CAJA,WAOA,SAIA,2CAJA,qCAIA,CACE,wBACA,OACA,YAEJ,gBACE,gBAIA,+CAKF,CAGE,kDAGA,CANF,8BAGE,CAGA,YAEA,CAdF,2BACE,CAHA,UAEF,CAYE,UAEA,CACA,0CACF,iEAOE,iCACA,8BAGA,wCAIA,wBAKE,0CAKF,CARE,6DAGA,CALF,qBAEE,CASA,YACA,yBAGA,CAEE,cAKN,CAPI,sBAOJ,gCAGE,qBAEA,WACA,aACA,sCAEA,mBACA,6BAGA,uEADA,qBACA,6BAIA,yBACA,qCAEE,UAEA,YACA,sBAEF,8BAGA,CAPE,aACA,WAMF,4BACE,sBACA,WAMJ,uBACE,cAYE,mBAXA,qDAKA,qCAGA,CAEA,YACA,CAHA,2BAEA,CACA,oCAEA,4CACA,uBAIA,oCAEJ,CAFI,cAIF,iBACE,CAHJ,kBAGI,yBAEA,oCAIA,qDAMF,mEAEA,CACE,8CAKA,gCAEA,qCAGA,oCAGE,sBACA,CAJF,WAEE,CAFF,eAEE,SAEA,mBACA,qCACE,aACA,CAFF,YADA,qBACA,WAEE,sBACA,kEAEN,2BAEE,iDAKA,uCAGF,CACE,0DAKA,kBACF,CAFE,sBAGA,mBACA,0BAEJ,yBAII,aADA,WACA,CAMF,UAFE,kBAEF,CAJF,gBACE,CAHE,iBAMF,6CC9ZF,yBACE,WACA,iBAEA,aAFA,iBAEA,6BAEA,kCACA,mBAKA,gCAGA,CARA,QAEA,CAGA,UALA,qBAEA,qDAGA,CALA,OAQA,4BACE,cAGF,2BACE,gCAEJ,CAHE,UAGF,8CAGE,CAHF,UAGE,wCAGA,qBACA,CAFA,UAEA,6CAGA,yCAIA,sBAHA,UAGA,kCACE,OACA,CAFF,KAEE,cAQF,0CACE,CAFF,kBACA,CACE,wEACA,CARA,YACA,CAKF,mBAFF,OAII,eACA,CAJF,iCAJE,cAGJ,CANI,oBAEA,CAKF,SAIE,2BADA,UACA,kBAGF,sCACA,CAFF,WACE,WACA,qCACE,gCACA,2EACA,sDAKJ,aACE,mDAII,CAJJ,6CAII,kEACA,iBACE,iDACA,+CACE,aACA,WADA,+BACA,uEANN,YACE,mDAEE,mBADF,0CACE,CADF,qBACE,0DACA,YACE,4DACA,sEANN,YACE,8CACA,kBADA,UACA,2CACE,2EACA,cACE,kEACA,mEANN,yBACE,4DACA,sBACE,+EAEE,iEACA,qEANN,sCACE,CAGE,iBAHF,gBAGE,qBACE,CAJJ,uBACA,gDACE,wDACA,6DAHF,2CACA,CADA,gBACA,eACE,CAGE,sBANN,8BACE,CAII,iBAFF,4DACA,WACE,YADF,uCACE,6EACA,2BANN,8CACE,kDACA,0CACE,8BACA,yFACE,sBACA,sFALJ,mEACA,sBACE,kEACA,6EACE,uCACA,kEALJ,qGAEE,kEACA,6EACE,uCACA,kEALJ,8CACA,uDACE,sEACA,2EACE,sCACA,iEALJ,mGACA,qCACE,oDACA,0DACE,6GACA,gDAGR,yDCrEA,sEACE,CACA,6GACE,gEACF,iGAIF,wFACE,qDAGA,mGAEE,2CAEF,4FACE,gCACF,wGACE,8DAEE,6FAIA,iJAKN,6GACE,gDAKF,yDACA,qCAGA,6BACA,kBACA,qDAKA,oCAEA,+DAGA,2CAGE,oDAIA,oEAEE,qBAGJ,wDAEE,uCAEF,kEAGA,8CAEA,uDAKA,oCAEA,yDAEE,gEAKF,+CC5FA,0EAGE,CACA,qDCLJ,+DAIE,sCAIA,kEACE,yBACA,2FAMA,gBACA,yGCbF,mBAOA,2MAIA,4HAYA,0DACE,8GAYF,8HAQE,mBAEA,6HAOF,YAGA,mIAME,eACA,CAFF,YAEE,4FAMJ,8BAEE,uBAYA,sCAEE,CAJF,oBAEA,CARA,wCAEA,CAHA,8BACA,CAFA,eACA,CAGA,wCAEA,CAEA,mDAIE,kCACE,6BACA,4CAKJ,kDAIA,eACE,aAGF,8BACE,uDACA,sCACA,cAEA,+BACA,CAFA,eAEA,wCAEF,YACE,iBACA,mCACA,0DAGF,qBAEE,CAFF,kBAEE,+BAIA,yCAEE,qBADA,gBACA,yBAKF,eACA,CAFF,YACE,CACA,iBACA,qDAEA,mDCvIJ,2FAOE,iCACA,CAEA,eACA,CAHA,kBAEA,CAFA,wBAGA,8BACA,eACE,CAFF,YAEE,0BACA,8CAGA,oBACE,oCAGA,kBACE,8DAEA,iBAEN,UACE,8BAIJ,+CAEE,qDAEF,kDAIE,YAEF,CAFE,YAEF,CCjCE,mFAJA,QACA,UAIE,CADF,iBACE,mCAGA,iDACE,+BAGF,wBAEA,mBAKA,6CAEF,CAHE,mBACA,CAEF,kCAIE,CARA,kBACA,CAFF,eASE,YACA,mBAGF,CAJE,UAIF,wCCjCA,oBDmCE,wBCpCJ,uCACE,8BACA,4CACA,oBAGA,2CCAA,6CAGE,CAPF,uBAIA,CDGA,gDACE,6BCVJ,CAWM,2CAEF,CAJA,kCAEE,CDJF,aCLF,gBDKE,uBCMA,gCAGA,gDAGE,wBAGJ,0BAEA,iBACE,aACF,CADE,UACF,uBACE,aACF,oBACE,YACF,4BACE,6CAMA,CAYF,6DAZE,mCAGE,iCASJ,4BAGE,4DADA,+BACA,CAFA,qBAEA,yBACE,aAEF,wBAHA,SAGA,iHACE,2DAKF,CANA,yCACE,CADF,oCAMA,uSAIA,sGACE,oDChEJ,WAEF,yBACE,QACA,eAEA,gBAEE,uCAGA,CALF,iCAKE,uCAGA,0BACA,CACA,oBACA,iCClBJ,gBACE,KAGF,qBACE,YAGF,CAHE,cAGF,gCAEE,mBACA,iEAEA,oCACA,wCAEA,sBACA,WAEA,CAFA,YAEA,8EAEA,mCAFA,iBAEA,6BAIA,wEAKA,sDAIE,CARF,mDAIA,CAIE,cAEF,8CAIA,oBAFE,iBAEF,8CAGE,eAEF,CAFE,YAEF,OAEE,kBAGJ,CAJI,eACA,CAFF,mBAKF,yCCjDE,oBACA,CAFA,iBAEA,uCAKE,iBACA,qCAGA,mBCZJ,CDWI,gBCXJ,6BAEE,eACA,sBAGA,eAEA,sBACA,oDACA,iGAMA,gBAFE,YAEF,8FAME,iJClBF,YACA,gNAUE,6BAEF,oTAcI,kBACF,gHAIA,qBACE,eACF,qDACE,kBACF,6DACE,4BCxCJ,oBAEF,qCAEI,+CAGF,uBACE,uDAGJ,oBAkBE,mDAhBA,+CAaA,CAbA,oBAaA,0FAEE,CAFF,gGAbA,+BAaA,0BAGA,mQAIA,oNAEE,iBAGJ,CAHI,gBADA,gBAIJ,8CAYI,CAZJ,wCAYI,sVACE,iCAGA,uEAHA,QAGA,qXAKJ,iDAGF,CARM,+CACE,iDAIN,CALI,gBAQN,mHACE,gBAGF,2DACE,0EAOA,0EAKA,6EC/EA,iDACA,gCACA,oDAGA,qBACA,oDCFA,cACA,eAEA,yBAGF,sBAEE,iBACA,sNAWA,iBACE,kBACA,wRAgBA,kBAEA,iOAgBA,uCACE,uEAEA,kBAEF,qUAuBE,iDAIJ,CACA,geCxFF,4BAEE,CAQA,6JACA,iDAIA,sEAGA,mDAOF,iDAGE,4DAIA,8CACA,qDAEE,eAFF,cAEE,oBAEF,uBAFE,kCAGA,eACA,iBACA,mBAIA,mDACA,CAHA,uCAEA,CAJA,0CACA,CAIA,gBAJA,gBACA,oBADA,gBAIA,wBAEJ,gBAGE,6BACA,YAHA,iBAGA,gCACA,iEAEA,6CACA,sDACA,0BADA,wBACA,0BACA,oIAIA,mBAFA,YAEA,qBACA,0CAIE,uBAEF,CAHA,yBACE,CAEF,iDACE,mFAKJ,oCACE,CANE,aAKJ,CACE,qEAIA,YAFA,WAEA,CAHA,aACA,CAEA,gBACE,4BACA,sBADA,aACA,gCAMF,oCACA,yDACA,2CAEA,qBAGE,kBAEA,CACA,mCAIF,CARE,YACA,CAOF,iCAEE,CAPA,oBACA,CAQA,oBACE,uDAEJ,sDAGA,CAHA,cAGA,0BACE,oDAIA,oCACA,4BACA,sBAGA,cAEA,oFAGA,sBAEA,yDACE,CAIA,iBAJA,wBAIA,6CAJA,6CAOA,4BAGJ,CAHI,cAGJ,yCAGA,kBACE,CAIA,iDAEA,CATA,YAEF,CACE,4CAGA,kBAIA,wEAEA,wDAIF,kCAOE,iDACA,CARF,WAIE,sCAGA,CANA,2CACA,CAMA,oEARF,iBACE,CACA,qCAMA,iBAuBE,uBAlBF,YAKA,2DALA,uDAKA,CALA,sBAiBA,4CACE,CALA,gRAIF,YACE,UAEN,uBACE,YACA,mCAOE,+CAGA,8BAGF,+CAGA,4BCjNA,SDiNA,qFCjNA,gDAGA,sCACA,qCACA,sDAIF,CAIE,kDAGA,CAPF,0CAOE,kBAEA,kDAEA,CAHA,eACA,CAFA,YACA,CADA,SAIA,mHAIE,CAGA,6CAFA,oCAeE,CAbF,yBACE,qBAEJ,CAGE,oBACA,CAEA,YAFA,2CACF,CACE,uBAEA,mFAEE,CALJ,oBACE,CAEA,UAEE,gCAGF,sDAEA,yCC7CJ,oCAGA,CD6CE,yXAQE,sCCrDJ,wCAGA,oCACE","sources":["webpack:///./node_modules/normalize.css/normalize.css","webpack:///./src/furo/assets/styles/base/_print.sass","webpack:///./src/furo/assets/styles/base/_screen-readers.sass","webpack:///./src/furo/assets/styles/base/_theme.sass","webpack:///./src/furo/assets/styles/variables/_fonts.scss","webpack:///./src/furo/assets/styles/variables/_spacing.scss","webpack:///./src/furo/assets/styles/variables/_icons.scss","webpack:///./src/furo/assets/styles/variables/_admonitions.scss","webpack:///./src/furo/assets/styles/variables/_colors.scss","webpack:///./src/furo/assets/styles/base/_typography.sass","webpack:///./src/furo/assets/styles/_scaffold.sass","webpack:///./src/furo/assets/styles/content/_admonitions.sass","webpack:///./src/furo/assets/styles/content/_api.sass","webpack:///./src/furo/assets/styles/content/_blocks.sass","webpack:///./src/furo/assets/styles/content/_captions.sass","webpack:///./src/furo/assets/styles/content/_code.sass","webpack:///./src/furo/assets/styles/content/_footnotes.sass","webpack:///./src/furo/assets/styles/content/_images.sass","webpack:///./src/furo/assets/styles/content/_indexes.sass","webpack:///./src/furo/assets/styles/content/_lists.sass","webpack:///./src/furo/assets/styles/content/_math.sass","webpack:///./src/furo/assets/styles/content/_misc.sass","webpack:///./src/furo/assets/styles/content/_rubrics.sass","webpack:///./src/furo/assets/styles/content/_sidebar.sass","webpack:///./src/furo/assets/styles/content/_tables.sass","webpack:///./src/furo/assets/styles/content/_target.sass","webpack:///./src/furo/assets/styles/content/_gui-labels.sass","webpack:///./src/furo/assets/styles/components/_footer.sass","webpack:///./src/furo/assets/styles/components/_sidebar.sass","webpack:///./src/furo/assets/styles/components/_table_of_contents.sass","webpack:///./src/furo/assets/styles/_shame.sass"],"sourcesContent":["/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n","// This file contains styles for managing print media.\n\n////////////////////////////////////////////////////////////////////////////////\n// Hide elements not relevant to print media.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Hide icon container.\n .content-icon-container\n display: none !important\n\n // Hide showing header links if hovering over when printing.\n .headerlink\n display: none !important\n\n // Hide mobile header.\n .mobile-header\n display: none !important\n\n // Hide navigation links.\n .related-pages\n display: none !important\n\n////////////////////////////////////////////////////////////////////////////////\n// Tweaks related to decolorization.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n // Apply a border around code which no longer have a color background.\n .highlight\n border: 0.1pt solid var(--color-foreground-border)\n\n////////////////////////////////////////////////////////////////////////////////\n// Avoid page break in some relevant cases.\n////////////////////////////////////////////////////////////////////////////////\n@media print\n ul, ol, dl, a, table, pre, blockquote\n page-break-inside: avoid\n\n h1, h2, h3, h4, h5, h6, img, figure, caption\n page-break-inside: avoid\n page-break-after: avoid\n\n ul, ol, dl\n page-break-before: avoid\n",".visually-hidden\n position: absolute !important\n width: 1px !important\n height: 1px !important\n padding: 0 !important\n margin: -1px !important\n overflow: hidden !important\n clip: rect(0,0,0,0) !important\n white-space: nowrap !important\n border: 0 !important\n\n:-moz-focusring\n outline: auto\n","// This file serves as the \"skeleton\" of the theming logic.\n//\n// This contains the bulk of the logic for handling dark mode, color scheme\n// toggling and the handling of color-scheme-specific hiding of elements.\n\nbody\n @include fonts\n @include spacing\n @include icons\n @include admonitions\n @include default-admonition(#651fff, \"abstract\")\n @include default-topic(#14B8A6, \"pencil\")\n\n @include colors\n\n.only-light\n display: block !important\nhtml body .only-dark\n display: none !important\n\n// Ignore dark-mode hints if print media.\n@media not print\n // Enable dark-mode, if requested.\n body[data-theme=\"dark\"]\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n // Enable dark mode, unless explicitly told to avoid.\n @media (prefers-color-scheme: dark)\n body:not([data-theme=\"light\"])\n @include colors-dark\n\n html & .only-light\n display: none !important\n .only-dark\n display: block !important\n\n//\n// Theme toggle presentation\n//\nbody[data-theme=\"auto\"]\n .theme-toggle svg.theme-icon-when-auto\n display: block\n\nbody[data-theme=\"dark\"]\n .theme-toggle svg.theme-icon-when-dark\n display: block\n\nbody[data-theme=\"light\"]\n .theme-toggle svg.theme-icon-when-light\n display: block\n","// Fonts used by this theme.\n//\n// There are basically two things here -- using the system font stack and\n// defining sizes for various elements in %ages. We could have also used `em`\n// but %age is easier to reason about for me.\n\n@mixin fonts {\n // These are adapted from https://systemfontstack.com/\n --font-stack: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,\n sans-serif, Apple Color Emoji, Segoe UI Emoji;\n --font-stack--monospace: \"SFMono-Regular\", Menlo, Consolas, Monaco,\n Liberation Mono, Lucida Console, monospace;\n\n --font-size--normal: 100%;\n --font-size--small: 87.5%;\n --font-size--small--2: 81.25%;\n --font-size--small--3: 75%;\n --font-size--small--4: 62.5%;\n\n // Sidebar\n --sidebar-caption-font-size: var(--font-size--small--2);\n --sidebar-item-font-size: var(--font-size--small);\n --sidebar-search-input-font-size: var(--font-size--small);\n\n // Table of Contents\n --toc-font-size: var(--font-size--small--3);\n --toc-font-size--mobile: var(--font-size--normal);\n --toc-title-font-size: var(--font-size--small--4);\n\n // Admonitions\n //\n // These aren't defined in terms of %ages, since nesting these is permitted.\n --admonition-font-size: 0.8125rem;\n --admonition-title-font-size: 0.8125rem;\n\n // Code\n --code-font-size: var(--font-size--small--2);\n\n // API\n --api-font-size: var(--font-size--small);\n}\n","// Spacing for various elements on the page\n//\n// If the user wants to tweak things in a certain way, they are permitted to.\n// They also have to deal with the consequences though!\n\n@mixin spacing {\n // Header!\n --header-height: calc(\n var(--sidebar-item-line-height) + 4 * #{var(--sidebar-item-spacing-vertical)}\n );\n --header-padding: 0.5rem;\n\n // Sidebar\n --sidebar-tree-space-above: 1.5rem;\n --sidebar-caption-space-above: 1rem;\n\n --sidebar-item-line-height: 1rem;\n --sidebar-item-spacing-vertical: 0.5rem;\n --sidebar-item-spacing-horizontal: 1rem;\n --sidebar-item-height: calc(\n var(--sidebar-item-line-height) + 2 *#{var(--sidebar-item-spacing-vertical)}\n );\n\n --sidebar-expander-width: var(--sidebar-item-height); // be square\n\n --sidebar-search-space-above: 0.5rem;\n --sidebar-search-input-spacing-vertical: 0.5rem;\n --sidebar-search-input-spacing-horizontal: 0.5rem;\n --sidebar-search-input-height: 1rem;\n --sidebar-search-icon-size: var(--sidebar-search-input-height);\n\n // Table of Contents\n --toc-title-padding: 0.25rem 0;\n --toc-spacing-vertical: 1.5rem;\n --toc-spacing-horizontal: 1.5rem;\n --toc-item-spacing-vertical: 0.4rem;\n --toc-item-spacing-horizontal: 1rem;\n}\n","// Expose theme icons as CSS variables.\n\n$icons: (\n // Adapted from tabler-icons\n // url: https://tablericons.com/\n \"search\":\n url('data:image/svg+xml;charset=utf-8,'),\n // Factored out from mkdocs-material on 24-Aug-2020.\n // url: https://squidfunk.github.io/mkdocs-material/reference/admonitions/\n \"pencil\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"abstract\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"info\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"flame\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"question\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"warning\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"failure\":\n url('data:image/svg+xml;charset=utf-8,'),\n \"spark\":\n url('data:image/svg+xml;charset=utf-8,')\n);\n\n@mixin icons {\n @each $name, $glyph in $icons {\n --icon-#{$name}: #{$glyph};\n }\n}\n","// Admonitions\n\n// Structure of these is:\n// admonition-class: color \"icon-name\";\n//\n// The colors are translated into CSS variables below. The icons are\n// used directly in the main declarations to set the `mask-image` in\n// the title.\n\n// prettier-ignore\n$admonitions: (\n // Each of these has an reST directives for it.\n \"caution\": #ff9100 \"spark\",\n \"warning\": #ff9100 \"warning\",\n \"danger\": #ff5252 \"spark\",\n \"attention\": #ff5252 \"warning\",\n \"error\": #ff5252 \"failure\",\n \"hint\": #00c852 \"question\",\n \"tip\": #00c852 \"info\",\n \"important\": #00bfa5 \"flame\",\n \"note\": #00b0ff \"pencil\",\n \"seealso\": #448aff \"info\",\n \"admonition-todo\": #808080 \"pencil\"\n);\n\n@mixin default-admonition($color, $icon-name) {\n --color-admonition-title: #{$color};\n --color-admonition-title-background: #{rgba($color, 0.2)};\n\n --icon-admonition-default: var(--icon-#{$icon-name});\n}\n\n@mixin default-topic($color, $icon-name) {\n --color-topic-title: #{$color};\n --color-topic-title-background: #{rgba($color, 0.2)};\n\n --icon-topic-default: var(--icon-#{$icon-name});\n}\n\n@mixin admonitions {\n @each $name, $values in $admonitions {\n --color-admonition-title--#{$name}: #{nth($values, 1)};\n --color-admonition-title-background--#{$name}: #{rgba(\n nth($values, 1),\n 0.2\n )};\n }\n}\n","// Colors used throughout this theme.\n//\n// The aim is to give the user more control. Thus, instead of hard-coding colors\n// in various parts of the stylesheet, the approach taken is to define all\n// colors as CSS variables and reusing them in all the places.\n//\n// `colors-dark` depends on `colors` being included at a lower specificity.\n\n@mixin colors {\n --color-problematic: #b30000;\n\n // Base Colors\n --color-foreground-primary: black; // for main text and headings\n --color-foreground-secondary: #5a5c63; // for secondary text\n --color-foreground-muted: #646776; // for muted text\n --color-foreground-border: #878787; // for content borders\n\n --color-background-primary: white; // for content\n --color-background-secondary: #f8f9fb; // for navigation + ToC\n --color-background-hover: #efeff4ff; // for navigation-item hover\n --color-background-hover--transparent: #efeff400;\n --color-background-border: #eeebee; // for UI borders\n --color-background-item: #ccc; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2962ff;\n --color-brand-content: #2a5adf;\n\n // API documentation\n --color-api-background: var(--color-background-hover--transparent);\n --color-api-background-hover: var(--color-background-hover);\n --color-api-overall: var(--color-foreground-secondary);\n --color-api-name: var(--color-problematic);\n --color-api-pre-name: var(--color-problematic);\n --color-api-paren: var(--color-foreground-secondary);\n --color-api-keyword: var(--color-foreground-primary);\n --color-highlight-on-target: #ffffcc;\n\n // Inline code background\n --color-inline-code-background: var(--color-background-secondary);\n\n // Highlighted text (search)\n --color-highlighted-background: #ddeeff;\n --color-highlighted-text: var(--color-foreground-primary);\n\n // GUI Labels\n --color-guilabel-background: #ddeeff80;\n --color-guilabel-border: #bedaf580;\n --color-guilabel-text: var(--color-foreground-primary);\n\n // Admonitions!\n --color-admonition-background: transparent;\n\n //////////////////////////////////////////////////////////////////////////////\n // Everything below this should be one of:\n // - var(...)\n // - *-gradient(...)\n // - special literal values (eg: transparent, none)\n //////////////////////////////////////////////////////////////////////////////\n\n // Tables\n --color-table-header-background: var(--color-background-secondary);\n --color-table-border: var(--color-background-border);\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: transparent;\n --color-card-marginals-background: var(--color-background-secondary);\n\n // Header\n --color-header-background: var(--color-background-primary);\n --color-header-border: var(--color-background-border);\n --color-header-text: var(--color-foreground-primary);\n\n // Sidebar (left)\n --color-sidebar-background: var(--color-background-secondary);\n --color-sidebar-background-border: var(--color-background-border);\n\n --color-sidebar-brand-text: var(--color-foreground-primary);\n --color-sidebar-caption-text: var(--color-foreground-muted);\n --color-sidebar-link-text: var(--color-foreground-secondary);\n --color-sidebar-link-text--top-level: var(--color-brand-primary);\n\n --color-sidebar-item-background: var(--color-sidebar-background);\n --color-sidebar-item-background--current: var(\n --color-sidebar-item-background\n );\n --color-sidebar-item-background--hover: linear-gradient(\n 90deg,\n var(--color-background-hover--transparent) 0%,\n var(--color-background-hover) var(--sidebar-item-spacing-horizontal),\n var(--color-background-hover) 100%\n );\n\n --color-sidebar-item-expander-background: transparent;\n --color-sidebar-item-expander-background--hover: var(\n --color-background-hover\n );\n\n --color-sidebar-search-text: var(--color-foreground-primary);\n --color-sidebar-search-background: var(--color-background-secondary);\n --color-sidebar-search-background--focus: var(--color-background-primary);\n --color-sidebar-search-border: var(--color-background-border);\n --color-sidebar-search-icon: var(--color-foreground-muted);\n\n // Table of Contents (right)\n --color-toc-background: var(--color-background-primary);\n --color-toc-title-text: var(--color-foreground-muted);\n --color-toc-item-text: var(--color-foreground-secondary);\n --color-toc-item-text--hover: var(--color-foreground-primary);\n --color-toc-item-text--active: var(--color-brand-primary);\n\n // Actual page contents\n --color-content-foreground: var(--color-foreground-primary);\n --color-content-background: transparent;\n\n // Links\n --color-link: var(--color-brand-content);\n --color-link--hover: var(--color-brand-content);\n --color-link-underline: var(--color-background-border);\n --color-link-underline--hover: var(--color-foreground-border);\n}\n\n@mixin colors-dark {\n --color-problematic: #ee5151;\n\n // Base Colors\n --color-foreground-primary: #ffffffcc; // for main text and headings\n --color-foreground-secondary: #9ca0a5; // for secondary text\n --color-foreground-muted: #81868d; // for muted text\n --color-foreground-border: #666666; // for content borders\n\n --color-background-primary: #131416; // for content\n --color-background-secondary: #1a1c1e; // for navigation + ToC\n --color-background-hover: #1e2124ff; // for navigation-item hover\n --color-background-hover--transparent: #1e212400;\n --color-background-border: #303335; // for UI borders\n --color-background-item: #444; // for \"background\" items (eg: copybutton)\n\n // Announcements\n --color-announcement-background: #000000dd;\n --color-announcement-text: #eeebee;\n\n // Brand colors\n --color-brand-primary: #2b8cee;\n --color-brand-content: #368ce2;\n\n // Highlighted text (search)\n --color-highlighted-background: #083563;\n\n // GUI Labels\n --color-guilabel-background: #08356380;\n --color-guilabel-border: #13395f80;\n\n // API documentation\n --color-api-keyword: var(--color-foreground-secondary);\n --color-highlight-on-target: #333300;\n\n // Admonitions\n --color-admonition-background: #18181a;\n\n // Cards\n --color-card-border: var(--color-background-secondary);\n --color-card-background: #18181a;\n --color-card-marginals-background: var(--color-background-hover);\n}\n","// This file contains the styling for making the content throughout the page,\n// including fonts, paragraphs, headings and spacing among these elements.\n\nbody\n font-family: var(--font-stack)\npre,\ncode,\nkbd,\nsamp\n font-family: var(--font-stack--monospace)\n\n// Make fonts look slightly nicer.\nbody\n -webkit-font-smoothing: antialiased\n -moz-osx-font-smoothing: grayscale\n\n// Line height from Bootstrap 4.1\narticle\n line-height: 1.5\n\n//\n// Headings\n//\nh1,\nh2,\nh3,\nh4,\nh5,\nh6\n line-height: 1.25\n font-weight: bold\n\n border-radius: 0.5rem\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n margin-left: -0.5rem\n margin-right: -0.5rem\n padding-left: 0.5rem\n padding-right: 0.5rem\n\n + p\n margin-top: 0\n\nh1\n font-size: 2.5em\n margin-top: 1.75rem\n margin-bottom: 1rem\nh2\n font-size: 2em\n margin-top: 1.75rem\nh3\n font-size: 1.5em\nh4\n font-size: 1.25em\nh5\n font-size: 1.125em\nh6\n font-size: 1em\n\nsmall\n opacity: 75%\n font-size: 80%\n\n// Paragraph\np\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n\n// Horizontal rules\nhr.docutils\n height: 1px\n padding: 0\n margin: 2rem 0\n background-color: var(--color-background-border)\n border: 0\n\n.centered\n text-align: center\n\n// Links\na\n text-decoration: underline\n\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n &:hover\n color: var(--color-link--hover)\n text-decoration-color: var(--color-link-underline--hover)\n &.muted-link\n color: inherit\n &:hover\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline--hover)\n","// This file contains the styles for the overall layouting of the documentation\n// skeleton, including the responsive changes as well as sidebar toggles.\n//\n// This is implemented as a mobile-last design, which isn't ideal, but it is\n// reasonably good-enough and I got pretty tired by the time I'd finished this\n// to move the rules around to fix this. Shouldn't take more than 3-4 hours,\n// if you know what you're doing tho.\n\n// HACK: Not all browsers account for the scrollbar width in media queries.\n// This results in horizontal scrollbars in the breakpoint where we go\n// from displaying everything to hiding the ToC. We accomodate for this by\n// adding a bit of padding to the TOC drawer, disabling the horizontal\n// scrollbar and allowing the scrollbars to cover the padding.\n// https://www.456bereastreet.com/archive/201301/media_query_width_and_vertical_scrollbars/\n\n// HACK: Always having the scrollbar visible, prevents certain browsers from\n// causing the content to stutter horizontally between taller-than-viewport and\n// not-taller-than-viewport pages.\n\nhtml\n overflow-x: hidden\n overflow-y: scroll\n scroll-behavior: smooth\n\n.sidebar-scroll, .toc-scroll, article[role=main] *\n // Override Firefox scrollbar style\n scrollbar-width: thin\n scrollbar-color: var(--color-foreground-border) transparent\n\n // Override Chrome scrollbar styles\n &::-webkit-scrollbar\n width: 0.25rem\n height: 0.25rem\n &::-webkit-scrollbar-thumb\n background-color: var(--color-foreground-border)\n border-radius: 0.125rem\n\n//\n// Overalls\n//\nhtml,\nbody\n height: 100%\n color: var(--color-foreground-primary)\n background: var(--color-background-primary)\n\narticle\n color: var(--color-content-foreground)\n background: var(--color-content-background)\n overflow-wrap: break-word\n\n.page\n display: flex\n // fill the viewport for pages with little content.\n min-height: 100%\n\n.mobile-header\n width: 100%\n height: var(--header-height)\n background-color: var(--color-header-background)\n color: var(--color-header-text)\n border-bottom: 1px solid var(--color-header-border)\n\n // Looks like sub-script/super-script have this, and we need this to\n // be \"on top\" of those.\n z-index: 10\n\n // We don't show the header on large screens.\n display: none\n\n // Add shadow when scrolled\n &.scrolled\n border-bottom: none\n box-shadow: 0 0 0.2rem rgba(0, 0, 0, 0.1), 0 0.2rem 0.4rem rgba(0, 0, 0, 0.2)\n\n .header-center\n a\n color: var(--color-header-text)\n text-decoration: none\n\n.main\n display: flex\n flex: 1\n\n// Sidebar (left) also covers the entire left portion of screen.\n.sidebar-drawer\n box-sizing: border-box\n\n border-right: 1px solid var(--color-sidebar-background-border)\n background: var(--color-sidebar-background)\n\n display: flex\n justify-content: flex-end\n // These next two lines took me two days to figure out.\n width: calc((100% - #{$full-width}) / 2 + #{$sidebar-width})\n min-width: $sidebar-width\n\n// Scroll-along sidebars\n.sidebar-container,\n.toc-drawer\n box-sizing: border-box\n width: $sidebar-width\n\n.toc-drawer\n background: var(--color-toc-background)\n // See HACK described on top of this document\n padding-right: 1rem\n\n.sidebar-sticky,\n.toc-sticky\n position: sticky\n top: 0\n height: min(100%, 100vh)\n height: 100vh\n\n display: flex\n flex-direction: column\n\n.sidebar-scroll,\n.toc-scroll\n flex-grow: 1\n flex-shrink: 1\n\n overflow: auto\n scroll-behavior: smooth\n\n// Central items.\n.content\n padding: 0 $content-padding\n width: $content-width\n\n display: flex\n flex-direction: column\n justify-content: space-between\n\n.icon\n display: inline-block\n height: 1rem\n width: 1rem\n svg\n width: 100%\n height: 100%\n\n//\n// Accommodate announcement banner\n//\n.announcement\n background-color: var(--color-announcement-background)\n color: var(--color-announcement-text)\n\n height: var(--header-height)\n display: flex\n align-items: center\n overflow-x: auto\n & + .page\n min-height: calc(100% - var(--header-height))\n\n.announcement-content\n box-sizing: border-box\n padding: 0.5rem\n min-width: 100%\n white-space: nowrap\n text-align: center\n\n a\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-announcement-text)\n\n &:hover\n color: var(--color-announcement-text)\n text-decoration-color: var(--color-link--hover)\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for theme\n////////////////////////////////////////////////////////////////////////////////\n.no-js .theme-toggle-container // don't show theme toggle if there's no JS\n display: none\n\n.theme-toggle-container\n vertical-align: middle\n\n.theme-toggle\n cursor: pointer\n border: none\n padding: 0\n background: transparent\n\n.theme-toggle svg\n vertical-align: middle\n height: 1rem\n width: 1rem\n color: var(--color-foreground-primary)\n display: none\n\n.theme-toggle-header\n float: left\n padding: 1rem 0.5rem\n\n////////////////////////////////////////////////////////////////////////////////\n// Toggles for elements\n////////////////////////////////////////////////////////////////////////////////\n.toc-overlay-icon, .nav-overlay-icon\n display: none\n cursor: pointer\n\n .icon\n color: var(--color-foreground-secondary)\n height: 1rem\n width: 1rem\n\n.toc-header-icon, .nav-overlay-icon\n // for when we set display: flex\n justify-content: center\n align-items: center\n\n.toc-content-icon\n height: 1.5rem\n width: 1.5rem\n\n.content-icon-container\n float: right\n display: flex\n margin-top: 1.5rem\n margin-left: 1rem\n margin-bottom: 1rem\n gap: 0.5rem\n\n .edit-this-page svg\n color: inherit\n height: 1rem\n width: 1rem\n\n.sidebar-toggle\n position: absolute\n display: none\n// \n.sidebar-toggle[name=\"__toc\"]\n left: 20px\n.sidebar-toggle:checked\n left: 40px\n// \n\n.overlay\n position: fixed\n top: 0\n width: 0\n height: 0\n\n transition: width 0ms, height 0ms, opacity 250ms ease-out\n\n opacity: 0\n background-color: rgba(0, 0, 0, 0.54)\n.sidebar-overlay\n z-index: 20\n.toc-overlay\n z-index: 40\n\n// Keep things on top and smooth.\n.sidebar-drawer\n z-index: 30\n transition: left 250ms ease-in-out\n.toc-drawer\n z-index: 50\n transition: right 250ms ease-in-out\n\n// Show the Sidebar\n#__navigation:checked\n & ~ .sidebar-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .sidebar-drawer\n top: 0\n left: 0\n // Show the toc sidebar\n#__toc:checked\n & ~ .toc-overlay\n width: 100%\n height: 100%\n opacity: 1\n & ~ .page\n .toc-drawer\n top: 0\n right: 0\n\n////////////////////////////////////////////////////////////////////////////////\n// Back to top\n////////////////////////////////////////////////////////////////////////////////\n.back-to-top\n text-decoration: none\n\n display: none\n position: fixed\n left: 0\n top: 1rem\n padding: 0.5rem\n padding-right: 0.75rem\n border-radius: 1rem\n font-size: 0.8125rem\n\n background: var(--color-background-primary)\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), #6b728080 0px 0px 1px 0px\n\n z-index: 10\n\n margin-left: 50%\n transform: translateX(-50%)\n svg\n height: 1rem\n width: 1rem\n fill: currentColor\n display: inline-block\n\n span\n margin-left: 0.25rem\n\n .show-back-to-top &\n display: flex\n align-items: center\n\n////////////////////////////////////////////////////////////////////////////////\n// Responsive layouting\n////////////////////////////////////////////////////////////////////////////////\n// Make things a bit bigger on bigger screens.\n@media (min-width: $full-width + $sidebar-width)\n html\n font-size: 110%\n\n@media (max-width: $full-width)\n // Collapse \"toc\" into the icon.\n .toc-content-icon\n display: flex\n .toc-drawer\n position: fixed\n height: 100vh\n top: 0\n right: -$sidebar-width\n border-left: 1px solid var(--color-background-muted)\n .toc-tree\n border-left: none\n font-size: var(--toc-font-size--mobile)\n\n // Accomodate for a changed content width.\n .sidebar-drawer\n width: calc((100% - #{$full-width - $sidebar-width}) / 2 + #{$sidebar-width})\n\n@media (max-width: $full-width - $sidebar-width)\n // Collapse \"navigation\".\n .nav-overlay-icon\n display: flex\n .sidebar-drawer\n position: fixed\n height: 100vh\n width: $sidebar-width\n\n top: 0\n left: -$sidebar-width\n\n // Swap which icon is visible.\n .toc-header-icon\n display: flex\n .toc-content-icon, .theme-toggle-content\n display: none\n .theme-toggle-header\n display: block\n\n // Show the header.\n .mobile-header\n position: sticky\n top: 0\n display: flex\n justify-content: space-between\n align-items: center\n\n .header-left,\n .header-right\n display: flex\n height: var(--header-height)\n padding: 0 var(--header-padding)\n label\n height: 100%\n width: 100%\n user-select: none\n\n .nav-overlay-icon .icon,\n .theme-toggle svg\n height: 1.25rem\n width: 1.25rem\n\n // Add a scroll margin for the content\n :target\n scroll-margin-top: var(--header-height)\n\n // Show back-to-top below the header\n .back-to-top\n top: calc(var(--header-height) + 0.5rem)\n\n // Center the page, and accommodate for the header.\n .page\n flex-direction: column\n justify-content: center\n .content\n margin-left: auto\n margin-right: auto\n\n@media (max-width: $content-width + 2* $content-padding)\n // Content should respect window limits.\n .content\n width: 100%\n overflow-x: auto\n\n@media (max-width: $content-width)\n .content\n padding: 0 $content-padding--small\n // Don't float sidebars to the right.\n article aside.sidebar\n float: none\n width: 100%\n margin: 1rem 0\n","//\n// The design here is strongly inspired by mkdocs-material.\n.admonition, .topic\n margin: 1rem auto\n padding: 0 0.5rem 0.5rem 0.5rem\n\n background: var(--color-admonition-background)\n\n border-radius: 0.2rem\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n font-size: var(--admonition-font-size)\n\n overflow: hidden\n page-break-inside: avoid\n\n // First element should have no margin, since the title has it.\n > :nth-child(2)\n margin-top: 0\n\n // Last item should have no margin, since we'll control that w/ padding\n > :last-child\n margin-bottom: 0\n\n.admonition p.admonition-title,\np.topic-title\n position: relative\n margin: 0 -0.5rem 0.5rem\n padding-left: 2rem\n padding-right: .5rem\n padding-top: .4rem\n padding-bottom: .4rem\n\n font-weight: 500\n font-size: var(--admonition-title-font-size)\n line-height: 1.3\n\n // Our fancy icon\n &::before\n content: \"\"\n position: absolute\n left: 0.5rem\n width: 1rem\n height: 1rem\n\n// Default styles\np.admonition-title\n background-color: var(--color-admonition-title-background)\n &::before\n background-color: var(--color-admonition-title)\n mask-image: var(--icon-admonition-default)\n mask-repeat: no-repeat\n\np.topic-title\n background-color: var(--color-topic-title-background)\n &::before\n background-color: var(--color-topic-title)\n mask-image: var(--icon-topic-default)\n mask-repeat: no-repeat\n\n//\n// Variants\n//\n.admonition\n border-left: 0.2rem solid var(--color-admonition-title)\n\n @each $type, $value in $admonitions\n &.#{$type}\n border-left-color: var(--color-admonition-title--#{$type})\n > .admonition-title\n background-color: var(--color-admonition-title-background--#{$type})\n &::before\n background-color: var(--color-admonition-title--#{$type})\n mask-image: var(--icon-#{nth($value, 2)})\n\n.admonition-todo > .admonition-title\n text-transform: uppercase\n","// This file stylizes the API documentation (stuff generated by autodoc). It's\n// deeply nested due to how autodoc structures the HTML without enough classes\n// to select the relevant items.\n\n// API docs!\ndl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)\n // Tweak the spacing of all the things!\n dd\n margin-left: 2rem\n > :first-child\n margin-top: 0.125rem\n > :last-child\n margin-bottom: 0.75rem\n\n // This is used for the arguments\n .field-list\n margin-bottom: 0.75rem\n\n // \"Headings\" (like \"Parameters\" and \"Return\")\n > dt\n text-transform: uppercase\n font-size: var(--font-size--small)\n\n dd:empty\n margin-bottom: 0.5rem\n dd > ul\n margin-left: -1.2rem\n > li\n > p:nth-child(2)\n margin-top: 0\n // When the last-empty-paragraph follows a paragraph, it doesn't need\n // to augument the existing spacing.\n > p + p:last-child:empty\n margin-top: 0\n margin-bottom: 0\n\n // Colorize the elements\n > dt\n color: var(--color-api-overall)\n\n.sig:not(.sig-inline)\n font-weight: bold\n\n font-size: var(--api-font-size)\n font-family: var(--font-stack--monospace)\n\n margin-left: -0.25rem\n margin-right: -0.25rem\n padding-top: 0.25rem\n padding-bottom: 0.25rem\n padding-right: 0.5rem\n\n // These are intentionally em, to properly match the font size.\n padding-left: 3em\n text-indent: -2.5em\n\n border-radius: 0.25rem\n\n background: var(--color-api-background)\n transition: background 100ms ease-out\n\n &:hover\n background: var(--color-api-background-hover)\n\n // adjust the size of the [source] link on the right.\n a.reference\n .viewcode-link\n font-weight: normal\n width: 3.5rem\n\nem.property\n font-style: normal\n &:first-child\n color: var(--color-api-keyword)\n.sig-name\n color: var(--color-api-name)\n.sig-prename\n font-weight: normal\n color: var(--color-api-pre-name)\n.sig-paren\n color: var(--color-api-paren)\n.sig-param\n font-style: normal\n\n.versionmodified\n font-style: italic\ndiv.versionadded, div.versionchanged, div.deprecated\n p\n margin-top: 0.125rem\n margin-bottom: 0.125rem\n\n// Align the [docs] and [source] to the right.\n.viewcode-link, .viewcode-back\n float: right\n text-align: right\n",".line-block\n margin-top: 0.5rem\n margin-bottom: 0.75rem\n .line-block\n margin-top: 0rem\n margin-bottom: 0rem\n padding-left: 1rem\n","// Captions\narticle p.caption,\ntable > caption,\n.code-block-caption\n font-size: var(--font-size--small)\n text-align: center\n\n// Caption above a TOCTree\n.toctree-wrapper.compound\n .caption, :not(.caption) > .caption-text\n font-size: var(--font-size--small)\n text-transform: uppercase\n\n text-align: initial\n margin-bottom: 0\n\n > ul\n margin-top: 0\n margin-bottom: 0\n","// Inline code\ncode.literal, .sig-inline\n background: var(--color-inline-code-background)\n border-radius: 0.2em\n // Make the font smaller, and use padding to recover.\n font-size: var(--font-size--small--2)\n padding: 0.1em 0.2em\n\n pre.literal-block &\n font-size: inherit\n padding: 0\n\n p &\n border: 1px solid var(--color-background-border)\n\n.sig-inline\n font-family: var(--font-stack--monospace)\n\n// Code and Literal Blocks\n$code-spacing-vertical: 0.625rem\n$code-spacing-horizontal: 0.875rem\n\n// Wraps every literal block + line numbers.\ndiv[class*=\" highlight-\"],\ndiv[class^=\"highlight-\"]\n margin: 1em 0\n display: flex\n\n .table-wrapper\n margin: 0\n padding: 0\n\npre\n margin: 0\n padding: 0\n overflow: auto\n\n // Needed to have more specificity than pygments' \"pre\" selector. :(\n article[role=\"main\"] .highlight &\n line-height: 1.5\n\n &.literal-block,\n .highlight &\n font-size: var(--code-font-size)\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n // Make it look like all the other blocks.\n &.literal-block\n margin-top: 1rem\n margin-bottom: 1rem\n\n border-radius: 0.2rem\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n\n// All code is always contained in this.\n.highlight\n width: 100%\n border-radius: 0.2rem\n\n // Make line numbers and prompts un-selectable.\n .gp, span.linenos\n user-select: none\n pointer-events: none\n\n // Expand the line-highlighting.\n .hll\n display: block\n margin-left: -$code-spacing-horizontal\n margin-right: -$code-spacing-horizontal\n padding-left: $code-spacing-horizontal\n padding-right: $code-spacing-horizontal\n\n/* Make code block captions be nicely integrated */\n.code-block-caption\n display: flex\n padding: $code-spacing-vertical $code-spacing-horizontal\n\n border-radius: 0.25rem\n border-bottom-left-radius: 0\n border-bottom-right-radius: 0\n font-weight: 300\n border-bottom: 1px solid\n\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n border-color: var(--color-background-border)\n\n + div[class]\n margin-top: 0\n pre\n border-top-left-radius: 0\n border-top-right-radius: 0\n\n// When `html_codeblock_linenos_style` is table.\n.highlighttable\n width: 100%\n display: block\n tbody\n display: block\n\n tr\n display: flex\n\n // Line numbers\n td.linenos\n background-color: var(--color-code-background)\n color: var(--color-code-foreground)\n padding: $code-spacing-vertical $code-spacing-horizontal\n padding-right: 0\n border-top-left-radius: 0.2rem\n border-bottom-left-radius: 0.2rem\n\n .linenodiv\n padding-right: $code-spacing-horizontal\n font-size: var(--code-font-size)\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n\n // Actual code\n td.code\n padding: 0\n display: block\n flex: 1\n overflow: hidden\n\n .highlight\n border-top-left-radius: 0\n border-bottom-left-radius: 0\n\n// When `html_codeblock_linenos_style` is inline.\n.highlight\n span.linenos\n display: inline-block\n padding-left: 0\n padding-right: $code-spacing-horizontal\n margin-right: $code-spacing-horizontal\n box-shadow: -0.0625rem 0 var(--color-foreground-border) inset\n","// Inline Footnote Reference\n.footnote-reference\n font-size: var(--font-size--small--4)\n vertical-align: super\n\n// Definition list, listing the content of each note.\n// docutils <= 0.17\ndl.footnote.brackets\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\n display: grid\n grid-template-columns: max-content auto\n dt\n margin: 0\n > .fn-backref\n margin-left: 0.25rem\n\n &:after\n content: \":\"\n\n .brackets\n &:before\n content: \"[\"\n &:after\n content: \"]\"\n\n dd\n margin: 0\n padding: 0 1rem\n\n// docutils >= 0.18\naside.footnote\n font-size: var(--font-size--small)\n color: var(--color-foreground-secondary)\n\naside.footnote > span,\ndiv.citation > span\n float: left\n font-weight: 500\n padding-right: 0.25rem\n\naside.footnote > p,\ndiv.citation > p\n margin-left: 2rem\n","//\n// Figures\n//\nimg\n box-sizing: border-box\n max-width: 100%\n height: auto\n\narticle\n figure, .figure\n border-radius: 0.2rem\n\n margin: 0\n :last-child\n margin-bottom: 0\n\n .align-left\n float: left\n clear: left\n margin: 0 1rem 1rem\n\n .align-right\n float: right\n clear: right\n margin: 0 1rem 1rem\n\n .align-default,\n .align-center\n display: block\n text-align: center\n margin-left: auto\n margin-right: auto\n\n // WELL, table needs to be stylised like a table.\n table.align-default\n display: table\n text-align: initial\n",".genindex-jumpbox, .domainindex-jumpbox\n border-top: 1px solid var(--color-background-border)\n border-bottom: 1px solid var(--color-background-border)\n padding: 0.25rem\n\n.genindex-section, .domainindex-section\n h2\n margin-top: 0.75rem\n margin-bottom: 0.5rem\n ul\n margin-top: 0\n margin-bottom: 0\n","ul,\nol\n padding-left: 1.2rem\n\n // Space lists out like paragraphs\n margin-top: 1rem\n margin-bottom: 1rem\n // reduce margins within li.\n li\n > p:first-child\n margin-top: 0.25rem\n margin-bottom: 0.25rem\n\n > p:last-child\n margin-top: 0.25rem\n\n > ul,\n > ol\n margin-top: 0.5rem\n margin-bottom: 0.5rem\n\nol\n &.arabic\n list-style: decimal\n &.loweralpha\n list-style: lower-alpha\n &.upperalpha\n list-style: upper-alpha\n &.lowerroman\n list-style: lower-roman\n &.upperroman\n list-style: upper-roman\n\n// Don't space lists out when they're \"simple\" or in a `.. toctree::`\n.simple,\n.toctree-wrapper\n li\n > ul,\n > ol\n margin-top: 0\n margin-bottom: 0\n\n// Definition Lists\n.field-list,\n.option-list,\ndl:not([class]),\ndl.simple,\ndl.footnote,\ndl.glossary\n dt\n font-weight: 500\n margin-top: 0.25rem\n + dt\n margin-top: 0\n\n .classifier::before\n content: \":\"\n margin-left: 0.2rem\n margin-right: 0.2rem\n\n dd\n > p:first-child,\n ul\n margin-top: 0.125rem\n\n ul\n margin-bottom: 0.125rem\n",".math-wrapper\n width: 100%\n overflow-x: auto\n\ndiv.math\n position: relative\n text-align: center\n\n .headerlink,\n &:focus .headerlink\n display: none\n\n &:hover .headerlink\n display: inline-block\n\n span.eqno\n position: absolute\n right: 0.5rem\n top: 50%\n transform: translate(0, -50%)\n z-index: 1\n","// Abbreviations\nabbr[title]\n cursor: help\n\n// \"Problematic\" content, as identified by Sphinx\n.problematic\n color: var(--color-problematic)\n\n// Keyboard / Mouse \"instructions\"\nkbd:not(.compound)\n margin: 0 0.2rem\n padding: 0 0.2rem\n border-radius: 0.2rem\n border: 1px solid var(--color-foreground-border)\n color: var(--color-foreground-primary)\n vertical-align: text-bottom\n\n font-size: var(--font-size--small--3)\n display: inline-block\n\n box-shadow: 0 0.0625rem 0 rgba(0, 0, 0, 0.2), inset 0 0 0 0.125rem var(--color-background-primary)\n\n background-color: var(--color-background-secondary)\n\n// Blockquote\nblockquote\n border-left: 4px solid var(--color-background-border)\n background: var(--color-background-secondary)\n\n margin-left: 0\n margin-right: 0\n padding: 0.5rem 1rem\n\n .attribution\n font-weight: 600\n text-align: right\n\n &.pull-quote,\n &.highlights\n font-size: 1.25em\n\n &.epigraph,\n &.pull-quote\n border-left-width: 0\n border-radius: 0.5rem\n\n &.highlights\n border-left-width: 0\n background: transparent\n\n// Center align embedded-in-text images\np .reference img\n vertical-align: middle\n","p.rubric\n line-height: 1.25\n font-weight: bold\n font-size: 1.125em\n\n // For Numpy-style documentation that's got rubrics within it.\n // https://github.com/pradyunsg/furo/discussions/505\n dd &\n line-height: inherit\n font-weight: inherit\n\n font-size: var(--font-size--small)\n text-transform: uppercase\n","article .sidebar\n float: right\n clear: right\n width: 30%\n\n margin-left: 1rem\n margin-right: 0\n\n border-radius: 0.2rem\n background-color: var(--color-background-secondary)\n border: var(--color-background-border) 1px solid\n\n > *\n padding-left: 1rem\n padding-right: 1rem\n\n > ul, > ol // lists need additional padding, because bullets.\n padding-left: 2.2rem\n\n .sidebar-title\n margin: 0\n padding: 0.5rem 1rem\n border-bottom: var(--color-background-border) 1px solid\n\n font-weight: 500\n\n// TODO: subtitle\n// TODO: dedicated variables?\n",".table-wrapper\n width: 100%\n overflow-x: auto\n margin-top: 1rem\n margin-bottom: 0.5rem\n padding: 0.2rem 0.2rem 0.75rem\n\ntable.docutils\n border-radius: 0.2rem\n border-spacing: 0\n border-collapse: collapse\n\n box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.05), 0 0 0.0625rem rgba(0, 0, 0, 0.1)\n\n th\n background: var(--color-table-header-background)\n\n td,\n th\n // Space things out properly\n padding: 0 0.25rem\n\n // Get the borders looking just-right.\n border-left: 1px solid var(--color-table-border)\n border-right: 1px solid var(--color-table-border)\n border-bottom: 1px solid var(--color-table-border)\n\n p\n margin: 0.25rem\n\n &:first-child\n border-left: none\n &:last-child\n border-right: none\n\n // MyST-parser tables set these classes for control of column alignment\n &.text-left\n text-align: left\n &.text-right\n text-align: right\n &.text-center\n text-align: center\n",":target\n scroll-margin-top: 0.5rem\n\n@media (max-width: $full-width - $sidebar-width)\n :target\n scroll-margin-top: calc(0.5rem + var(--header-height))\n\n // When a heading is selected\n section > span:target\n scroll-margin-top: calc(0.8rem + var(--header-height))\n\n// Permalinks\n.headerlink\n font-weight: 100\n user-select: none\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\ndl dt,\np.caption,\nfigcaption p,\ntable > caption,\n.code-block-caption\n > .headerlink\n margin-left: 0.5rem\n visibility: hidden\n &:hover > .headerlink\n visibility: visible\n\n // Don't change to link-like, if someone adds the contents directive.\n > .toc-backref\n color: inherit\n text-decoration-line: none\n\n// Figure and table captions are special.\nfigure:hover > figcaption > p > .headerlink,\ntable:hover > caption > .headerlink\n visibility: visible\n\n:target >, // Regular section[id] style anchors\nspan:target ~ // Non-regular span[id] style \"extra\" anchors\n h1,\n h2,\n h3,\n h4,\n h5,\n h6\n &:nth-of-type(1)\n background-color: var(--color-highlight-on-target)\n // .headerlink\n // visibility: visible\n code.literal\n background-color: transparent\n\ntable:target > caption,\nfigure:target\n background-color: var(--color-highlight-on-target)\n\n// Inline page contents\n.this-will-duplicate-information-and-it-is-still-useful-here li :target\n background-color: var(--color-highlight-on-target)\n\n// Code block permalinks\n.literal-block-wrapper:target .code-block-caption\n background-color: var(--color-highlight-on-target)\n\n// When a definition list item is selected\n//\n// There isn't really an alternative to !important here, due to the\n// high-specificity of API documentation's selector.\ndt:target\n background-color: var(--color-highlight-on-target) !important\n\n// When a footnote reference is selected\n.footnote > dt:target + dd,\n.footnote-reference:target\n background-color: var(--color-highlight-on-target)\n",".guilabel\n background-color: var(--color-guilabel-background)\n border: 1px solid var(--color-guilabel-border)\n color: var(--color-guilabel-text)\n\n padding: 0 0.3em\n border-radius: 0.5em\n font-size: 0.9em\n","// This file contains the styles used for stylizing the footer that's shown\n// below the content.\n\nfooter\n font-size: var(--font-size--small)\n display: flex\n flex-direction: column\n\n margin-top: 2rem\n\n// Bottom of page information\n.bottom-of-page\n display: flex\n align-items: center\n justify-content: space-between\n\n margin-top: 1rem\n padding-top: 1rem\n padding-bottom: 1rem\n\n color: var(--color-foreground-secondary)\n border-top: 1px solid var(--color-background-border)\n\n line-height: 1.5\n\n @media (max-width: $content-width)\n text-align: center\n flex-direction: column-reverse\n gap: 0.25rem\n\n .left-details\n font-size: var(--font-size--small)\n\n .right-details\n display: flex\n flex-direction: column\n gap: 0.25rem\n text-align: right\n\n .icons\n display: flex\n justify-content: flex-end\n gap: 0.25rem\n font-size: 1rem\n\n a\n text-decoration: none\n\n svg,\n img\n font-size: 1.125rem\n height: 1em\n width: 1em\n\n// Next/Prev page information\n.related-pages\n a\n display: flex\n align-items: center\n\n text-decoration: none\n &:hover .page-info .title\n text-decoration: underline\n color: var(--color-link)\n text-decoration-color: var(--color-link-underline)\n\n svg.furo-related-icon,\n svg.furo-related-icon > use\n flex-shrink: 0\n\n color: var(--color-foreground-border)\n\n width: 0.75rem\n height: 0.75rem\n margin: 0 0.5rem\n\n &.next-page\n max-width: 50%\n\n float: right\n clear: right\n text-align: right\n\n &.prev-page\n max-width: 50%\n\n float: left\n clear: left\n\n svg\n transform: rotate(180deg)\n\n.page-info\n display: flex\n flex-direction: column\n overflow-wrap: anywhere\n\n .next-page &\n align-items: flex-end\n\n .context\n display: flex\n align-items: center\n\n padding-bottom: 0.1rem\n\n color: var(--color-foreground-muted)\n font-size: var(--font-size--small)\n text-decoration: none\n","// This file contains the styles for the contents of the left sidebar, which\n// contains the navigation tree, logo, search etc.\n\n////////////////////////////////////////////////////////////////////////////////\n// Brand on top of the scrollable tree.\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-brand\n display: flex\n flex-direction: column\n flex-shrink: 0\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n text-decoration: none\n\n.sidebar-brand-text\n color: var(--color-sidebar-brand-text)\n overflow-wrap: break-word\n margin: var(--sidebar-item-spacing-vertical) 0\n font-size: 1.5rem\n\n.sidebar-logo-container\n margin: var(--sidebar-item-spacing-vertical) 0\n\n.sidebar-logo\n margin: 0 auto\n display: block\n max-width: 100%\n\n////////////////////////////////////////////////////////////////////////////////\n// Search\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-search-container\n display: flex\n align-items: center\n margin-top: var(--sidebar-search-space-above)\n\n position: relative\n\n background: var(--color-sidebar-search-background)\n &:hover,\n &:focus-within\n background: var(--color-sidebar-search-background--focus)\n\n &::before\n content: \"\"\n position: absolute\n left: var(--sidebar-item-spacing-horizontal)\n width: var(--sidebar-search-icon-size)\n height: var(--sidebar-search-icon-size)\n\n background-color: var(--color-sidebar-search-icon)\n mask-image: var(--icon-search)\n\n.sidebar-search\n box-sizing: border-box\n\n border: none\n border-top: 1px solid var(--color-sidebar-search-border)\n border-bottom: 1px solid var(--color-sidebar-search-border)\n\n padding-top: var(--sidebar-search-input-spacing-vertical)\n padding-bottom: var(--sidebar-search-input-spacing-vertical)\n padding-right: var(--sidebar-search-input-spacing-horizontal)\n padding-left: calc(var(--sidebar-item-spacing-horizontal) + var(--sidebar-search-input-spacing-horizontal) + var(--sidebar-search-icon-size))\n\n width: 100%\n\n color: var(--color-sidebar-search-foreground)\n background: transparent\n z-index: 10\n\n &:focus\n outline: none\n\n &::placeholder\n font-size: var(--sidebar-search-input-font-size)\n\n//\n// Hide Search Matches link\n//\n#searchbox .highlight-link\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal) 0\n margin: 0\n text-align: center\n\n a\n color: var(--color-sidebar-search-icon)\n font-size: var(--font-size--small--2)\n\n////////////////////////////////////////////////////////////////////////////////\n// Structure/Skeleton of the navigation tree (left)\n////////////////////////////////////////////////////////////////////////////////\n.sidebar-tree\n font-size: var(--sidebar-item-font-size)\n margin-top: var(--sidebar-tree-space-above)\n margin-bottom: var(--sidebar-item-spacing-vertical)\n\n ul\n padding: 0\n margin-top: 0\n margin-bottom: 0\n\n display: flex\n flex-direction: column\n\n list-style: none\n\n li\n position: relative\n margin: 0\n\n > ul\n margin-left: var(--sidebar-item-spacing-horizontal)\n\n .icon\n color: var(--color-sidebar-link-text)\n\n .reference\n box-sizing: border-box\n color: var(--color-sidebar-link-text)\n\n // Fill the parent.\n display: inline-block\n line-height: var(--sidebar-item-line-height)\n text-decoration: none\n\n // Don't allow long words to cause wrapping.\n overflow-wrap: anywhere\n\n height: 100%\n width: 100%\n\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n &:hover\n background: var(--color-sidebar-item-background--hover)\n\n // Add a nice little \"external-link\" arrow here.\n &.external::after\n content: url('data:image/svg+xml,')\n margin: 0 0.25rem\n vertical-align: middle\n color: var(--color-sidebar-link-text)\n\n // Make the current page reference bold.\n .current-page > .reference\n font-weight: bold\n\n label\n position: absolute\n top: 0\n right: 0\n height: var(--sidebar-item-height)\n width: var(--sidebar-expander-width)\n\n cursor: pointer\n user-select: none\n\n display: flex\n justify-content: center\n align-items: center\n\n .caption, :not(.caption) > .caption-text\n font-size: var(--sidebar-caption-font-size)\n color: var(--color-sidebar-caption-text)\n\n font-weight: bold\n text-transform: uppercase\n\n margin: var(--sidebar-caption-space-above) 0 0 0\n padding: var(--sidebar-item-spacing-vertical) var(--sidebar-item-spacing-horizontal)\n\n // If it has children, add a bit more padding to wrap the content to avoid\n // overlapping with the