Planet Python

Subscribe to Planet Python feed
Planet Python - http://planetpython.org/
Updated: 6 hours 26 min ago

Real Python: Generating QR Codes With Python

Tue, 2024-04-09 10:00

From restaurant e-menus to airline boarding passes, QR codes have numerous applications that impact your day-to-day life and enrich the user’s experience. Wouldn’t it be great to make them look good, too? With the help of this video course, you’ll learn how to use Python to generate beautiful QR codes for your personal use case.

In its most basic format, a QR code contains black squares and dots on a white background, with information that any smartphone or device with a dedicated QR scanner can decode. Unlike a traditional bar code, which holds information horizontally, a QR code holds the data in two dimensions, and it can hold over a hundred times more information.

In this video course, you’ll learn how to:

  • Generate a basic black-and-white QR code
  • Change the size and margins of the QR code
  • Create colorful QR codes
  • Rotate the QR code
  • Replace the static background with an animated GIF

[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

Categories: FLOSS Project Planets

Python Bytes: #378 Python is on the edge

Tue, 2024-04-09 04:00
<strong>Topics covered in this episode:</strong><br> <ul> <li><a href="https://github.com/brohrer/pacemaker"><strong>pacemaker</strong></a> - For controlling time per iteration loop in Python.</li> <li><a href="https://www.bleepingcomputer.com/news/security/pypi-suspends-new-user-registration-to-block-malware-campaign/">PyPI suspends new user registration to block malware campaign</a></li> <li><a href="https://hynek.me/articles/python-virtualenv-redux/"><strong>Python Project-Local Virtualenv Management Redux</strong></a></li> <li><a href="https://blog.cloudflare.com/python-workers">Python Edge Workers at Cloudflare</a></li> <li><strong>Extras</strong></li> <li><strong>Joke</strong></li> </ul><a href='https://www.youtube.com/watch?v=4oALfE-zDf8' style='font-weight: bold;'data-umami-event="Livestream-Past" data-umami-event-episode="378">Watch on YouTube</a><br> <p><strong>About the show</strong></p> <p>Sponsored by us! Support our work through:</p> <ul> <li>Our <a href="https://training.talkpython.fm/"><strong>courses at Talk Python Training</strong></a></li> <li><a href="https://courses.pythontest.com/p/the-complete-pytest-course"><strong>The Complete pytest Course</strong></a></li> <li><a href="https://www.patreon.com/pythonbytes"><strong>Patreon Supporters</strong></a></li> </ul> <p><strong>Connect with the hosts</strong></p> <ul> <li>Michael: <a href="https://fosstodon.org/@mkennedy"><strong>@mkennedy@fosstodon.org</strong></a></li> <li>Brian: <a href="https://fosstodon.org/@brianokken"><strong>@brianokken@fosstodon.org</strong></a></li> <li>Show: <a href="https://fosstodon.org/@pythonbytes"><strong>@pythonbytes@fosstodon.org</strong></a></li> </ul> <p>Join us on YouTube at <a href="https://pythonbytes.fm/stream/live"><strong>pythonbytes.fm/live</strong></a> to be part of the audience. Usually Tuesdays at 11am PT. Older video versions available there too.</p> <p>Finally, if you want an artisanal, hand-crafted digest of every week of the show notes in email form? Add your name and email to <a href="https://pythonbytes.fm/friends-of-the-show">our friends of the show list</a>, we'll never share it.</p> <p><strong>Brian #1:</strong> <a href="https://github.com/brohrer/pacemaker"><strong>pacemaker</strong></a> - For controlling time per iteration loop in Python.</p> <ul> <li>Brandon Rohrer</li> <li>Good example of a small bit of code made into a small package.</li> <li>With speedups to dependencies, like with uv, for example, I think we’ll see more small projects.</li> <li>Cool stuff <ul> <li>Great README, including quirks that need to be understood by users. <ul> <li>“If the pacemaker experiences a delay, it will allow faster iterations to try to catch up. Heads up: because of this, any individual iteration might end up being much shorter than suggested by the pacemaker's target rate.”</li> </ul></li> <li>Nice use of <a href="https://docs.python.org/3/library/time.html#time.monotonic">time.monotonic()</a> <ul> <li>deltas are guaranteed to never go back in time regardless of what adjustments are made to the system clock.</li> </ul></li> </ul></li> <li>Watch out for <ul> <li>pip install pacemaker-lite <ul> <li>NOT pacemaker</li> <li>pacemaker is taken by a package named PaceMaker with a repo named pace-maker, that hasn’t been updated in 3 years. Not sure if it’s alive. </li> </ul></li> <li>No tests (yet). I’m sure they’re coming. ;) <ul> <li>Seriously though, Brandon says this is “a glorified snippet”. And I love the use of packaging to encapsulate shared code. Realistically, small snippet like packages have functionality that’s probably going to be tested by end user code.</li> <li>And even if there are tests, users should test the functionality they are depending on.</li> </ul></li> </ul></li> </ul> <p><strong>Michael #2:</strong> <a href="https://www.bleepingcomputer.com/news/security/pypi-suspends-new-user-registration-to-block-malware-campaign/">PyPI suspends new user registration to block malware campaign</a></p> <ul> <li><a href="https://status.python.org/incidents/dc9zsqzrs0bv">Incident Report for Python Infrastructure</a></li> <li><a href="https://medium.com/checkmarx-security/pypi-is-under-attack-project-creation-and-user-registration-suspended-heres-the-details-c3b6291d4579">PyPi Is Under Attack: Project Creation and User Registration Suspended — Here’s the details</a> <ul> <li>I hate medium, but it’s the best details I’ve found so far</li> </ul></li> </ul> <p><strong>Brian #3:</strong> <a href="https://hynek.me/articles/python-virtualenv-redux/"><strong>Python Project-Local Virtualenv Management Redux</strong></a></p> <ul> <li>Hynek</li> <li>Concise writeup of how Hynek uses various tools for dealing with environments</li> <li>Covers (paren notes are from Brian) <ul> <li>In project .venv directories</li> <li>direnv for handling .envrc files per project (time for me to try this again)</li> <li>uv for pip and pip-compile functionality</li> <li>Installing Python via python.org</li> <li>Using a .python-version-default file (I’ll need to play with this a bit) <ul> <li>Works with GH Action setup-python. (ok. that’s cool)</li> </ul></li> <li>Some fish shell scripting</li> <li>Bonus tip on using requires-python in .pyproject.toml and extracting it in GH actions to be able to get the python exe name, and then be able to pass it to Docker and reference it in a Dockerfile. (very cool)</li> </ul></li> </ul> <p><strong>Michael #4:</strong> <a href="https://blog.cloudflare.com/python-workers">Python Edge Workers at Cloudflare</a></p> <ul> <li>What are <a href="https://developers.cloudflare.com/workers/">edge workers</a>?</li> <li>Based on workers using Pyodide and WebAssembly</li> <li>This new support for Python is different from how Workers have historically supported languages beyond JavaScript — in this case, we have directly integrated a Python implementation into <a href="https://github.com/cloudflare/workerd">workerd</a>, the open-source Workers runtime.</li> <li>Python Workers can import a subset of popular Python <a href="https://developers.cloudflare.com/workers/languages/python/packages/">packages</a> including <a href="https://fastapi.tiangolo.com/">FastAPI</a>, <a href="https://python.langchain.com/docs/get_started/introduction">Langchain</a>, <a href="https://numpy.org/">numpy</a></li> <li>Check out the <a href="https://github.com/cloudflare/python-workers-examples">examples repo</a>.</li> </ul> <p><strong>Extras</strong> </p> <p>Michael:</p> <ul> <li><a href="https://fosstodon.org/@btskinn/112226004327304352">LPython follow up</a> from Brian Skinn</li> <li><a href="https://github.com/epogrebnyak/justpath/issues/26">Featured on Python Bytes badge</a></li> <li><a href="https://twitter.com/TalkPython/status/1777505296807850101">A little downtime</a>, thanks for the understanding <ul> <li>We were rocking a <a href="https://python-bytes-static.nyc3.digitaloceanspaces.com/python-bytes-health.png">99.98% uptime</a> until then. :)</li> </ul></li> </ul> <p><strong>Joke:</strong> </p> <ul> <li><a href="https://devhumor.com/media/gemini-says-that-c-is-not-safe-for-people-under-18">C++ is not safe for people under 18</a></li> <li>Baseball joke</li> </ul>
Categories: FLOSS Project Planets

PyBites: Adventures in Import-land, Part II

Mon, 2024-04-08 14:15

“KeyError: 'GOOGLE_APPLICATION_CREDENTIALS‘”

It was way too early in the morning for this error. See if you can spot the problem. I hadn’t had my coffee before trying to debug the code I’d written the night before, so it will probably take you less time than it did me.

app.py:

from dotenv import load_dotenv from file_handling import initialize_constants load_dotenv() #...

file_handling.py:

import os from google.cloud import storage UPLOAD_FOLDER=None DOWNLOAD_FOLDER = None def initialize_cloud_storage(): """ Initializes the Google Cloud Storage client. """ os.environ["GOOGLE_APPLICATION_CREDENTIALS"] storage_client = storage.Client() bucket_name = #redacted return storage_client.bucket(bucket_name) def set_upload_folder(): """ Determines the environment and sets the path to the upload folder accordingly. """ if os.environ.get("FLASK_ENV") in ["production", "staging"]: UPLOAD_FOLDER = os.path.join("/tmp", "upload") os.makedirs(UPLOAD_FOLDER, exist_ok=True) else: UPLOAD_FOLDER = os.path.join("src", "upload_folder") return UPLOAD_FOLDER def initialize_constants(): """ Initializes the global constants for the application. """ UPLOAD_FOLDER = initialize_upload_folder() DOWNLOAD_FOLDER = initialize_cloud_storage() return UPLOAD_FOLDER, DOWNLOAD_FOLDER DOWNLOAD_FOLDER=initialize_cloud_storage() def write_to_gcs(content: str, file: str): "Writes a text file to a Google Cloud Storage file." blob = DOWNLOAD_FOLDER.blob(file) blob.upload_from_string(content, content_type="text/plain") def upload_file_to_gcs(file_path:str, gcs_file: str): "Uploads a file to a Google Cloud Storage bucket" blob = DOWNLOAD_FOLDER.blob(gcs_file) with open(file_path, "rb") as f: blob.upload_from_file(f, content_type="application/octet-stream")

See the problem?

This was just the discussion of a recent Pybites article.

When app.py imported initialize_constants from file_handling, the Python interpreter ran

DOWNLOAD_FOLDER = initialize_cloud_storage()

and looked for GOOGLE_APPLICATION_CREDENTIALS from the environment path, but load_dotenv hadn’t added them to the environment path from the .env file yet.

Typically, configuration variables, secret keys, and passwords are stored in a file called .env and then read as environment variables rather than as pure text using a package such as python-dotenv, which is what is being used here.

So, I had a few options.

I could call load_dotenv before importing from file_handling:

from dotenv import load_dotenv load_dotenv() from file_handling import initialize_constants

But that’s not very Pythonic.

I could call initialize_cloud_storage inside both upload_file_to_gcs and write_to_gcs

def write_to_gcs(content: str, file: str): "Writes a text file to a Google Cloud Storage file." DOWNLOAD_FOLDER = initialize_cloud_storage() blob = DOWNLOAD_FOLDER.blob(file) blob.upload_from_string(content, content_type="text/plain") def upload_file_to_gcs(file_path:str, gcs_file: str): "Uploads a file to a Google Cloud Storage bucket" DOWNLOAD_FOLDER = initialize_cloud_storage() blob = DOWNLOAD_FOLDER.blob(gcs_file) with open(file_path, "rb") as f: blob.upload_from_file(f, content_type="application/octet-stream")

But this violates the DRY principle. Plus we really shouldn’t be initializing the storage client multiple times. In fact, we already are initializing it twice in the way the code was originally written.

Going Global

So what about this?

DOWNLOAD_FOLDER = None def initialize_constants(): """ Initializes the global constants for the application. """ global DOWNLOAD_FOLDER UPLOAD_FOLDER = initialize_upload_folder() DOWNLOAD_FOLDER = initialize_cloud_storage() return UPLOAD_FOLDER, DOWNLOAD_FOLDER

Here, we are defining DOWNLOAD_FOLDER as having global scope.

This will work here.

This will work here, because upload_file_to_gcs and write_to_gcs are in the same module. But if they were in a different module, it would break.

Why does it matter?

Well, let’s go back to how Python handles imports. Remember that Python runs any code outside of a function or class at import. That applies to variable (or constant) assignment, as well. So if upload_file_to_gcs and write_to_gcs were in another module and importing DOWNLOAD_FOLDER from file_handling,p it would be importing it while assigned a value of None. It wouldn’t matter that by the time it was needed, it wouldn’t be assigned to None any longer. Inside this other module, it would still be None.

What would be necessary in this situation would be another function called get_download_folder.

def get_download_folder(): """ Returns the current value of the Google Cloud Storage bucket """ return DOWNLOAD_FOLDER

Then, in this other module containing the upload_file_to_gcs and write_to_gcs functions, I would import get_download_folder instead of DOWNLOAD_FOLDER. By importing get_download_folder, you can get the value of DOWNLOAD_FOLDER after it has been assigned to an actual value, because get_download_folder won’t run until you explicitly call it. Which, presumably wouldn’t be until after you’ve let initialize_cloud_storage do its thing.

I have another part of my codebase where I have done this. On my site, I have a tool that helps authors create finetunes of GPT 3.5 from their books. This Finetuner is BYOK, or ‘bring your own key’ meaning that users supply their own OpenAI API key to use the tool. I chose this route because charging authors to fine-tune a model and then charging them to use it, forever, is just not something that benefits either of us. This way, they can take their finetuned model and use it an any of the multiple other BYOK AI writing tools that are out there, and I don’t have to maintain writing software on top of everything else. So the webapp’s form accepts the user’s API key, and after a valid form submit, starts a thread of my Finetuner application.

This application starts in the training_management.py module, which imports set_client and get_client from openai_client.py and passes the user’s API key to set_client right away. I can’t import client directly, because client is None until set_client has been passed the API key, which happens after import.

from openai import OpenAI client = None def set_client(api_key:str): """ Initializes OpenAI API client with user API key """ global client client = OpenAI(api_key = api_key) def get_client(): """ Returns the initialized OpenAI client """ return client

When the function that starts a fine tuning job starts, it calls get_client to retrieve the initialized client. And by moving the API client initialization into another module, it becomes available to be used for an AI-powered chunking algorithm I’m working on. Nothing amazing. Basically, just generating scene beats from each chapter to use as the prompt, with the actual chapter as the response. It needs work still, but it’s available for authors who want to try it.

A Class Act

Now, we could go one step further from here. The code we’ve settled on so far relies on global names. Perhaps we can get away with this. DOWNLOAD_FOLDER is a constant. Well, sort of. Remember, it’s defined by initializing a connection to a cloud storage container. It’s actually a class. By rights, we should be encapsulating all of this logic inside of another class.

So what could that look like? Well, it should initialize the upload and download folders, and expose them as properties, and then use the functions write_to_gcs and upload_file_to_gcs as methods like this:

class FileStorageHandler: def __init__(self): self._upload_folder = self._set_upload_folder() self._download_folder = self._initialize_cloud_storage() @property def upload_folder(self): return self._upload_folder @property def download_folder(self): return self._download_folder def _initialize_cloud_storage(self): """ Initializes the Google Cloud Storage client. """ os.environ["GOOGLE_APPLICATION_CREDENTIALS"] storage_client = storage.Client() bucket_name = #redacted return storage_client.bucket(bucket_name) def _set_upload_folder(self): """ Determines the environment and sets the path to the upload folder accordingly. """ if os.environ.get("FLASK_ENV") in ["production", "staging"]: upload_folder = os.path.join("/tmp", "upload") os.makedirs(upload_folder, exist_ok=True) else: upload_folder = os.path.join("src", "upload_folder") return upload_folder def write_to_gcs(self, content: str, file_name: str): """ Writes a text file to a Google Cloud Storage file. """ blob = self._download_folder.blob(file_name) blob.upload_from_string(content, content_type="text/plain") def upload_file_to_gcs(self, file_path: str, gcs_file_name: str): """ Uploads a file to a Google Cloud Storage bucket. """ blob = self._download_folder.blob(gcs_file_name) with open(file_path, "rb") as file_obj: blob.upload_from_file(file_obj)

Now, we can initialize an instance of FileStorageHandler in app.py and assign UPLOAD_FOLDER and DOWNLOAD_FOLDER to the properties of the class.

from dotenv import load_dotenv from file_handling import FileStorageHandler load_dotenv() folders = FileStorageHandler() UPLOAD_FOLDER = folders.upload_folder DOWNLOAD_FOLDER = folders.download_folder Key take away

In the example, the error arose because initialize_cloud_storage was called at the top level in file_handling.py. This resulted in Python attempting to access environment variables before load_dotenv had a chance to set them.

I had been thinking of module level imports as “everything at the top runs at import.” But that’s not true. Or rather, it is true, but not accurate. Python executes code based on indentation, and functions are indented within the module. So, it’s fair to say that every line that isn’t indented is at the top of the module. In fact, it’s even called that: top-level code, which is defined as basically anything that is not part of a function, class or other code block.

And top-level code runs runs when the module is imported. It’s not enough to bury an expression below some functions, it will still run immediately when the module is imported, whether you are ready for it to run or not. Which is really what the argument against global variables and state is all about, managing when and how your code runs.

Understanding top-level code execution at import helped solved the initial error and design a more robust pattern.

Next steps

The downside with using a class is that if it gets called again, a new instance is created, with a new connection to the cloud storage. To get around this, something to look into would be to implement something called a Singleton Pattern, which is outside of the scope of this article.

Also, the code currently doesn’t handle exceptions that might arise during initialization (e.g., issues with credentials or network connectivity). Adding robust error handling mechanisms will make the code more resilient.

Speaking of robustness, I would be remiss if I didn’t point out that a properly abstracted initialization method should retrieve the bucket name from a configuration or .env file instead of leaving it hardcoded in the method itself.

Categories: FLOSS Project Planets

Anwesha Das: Test container image with eercheck

Mon, 2024-04-08 10:25

Execution Environments serves us the benefits of containerization by solving the issues such as software dependencies, portability. Ansible Execution Environment are Ansible control nodes packaged as container images. There are two kinds of Ansible execution environments

  • Base, includes the following

    • fedora base image
    • ansible core
    • ansible collections : The following set of collections
      ansible.posix
      ansible.utils
      ansible.windows
  • Minimal, includes the following

    • fedora base image
    • ansible core

I have been the release manager for Ansible Execution Environments. After building the images I perform certain steps of tests to check if the versions of different components of the newly built correct or not. So I wrote eercheck to ease the steps of tests.

What is eercheck?

eercheck is a command line tool to test Ansible community execution environment before release. It uses podman py to connect and work with the podman container image, and Python unittest for testing the containers.

eercheck is a command line tool to test Ansible Community Execution Environment before release. It uses podman-py to connect and work with the podman container image, and Python unittest for testing the containers. The project is licensed under GPL-3.0-or-later.

How to use eercheck?

Activate the virtual environment in the working directory.

python3 -m venv .venv source .venv/bin/activate python -m pip install -r requirements.txt

Activate the podman socket.

systemctl start podman.socket --user

Update vars.json with correct version numbers.Pick the correct versions of the Ansible Collections from the .deps file of the corresponding Ansible community package release. For example for 9.4.0 the Collection versions can be found in here. You can find the appropriate version of Ansible Community Package here. The check needs to be carried out each time before the release of the Ansible Community Execution Environment.

Execute the program by giving the correct container image id.

./containertest.py image_id

Happy automating.

Categories: FLOSS Project Planets

Real Python: Python News: What's New From March 2024

Mon, 2024-04-08 10:00

While many people went hunting for Easter eggs, the Python community stayed active through March 2024. The free-threaded Python project reached a new milestone, and you can now experiment with disabling the GIL in your interpreter.

The Python Software Foundation does a great job supporting the language with limited resources. They’ve now announced a new position that will support users of PyPI. NumPy is an old workhorse in the data science space. The library is getting a big facelift, and the first release candidate of NumPy 2 is now available.

Dive in to learn more about last month’s most important Python news.

Free-Threaded Python Reaches an Important Milestone

Python’s global interpreter lock (GIL) has been part of the CPython implementation since the early days. The lock simplifies a lot of the code under the hood of the language, but also causes some issues with parallel processing.

Over the years, there have been many attempts to remove the GIL. However, until PEP 703 was accepted by the steering council last year, none had been successful.

The PEP describes how the GIL can be removed based on experimental work done by Sam Gross. It suggests that what’s now called free-threaded Python is activated through a build option. In time, this free-threaded Python is expected to become the default version of CPython, but for now, it’s only meant for testing and experiments.

When free-threaded Python is ready for bigger audiences, the GIL will still be enabled by default. You can then set an environment variable or add a command-line option to try out free-threaded Python:

Read the full article at https://realpython.com/python-news-march-2024/ »

[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

Categories: FLOSS Project Planets

Zato Blog: Integrating with Jira APIs

Mon, 2024-04-08 09:44
Integrating with Jira APIs 2024-04-08, by Dariusz Suchojad Overview

Continuing in the series of articles about newest cloud connections in Zato 3.2, this episode covers Atlassian Jira from the perspective of invoking its APIs to build integrations between Jira and other systems.

There are essentially two use modes of integrations with Jira:

  1. Jira reacts to events taking place in your projects and invokes your endpoints accordingly via WebHooks. In this case, it is Jira that explicitly establishes connections with and sends requests to your APIs.
  2. Jira projects are queried periodically or as a consequence of events triggered by Jira using means other than WebHooks.

The first case is usually more straightforward to conceptualize - you create a WebHook in Jira, point it to your endpoint and Jira invokes it when a situation of interest arises, e.g. a new ticket is opened or updated. I will talk about this variant of integrations with Jira in a future instalment as the current one is about the other situation, when it is your systems that establish connections with Jira.

The reason why it is more practical to first speak about the second form is that, even if WebHooks are somewhat easier to reason about, they do come with their own ramifications.

To start off, assuming that you use the cloud-based version of Jira (e.g. https://example.atlassian.net), you need to have a publicly available endpoint for Jira to invoke through WebHooks. Very often, this is undesirable because the systems that you need to integrate with may be internal ones, never meant to be exposed to public networks.

Secondly, your endpoints need to have a TLS certificate signed by a public Certificate Authority and they need to be accessible on port 443. Again, both of these are something that most enterprise systems will not allow at all or it may take months or years to process such a change internally across the various corporate departments involved.

Lastly, even if a WebHook can be used, it is not always a given that the initial information that you receive in the request from a WebHook will already contain everything that you need in your particular integration service. Thus, you will still need a way to issue requests to Jira to look up details of a particular object, such as tickets, in this way reducing WebHooks to the role of initial triggers of an interaction with Jira, e.g. a WebHook invokes your endpoint, you have a ticket ID on input and then you invoke Jira back anyway to obtain all the details that you actually need in your business integration.

The end situation is that, although WebHooks are a useful concept that I will write about in a future article, they may very well not be sufficient for many integration use cases. That is why I start with integration methods that are alternative to WebHooks.

Alternatives to WebHooks

If, in our case, we cannot use WebHooks then what next? Two good approaches are:

  1. Scheduled jobs
  2. Reacting to emails (via IMAP)

Scheduled jobs will let you periodically inquire with Jira about the changes that you have not processed yet. For instance, with a job definition as below:

Now, the service configured for this job will be invoked once per minute to carry out any integration works required. For instance, it can get a list of tickets since the last time it ran, process each of them as required in your business context and update a database with information about what has been just done - the database can be based on Redis, MongoDB, SQL or anything else.

Integrations built around scheduled jobs make most sense when you need to make periodic sweeps across a large swaths of business data, these are the "Give me everything that changed in the last period" kind of interactions when you do not know precisely how much data you are going to receive.

In the specific case of Jira tickets, though, an interesting alternative may be to combine scheduled jobs with IMAP connections:

The idea here is that when new tickets are opened, or when updates are made to existing ones, Jira will send out notifications to specific email addresses and we can take advantage of it.

For instance, you can tell Jira to CC or BCC an address such as zato@example.com. Now, Zato will still run a scheduled job but instead of connecting with Jira directly, that job will look up unread emails for it inbox ("UNSEEN" per the relevant RFC).

Anything that is unread must be new since the last iteration which means that we can process each such email from the inbox, in this way guaranteeing that we process only the latest updates, dispensing with the need for our own database of tickets already processed. We can extract the ticket ID or other details from the email, look up its details in Jira and the continue as needed.

All the details of how to work with IMAP emails are provided in the documentation but it would boil down to this:

# -*- coding: utf-8 -*- # Zato from zato.server.service import Service class MyService(Service): def handle(self): conn = self.email.imap.get('My Jira Inbox').conn for msg_id, msg in conn.get(): # Process the message here .. process_message(msg.data) # .. and mark it as seen in IMAP. msg.mark_seen()

The natural question is - how would the "process_message" function extract details of a ticket from an email?

There are several ways:

  1. Each email has a subject of a fixed form - "[JIRA] (ABC-123) Here goes description". In this case, ABC-123 is the ticket ID.
  2. Each email will contain a summary, such as the one below, which can also be parsed:
Summary: Here goes description Key: ABC-123 URL: https://example.atlassian.net/browse/ABC-123 Project: My Project Issue Type: Improvement Affects Versions: 1.3.17 Environment: Production Reporter: Reporter Name Assignee: Assignee Name
  1. Finally, each email will have an "X-Atl-Mail-Meta" header with interesting metadata that can also be parsed and extracted:
X-Atl-Mail-Meta: user_id="123456:12d80508-dcd0-42a2-a2cd-c07f230030e5", event_type="Issue Created", tenant="https://example.atlassian.net"

The first option is the most straightforward and likely the most convenient one - simply parse out the ticket ID and call Jira with that ID on input for all the other information about the ticket. How to do it exactly is presented in the next chapter.

Regardless of how we parse the emails, the important part is that we know that we invoke Jira only when there are new or updated tickets - otherwise there would not have been any new emails to process. Moreover, because it is our side that invokes Jira, we do not expose our internal system to the public network directly.

However, from the perspective of the overall security architecture, email is still part of the attack surface so we need to make sure that we read and parse emails with that in view. In other words, regardless of whether it is Jira invoking us or our reading emails from Jira, all the usual security precautions regarding API integrations and accepting input from external resources, all that still holds and needs to be part of the design of the integration workflow.

Creating Jira connections

The above presented the ways in which we can arrive at the step of when we invoke Jira and now we are ready to actually do it.

As with other types of connections, Jira connections are created in Zato Dashboard, as below. Note that you use the email address of a user on whose behalf you connect to Jira but the only other credential is that user's API token previously generated in Jira, not the user's password.

Invoking Jira

With a Jira connection in place, we can now create a Python API service. In this case, we accept a ticket ID on input (called "a key" in Jira) and we return a few details about the ticket to our caller.

This is the kind of a service that could be invoked from a service that is triggered by a scheduled job. That is, we would separate the tasks, one service would be responsible for opening IMAP inboxes and parsing emails and the one below would be responsible for communication with Jira.

Thanks to this loose coupling, we make everything much more reusable - that the services can be changed independently is but one part and the more important side is that, with such separation, both of them can be reused by future services as well, without tying them rigidly to this one integration alone.

# -*- coding: utf-8 -*- # stdlib from dataclasses import dataclass # Zato from zato.common.typing_ import cast_, dictnone from zato.server.service import Model, Service # ########################################################################### if 0: from zato.server.connection.jira_ import JiraClient # ########################################################################### @dataclass(init=False) class GetTicketDetailsRequest(Model): key: str @dataclass(init=False) class GetTicketDetailsResponse(Model): assigned_to: str = '' progress_info: dictnone = None # ########################################################################### class GetTicketDetails(Service): class SimpleIO: input = GetTicketDetailsRequest output = GetTicketDetailsResponse def handle(self): # This is our input data input = self.request.input # type: GetTicketDetailsRequest # .. create a reference to our connection definition .. jira = self.cloud.jira['My Jira Connection'] # .. obtain a client to Jira .. with jira.conn.client() as client: # Cast to enable code completion client = cast_('JiraClient', client) # Get details of a ticket (issue) from Jira ticket = client.get_issue(input.key) # Observe that ticket may be None (e.g. invalid key), hence this 'if' guard .. if ticket: # .. build a shortcut reference to all the fields in the ticket .. fields = ticket['fields'] # .. build our response object .. response = GetTicketDetailsResponse() response.assigned_to = fields['assignee']['emailAddress'] response.progress_info = fields['progress'] # .. and return the response to our caller. self.response.payload = response # ########################################################################### Creating a REST channel and testing it

The last remaining part is a REST channel to invoke our service through. We will provide the ticket ID (key) on input and the service will reply with what was found in Jira for that ticket.

We are now ready for the final step - we invoke the channel, which invokes the service which communicates with Jira, transforming the response from Jira to the output that we need:

$ curl localhost:17010/jira1 -d '{"key":"ABC-123"}' { "assigned_to":"zato@example.com", "progress_info": { "progress": 10, "total": 30 } } $

And this is everything for today - just remember that this is just one way of integrating with Jira. The other one, using WebHooks, is something that I will go into in one of the future articles.

More blog posts
Categories: FLOSS Project Planets

EuroPython: EuroPython April 2024 Newsletter

Mon, 2024-04-08 06:42

Hello, Python enthusiasts! &#x1F44B;

Guess what? We&aposre on the home stretch now, with less than 100 days left until we all rendezvous in the enchanting city of Prague for EuroPython 2024!

Only 91 days left until EuroPython 2024!

Can you feel the excitement tingling in your Pythonic veins?

Let’s look up what&aposs been cooking in the EuroPython pot lately. &#x1FA84;&#x1F35C;

&#x1F4E3; Programme

The curtains have officially closed on the EuroPython 2024 Call for Proposals! &#x1F3AC;

We&aposve hit records with an incredible 627 submissions this year!! &#x1F389;

Thank you to each and every one of you brave souls who tossed your hats into the ring! &#x1F3A9; Your willingness to share your ideas has truly made this a memorable journey.

&#x1F5C3;️ Community Voting

EuroPython 2024 Community Voting was a blast!

The Community Voting is composed of all past EuroPython attendees and prospective speakers between 2015 and 2024.

We had 297 people contributing, making EuroPython more responsive to the community’s choices. &#x1F60E; We can’t thank you enough for helping us hear the voice of the Community.

Now, our wonderful programme crew along with the team of reviewers and community voters have been working hard to create the schedule for the conference! &#x1F4CB;✨

&#x1F4B0; Sponsor EuroPython 2024

EuroPython is a volunteer-run, non-profit conference. All sponsor support goes to cover the cost of running the Europython Conference and supporting the community with Grants and Financial Aid.

If you want to support EuroPython and its efforts to make the event accessible to everyone, please consider sponsoring (or asking your employer to sponsor).

Sponsoring EuroPython guarantees you highly targeted visibility and the opportunity to present your company to one of the largest and most diverse Python communities in Europe and beyond!

There are various sponsor tiers and some have limited slots available. This year, besides our main packages, we offer add-ons as optional extras. For more information, check out our Sponsorship brochure.

&#x1F426; We have an Early Bird 10% discount for companies that sign up by April 15th.&#x1F426;

More information at:  https://ep2024.europython.eu/sponsor &#x1FAC2; Contact us at sponsoring@europython.eu

&#x1F39F;️ Ticket Sales

The tickets are now open to purchase, and there is a variety of options:

  • Conference Tickets: access to Conference and Sprint Weekends.
  • Tutorial Tickets: access to the Workshop/Tutorial Days and Sprint Weekend (no access to the main conference).
  • Combined Tickets: access to everything during the whole seven-day, i.e. workshops, conference talks and sprint weekend!

We also offer different payment tiers designed to answer each attendee&aposs needs. They are:

Business Tickets: for companies and employees funded by their companies
  • Tutorial Only Business (Net price €400.00 + 21% VAT)
  • Conference Only Business (Net price €500.00 + 21% VAT)
  • Late Bird (Net price €750.00 + 21% VAT)
  • Combines Business (Net price €800.00 + 21% VAT)
  • Late Bird (Net price €1200.00 + 21% VAT)
Personal Tickets: for individuals
  • Tutorial Only Personal (€200.00 incl. 21%VAT)
  • Conference Only Personal (€300.00 incl. 21% VAT)
  • Late Bird (€450.00 incl. 21% VAT)
  • Combined Personal (€450.00 incl. 21% VAT)
  • Late Bird (€675.00 incl. 21% VAT)
Education Tickets: for students and active teachers (Educational ID is required at registration)
  • Conference Only Education (€135.00 incl. 21% VAT)
  • Tutorial Only Education (€100.00 incl. 21% VAT)
  • Combined Education (€210.00 incl. 21% VAT)
Fun fact: Czechia has been ranked among the world&aposs top 20 happiest countries recently.

Seize the chance to grab an EP24 ticket and connect with the delightful community of Pythonistas and happy locals this summer! ☀️

Need more information regarding tickets? Please visit https://ep2024.europython.eu/tickets or contact us at helpdesk@europython.eu.

⚖️ Visa Application

If you require a visa to attend EuroPython 2024 in Prague, now is the time to start preparing.

The first step is to verify if you require a visa to travel to the Czech Republic.

The Czech Republic is a part of the EU and the Schengen Area. If you already have a valid Schengen visa, you may NOT need to apply for a Czech visa. If you are uncertain, please check this website and consult your local consular office or embassy. &#x1F3EB;

If you need a visa to attend EuroPython, you can lodge a visa application for Short Stay (C), up to 90 days, for the purpose of “Business /Conference”. We recommend you do this as soon as possible.

Please, make sure you read all the visa pages carefully and prepare all the required documents before making your application. The EuroPython organisers are not able nor qualified to give visa advice.

However, we are more than happy to help with the visa support letter issued by the EuroPython Society. Every registered attendee can request one; we only issue visa support letters to confirmed attendees. We kindly ask you to purchase your ticket before filling in the request form.

For more information, please check https://ep2024.europython.eu/visa or contact us at visa@europython.eu. ✈️

&#x1F4B6; Financial Aid

We are also pleased to announce our financial aid, sponsored by the EuroPython Society. The goal is to make the conference open to everyone, including those in need of financial assistance.

Submissions for the first round of our financial aid programme are open until April 21st 2024.

There are three types of grants including:

  • Free Ticket Voucher Grant
  • Travel/Accommodation Grant (reimbursement of travel costs up to €400.)
  • Visa Application Fee Grant (up to €80)
⏰ FinAid timeline

If you apply for the first round and do not get selected, you will automatically be considered for the second round. No need to reapply.

8 March 2024Applications open21 April 2024Deadline for submitting first-round applications8 May 2024First round of grant notifications12 May 2024Deadline to accept a first-round grant19 May 2024Deadline for submitting second-round applications15 June 2024Second round of grant notifications12 June 2024Deadline to accept a second-round grant21 July 2024Deadline for submitting receipts/invoices

Visit https://europython.eu/finaid for information on eligibility and application procedures for Financial Aid grants.

&#x1F3A4; Public Speaking Workshop for Mentees

We are excited to announce that this year’s Speaker Mentorship Programme comes with an extra package!

We have selected a limited number of mentees for a 5-week interactive course covering the basics of a presentation from start to finish.

The main facilitator is the seasoned speaker Cheuk Ting Ho and the participants will end the course by delivering a talk covering all they have learned.

We look forward to the amazing talks the workshop participants will give us. &#x1F64C;

&#x1F40D; Upcoming Events in Europe

Here are some upcoming events happening in Europe soon.

Czech Open Source Policy Forum: Apr 24, 2024 (In-Person)

Interested in open source and happen to be near Brno/Czech Republic in April? Join the Czech Open Source Policy Forum and have the chance to celebrate the launch of the Czech Republic&aposs first Open Source Policy Office (OSPO). More info at: https://pretix.eu/om/czospf2024/

OSSCi Prague Meetup: May 16, 2024 (In-Person)

Join the forefront of innovation at OSSci Prague Meetup, where open source meets science. Call for Speakers is open!  https://pydata.cz/ossci-cfs.html

PyCon DE & PyData Berlin: April 22-24 2024

Dive into three days of Python and PyData excellence at Pycon DE! Visit https://2024.pycon.de/ for details.

PyCon Italy: May 22-25 2024

PyCon Italia 2024 will happen in Florence. The schedule is online and you can check it out at their nice website: https://2024.pycon.it/

GeoPython 2024: May 27-29, 2024

GeoPython 2024 will happen in Basel, Switzerland. For more information visit their website: https://2024.geopython.net/

&#x1F92D; Py.Jokes

Can you imagine our newsletter without joy and laughter? We can’t. &#x1F63E;&#x1F645;‍♀️❌ Here’s this month&aposs PyJoke:

pip install pyjokesimport pyjokesprint(pyjokes.get_joke()) How many programmers does it take to change a lightbulb? None, they just make darkness a standard! &#x1F423; See You All Next Month

Before saying goodbye, thank you so much for reading this far.

We can’t wait to reunite with all you amazing people in beautiful Prague again.

Let me remind you how pretty Prague is during summer. &#x1F33A;&#x1F33C;&#x1F33A;

Rozkvetlá jarní Praha, b&#x159;ezen 2024 by Radoslav Vnen&#x10D;ák

Remember to take good care of yourselves, stay hydrated and mind your posture!

Oh, and don’t forget to force encourage your friends to join us at EuroPython 2024! &#x1F60C;

It’s time again to make new Python memories together!

Looking forward to meeting you all here next month!

With much joy and excitement,

EuroPython 2024 Team &#x1F917;

Categories: FLOSS Project Planets

Python Insider: Python 3.11.9 is now available

Mon, 2024-04-08 00:50

  


 This is the last bug fix release of Python 3.11 

This is the ninth maintenance release of Python 3.11

Python 3.11.9 is the newest major release of the Python programming language, and it contains many new features and optimizations. Get it here:

https://www.python.org/downloads/release/python-3119/

Major new features of the 3.11 series, compared to 3.10

Among the new major new features and changes so far:

  • PEP 657 – Include Fine-Grained Error Locations in Tracebacks
  • PEP 654 – Exception Groups and except*
  • PEP 673 – Self Type
  • PEP 646 – Variadic Generics
  • PEP 680 – tomllib: Support for Parsing TOML in the Standard Library
  • PEP 675 – Arbitrary Literal String Type
  • PEP 655 – Marking individual TypedDict items as required or potentially-missing
  • bpo-46752 – Introduce task groups to asyncio
  • PEP 681 – Data Class Transforms
  • bpo-433030– Atomic grouping ((?>…)) and possessive quantifiers (*+, ++, ?+, {m,n}+) are now supported in regular expressions.
  • The Faster Cpython Project is already yielding some exciting results. Python 3.11 is up to 10-60% faster than Python 3.10. On average, we measured a 1.22x speedup on the standard benchmark suite. See Faster CPython for details.

More resources

And now for something completely different

A kugelblitz is a theoretical astrophysical object predicted by general relativity. It is a concentration of heat, light or radiation so intense that its energy forms an event horizon and becomes self-trapped. In other words, if enough radiation is aimed into a region of space, the concentration of energy can warp spacetime so much that it creates a black hole. This would be a black hole whose original mass–energy was in the form of radiant energy rather than matter, however as soon as it forms, it is indistinguishable from an ordinary black hole.

We hope you enjoy the new releases!Thanks to all of the many volunteers who help make Python Development and these releases possible! Please consider supporting our efforts by volunteering yourself or through organization contributions to the Python Software Foundation.

https://www.python.org/psf/

Your friendly release team,
Ned Deily @nad 
Steve Dower @steve.dower 
Pablo Galindo Salgado @pablogsal

Categories: FLOSS Project Planets

Python Morsels: Python's http.server module

Fri, 2024-04-05 19:41

Use Python's http.server module to serve up a static website on your own machine.

Table of contents

  1. A directory trees of index.html files
  2. Serving up HTML files with http.server
  3. Customizing http.server with CLI arguments
  4. Using http.server as a module
  5. Use python -m http.server for a local HTTP server

A directory trees of index.html files

We have a directory here that represents a static website:

~/comprehensions/_build/dirhtml $ ls index.html index.html

We not only have an index.html file, but also a bunch of sub-directories, each with their own index.html file:

~/comprehensions/_build/dirhtml $ ls generator-expressions index.html

The only way to really navigate this website locally is to serve up these files using some sort of HTTP server that is aware of these index files.

Python comes bundled with an HTTP server that we can use. It's called http.server.

Serving up HTML files with http.server

If we run this module …

Read the full article: https://www.pythonmorsels.com/http-server/
Categories: FLOSS Project Planets

PyBites: Python F-String Codes I Use Every Day

Fri, 2024-04-05 12:15

A few examples that will save the day probably* 95% of time.

*I don’t have the actual data but seriously, I bet you’ll find those tips useful more often than not!

Introduction

This article was originally posted on Medium.

I use f-strings every day. The irony is I also every day end up searching the Web to find the correct format to use. Until one day I thought a better use of my time would be to create a cheat sheet of the most common formatting cases — AKA this article. It covers the following:

  • integers, floats, numbers and scientific notation
  • percentages %
  • dates
  • padding
  • adding +/- sign in front of a number

It’s important to note that the f-strings were first introduced in Python 3.6 (PEP 498 if you REALLY must know) so make sure to check the Python version first, if things don’t work for you.

The format

The most basic f-string format goes like this:

You can use this format to print numbers, texts, or even evaluate expressions.

author = "pawjast" year = 2022 print(f"Example 1: {author}") # string print(f"Example 2: {year}") # number print(f"Example 3: {2 + 2}") # expression

The output:

Example 1: pawjast Example 2: 2022 Example 3: 4

You might have noticed that the 2+2 expression in Example 3 got evaluated and 4 was printed.

You’ll get both, the name and the value if you add = sign to a variable.

text = "Data Science Blog" print(f"{text=}")

The output:

text='Data Science Blog'

Template below generalizes how you can add a specific format to a variable:

Next paragraphs shows examples of how to use it.

Recap on numbers

Last stop before going any further. I’ll be using different ways of writing numbers later in the article so let’s review the most common ones.

int_1 = 1 int_with_separator = 1_000 # `int` with 1,000 separator float_1 = 1.125 float_2 = 3.50 scientific_1 = 1.23e2 # 1.23 * 10^2 print(f"Example 1 - int: {int_1}") print(f"Example 2 - int with _ as thousands separator: {int_with_separator}") print(f"Example 3 - float: {float_1}") print(f"Example 4 - float with trailing zero: {float_2}") print(f"Example 5 - float in scientific notation: {scientific_1}")

The output:

Example 1 - int: 1 Example 2 - int with _ as thousands separator: 1000 Example 3 - float: 1.125 Example 4 - float with trailing zero: 3.5 Example 5 - float in scientific notation: 123.0

You can observe that:

  • floats had the trailing zeros truncated (Example 4)
  • scientific notation was printed as a regular float
  • the rest of the variables were printed “as is”
Floats

The float type is enforced by using code f. What you want to control in floats is the number of decimal places.

pi_val = 3.141592 print(f"Example 1: {pi_val:f}") print(f"Example 2: {pi_val:.0f}") print(f"Example 3: {pi_val:.1f}") print(f"Example 4: {pi_val:.3f}")

The output:

Example 1: 3.141592 Example 2: 3 Example 3: 3.1 Example 4: 3.142

Side note: f-strings are flexible enough to allow nesting.

float_val = 1.5 precision = 3 print(f"{float_val:.{precision}f}")

The output:

1.500 Percentage

Use code % to enforce percentage type. Percentage is still a float so you can still use .<whole_number> to control the precision.

val = 0.5 print(f"Example 1: {val:%}") print(f"Example 2: {val:.0%}")

The output:

Example 1: 50.000000% Example 2: 50%

More examples of controlling precision in %:

val = 1.255 print(f"Example 1: {val:.0%}") print(f"Example 2: {val:.1%}")

The output:

Example 1: 125% Example 2: 125.5% Scientific notation

If you want scientific notation to be printed as such (and not as a regular float) it can be enforced with e or E code.

val = 1.23e3 # 1.23 * 10^3 print(f"Example 1: {val:e}") print(f"Example 2: {val:E}")

The output:

1: 1.230000e+03 2: 1.230000E+03

No surprise the precision can be controlled in this case too.

val = 1.2345e3 print(f"{val:.2e}")

The output:

1.23e+03

You can even print a regular number in scientific notation.

val = 2022 print(f"{val:.3e}")

The output:

2.022e+03 Integers

Integers are enforced using code d.

val = 1 print(f"{val:d}")

The output:

1

Adding , to the code will print the thousands separator.

int_1 = 1000 int_2 = 1000_000_000 print(f"{int_1:,d}") print(f"{int_2:,d}")

The output:

1,000 1,000,000,000 Numbers

If the aim is to just print a number, you can use generic code — n.

val_int = 1 val_float = 1.234 val_scient = 4.567e2 print(f"{val_int =: n}") print(f"{val_float =: n}") print(f"{val_scient =: n}")

The output:

val_int = 1 val_float = 1.234 val_scient = 456.7

You can still use .<whole_number> format to control the precision.

Side note: in this case whole_number determines the total number of digits printed, not the number of decimal points! On top of that, n code will decide the best output format for a number.

val_float_1 = 1.234 val_float_2 = 20.234 val_float_3 = 123.456 print(f"{val_float_1 =: .2n}") # prints as truncated float print(f"{val_float_2 =: .2n}") # prints as int print(f"{val_float_3 =: .2n}") # prints as scientific notation

The output:

val_float_1 = 1.2 val_float_2 = 20 val_float_3 = 1.2e+02 Dates from datetime import date, datetime

Printing a date “as is” works exactly like printing any other variable.

day = date( year=2022, month=9, day=1 ) print(f"{day}")

The output:

2022-09-01

To recreate the format you will use the following codes:

  • %Y for year
  • %m for month
  • %d for day

With those codes you can e.g. create a new date format.

print(f"{day:%Y-%m-%d}") # default appearance print(f"{day:%Y/%m/%d}") # use `/` as separator

The output:

2022-09-01 2022/09/01

It’s also possible to print a month as a text:

  • %b — short version
  • %B — long version
print(f"{day:%Y %b %d}") print(f"{day:%Y %B %d}")

The output:

2022 Sep 01 2022 September 01

The same variable can be printed multiple times.

print(f"{day:%b or %B}?") print(f"{day:%Y %Y %Y}"

The output:

Sep or September? 2022 2022 2022

Reusing the same variable can be useful e.g. when you want to print the date and provide the day of the week as text (using code %A ).

print(f"{day:%Y %b %d (%A)}")

The outcome:

2022 Sep 01 (Thursday)

Last but not least, you can swap %Y for %y to get short version of the year.

print(f"{day:%y.%m.%d}")

The output:

22.09.01 Datetime

Let’s specify two datetime variables.

day_and_time = datetime( year=2022, month=9, day=1, hour=17, minute=30, second=45 ) now = datetime.now() print(f"{day_and_time}") print(f"{now}") # with mircoseconds

The output:

2022-09-01 17:30:45 2022-12-08 15:49:37.810347

Time format requires the following codes:

  • %H — hour
  • %M — minute
  • %S — second
  • %f — millisecond (1s = 1000 milliseconds)

Therefore, the default format would look like below.

print(f"{now:%Y-%m-%d %H:%M:%S.%f}")

The outcome:

2022-12-08 15:49:37.810347

f-string obviously creates a string (try this code to confirm it: type(f”now:%Y-%m-%d %H:%M%S.%f}”)). Therefore, if e.g. you are not happy with the number of decimal points for milliseconds, you can easily truncate them.

print(f"{now:%Y-%m-%d %H:%M:%S.%f}"[:22])

The output:

2022-12-08 15:49:37.81

In order to change the 24hr format to 12hr format, you will need to:

  • swap hour code from %H to %I
  • Optionally add %p at the end if you want to add AM/PM to the time
print(f"24hr: {day_and_time:%Y-%m-%d %H:%M:%S}") print(f"12hr: {day_and_time:%Y-%m-%d %I:%M:%S}") print(f"12hr with AM/PM: {day_and_time:%Y-%m-%d %I:%M:%S %p}")

The output:

24hr: 2022-09-01 17:30:45 12hr: 2022-09-01 05:30:45 12hr with AM/PM: 2022-09-01 05:30:45 PM

Few more useful date formats:

  • %j for day of the year
  • %W for week of the year (assuming Monday as first day of the week)
  • %U for week of the year (assuming Sunday as first day of the week
day = date( year=2018, month=9, day=17 ) print(f"The date: {day}") print(f"Day of the year: {day: %j}") print(f"Week of the year (Mon): {day: %W}") print(f"Week of the year (Sun): {day: %U}")

The outcome:

The date: 2018-09-17 Day of the year: 260 Week of the year (Mon): 38 Week of the year (Sun): 37 Padding

Padding with empty spaces.

val = 1 print(f"1: {val:1d}") print(f"2: {val:2d}") print(f"3: {val:3d}")

The output:

1: 1 2: 1 3: 1

Padding with zeros.

val = 1 print(f"1: {val:01d}") print(f"2: {val:02d}") print(f"3: {val:03d}")

The output:

1: 1 2: 01 3: 001

I usually use zero — padding with for loop to keep the text nicely aligned in the terminal.

for i in range(11): print(f"{i:02d}")

The output:

01 02 ... 09 10 Positive/negative sign

In some cases you will require to show a +/- sign next to a number.

positive = 1.23 negative = -1.23 print(f"1: {positive:+.2f} {negative:+.2f}") print(f"2: {positive:-.2f} {negative:-.2f}") print(f"3: {positive: .2f} {negative: .2f}")

The output:

1: +1.23 -1.23 2: 1.23 -1.23 3: 1.23 -1.23 Combining all things together # print variable with name, limit precision and thousands separator val = 11500.23456 print(f"{val = :,.3f}")

The output:

val = 11,500.235

We’re done .

Full code

 GitHub notebook

Connect with me

 Medium  Twitter / X  GitHub  Substack  LinkedIn

Resources

1⃣ https://docs.python.org/3/reference/lexical_analysis.html#f-strings

2⃣ https://docs.python.org/3/library/string.html#format-specification-mini-language

3⃣ https://strftime.org/

Categories: FLOSS Project Planets

Real Python: The Real Python Podcast – Episode #199: Leveraging Documents and Data to Create a Custom LLM Chatbot

Fri, 2024-04-05 08:00

How do you customize a LLM chatbot to address a collection of documents and data? What tools and techniques can you use to build embeddings into a vector database? This week on the show, Calvin Hendryx-Parker is back to discuss developing an AI-powered, Large Language Model-driven chat interface.

[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

Categories: FLOSS Project Planets

Python Engineering at Microsoft: Python in Visual Studio Code – April 2024 Release

Thu, 2024-04-04 16:50

We’re excited to announce the April 2024 release of the Python and Jupyter extensions for Visual Studio Code!

This release includes the following announcements:

  • Improved debug config flow for Flask and Django
  • Module and import analysis on Jupyter’s Run Dependent Cells with Pylance
  • Hatch environment discovery
  • Automatic environment selection for pipenv, pyenv, and Poetry projects
  • Report Issue command improvements

If you’re interested, you can check the full list of improvements in our changelogs for the Python, Jupyter and Pylance extensions.

Improved debug config flow for Flask and Django

Creating launch configurations for Flask and Django apps just got easier! Improvements have been made to detect possible startup files in your workspace when creating a launch.json for your web app. For Django, the Python Debugger extension looks for manage.py or app.py files in the root or a subdirectory one level lower in your workspace. For Flask, the extension looks for wsgi.py, app.py, or init.py files that contain the declaration of a Flask application (for example, app = Flask()). If none of those files are not found in the project, the dropdown shows a Default option for the corresponding project type, even though that file may not be present.

In the case your file was not detected, you can enter the file path directly or browse other files in your directory.

Module and import analysis on Jupyter’s Run Dependent Cells with Pylance

Dependency analysis for Jupyter cells has improved with the latest Pylance pre-release. With these changes, Pylance aids Jupyter in understanding module imports, which is especially useful when you have a cell that imports a module that was defined in a previous cell.

To enable this feature, install the latest Pylance pre-release in VS Code Insiders, and enable the jupyter.executionAnalysis.enabled and notebook.consolidatedRunButton settings.

Hatch environment discovery

Hatch environments are now discovered and activated, by default, similar to other common environments, such as Venv, Conda, and Poetry. Furthermore, in the case of Hatch where an explicit environment identifier is not registered, the extension is able to determine the environment type (Hatch) from the environment locator.

Automatic environment selection for pipenv, pyenv, and Poetry projects

If your workspace contains a pipenv, pyenv, or Poetry environment, the corresponding environment is now automatically selected for your workspace. Previously, the extension correctly discovered these environments, but selected the default global interpreter, requiring you to manually select the appropriate environment for your workspace. Now, the Python extension infers the default environment based on its presence and any corresponding configuration files. For example, in the case of pyenv, the extension looks at the .python-version file to automatically select the appropriate interpreter for the workspace.

Report Issue command improvements

The Python and Python Debugger extensions have adopted changes making it easier for you to report issues to our repos! Filing an issue with the Report Issue command (workbench.action.openIssueReporter) does most of the heavy lifting, prompting you for additional info so our team can efficiently triage the problem you are encountering.

To file an issue using the Report Issue command for @vscode-python or @vscode-python-debugger, choose Python or Python Debugger respectively from the extension dropdown.

Other Changes and Enhancements

We have also added small enhancements and fixed issues requested by users that should improve your experience working with Python and Jupyter Notebooks in Visual Studio Code. Some notable changes include:

  • Better error messages when “Move to file” refactoring fails in @pylance-release#4345
  • REPL Smart Send (python.REPL.enableREPLSmartSend) is now enabled by default

We would also like to extend special thanks to this month’s contributors:

Call for Community Feedback

As we are planning and prioritizing future work, we value your feedback! Below are a few issues we would love feedback on:

Try out these new improvements by downloading the Python extension and the Jupyter extension from the Marketplace, or install them directly from the extensions view in Visual Studio Code (Ctrl + Shift + X or ⌘ + ⇧ + X). You can learn more about Python support in Visual Studio Code in the documentation. If you run into any problems or have suggestions, please file an issue on the Python VS Code GitHub page.

The post Python in Visual Studio Code – April 2024 Release appeared first on Python.

Categories: FLOSS Project Planets

PyCharm: PyCharm 2024.1 Is Here! Hugging Face Model Card Previews, Local Full Line Code Completion Updates, and more!

Thu, 2024-04-04 06:06
PyCharm 2024.1 is out with many key updates, including full line code completion for frontend frameworks, documentation preview for Hugging Face models and datasets, and multiple improvements to the user experience – just to name a few! You can download the latest version from our download page, or even easier, update your current version through […]
Categories: FLOSS Project Planets

Talk Python to Me: #455: Land Your First Data Job

Thu, 2024-04-04 04:00
Interested in data science but you're not quite working in it yet? In software, getting that very first job can truly be the hardest one to land. On this episode, we have Avery Smith from Data Career Jumpstart here to share his advice for getting your first data job.<br/> <br/> <strong>Episode sponsors</strong><br/> <br/> <a href='https://talkpython.fm/sentry'>Sentry Error Monitoring, Code TALKPYTHON</a><br> <a href='https://talkpython.fm/posit'>Posit</a><br> <a href='https://talkpython.fm/training'>Talk Python Courses</a><br/> <br/> <strong>Links from the show</strong><br/> <br/> <div><b>Avery Smith</b>: <a href="https://www.linkedin.com/in/averyjsmith/" target="_blank" rel="noopener">www.linkedin.com</a><br/> <b>Data Career Jumpstart</b>: <a href="https://www.datacareerjumpstart.com/" target="_blank" rel="noopener">www.datacareerjumpstart.com</a><br/> <b>Data Nerd Site</b>: <a href="https://datanerd.tech" target="_blank" rel="noopener">datanerd.tech</a><br/> <b>Write C# LINQ queries to query data</b>: <a href="https://learn.microsoft.com/en-us/dotnet/csharp/linq/get-started/write-linq-queries" target="_blank" rel="noopener">learn.microsoft.com</a><br/> <b>A faster way to build and share data apps</b>: <a href="https://streamlit.io" target="_blank" rel="noopener">streamlit.io</a><br/> <b>Plotly Dash</b>: <a href="https://dash.plotly.com" target="_blank" rel="noopener">dash.plotly.com</a><br/> <br/> <b>Michael's Keynote: State of Python in 2024</b>: <a href="https://www.youtube.com/watch?v=coz1CGRxjQ0" target="_blank" rel="noopener">youtube.com</a><br/> <b>Watch this episode on YouTube</b>: <a href="https://www.youtube.com/watch?v=0G89ZY5IWUM" target="_blank" rel="noopener">youtube.com</a><br/> <b>Episode transcripts</b>: <a href="https://talkpython.fm/episodes/transcript/455/land-your-first-data-job" target="_blank" rel="noopener">talkpython.fm</a><br/> <br/> <b>--- Stay in touch with us ---</b><br/> <b>Subscribe to us on YouTube</b>: <a href="https://talkpython.fm/youtube" target="_blank" rel="noopener">youtube.com</a><br/> <b>Follow Talk Python on Mastodon</b>: <a href="https://fosstodon.org/web/@talkpython" target="_blank" rel="noopener"><i class="fa-brands fa-mastodon"></i>talkpython</a><br/> <b>Follow Michael on Mastodon</b>: <a href="https://fosstodon.org/web/@mkennedy" target="_blank" rel="noopener"><i class="fa-brands fa-mastodon"></i>mkennedy</a><br/></div>
Categories: FLOSS Project Planets

Matt Layman: Flash messages and content encodings - Building SaaS with Python and Django #188

Wed, 2024-04-03 20:00
In this episode, we added flash messages (after a rough start with some networking issues). Then I tracked down a thorny issue. We found that there is a non-breaking space with ’timesince’ that affects the encoding and what links Gmail adds to emails.
Categories: FLOSS Project Planets

Real Python: Install and Execute Python Applications Using pipx

Wed, 2024-04-03 10:00

A straightforward way to distribute desktop and command-line applications written in Python is to publish them on the Python Package Index (PyPI), which hosts hundreds of thousands of third-party packages. Many of these packages include runnable scripts, but using them requires decent familiarity with the Python ecosystem. With pipx, you can safely install and execute such applications without affecting your global Python interpreter.

In this tutorial, you’ll learn how to:

  • Turn the Python Package Index (PyPI) into an app marketplace
  • Run installed applications without explicitly calling Python
  • Avoid dependency conflicts between different applications
  • Try throw-away applications in temporary locations
  • Manage the installed applications and their environments

To fully benefit from this tutorial, you should feel comfortable around the terminal. In particular, knowing how to manage Python versions, create virtual environments, and install third-party modules in your projects will go a long way.

Note: If you’re a Windows user, then it’s highly recommended you follow our Python coding setup guide before plunging into this tutorial. The gist of it is that you should avoid installing Python from the Microsoft Store, as it could prevent pipx from working correctly.

To help you get to grips with pipx, you can download the supplemental materials, which include a handy command cheat sheet. Additionally, you can test your understanding by taking a short quiz.

Get Your Cheatsheet: Click here to download the free cheatsheet of pipx commands you can use to install and execute Python applications.

Take the Quiz: Test your knowledge with our interactive “Install and Execute Python Applications Using pipx” quiz. Upon completion you will receive a score so you can track your learning progress over time:

Take the Quiz »

Get Started With pipx

On the surface, pipx resembles pip because it also lets you install Python packages from PyPI or another package index. However, unlike pip, it doesn’t install packages into your system-wide Python interpreter or even an activated virtual environment. Instead, it automatically creates and manages virtual environments for you to isolate the dependencies of every package that you install.

Additionally, pipx adds symbolic links to your PATH variable for every command-line script exposed by the installed packages. As a result, you can invoke those scripts directly from the command line without explicitly running them through the Python interpreter.

Think of pipx as Python’s equivalent of npx in the JavaScript ecosystem. Both tools let you install and execute third-party modules in the command line just as if they were standalone applications. However, not all modules are created equal.

Broadly speaking, you can classify the code distributed through PyPI into three categories:

  1. Importable: It’s either pure-Python source code or Python bindings of compiled shared objects that you want to import in your Python projects. Typically, they’re libraries like Requests or Polars, providing reusable pieces of code to help you solve a common problem. Alternatively, they might be frameworks like FastAPI or PyGame that you build your applications around.
  2. Runnable: These are usually command-line utility tools like black, isort, or flake8 that assist you during the development phase. They could also be full-fledged applications like bpython or the JupyterLab environment, which is primarily implemented in a foreign TypeScript programming language.
  3. Hybrid: They combine both worlds by providing importable code and runnable scripts at the same time. Flask and Django are good examples, as they offer utility scripts while remaining web frameworks for the most part.

Making a distribution package runnable or hybrid involves defining one or more entry points in the corresponding configuration file. Historically, these would be setup.py or setup.cfg, but modern build systems in Python should generally rely on the pyproject.toml file and define their entry points in the [project.scripts] TOML table.

Note: If you use Poetry to manage your project’s dependencies, then you can add the appropriate script declarations in the tool-specific [tool.poetry.scripts] table.

Each entry point represents an independent script that you can run by typing its name at the command prompt. For example, if you’ve ever used the django-admin command, then you’ve called out an entry point to the Django framework.

Note: Don’t confuse entry points, which link to individual functions or callables in your code, with runnable Python packages that rely on the __main__ module to provide a command-line interface.

For example, Rich is a library of building blocks for creating text-based user interfaces in Python. At the same time, you can run this package with python -m rich to display a demo application that illustrates various visual components at your fingertips. Despite this, pipx won’t recognize it as runnable because the library doesn’t define any entry points.

To sum up, the pipx tool will only let you install Python packages with at least one entry point. It’ll refuse to install runnable packages like Rich and bare-bones libraries that ship Python code meant just for importing.

Once you identify a Python package with entry points that you’d like to use, you should first create and activate a dedicated virtual environment as a best practice. By keeping the package isolated from the rest of your system, you’ll eliminate the risk of dependency conflicts across various projects that might require the same Python library in different versions. Furthermore, you won’t need the superuser permissions to install the package.

Deciding where and how to create a virtual environment and then remembering to activate it every time before running the corresponding script can become a burden. Fortunately, pipx automates these steps and provides even more features that you’ll explore in this tutorial. But first, you need to get pipx running itself.

Test Drive pipx Without Installation

If you’re unsure whether pipx will address your needs and would prefer not to commit to it until you’ve properly tested the tool, then there’s good news! Thanks to a self-contained executable available for download, you can give pipx a spin without having to install it.

To get that executable, visit the project’s release page on the official GitHub repository in your web browser and grab the latest version of a file named pipx.pyz. Files with the .pyz extension represent runnable Python ZIP applications, which are essentially ZIP archives containing Python source code and some metadata, akin to JAR files in Java. They can optionally vendor third-party dependencies that you’d otherwise have to install by hand.

Note: Internally, the pipx project uses shiv to build its Python ZIP application. When you first run a Python ZIP application that was built with shiv, it’ll unpack itself into a hidden folder named .shiv/ located in your user’s home directory. As a result, subsequent runs of the same application will reuse the already extracted files, speeding up the startup time.

Afterward, you can run pipx.pyz by passing the path to your downloaded copy of the file to your Python interpreter—just as you would with a regular Python script:

Read the full article at https://realpython.com/python-pipx/ »

[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

Categories: FLOSS Project Planets

Django Weblog: Django bugfix release issued: 5.0.4

Wed, 2024-04-03 09:52

Today we've issued the 5.0.4 bugfix release.

The release package and checksums are available from our downloads page, as well as from the Python Package Index. The PGP key ID used for this release is Natalia Bidart: 2EE82A8D9470983E.

Django 3.2 has reached the end of extended support

Note that with this release, Django 3.2 has reached the end of extended support. All Django 3.2 users are encouraged to upgrade to Django 4.2 or later to continue receiving fixes for security issues.

See the downloads page for a table of supported versions and the future release schedule.

Categories: FLOSS Project Planets

Robin Wilson: Simple self-hosted OpenStreetMap routing using Valhalla and Docker

Wed, 2024-04-03 06:39

I came up with an interesting plan for an artistic map recently (more on that when I’ve finished working on it), and to create it I needed to be able to calculate a large number of driving routes around Southampton, my home city.

Specifically, I needed to be able to get lines showing the driving route from A to B for around 5,000 combinations of A and B. I didn’t want to overload a free hosted routing server, or have to deal with rate limiting – so I decided to look into running some sort of routing service myself, and it was actually significantly easier than I thought it would be.

It so happened that I’d come across a talk recently on a free routing engine called Valhalla. I hadn’t actually watched the talk yet (it is on my ever-expanding list of talks to watch), but it had put the name into my head – so I started investigating Valhalla. It seemed to do what I wanted, so I started working out how to run it locally. Using Docker, it was nice and easy – and to make it even easier for you, here’s a set of instructions.

  1. Download an OpenStreetMap extract for your area of interest. All of your routes will need to be within the area of this extract. I was focusing on Southampton, UK, so I downloaded the Hampshire extract from the England page on GeoFabrik. If you start from the home page you should be able to navigate to any region in the world.

  2. Put the downloaded file (in this case, hampshire-latest.osm.pbf) in a folder called custom_files. You can download multiple files and put them all in this folder and they will all be processed.

  3. Run the following Docker command:

    docker run -p 8002:8002 -v $PWD/custom_files:/custom_files ghcr.io/gis-ops/docker-valhalla/valhalla:latest

    This will run the valhalla Docker image, exposing port 8002 and mapping the custom_files subdirectory to /custom_files inside the container. Full docs for the Docker image are available here.

  4. You’ll see various bits of output as Valhalla processes the OSM extract, and eventually the output will stop appearing and the API server will start.

  5. Visit http://localhost:8002 and you should see an error – this is totally expected, it is just telling you that you haven’t used one of the valid API endpoints. This shows that the server is running properly.

  6. Start using the API. See the documentation for instructions on what to pass the API.

Once you’ve got the server running, it’s quite easy to call the API from Python and get the resulting route geometries as Shapely LineString objects. These can easily be put into a GeoPandas GeoDataFrame. For example:

import urllib import requests from pypolyline.cutil import decode_polyline # Set up the API request parameters - in this case, from one point # to another, via car data = {"locations":[ {"lat":from_lat,"lon":from_lon},{"lat":to_lat,"lon":to_lon}], "costing":"auto"} # Convert to JSON and make the URL path = f"http://localhost:8002/route?json={urllib.parse.quote(json.dumps(data))}" # Get the URL resp = requests.get(path) # Extract the geometry of the route (ie. the line from start to end point) # This is in the polyline format that needs decoding polyline = bytes(resp.json()['trip']['legs'][0]['shape'], 'utf8') # Decode the polyline decoded_coords = decode_polyline(polyline, 6) # Convert to a shapely LineString geom = shapely.LineString(decoded_coords)

To run this, you’ll need to install pypolyline and requests.

Note that you need to pass a second parameter of 6 into decode_polyline or you’ll get nonsense out (this parameter tells it that it is in polyline6 format, which seems to not be documented particularly well in the Valhalla documentation). Also, I’m sure there is a better way of passing JSON in a URL parameter using requests, but I couldn’t find it – whatever I did I got a JSON parsing error back from the API. The urllib.parse.quote(json.dumps(data)) code was the simplest thing I found that worked.

This code could easily be extended to work with multi-leg routes, to extract other data like the length or duration of the route, and more. The Docker image can also do more, like load public transport information, download OSM files itself and even more – see the docs for more on this.

Check back soon to see how I used this for some cool map-based art.

Categories: FLOSS Project Planets

Matt Layman: NATS: Connecting Apps Over a Network Easily

Tue, 2024-04-02 20:00
NATS is an awesome open source technology to help connect code together over a network. Whether you’re build a distributed microservice architecture or connecting IoT devices, NATS provides the tools you need to do that easily. In this talk, you’ll learn about NATS via a presentation with plenty of live coding examples.
Categories: FLOSS Project Planets

PyCoder’s Weekly: Issue #623 (April 2, 2024)

Tue, 2024-04-02 15:30

#623 – APRIL 2, 2024
View in Browser »

Reading and Writing WAV Files in Python

In this tutorial, you’ll learn how to work with WAV audio files in Python using the standard-library wave module. Along the way, you’ll synthesize sounds from scratch, visualize waveforms in the time domain, animate real-time spectrograms, and apply special effects to widen the stereo field.
REAL PYTHON

Designing a Pure Python Web Framework

This blog post talks about Reflex, a Python web framework. The post talks about what makes Reflex different from other frameworks and shows you sample starting code. See also the associated HN Discussion.
NIKHIL RAO

Creating an Autopilot in X-Plane Using Python

X-Plane is a flight simulator, and Austin is using Python to create an autopilot using proportional integral derivative controllers. Read on to see how its done.
AUSTIN

Mojo Goes Open Source

MODULAR

PyPI Hiring a Support Specialist (Remote)

PYPI

Discussions Draft PEP: Sealed Decorator for Static Typing

PYTHON DISCUSS

What Are Some Good Python Codebases to Read?

LOBSTERS

Articles & Tutorials Using Python in Bioinformatics and the Laboratory

How is Python being used to automate processes in the laboratory? How can it speed up scientific work with DNA sequencing? This week on the show, Chemical Engineering PhD Student Parsa Ghadermazi is here to discuss Python in bioinformatics.
REAL PYTHON podcast

Handling Database Migrations With Alembic

Alembic is a change control tool for database content in SQLAlchemy. This article looks at the high-level architecture of how Alembic works, how to add it to your project, and some common workflows you’ll encounter.
PAUL ESCH-LAURENT • Shared by Michael Herman

Python Tricks: A Buffet of Awesome Python Features

Discover Python’s best practices with simple examples and start writing even more beautiful + Pythonic code. “Python Tricks: The Book” shows you exactly how. You’ll master intermediate and advanced-level features in Python with practical examples and a clear narrative. Get the book + video bundle 33% off →
DAN BADER sponsor

Python in List of Best Languages to Learn

The US Bureau of Labor Statistics has identified the top four languages for programmers to learn and Python made the list. Median annual wage of programmers in the US is expected to rise 25% in the next 5 years.
FORTUNE

Finding Python Easter Eggs

Python has its fair share of hidden surprises, commonly known as Easter eggs. From clever jokes to secret messages, these little mysteries are often meant to be discovered by curious geeks like you!
REAL PYTHON course

PyPI Temporarily Halted New Users and Projects

To fend off a supply-chain attack, PyPI temporarily halted new users and projects for about 10 hours last week. This article discusses why, and the scourge of supply-chain attacks.
ARS TECHNICA

Broadcasting in NumPy

Broadcasting in NumPy is not the most exciting topic, but this article explores the topic using a narrative perspective. This is not your standard “broadcasting in NumPy” article!
STEPHEN GRUPPETTA • Shared by Stephen Gruppetta

A Better Python Cache for Slow Function Calls

The folks at Sweep AI needed something more persistent than Python’s lru_cache. This post talks about the design behind a file based cached decorator they’ve recently released.
WILLIAM ZENG

Jupyter & IPython Terminology Explained

Are you trying to understand the differences between Jupyter Notebook, JupyterLab, IPython, Colab, and related terms? This article is for you.
DATA SCHOOL

How I Manage Python in 2024

This post covers the tools one developer uses in their day-to-day process. Read on for info about mise, uv, ruff, and more.
OUTLORE

Fixing a Bug in PyPy’s Incremental GC

A deep dive on hunting a tricky bug in the garbage collection code inside the alternate interpreter PyPy.
CARL FRIEDRICH BOLZ-TEREICK

Projects & Code django-prose-editor: Rich Text Editing for Django

GITHUB.COM/MATTHIASK

pycountry: ISO Country, Language, Currency and More

GITHUB.COM/PYCOUNTRY

sqlelf: Explore ELF Objects Through the Power of SQL

GITHUB.COM/FZAKARIA

Python Post-Mortem Debugger

GITHUB.COM/COCOLATO • Shared by cocolato

botasaurus: Framework to Build Awesome Scrapers

GITHUB.COM/OMKARCLOUD

Events Weekly Real Python Office Hours Q&A (Virtual)

April 3, 2024
REALPYTHON.COM

Canberra Python Meetup

April 4, 2024
MEETUP.COM

Sydney Python User Group (SyPy)

April 4, 2024
SYPY.ORG

PyCascades 2024

April 5 to April 9, 2024
PYCASCADES.COM

PyDelhi User Group Meetup

April 6, 2024
MEETUP.COM

Django Girls Ecuador 2024

April 6, 2024
OPENLAB.EC

Happy Pythoning!
This was PyCoder’s Weekly Issue #623.
View in Browser »

[ Subscribe to 🐍 PyCoder’s Weekly 💌 – Get the best Python news, articles, and tutorials delivered to your inbox once a week >> Click here to learn more ]

Categories: FLOSS Project Planets

Pages