Sitelet https://web.archive.org/web/20220401111053/https://planetpython.org/

skip to navigation
skip to content

Planet Python

Last update: April 01, 2022 10:40 AM UTC

April 01, 2022


Tryton News

Newsletter April 2022

Close to the future release 6.4, we still have a lot of new features landing in Tryton:

Changes for the User

Stock moves without quantity are no longer required to be put in a package.

The volume dimensions have been added to packages.

The multi-selection on desktop client behaves now the same way as the web client to select multiple value using CTRL and SHIFT controls.

The country name on address is now always displayed in English as requested by postal norm.

Now when multiple statement lines fulfill a payment or a group of payments, they are reconciled automatically with the clearing lines.

The amount manual payment processed can now be edited. This is useful for example when the amount read on a received check does not match the reading of the bank.

The lots have now upward and downward traces. If you were already using lots, the history of traceability will available.

There is now a configurable expiration delay for the Stripe setup intent. This is useful to avoid to keep old intent that online customer will never complete.

The production is now displayed on the stock move form.

It is no more possible to delete a tax identifier on a party if it is used on an invoice.

We use now the invoice date instead of the accounting date to enforce the sequence of customer invoice. This is more flexible and is valid for most of the countries.

A negative debit/credit is used now to book cash change on the point of sale. This avoid to increase artificially the debit/credit total of the account.

The stock package type can now be deactivated if they are no more used.

We added the support for the UPS notification service options.

The cost price on the outgoing moves of drop shipments are now recomputed when the unit price of the supplier is changed. This provides more accurate margin report.

We store now the employee who approved the refund of a Stripe or Braintree payment.

The sales from the POS are now included in the sale reporting.

The clients disable the previous/next navigation button when there is no record to select.

We warn the user who tries to deactivate a product that still has stock.

Changes for the Developer

We use now a unique Reference fields for the relation of statement line with invoice, payment etc.

The parse of XML data file have been improved to enforce the type of record used in ref attribute, to support Reference field value with ref attribute.

There is now a batch size when pushing to the queue. When the number of records is greater, the task is divided.

The invoices and lines have now a field that contains the numbers of the linked shipments. This field can be used to customize the invoice report to display the shipments.

Tryton uses now the best selectors available on the OS to wait for data to read instead of always using select.

The MultiSelection field always returns a immutable tuple.

It is now possible to deactivate the record of button. This is useful when customizing existing buttons.

The CORS options is now also support on the root path.

We prevent to create/delete singleton from the clients. This provides a better user experience.

The domain inversion removes now also the duplicate clauses.

1 post - 1 participant

Read full topic

April 01, 2022 08:00 AM UTC

March 31, 2022


ItsMyCode

Calculate Standard Error in R

The standard error (SE) of a statistic is the standard deviation of its sampling distribution or an estimate of that standard deviation. The standard error is calculated by dividing the standard deviation by the square root of the number of sample data.

The formula for calculating Standard Deviation in the Mathematics world is 

SE = \sigma / sqrt(n)Standard Error Formula
standard error= standard deviation/squareroot(n)

In this tutorial, we will look at how to Calculate Standard Error in R with examples.

How to Calculate Standard Error in R?

We can calculate Standard Error in three ways in the R language, as shown below.

Using sd() method

The sd() method takes a numeric vector as input and computes the standard deviation.

> std <- function(x) sd(x)/sqrt(length(x))
> std(c(1,2,3,4))
[1] 0.6454972

Using the standard error formula

We can use the standard error formula and calculate the standard error manually as shown below.

Syntax: sqrt(sum((a-mean(a))^2/(length(a)-1)))/sqrt(length(a))

where


# consider a vector with 10 elements
a <- c(1,2,3,4)
 
# calculate standard error
print(sqrt(sum((a - mean(a)) ^ 2/(length(a) - 1)))
      /sqrt(length(a)))

[1] 0.6454972

Using std.error() method from plotrix

We can import the plotrix library and use the std.error() method to calculate the standard error.

# import plotrix package
library("plotrix")
 
# vector data
a <- c(1,2,3,4)
 
# calculate standard error using builtin function
print(std.error(a))

[1] 0.6454972

March 31, 2022 04:15 PM UTC


EuroPython

EuroPython March 2022 Newsletter

Hey hey!

To say that March went by quickly would be an understatement. Our volunteers have been working round the clock to put together a fantastic conference. We’ve been busy launching the Call for Proposals, Ticket sales and the AMA session for first-time speakers. Read along to find out more about them.

📝EuroPython Society Update

Ukraine Donation

Last month we asked you about how EuroPython Society can help our community in these trying times. We are pleased to inform you that we made a total of 10,000 EUR donation split equally between UNICEF and UNHCR. These donations will be matched 100% by a local Swedish organisation, thus doubling the amount.

Over the last month, we are relieved to hear many of our friends are safe. We&aposll continue with our everyday activities in supporting Python communities across Europe. If you have any suggestions on specific ways to directly help our affected communities, drop us a line at board@europython.eu

Trans*Code @ EuroPython

EuroPython Society champions diversity & inclusion. In line with our mission of making EuroPython and Python more accessible to everyone, especially the underrepresented folks, we have decided to host a Trans*Code event at our 2022 conference - Trans*Code is an informal hackday & workshop event which aims to help draw attention to transgender issues and opportunities.

The event is open to trans and non-binary folk, allies, coders, designers and visionaries of all sorts. We are privileged to have Noami Ceder on board to help and advise us with the organisation. Although she is experienced and awesome, it takes more than one to sow the seeds of pluralism.  

We want to make EuroPython 2022 an exceptionally welcoming place for trans people and other marginalised ones. If you identify as trans or non-binary and would like to volunteer your experience and time to help us organise the event and achieve our goal, please write to volunteers@europython.eu; or to our EuroPython Society chair, raquel@europython.eu (if you need to discuss something more private). If you are an ally, help us spread the word and lend us your support.

🍀EuroPython 2022 Conference Update

🗣Call for Proposals (CFP) & Financial Aid

The EuroPython 2022 Call for Proposals (CFP) has been extended!!

🗣️
The CFP is closing this Sunday, 3rd April, 23:59:59 AoE
Submit your proposals now: https://europython.eu/cfp

As a community conference, we are eager to hear about how you use Python in your professional and personal activities. All accepted speakers are eligible for a free conference ticket.

Submit and share your proposals in any format of your choice, be it talks, tutorials, workshops, panels, posters, helpdesks or other interactive sessions: https://europython.eu/cfp

🚨
Towards our commitment to diversity & inclusion, we’re running a Financial Aid Programme to help individuals who would otherwise not be able to attend/speak at the conference. If you need help attending the conference, don’t hesitate to apply for help: https://europython.eu/finaid

Got any questions? Hit us up at helpdesk@europython.eu

💶Call for Sponsors

EuroPython 2022 will be held in-person this year, allowing our community to safely reconnect,  network, learn, share ideas, and create new relationships and partnerships.

Sponsors are what make EuroPython possible. Without our sponsors, EuroPython would not be the success that it is. With EuroPython back in-person this year, we’re excited to be able to provide our sponsors with opportunities to connect with and support the Python community, be face-to-face with talented developers and job recruits, access a large and diverse audience, and elevate their visibility and corporate identity within the Python community.

We’re offering 7 main packages with an aim to keep EuroPython accessible to most organisations keen to help the community.

👋
We will offer a 10% discount for early bird sponsors! Sign up by 29th April to take advantage! Head over to: https://europython.eu/sponsors

In case of any queries, write to us at sponsoring@europython.eu

🎟Tickets

Ticket sales are open now! After months of planning, here are the three types we have on offer for you:

Combined Tickets: access to everything during the whole seven-day conference.
Conference Tickets: access to Conference & Sprint Weekend (13-17 July)
Tutorial Tickets: access to Workshop/Tutorial days (11-12 July) and the Sprint Weekend (16-17 July)

You can learn all the other details here: https://europython.eu/tickets

⚠️
Early Bird Tickets: We still have some left! Grab your conference ticket now and get a 33% discount: https://europython.eu/tickets 

Free childcare: we provide free childcare service at the venue. If you are attending with your children, please register a ticket for yourself and select how many children will require childcare at checkout.

🥙Python Organisers’ Lunch @ Dublin

Organisers of other Python conferences around Europe, we invite you to attend lunch with us in Dublin! Let’s get together, share our joys and pains of running Python events, and chat about how the EPS can better support the community.

We are offering one free conference ticket per conference team. If you are interested, please email board@europython.eu to introduce yourselves and your event.

The lunch is planned for 14 or 15 July. We will be in touch with more details.

⚡Community Discounts

In addition, we would also like to support conferences and smaller user groups around Europe by offering a 10% discount across all ticket prices excluding Early Bird sales.

🤟
If you are a European Python event organiser, please reach out to board@europython.eu to get a discount code for your group.

If you are a regular participant to Python events, ask your local organisers to submit a request for the community discount, so they can distribute the vouchers to you.

💖Community Partners

We’re constantly looking for ways to strengthen our ties with the developers and communities on-ground. We’re psyched to announce our Community partners programme for communities across the island of Ireland.

As part of the programme, we’d like to connect with you and help you connect with like-minded others around Europe. Our team are planning many exciting activities. Apart from featuring you in our newsletter, we also invite your group attending the conference to the Irish Tech Community Mixer event, and guest post on our dev.to. By sharing, let’s amplify our knowledge and love for Python and Open Source in Ireland!

If you are interested, hit us at community@europython.eu

🌍New Website

EuroPython has a new face!! We’ve been collaborating with our friends at The Developer Society to put together a Dublin-esque theme for EuroPython.

alt

We&aposd love to know how you find the new design, hit us up at plaza@europython.eu

🎗️Upcoming Python Events

🗓️
If you have a cool Python event and want to be featured, hit the reply button and write to us!

Python Conference 2022 (Tuesday, 5th April 2022)

Data Science and Engineering Club: Workshop - Graph analytics and graph databases in Python (Saturday, 9th April 2022)

Python Ireland Meetup (Wednesday, 13th April 2022)

PyLadies Dublin Meetup (Tuesday, 19th April 2022)

🐍Cool Python Projects

📢
Know a cool Python project? Hit the reply button and write to us!

Mercury - Mercury is a tool to convert your Python notebook into a web app and share it with anyone.
Viztracer - VizTracer is a low-overhead logging/debugging/profiling tool that can trace and visualise your python code execution.
Fast F1 - FastF1 is a python package for accessing and analysing Formula 1 results, schedules, timing data and telemetry.
Pythoncapi_compat  - can be used to write a C extension supporting a wide range of Python versions with a single code base.
nbdev - nbdev is a library that allows you to develop a python library in Jupyter Notebooks, putting all your code, tests and documentation in one place.

March 31, 2022 12:31 PM UTC


Zero to Mastery

Python Monthly Newsletter 💻🐍 March 2022

28th issue of the Python Monthly Newsletter! Read by 20,000+ Python developers every month. This monthly Python newsletter covers the latest Python news so that you stay up-to-date with the industry and keep your skills sharp.

March 31, 2022 10:00 AM UTC


ItsMyCode

How to enable CORS on Django REST Framework?

 If we are building an API layer using the Django REST framework and accessing these APIs in the front-end application we need to enable the CORS on Django Rest Framework otherwise we will get an error “Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at $somesite“

In this tutorial, we will look at how to enable CORS on the Django REST framework with examples.

How to enable CORS on Django REST Framework?

CORS stands for Cross-Origin Resource Sharing. It is an HTTP-header-based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.

For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts that are present in the front-end application. Adding CORS headers allows your resources to be accessed on other domains

So if we have to allow the Django REST API to be accessed from the other front-end application which is hosted on a different domain we need to enable CORS(Cross-Origin Resource Sharing).

The easiest way to enable CORS on the Django REST framework is by installing a library django-cors-headers.

Step 1 – Install the django-cors-headers using pip

python -m pip install django-cors-headers

Step 2 – Open the settings.py file and add the CORS headers to your installed apps as shown below.

INSTALLED_APPS = [
    ...,
    "corsheaders",
    ...,
]

Step 3 – Add the CORS middlewares classes in the settings.py Middleware section as shown below.

MIDDLEWARE = [
    ...,
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    ...,
]

Step 4 – The last step is to allow the domain which needs to access the API. 

You can allow all the domains to access the API by setting CORS_ORIGIN_ALLOW_ALL=True 

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True

It is not recommended to allow all the domains as it will increase the security risk and we should only allow only the domains that need access to this API.

CORS_ALLOWED_ORIGINS = [
    "https://example.com",
    "https://sub.example.com",
    "http://localhost:8080",
    "http://127.0.0.1:9000",
]

Previously this setting was called CORS_ORIGIN_WHITELIST, which still works as an alias, with the new name taking precedence.

You can also allow which HTTP methods can be accessed by providing the list of HTTP verbs as shown below.

CORS_ALLOW_METHODS = [
    "DELETE",
    "GET",
    "OPTIONS",
    "PATCH",
    "POST",
    "PUT",
]

March 31, 2022 09:36 AM UTC


Stack Abuse

Graphs in Python: Breadth-First Search (BFS) Algorithm

Introduction

Graphs are one of the most useful data structures. They can be used to model practically everything - object relations and networks being the most common ones. An image can be represented as a grid-like graph of pixels, and sentences can be represented as graphs of words. Graphs are used in various fields, from cartography to social psychology even, and of course, they are widely used in Computer Science.

Due to their widespread use, graph search and traversal play an important computational role. The two fundamental, complementary and introductory algorithms used for graph search and traversal are Depth-First Search (DFS) and Breadth-First Search (BFS).

If you'd like to read more about Depth-First Search, read our Graphs in Python: Depth-First Search (DFS) Algorithm!

In this article, we will go over the theory behind the algorithm and the Python implementation of Breadth-First Search and Traversal. First, we'll be focusing on node search, before delving into graph traversal using the BFS algorithm, as the two main tasks you can employ it for.

Note: We're assuming an adjacency-list implemented graph in the guide.

Breadth-First Search - Theory

Breadth-First Search (BFS) traverses the graph systematically, level by level, forming a BFS tree along the way.

If we start our search from node v (the root node of our graph or tree data structure), the BFS algorithm will first visit all the neighbors of node v (it's child nodes, on level one), in the order that is given in the adjacency list. Next, it takes the child nodes of those neighbors (level two) into consideration, and so on.

This algorithm can be used for both graph traversal and search. When searching for a node that satisfies a certain condition (target node), the path with the shortest distance from the starting node to the target node. The distance is defined as the number of branches traversed.

BFS animated

Breadth-First Search can be used to solve many problems such as finding the shortest path between two nodes, determining the levels of each node, and even solving puzzle games and mazes.

While it's not the most efficient algorithm for solving large mazes and puzzles - and it's outshined by algorithms such as Dijkstra's Algorithm and A* - it still plays an important role in the bunch and depending on the problem at hand - DFS and BFS can outperform their heuristic cousins.

If you'd like to read more about Dijkstra's Algorithm or A* - read our Graphs in Python: Dijkstra's Algorithm and Graphs in Python: A* Search Algorithm!

Breadth-First Search - Algorithm

When implementing BFS, we usually use a FIFO structure like a Queue to store nodes that will be visited next.

Note: To use a Queue in Python, we need to import the corresponding Queue class from the queue module.

We need to pay attention to not fall into infinity loops by revisiting the same nodes over and over, which can easily happen with graphs that have cycles. Having that in mind, we'll be keeping track of the nodes that have been visited. That information doesn't have to be explicitly saved, we can simply keep track of the parent nodes, so we don't accidentally go back to one after it's been visited.

To sum up the logic, the BFS Algorithm steps look like this:

  1. Add the root/start node to the Queue.
  2. For every node, set that they don't have a defined parent node.
  3. Until the Queue is empty:
    • Extract the node from the beginning of the Queue.
    • Perform output processing.
    • For every neighbor of the current node that doesn't have a defined parent (is not visited), add it to the Queue, and set the current node as their parent.

Output processing is performed depending on the purpose behind the graph search. When searching for a target node, output processing is usually testing if the current node is equal to the target node. This is the step on which you can get creative!

Breadth-First Search Implementation - Target Node Search

Let's first start out with search - and search for a target node. Besides the target node, we'll need a start node as well. The expected output is a path that leads us from the start node to the target node.

With those in mind, and taking the steps of the algorithm into account, we can implement it. We'll define a Graph class to "wrap" the implementation of the BFS search.

Graph contains a graph representation - in this case an adjacency matrix, and all methods you might need when working with graphs. We'll implement both BFS search and BFS traversal as methods of that class:

from queue import Queue

class Graph:
    # Constructor
    def __init__(self, num_of_nodes, directed=True):
        self.m_num_of_nodes = num_of_nodes
        self.m_nodes = range(self.m_num_of_nodes)
		
        # Directed or Undirected
        self.m_directed = directed
		
        # Graph representation - Adjacency list
        # We use a dictionary to implement an adjacency list
        self.m_adj_list = {node: set() for node in self.m_nodes}      
	
    # Add edge to the graph
    def add_edge(self, node1, node2, weight=1):
        self.m_adj_list[node1].add((node2, weight))

        if not self.m_directed:
            self.m_adj_list[node2].add((node1, weight))
    
    # Print the graph representation
    def print_adj_list(self):
    for key in self.m_adj_list.keys():
        print("node", key, ": ", self.m_adj_list[key])

Note: For more in-depth overview of the Graph class, you should read our article "Graphs in Python: Representing Graphs in Code"

After implementing a wrapper class, we can implement BFS search as one of its methods:

def bfs(self, start_node, target_node):
    # Set of visited nodes to prevent loops
    visited = set()
    queue = Queue()

    # Add the start_node to the queue and visited list
    queue.put(start_node)
    visited.add(start_node)
    
    # start_node has not parents
    parent = dict()
    parent[start_node] = None

    # Perform step 3
    path_found = False
    while not queue.empty():
        current_node = queue.get()
        if current_node == target_node:
            path_found = True
            break

        for (next_node, weight) in self.m_adj_list[current_node]:
            if next_node not in visited:
                queue.put(next_node)
                parent[next_node] = current_node
                visited.add(next_node)
                
    # Path reconstruction
    path = []
    if path_found:
        path.append(target_node)
        while parent[target_node] is not None:
            path.append(parent[target_node]) 
            target_node = parent[target_node]
        path.reverse()
    return path 

When we're reconstructing the path (if it is found), we're going backward from the target node, through its parents, re-tracing all the way to the start node. Additionally, we reverse the path for our own intuition of going from the start_node towards the target_node, though, this step is optional.

On the other hand, if there is no path, the algorithm will return an empty list.

With the previously explained implementation in mind, we can test it by running the BFS search on the example graph:

search graph

Let's recreate this graph using our Graph class. It is an undirected graph with 6 nodes, so we'll instantiate it as:

graph = Graph(6, directed=False)

Next, we need to add all edges of the graph to the instance of the Graph class we've created:

graph.add_edge(0, 1)
graph.add_edge(0, 2)
graph.add_edge(0, 3)
graph.add_edge(0, 4)
graph.add_edge(1, 2)
graph.add_edge(2, 3)
graph.add_edge(2, 5)
graph.add_edge(3, 4)
graph.add_edge(3, 5)
graph.add_edge(4, 5)

Now, let's see how the Graph class internally represents our example graph.

graph.print_adj_list()

This will print the adjacency list used to represent a graph we've created:

node 0 :  {(3, 1), (1, 1), (4, 1), (2, 1)}
node 1 :  {(0, 1), (2, 1)}
node 2 :  {(0, 1), (1, 1), (5, 1), (3, 1)}
node 3 :  {(0, 1), (5, 1), (4, 1), (2, 1)}
node 4 :  {(0, 1), (5, 1), (3, 1)}
node 5 :  {(3, 1), (4, 1), (2, 1)}

At this moment, we have created a graph and understand how it's stored as an adjacency matrix. With all that in mind, we can perform a search itself. Say we'd like to search for node 5 starting from node 0:

path = []
path = graph.bfs(0, 5)
print(path)

Running this code results in:

[0, 3, 5]

After taking a quick look at the example graph, we can see that the shortest path between 0 and 5 is indeed [0, 3, 5]. Though, you could also traverse [0, 2, 5] and [0, 4, 5]. These alternative paths are, fundamentally, the same distance as [0, 3, 5] - however, consider how BFS compares nodes. It "scans" from left to right and 3 is the first node on the left-hand side of the adjacency list that leads to 5, so this path is taken instead of the others.

This is a characteristic of BFS you'll want to anticipate. It'll search from left to right - and won't find an equally valid path if it's found "after" the first one.

Note: There are cases in which a path between two nodes cannot be found. This scenario is typical for disconnected graphs, where there are at least two nodes that are not connected by a path.

Here's what a disconnected graph looks like:

disconnected graph

If we were to try and perform a search for a path between nodes 0 and 3 in this graph, that search would be unsuccessful, and an empty path would be returned.

Breadth-First Implementation - Graph Traversal

Breadth-First Traversal is a special case of Breadth-First Search that traverses the whole graph, instead of searching for a target node. The algorithm stays the same as we've defined it before, the difference being that we don't check for a target node and we don't need to find a path that leads to it.

This simplifies the implementation significantly - let's just print out each node being traversed to gain an intuition of how it passes through the nodes:

def bfs_traversal(self, start_node):
    visited = set()
    queue = Queue()
    queue.put(start_node)
    visited.add(start_node)

    while not queue.empty():
        current_node = queue.get()
        print(current_node, end = " ")
        for (next_node, weight) in self.m_adj_list[current_node]:
            if next_node not in visited:
                queue.put(next_node)
                visited.add(next_node)  

Note: This method should be implemented as part of the Graph class implemented before.

Now, let's define the following example graph in the previously shown way:

traversal graph

# Create an instance of the `Graph` class
# This graph is undirected and has 5 nodes
graph = Graph(5, directed=False)

# Add edges to the graph
graph.add_edge(0, 1)
graph.add_edge(0, 2)
graph.add_edge(1, 2)
graph.add_edge(1, 4)
graph.add_edge(2, 3)

Finally, let's run the code:

graph.bfs_traversal(0)

Running this code will print nodes in the order that BFS scanned them:

0 1 2 4 3

Step by step

Let's dive into this example a bit deeper and see how the algorithm works step by step. As we start the traversal from the start node 0, it is put into the visited set and into the queue as well. While we still have nodes in the queue, we extract the first one, print it, and check all of its neighbors.

When going through the neighbors, we check if each of them is visited, and if not we add them to the queue and mark them as visited:

Steps Queue Visited
Add start node 0 [0] {0}
Visit 0, add 1 & 2 to Queue [1, 2] {0}
Visit 1, add 4 to Queue [2, 4] {0, 2}
Visit 2, add 3 to Queue [4, 3] {0, 1, 2}
Visit 4, no unvisited neighbours [3] {0, 1, 1, 4}
Visit 3, no unvisited neighbours [ ] {0, 1, 2, 4, 3}

Time complexity

During Breadth-First Traversal, every node is visited exactly once, and every branch is also viewed once in case of a directed graph, that is, twice if the graph is undirected. Therefore, the time complexity of the BFS algorithm is O(|V| + |E|), where V is a set of the graph's nodes, and E is a set consisting of all of its branches (edges).

Conclusion

In this guide, we've explained the theory behind the Breadth-First Search algorithm and defined its steps.

We've depicted the Python implementation of both Breadth-First Search and Breadth-First Traversal, and tested them on example graphs to see how they work step by step. Finally, we've explained the time complexity of this algorithm.

March 31, 2022 09:30 AM UTC


Matt Layman

PDF Courses Report - Building SaaS with Python and Django #132

In this episode, we added a final PDF report to the PDF bundle. This report was different than the other because there wasn’t a pre-existing HTML report to mimic. I built a report that shows all the completed course tasks for each student.

March 31, 2022 12:00 AM UTC

March 30, 2022


Python Engineering at Microsoft

Python in Visual Studio Code – April 2022 Release

The April 2022 release of the Python Extension for Visual Studio Code is now available.

In this release we’re introducing the following changes:

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

Pylint extension

Our team is working towards breaking the tools support we offer in the Python extension into separate extensions, with the intent of improving performance, stability and no longer requiring the tools to be installed in a Python environment – as they can be shipped alongside an extension. The first one we started to work on is Pylint.

This new extension uses the Language Server Protocol to provide linting support, and it ships with the latest version of pylint.Pylint errors and warnings being displayed on Python code in Visual Studio Code.

It also provides additional ways to configure the severity levels of the issues reported via pylint. For example:

“pylint.severity” : {
    "convention": "Information",
    "error": "Error",
    "fatal": "Error",
    "refactor": "Hint",
    "warning": "Warning",
    "info": "Information",
    "W0611": "Error", //per error code
    "unused-import": "Error" //per error diagnostic
}

Note: You may see two entries for the same problem in the Problems panel if you also have Pylint enabled in the Python extension. You can disable the built-in linting functionality by setting “python.linting.pylintEnabled”: false.

You can try this new extension out today by installing it from the marketplace. If you have any issues or feature requests, you can file them at the Pylint extension’s GitHub repository.

Interpreter display in the status bar moved to the right

To be more consistent with other extensions in VS Code, we moved the selected interpreter version display towards the right side in the status bar, next to the language status item Python. It is now only displayed when a Python or a settings.json file is currently open, to avoid cluttering the status bar:

Interpreter information displayed on the bottom-right of the editor, on the status bar.

Simpler way to create empty Python and Jupyter Notebook files

You can now create empty Python or Jupyter notebook files by running the “File: Create New…” command in the command palette, or by clicking on “New File…”  from VS Code’s welcome page:

Clicking on

Fix for running and debugging files with conda environments

In the February 2022 release, we made some improvements to the experience when using Anaconda environments, which caused a regression when running files in activated conda environments. These issues are now fixed in this release.

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:

Add support for detection and selection of conda environments lacking a python interpreter. When selecting such environment, the Python extension will automatically install a Python interpreter. (vscode-python#18357)

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

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 2022 Release appeared first on Python.

March 30, 2022 10:43 PM UTC


Real Python

Python GUI Programming With Tkinter

Python has a lot of GUI frameworks, but Tkinter is the only framework that’s built into the Python standard library. Tkinter has several strengths. It’s cross-platform, so the same code works on Windows, macOS, and Linux. Visual elements are rendered using native operating system elements, so applications built with Tkinter look like they belong on the platform where they’re run.

Although Tkinter is considered the de facto Python GUI framework, it’s not without criticism. One notable criticism is that GUIs built with Tkinter look outdated. If you want a shiny, modern interface, then Tkinter may not be what you’re looking for.

However, Tkinter is lightweight and relatively painless to use compared to other frameworks. This makes it a compelling choice for building GUI applications in Python, especially for applications where a modern sheen is unnecessary, and the top priority is to quickly build something that’s functional and cross-platform.

In this tutorial, you’ll learn how to:

  • Get started with Tkinter with a Hello, World application
  • Work with widgets, such as buttons and text boxes
  • Control your application layout with geometry managers
  • Make your applications interactive by associating button clicks with Python functions

Once you’ve mastered these skills by working through the exercises at the end of each section, you’ll tie everything together by building two applications. The first is a temperature converter, and the second is a text editor. It’s time to dive right in and learn how to build an application with Tkinter!

Note: This tutorial is adapted from the chapter “Graphical User Interfaces” of Python Basics: A Practical Introduction to Python 3.

The book uses Python’s built-in IDLE editor to create and edit Python files and interact with the Python shell. In this tutorial, references to IDLE have been removed in favor of more general language.

The bulk of the material in this tutorial has been left unchanged, and you should have no problems running the example code from the editor and environment of your choice.

Free Bonus: 5 Thoughts On Python Mastery, a free course for Python developers that shows you the roadmap and the mindset you’ll need to take your Python skills to the next level.

Building Your First Python GUI Application With Tkinter

The foundational element of a Tkinter GUI is the window. Windows are the containers in which all other GUI elements live. These other GUI elements, such as text boxes, labels, and buttons, are known as widgets. Widgets are contained inside of windows.

First, create a window that contains a single widget. Start up a new Python shell session and follow along!

Note: The code examples in this tutorial have all been tested on Windows, macOS, and Ubuntu Linux 20.04 with Python version 3.10.

If you’ve installed Python with the official installers available for Windows and macOS from python.org, then you should have no problem running the sample code. You can safely skip the rest of this note and continue with the tutorial!

If you haven’t installed Python with the official installers, or there’s no official distribution for your system, then here are some tips for getting up and going.

Python on macOS with Homebrew:

The Python distribution for macOS available on Homebrew doesn’t come bundled with the Tcl/Tk dependency required by Tkinter. The default system version is used instead. This version may be outdated and prevent you from importing the Tkinter module. To avoid this problem, use the official macOS installer.

Ubuntu Linux 20.04:

To conserve memory space, the default version of the Python interpreter that comes pre-installed on Ubuntu Linux 20.04 has no support for Tkinter. However, if you want to continue using the Python interpreter bundled with your operating system, then install the following package:

$ sudo apt-get install python3-tk

This installs the Python GUI Tkinter module.

Other Linux Flavors:

If you’re unable to get a working Python installation on your flavor of Linux, then you can build Python with the correct version of Tcl/Tk from the source code. For a step-by-step walk-through of this process, check out the Python 3 Installation & Setup Guide. You may also try using pyenv to manage multiple Python versions.

With your Python shell open, the first thing you need to do is import the Python GUI Tkinter module:

>>>
>>> import tkinter as tk

A window is an instance of Tkinter’s Tk class. Go ahead and create a new window and assign it to the variable window:

>>>
>>> window = tk.Tk()

When you execute the above code, a new window pops up on your screen. How it looks depends on your operating system:

A blank Tkinter application window on Windows 10, macOS, and Ubuntu Linux

Throughout the rest of this tutorial, you’ll see Windows screenshots.

Adding a Widget

Now that you have a window, you can add a widget. Use the tk.Label class to add some text to a window. Create a Label widget with the text "Hello, Tkinter" and assign it to a variable called greeting:

>>>
>>> greeting = tk.Label(text="Hello, Tkinter")

The window you created earlier doesn’t change. You just created a Label widget, but you haven’t added it to the window yet. There are several ways to add widgets to a window. Right now, you can use the Label widget’s .pack() method:

>>>
>>> greeting.pack()

The window now looks like this:

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


[ 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 ]

March 30, 2022 02:00 PM UTC


Codementor

Tutorial: Python Variables

Introduction Variables in any programming language are similar to the variables in mathematics. For instance, we write x = 5 in mathematics. This means that x is the name of a variable and it...

March 30, 2022 01:54 PM UTC


Peter Bengtsson

How to close a HTTP GET request in Python before the end

Does you server barf if your clients close the connection before it's fully downloaded? Well, there's an easy way to find out. You can use this Python script:

import sys
import requests

url = sys.argv[1]
assert '://' in url, url
r = requests.get(url, stream=True)
if r.encoding is None:
    r.encoding = 'utf-8'
for chunk in r.iter_content(1024, decode_unicode=True):
    break

I use the xh CLI tool a lot. It's like curl but better in some things. By default, if you use --headers it will make a regular GET request but close the connection as soon as it has gotten all the headers. E.g.

▶ xh --headers https://www.peterbe.com
HTTP/2.0 200 OK
cache-control: public,max-age=3600
content-type: text/html; charset=utf-8
date: Wed, 30 Mar 2022 12:37:09 GMT
etag: "3f336-Rohm58s5+atf5Qvr04kmrx44iFs"
server: keycdn-engine
strict-transport-security: max-age=63072000; includeSubdomains; preload
vary: Accept-Encoding
x-cache: HIT
x-content-type-options: nosniff
x-edge-location: usat
x-frame-options: SAMEORIGIN
x-middleware-cache: hit
x-powered-by: Express
x-shield: active
x-xss-protection: 1; mode=block

That's not be confused with doing HEAD like curl -I ....

So either with xh or the Python script above, you can get that same effect. It's a useful trick when you want to make sure your (async) server doesn't attempt to do weird stuff with the "Response" object after the connection has closed.

March 30, 2022 12:33 PM UTC


Stack Abuse

Graphs in Python: Depth-First Search (DFS) Algorithm

Introduction

Originating from mathematics, graphs are now widely used data structures in Computer Science. One of the first problems we encounter when constructing any algorithm regarding Graph processing or traversal is how we represent the graph and then, how to traverse that representation.

Graph traversal is not a trivial problem, and given the difficulty of the task - many algorithms have been devised for efficient (yet not perfect) graph traversal.

In this guide, we'll take a look at one of the two complementary, fundamental and simplest algorithms for Graph traversal - Depth-First Search (DFS). It's the most commonly used algorithm alongside the related Breadth-First Search (BFS) given their simplicity. After going over the main idea used for DFS, we'll implement it in Python on a Graph representation - an adjacency list.

Depth-First Search - Theory

Depth-First Search (DFS) is an algorithm used to traverse or locate a target node in a graph or tree data structure. It priorities depth and searches along one branch, as far as it can go - until the end of that branch. Once there, it backtracks to the first possible divergence from that branch, and searches until the end of that branch, repeating the process.

Given the nature of the algorithm, you can easily implement it recursively - and you can always implement a recursive algorithm iteratively as well:

dfs animation

The start node is the root node for tree data structures, while with more generic graphs - it can be any node.

DFS is widely-used as a part of many other algorithms that resolve graph-represented problems. From cycle searches, path finding, topological sorting, to finding articulation points and strongly connected components. The reason behind this widespread use of the DFS algorithm lies in its overall simplicity and easy recursive implementation.

The DFS Algorithm

The DFS algorithm is pretty simple and consists of the following steps:

  1. Mark the current node as visited.
  2. Traverse the neighboring nodes that aren't visited and recursively call the DFS function for that node.

The algorithm stops either when the target node is found, or the whole graph has been traversed (all nodes are visited).

Note: Since graphs can have cycles, we'll need a system to avoid them so we don't fall into infinity loops. That's why we "mark" every node we pass as visited by adding them to a Set containing only unique entries.

By marking nodes as "visited", if we ever encounter that node again - we're in a loop! Endless computational power and time have been wasted on loops, lost in the aether.

Pseudocode

Given these steps, we can summarize DFS in pseudocode:

DFS(G, u):
    # Input processing
    u.visited = true
    for each v in G.adj[u]:
        if !v.visited:
            DFS(G, v)
            # Output processing

Input and output processing is performed depending on the purpose of the graph search. Our input processing for DFS will be checking if the current node is equal to the target node.

With this view, you can really start to appreciate just how simple yet useful this algorithm is.

Depth-First Search - Implementation

The first thing we need to consider before diving into the implementation of the DFS algorithm itself is how to implement a graph. As in any other article from this series, we've opted to implement it using a fairly basic Graph class. It contains a graph representation and a couple of methods you need to operate with graphs:

class Graph:
    # Constructor
    def __init__(self, num_of_nodes, directed=True):
        self.m_num_of_nodes = num_of_nodes
        self.m_nodes = range(self.m_num_of_nodes)
		
        # Directed or Undirected
        self.m_directed = directed
		
        # Graph representation - Adjacency list
        # We use a dictionary to implement an adjacency list
        self.m_adj_list = {node: set() for node in self.m_nodes}      
	
    # Add edge to the graph
    def add_edge(self, node1, node2, weight=1):
        self.m_adj_list[node1].add((node2, weight))

        if not self.m_directed:
            self.m_adj_list[node2].add((node1, weight))
    
    # Print the graph representation
    def print_adj_list(self):
    for key in self.m_adj_list.keys():
        print("node", key, ": ", self.m_adj_list[key])

Let's just quickly recap how does the Graph class work. The __init__() method defines a constructor. It consists of a number of nodes, a set of nodes, and the graph representation. In this case, a graph is represented by an adjacency list - effectively a Python dictionary with each node of the graph set to be one key. A set of adjacent nodes is assigned to each node (key).

Note: An adjacency list is a type of graph representation in code, it consists of keys that represent each node, and a set of values for each of them containing nodes that are connected to the key node with an edge.
Using a dictionary for this is the easiest way to quickly represent a graph in Python, though you could also define your own Node classes and add them to a Graph instance instead.

As its name suggests, the add_edge() method is used to add edges to the graph representation. Each edge is represented as in any usual adjacency list. For example, edge 1-2 is represented by adding 2 as the adjacent node of node 1 in the adjacency list. Additionally, our implementation enables you to assign a weight to any edge.

Lastly, we've created the method that prints the graph representation - print_adj_list().

Now we can implement the DFS algorithm in the Graph class. Depth-First Search implementation is usually recursive in code given how natural of a pair that is, but it can also be easily implemented non-recursively. We'll be using the recursive method:

def dfs(self, start, target, path = [], visited = set()):
    path.append(start)
    visited.add(start)
    if start == target:
        return path
    for (neighbour, weight) in self.m_adj_list[start]:
        if neighbour not in visited:
            result = self.dfs(neighbour, target, path, visited)
            if result is not None:
                return result
    path.pop()
    return None   

We added the start node to the beginning of our traversal path and marked it as visited by adding it to a set of visited nodes. Then, we traversed the start node's neighbors that aren't already visited and called the function recursively for each of them. Recursive calls result in going as deep as we can along one "branch".

We then saved a reference to the result. In the case the function returns None, that means that the target node was not found in this branch and that we should try another. If the recursive call, in fact, does not return None, that means we have found our target node and we return the traversal path as result.

In the end, if we find ourselves outside of the for loop, it means that all the neighbor branches of the current node have been visited and none of them lead to our target node. So, we remove the current node from the path and return None as result.

Running DFS

Let's illustrate how the code works through an example. As we've stated before, we'll be using Graph class to represent the graph. Internally, it represents a graph as an adjacency list. Here's the graph we'll be using in the following example:

graph

We'll be searching for a path from node 0 to node 3, if it exists, the path will be saved into a set of visited nodes, called traversal_path so we can reconstruct it for printing.

The first thing we need to do is to create an instance of the Graph class. Our example graph is undirected and has 5 nodes, so we'll create its representation in the following way:

graph = Graph(5, directed=False)

This will create the instance of the Graph representing undirected graph with 5 nodes. Next, we need to add all edges from the example graph into our graph representation:

graph.add_edge(0, 1)
graph.add_edge(0, 2)
graph.add_edge(1, 3)
graph.add_edge(2, 3)
graph.add_edge(3, 4)

Now, we've created the complete representation of the example graph. Let's take a look at how does the Graph class internally store our example graph:

graph.print_adj_list()

Which outputs the following:

node 0 :  {(1, 1), (2, 1)}
node 1 :  {(0, 1), (3, 1)}
node 2 :  {(0, 1), (3, 1)}
node 3 :  {(1, 1), (4, 1), (2, 1)}
node 4 :  {(3, 1)}

This shows the structure of the dictionary used to represent an adjacency list. It's perfectly in line with the implementation of an adjacency list provided in the article about graph representations in Python.

Finally, we can find a path from node 0 to node 3:

traversal_path = []
traversal_path = graph.dfs(0, 3)
print(traversal_path)

This will output the found path:

[0, 1, 3]

Now it would be useful to take a look at the steps our algorithm took:

Current Node Path Visited
0 [0] {0}
1 [0, 1] {0, 1}
3 [0, 1, 3] {0, 1, 3}

The algorithm stops and our program prints out the resulting traversal path from node 0 to node 3:

[0, 1, 3]

After the search, the marked nodes on the graph represent the path we took to get to the target node:

marked graph

In case there was no path between the start and target node, the traversal path would be empty.

Note: Graphs can also be disconnected, meaning that there are at least two nodes that cannot be connected by a path. In this case, DFS would ignore the nodes that it can't get to.

disconnected graph

For example in this graph, if we were to start DFS from node 0 to node 4, there would be no such path because it has no way of getting to the target node.

Conclusion

In this article, we've explained the theory behind the Depth-First Search algorithm. We've depicted the widely-used recursive Python implementation, and went over the borderline cases for which the algorithm will not work properly.

March 30, 2022 08:30 AM UTC


PyPy

PyPy v7.3.9 security release

PyPy v7.3.9 security release

The PyPy team is proud to release version 7.3.9 of PyPy. This is a security release to match the recent CPython release and updates the portable pypy tarballs with bzip2 1.0.8, openssl1.1.1n, and libexpat 2.4.7. Along the way this release fixes some issues discovered after the 7.3.8 release and updates sqlite3 to 3.38.2. It includes:

  • PyPy2.7, which is an interpreter supporting the syntax and the features of Python 2.7 including the stdlib for CPython 2.7.18+ (the + is for backported security updates)

  • PyPy3.7, which is an interpreter supporting the syntax and the features of Python 3.7, including the stdlib for CPython 3.7.13. This will be the last release of PyPy3.7.

  • PyPy3.8, which is an interpreter supporting the syntax and the features of Python 3.8, including the stdlib for CPython 3.8.13.

  • PyPy3.9, which is an interpreter supporting the syntax and the features of Python 3.9, including the stdlib for CPython 3.9.12. We relate to this as "beta" quality. We welcome testing of this version, if you discover incompatibilities, please report them so we can gain confidence in the version.

The interpreters are based on much the same codebase, thus the multiple release. This is a micro release, all APIs are compatible with the other 7.3 releases. Highlights of the release, since the release of 7.3.8 in February 2022, include:

  • Fixed some failing stdlib tests on PyPy3.9

  • Update the bundled libexpat to 2.4.6 and sqlite3 to 3.38.2

We recommend updating. You can find links to download the v7.3.9 releases here:

https://pypy.org/download.html

We would like to thank our donors for the continued support of the PyPy project. If PyPy is not quite good enough for your needs, we are available for direct consulting work. If PyPy is helping you out, we would love to hear about it and encourage submissions to our blog via a pull request to https://github.com/pypy/pypy.org

We would also like to thank our contributors and encourage new people to join the project. PyPy has many layers and we need help with all of them: PyPy and RPython documentation improvements, tweaking popular modules to run on PyPy, or general help with making RPython's JIT even better. Since the 7.3.7 release, we have accepted contributions from 6 new contributors, thanks for pitching in, and welcome to the project!

If you are a python library maintainer and use C-extensions, please consider making a HPy / CFFI / cppyy version of your library that would be performant on PyPy. In any case both cibuildwheel and the multibuild system support building wheels for PyPy.

What is PyPy?

PyPy is a Python interpreter, a drop-in replacement for CPython 2.7, 3.7, 3.8 and 3.9. It's fast (PyPy and CPython 3.7.4 performance comparison) due to its integrated tracing JIT compiler.

We also welcome developers of other dynamic languages to see what RPython can do for them.

This PyPy release supports:

  • x86 machines on most common operating systems (Linux 32/64 bits, Mac OS X 64 bits, Windows 64 bits, OpenBSD, FreeBSD)

  • 64-bit ARM machines running Linux. A shoutout to Huawei for sponsoring the VM running the tests.

  • s390x running Linux

  • big- and little-endian variants of PPC64 running Linux,

PyPy support Windows 32-bit, PPC64 big- and little-endian, and ARM 32 bit, but does not release binaries. Please reach out to us if you wish to sponsor releases for those platforms.

Known Issues with PyPy3.9

What else is new?

For more information about the 7.3.9 release, see the full changelog.

Please update, and continue to help us make PyPy better.

Cheers, The PyPy team

March 30, 2022 05:53 AM UTC


Wingware

Wing Python IDE Version 8.3 - March 30, 2022

Wing 8.3 improves remote development by allowing it to work without an SSH agent or command line OpenSSH or PuTTY configuration. This release also supports forwarding of the SSH agent to the remote host, allows blocking access to any SSH agent, improves analysis of match/case statements, avoids reporting spurious exceptions in async def coroutines, and makes a number of other improvements to refactoring, code reformatting, debugging, and other features.

See the change log for details.

Download Wing 8.3 Now: Wing Pro | Wing Personal | Wing 101 | Compare Products


What's New in Wing 8.3


Wing 8 Screen Shot

Support for Containers and Clusters

Wing 8 adds support for developing, testing, and debugging Python code that runs inside containers, such as those provided by Docker and LXC/LXD, and clusters of containers managed by a container orchestration system like Docker Compose. A new Containers tool can be used to start, stop, and monitor container services, and new Docker container environments may be created during project creation.

For details, see Working with Containers and Clusters.

New Package Management Tool

Wing 8 adds a new Packages tool that provides the ability to install, remove, and update packages found in the Python environment used by your project. This supports pipenv, pip, and conda as the underlying package manager. Packages may be selected manually from PyPI or by package specifications found in a requirements.txt or Pipfile.

For details, see Package Manager .

Improved Project Creation

Wing 8 redesigns New Project support so that the host, project directory, Python environment, and project type may all be selected independently. New projects may use either an existing or newly created source directory, optionally cloning code from a revision control repository. An existing or newly created Python environment may be selected, using virtualenv, pipenv, conda, or Docker.

Improved Python Code Analysis and Warnings

Wing 8 expands the capabilities of Wing's static analysis engine, by improving its support for f-strings, named tuples, and other language constructs. Find Uses, Refactoring, and auto-completion now work within f-string expressions, Wing's built-in code warnings work with named tuples, the Source Assistant displays more detailed and complete value type information, and code warning indicators are updated more cleanly during edits.

Improved Remote Development

Wing 8 makes it easier to configure remote development and provides more flexibility in conforming to local security policies. The new built-in SSH implementation may be used instead of separately configuring OpenSSH or PuTTY. Remote development can now also work without access to an SSH agent: Wing will prompt for passwords, private key passphrases, and other input needed to establish an SSH connection.

And More

Wing 8 also adds support for Python 3.10, including match/case statements, native executable for Apple Silicon (M1) hardware, a new Nord style display theme, reduced application startup time, support for Unreal Engine, Delete Symbol and Rename Current Module refactoring operations, improved debug stepping and exception handling in async code, remote development without an SSH agent, expanded support for branching, stashing/shelving and other operations for Git and Mercurial, and much more.

For a complete list of new features in Wing 8, see What's New in Wing 8.


Try Wing 8.3 Now!


Wing 8.3 is an exciting new step for Wingware's Python IDE product line. Find out how Wing 8 can turbocharge your Python development by trying it today.

Downloads: Wing Pro | Wing Personal | Wing 101 | Compare Products

See Upgrading for details on upgrading from Wing 7 and earlier, and Migrating from Older Versions for a list of compatibility notes.

March 30, 2022 01:00 AM UTC

March 29, 2022


PyCoder’s Weekly

Issue #518 (March 29, 2022)

#518 – MARCH 29, 2022
View in Browser »

The PyCoder’s Weekly Logo


Python and the James Webb Space Telescope

Python is used extensively in the data pipeline for the James Web Space Telescope. Michael Kennedy interviews Megan Sosey and Mike Swarm from the project and they talk all about it.
TALK PYTHON podcast

Processing Large JSON Files Without Running Out of Memory

Loading complete JSON files into Python can use too much memory, leading to slowness or crashes. The solution: process JSON data one chunk at a time.
ITAMAR TURNER-TRAURING

Data Elixir: Data Science Newsletter

alt

Data Elixir is an email newsletter that keeps you on top of the latest tools and trends in Data Science. Covers machine learning, data visualization, analytics, and strategy. Curated weekly with top picks from around the web →
DATA ELIXIR sponsor

Image Processing With the Python Pillow Library

Learn how to use the Python Pillow library to deal with images. Combine this with some NumPy for image processing and to creating animations.
REAL PYTHON

Python 3.10.4 and 3.9.12 Available Out of Schedule

PYTHON.ORG

Discussions

API to Run Python Code, What Can Go Wrong?

HACKER NEWS

Do You Contribute to Open Source Projects?

HACKER NEWS

Python Jobs

Python Technical Architect (USA)

Blenderbox

Academic Innovation Software Developer (Ann Arbor, MI, USA)

University of Michigan

Software Development Lead (Ann Arbor, MI, USA)

University of Michigan

Senior Platform Engineer (USA)

Parade

Senior Backend Software Engineer (USA)

Parade

Senior Backend Software Engineer (USA)

Clay

Advanced Python Engineer (Newport Beach, CA, USA)

Research Affiliates

Senior Full-Stack Web Developer (White Rock, BC, Canada)

MonetizeMore

Full-Stack Software Engineer Python (USA)

Deep Sentinel

Senior Software Engineer (Anywhere)

Droice Labs

More Python Jobs >>>

Articles & Tutorials

Becoming More Effective at Manipulating Data With Pandas

Do you wonder if you’re taking the right approach when shaping data in pandas? Is your Jupyter workflow getting out of hand? This week on the show, Matt Harrison talks about his new book, “Effective Pandas: Patterns for Data Manipulation.”
REAL PYTHON podcast

How To Classify Text With Python, Transformers & scikit-learn

Natural Language Processing is a powerful tool for gaining semantic knowledge about text based data. Text classification is about categorizing such data and scikit-learn is popular toolkit that does this and more.
NEWSCATCHERAPI.COM

Scout APM: Find and Fix Performance Issues with Ease

alt

Scout’s APM tool pinpoints and prioritizes performance and stability issues in Python applications. With Scout’s tracing logic, developers can detect the exact line of code causing the performance abnormality, and with detailed backtraces, you can fix the issue before customers ever notice →
SCOUT APM sponsor

Validating JSON Documents Using Pydantic

Pydantic is a popular Python library for doing data validation. This article introduces you to how to use it to specify and validate against a schema for your JSON encoded data.
NITHISH RAGHUNANDANAN • Shared by Nithish Raghunandanan

Deploying a Django Application to Elastic Beanstalk

Elastic Beanstalk is an AWS service that wraps hosting and deploying inside of the AWS environment. Learn how to deploy production-ready Django to Elastic Beanstalk.
TESTDRIVEN.IO • Shared by Nik Tomazic

How to run uWSGI

uWSGI has loads of options to choose from, how do you know which ones to choose? This article talks about the more common settings and how to pick good values.
IONEL CRISTIAN MĂRIEȘ

Python Class Constructors: Control Your Object Instantiation

Learn how class constructors work in Python and explore the two steps of Python’s instantiation process: instance creation and instance initialization.
REAL PYTHON

Setting Up a Basic Django Project With Poetry

Poetry is a package dependency management tool. In this article you’ll learn the step-by-step process for setting up a Django project using Poetry.
RASUL KIREEV

Up Your Coding Game and Discover Python Issues Early

SonarLint is a free & Open Source IDE extension that helps you find & fix bugs and security issues from the moment you start writing Python code. Simply install from your IDE marketplace. Learn More.
SONARLINT sponsor

The Right Way to Compare Floats in Python

Floating-point numbers are prone to errors. Learn why floating-point errors are common, why they make sense, and how to deal with them in Python.
DAVID AMOS

Python Virtual Environments and Package Management

Modern Python projects need a bit more than venv and pip. Learn about the best tools for package management and environment isolation.
BAS STEINS

Projects & Code

pointers.py: Bringing the Hell of Pointers to Python

GITHUB.COM/ZEROINTENSITY

UltraDict: Shared, Streaming Python Dict

GITHUB.COM/RONNY-RENTNER

awesome-python: Awesome Python Libraries and Resources

GITHUB.COM/VINTA

perflint: Pylint Extension for Performance Anti Patterns

GITHUB.COM/TONYBALONEY

py-dynacli: Convert Python Functions Into Shell Commands

GITHUB.COM/BSTLABS

Events

Heidelberg Python Meetup

March 30, 2022
MEETUP.COM

PyStaDa

March 30, 2022
PYSTADA.GITHUB.IO

Weekly Real Python Office Hours Q&A (Virtual)

March 30, 2022
REALPYTHON.COM

SPb Python Drinkup

March 31, 2022
MEETUP.COM

Melbourne Python Users Group, Australia

April 4, 2022
J.MP


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

alt

[ 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 ]

March 29, 2022 07:30 PM UTC


ItsMyCode

SyntaxError: (unicode error) ‘unicodeescape’ codec can’t decode bytes in position 2-3: truncated \UXXXXXXXX escape

The SyntaxError: (unicode error) ‘unicodeescape’ codec can’t decode bytes in position 2-3: truncated \UXXXXXXXX escape occurs if you are trying to access a file path with a regular string.

In this tutorial, we will take a look at what exactly (unicode error) ‘unicodeescape’ codec can’t decode bytes in position 2-3: truncated \UXXXXXXXX escape means and how to fix it with examples.

What is SyntaxError: (unicode error) ‘unicodeescape’ codec can’t decode bytes in position 2-3: truncated \UXXXXXXXX escape?

The Python String literals can be enclosed in matching single quotes (‘) or double quotes (“). 

String literals can also be prefixed with a letter ‘r‘ or ‘R‘; such strings are called raw strings and use different rules for backslash escape sequences.

They can also be enclosed in matching groups of three single or double quotes (these are generally referred to as triple-quoted strings). 

The backslash (\) character is used to escape characters that otherwise have a special meaning, such as newline, backslash itself, or the quote character. 

Now that we have understood the string literals. Let us take an example to demonstrate the issue.

import pandas

# read the file
pandas.read_csv("C:\Users\itsmycode\Desktop\test.csv")

Output

  File "c:\Personal\IJS\Code\program.py", line 4
    pandas.read_csv("C:\Users\itsmycode\Desktop\test.csv")                                                                                     ^
SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \UXXXXXXXX escape

We are using the single backslash in the above code while providing the file path. Since the backslash is present in the file path, it is interpreted as a special character or escape character (any sequence starting with ‘\’). In particular, “\U” introduces a 32-bit Unicode character.

How to fix SyntaxError: (unicode error) ‘unicodeescape’ codec can’t decode bytes in position 2-3: truncated \UXXXXXXXX escape?

Solution 1 – Using Double backslash (\\)

In Python, the single backslash in the string is interpreted as a special character, and the character U(in users) will be treated as the Unicode code point.

We can fix the issue by escaping the backslash, and we can do that by adding an additional backslash, as shown below.

import pandas

# read the file
pandas.read_csv("C:\\Users\\itsmycode\\Desktop\\test.csv")

Solution 2 – Using raw string by prefixing ‘r’

We can also escape the Unicode by prefixing r in front of the string. The r stands for “raw” and indicates that backslashes need to be escaped, and they should be treated as a regular backslash.

import pandas

# read the file
pandas.read_csv("C:\\Users\\itsmycode\\Desktop\\test.csv")

Solution 3 – Using forward slash 

Another easier way is to avoid the backslash and instead replace it with the forward-slash character(/), as shown below.

import pandas

# read the file
pandas.read_csv("C:/Users/itsmycode/Desktop/test.csv")

Conclusion

The SyntaxError: (unicode error) ‘unicodeescape’ codec can’t decode bytes in position 2-3: truncated \UXXXXXXXX escape occurs if you are trying to access a file path and provide the path as a regular string.

We can solve the issue by escaping the single backslash with a double backslash or prefixing the string with ‘r,’ which converts it into a raw string. Alternatively, we can replace the backslash with a forward slash.

March 29, 2022 04:23 PM UTC


Trey Hunner

Overlooked facts about variables and objects in Python: it's all about pointers

This article was originally published on Python Morsels.

In Python, variables and data structures don’t contain objects. This fact is both commonly overlooked and tricky to internalize.

You can happily use Python for years without really understanding the below concepts, but this knowledge can certainly help alleviate many common Python gotchas.

Table of Contents:

Terminology

Let’s start with by introducing some terminology. The last few definitions likely won’t make sense until we define them in more detail later on.

Object (a.k.a. value): a “thing”. Lists, dictionaries, strings, numbers, tuples, functions, and modules are all objects. “Object” defies definition because everything is an object in Python.

Variable (a.k.a. name): a name used to refer to an object.

Pointer (a.k.a. reference): describes where an object lives (often shown visually as an arrow)

Equality: whether two objects represent the same data

Identity: whether two pointers refer to the same object

These terms are best understood by their relationships to each other and that’s the primarily purpose of this article.

Python’s variables are pointers, not buckets

Variables in Python are not buckets containing things; they’re pointers (they point to objects).

The word “pointer” may sound scary, but a lot of that scariness comes from related concepts (e.g. dereferencing) which aren’t relevant in Python. In Python a pointer just represents the connection between a variable and an objects.

Imagine variables living in variable land and objects living in object land. A pointer is a little arrow that connects each variable to the object it points to.

This above diagram represents the state of our Python process after running this code:

1
2
3
>>> numbers = [2, 1, 3, 4, 7]
>>> numbers2 = [11, 18, 29]
>>> name = "Trey"

If the word pointer scares you, use the word reference instead. Whenever you see pointer-based phrases in this article, do a mental translation to a reference-based phrase:

Assignments point a variable to an object

Assignment statements point a variable to an object. That’s it.

If we run this code:

1
2
3
>>> numbers = [2, 1, 3, 4, 7]
>>> numbers2 = numbers
>>> name = "Trey"

The state of our variables and objects would look like this:

Note that numbers and numbers2 point to the same object. If we change that object, both variables will seem to “see” that change:

1
2
3
4
5
6
>>> numbers.pop()
7
>>> numbers
[2, 1, 3, 4]
>>> numbers2
[2, 1, 3, 4]

That strangeness was all due to this assignment statement:

1
>>> numbers2 = numbers

Assignment statements don’t copy anything: they just point a variable to an object. So assigning one variable to another variable just points two variables to the same object.

The 2 types of “change” in Python

Python has 2 distinct types of “change”:

  1. Assignment changes a variable (it changes which object it points to)
  2. Mutation changes an object (which any number of variables might point to)

The word “change” is often ambiguous. The phrase “we changed x” could mean “we re-assigned x” or it might mean “we mutated the object x points to”.

Mutations change objects, not variables. But variables point to objects. So if another variable points to an object that we’ve just mutated, that other variable will reflect the same change; not because the variable changed but because the object it points to changed.

Equality compares objects and identity compares pointers

Python’s == operator checks that two objects represent the same data (a.k.a. equality):

1
2
3
4
>>> my_numbers = [2, 1, 3, 4]
>>> your_numbers = [2, 1, 3, 4]
>>> my_numbers == your_numbers
True

Python’s is operator checks whether two objects are the same object (a.k.a. identity):

1
2
>>> my_numbers is your_numbers
False

The variables my_numbers and your_numbers point to objects representing the same data, but the objects they point to are not the same object.

So changing one object doesn’t change the other:

1
2
3
>>> my_numbers[0] = 7
>>> my_numbers == your_numbers
False

If two variables point to the same object:

1
2
3
>>> my_numbers_again = my_numbers
>>> my_numbers is my_numbers_again
True

Changing the object one variable points also changes the object the other points to because they both point to the same object:

1
2
3
4
5
>>> my_numbers_again.append(7)
>>> my_numbers_again
[2, 1, 3, 4, 7]
>>> my_numbers
[2, 1, 3, 4, 7]

The == operator checks for equality and the is operator checks for identity. This distinction between identity and equality exists because variables don’t contain objects, they point to objects.

In Python equality checks are very common and identity checks are very rare.

There’s no exception for immutable objects

But wait, modifying a number doesn’t change other variables pointing to the same number, right?

1
2
3
4
5
6
7
>>> n = 3
>>> m = n  # n and m point to the same number
>>> n += 2
>>> n  # n has changed
5
>>> m  # but m hasn't changed!
3

Well, modifying a number is not possible in Python. Numbers and strings are both immutable, meaning you can’t mutate them. You cannot change an immutable object.

So what about that += operator above? Didn’t that mutate a number? (It didn’t.)

With immutable objects, these two statements are equivalent:

1
2
>>> n += 2
>>> n = n + 2

For immutable objects, augmented assignments (+=, *=, %=, etc.) perform an operation (which returns a new object) and then do an assignment (to that new object).

Any operation you might think changes a string or a number instead returns a new object. Any operation on an immutable object always returns a new object instead of modifying the original.

Data structures contain pointers

Like variables, data structures don’t contain objects, they contain pointers to objects.

Let’s say we make a list-of-lists:

1
>>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

And then we make a variable pointing to the second list in our list-of-lists:

1
2
3
>>> row = matrix[1]
>>> row
[4, 5, 6]

The state of our variables and objects now looks like this:

Our row variable points to the same object as index 1 in our matrix list:

1
2
>>> row is matrix[1]
True

So if we mutate the list that row points to:

1
>>> row[0] = 1000

We’ll see that change in both places:

1
2
3
4
>>> row
[1000, 5, 6]
>>> matrix
[[1, 2, 3], [1000, 5, 6], [7, 8, 9]]

It’s common to speak of data structures “containing” objects, but they actually only contain pointers to objects.

Function arguments act like assignment statements

Function calls also perform assignments.

If you mutate an object that was passed-in to your function, you’ve mutated the original object:

1
2
3
4
5
6
7
8
9
>>> def smallest_n(items, n):
...     items.sort()  # This mutates the list (it sorts in-place)
...     return items[:n]
...
>>> numbers = [29, 7, 1, 4, 11, 18, 2]
>>> smallest_n(numbers, 4)
[1, 2, 4, 7]
>>> numbers
[1, 2, 4, 7, 11, 18, 29]

But if you reassign a variable to a different object, the original object will not change:

1
2
3
4
5
6
7
8
9
>>> def smallest_n(items, n):
...     items = sorted(items)  # this makes a new list (original is unchanged)
...     return items[:n]
...
>>> numbers = [29, 7, 1, 4, 11, 18, 2]
>>> smallest_n(numbers, 4)
[1, 2, 4, 7]
>>> numbers
[29, 7, 1, 4, 11, 18, 2]

We’re reassigning the items variable here. That reassignment changes which object the items variable points to, but it doesn’t change the original object.

We changed an object in the first case and we changed a variable in the second case.

Here’s another example you’ll sometimes see:

1
2
3
4
class Widget:
    def __init__(self, attrs=(), choices=()):
        self.attrs = list(attrs)
        self.choices = list(choices)

Class initializer methods often copy iterables given to them by making a new list out of their items. This allows the class to accept any iterable (not just lists) and decouples the original iterable from the class (modifying these lists won’t upset the original caller). The above example was borrowed from Django.

Don’t mutate the objects passed-in to your function unless the function caller expects you to.

Copies are shallow and that’s usually okay

Need to copy a list in Python?

1
>>> numbers = [2000, 1000, 3000]

You could call the copy method (if you’re certain your iterable is a list):

1
>>> my_numbers = numbers.copy()

Or you could pass it to the list constructor (this works on any iterable):

1
>>> my_numbers = list(numbers)

Both of these techniques make a new list which points to the same objects as the original list.

The two lists are distinct, but the objects within them are the same:

1
2
3
4
>>> numbers is my_numbers
False
>>> numbers[0] is my_numbers[0]
True

Since integers (and all numbers) are immutable in Python we don’t really care that each list contains the same objects because we can’t mutate those objects anyway.

With mutable objects, this distinction matters. This makes two list-of-lists which each contain pointers to the same three lists:

1
2
>>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>> new_matrix = list(matrix)

These two lists aren’t the same, but each item within them is the same:

1
2
3
4
>>> matrix is new_matrix
False
>>> matrix[0] is new_matrix[0]
True

Here’s a rather complex visual representation of these two objects and the pointers they contain:

So if we mutate the first item in one list, it’ll mutate the same item within the other list:

1
2
3
4
5
>>> matrix[0].append(100)
>>> matrix
[[1, 2, 3, 100], [4, 5, 6], [7, 8, 9]]
>>> new_matrix
[[1, 2, 3, 100], [4, 5, 6], [7, 8, 9]]

When you copy an object in Python, if that object points to other objects, you’ll copy pointers to those other objects instead of copying the objects themselves.

New Python programmers respond to this behavior by sprinkling copy.deepcopy into their code. The deepcopy function attempts to recursively copy an object along with all objects it points to.

Sometimes new Python programmers will use deepcopy to recursively copy data structures:

1
2
3
4
5
6
7
8
9
from copy import deepcopy
from datetime import datetime

tweet_data = [{"date": "Feb 04 2014", "text": "Hi Twitter"}, {"date": "Apr 16 2014", "text": "At #pycon2014"}]

# Parse date strings into datetime objects
processed_data = deepcopy(tweet_data)
for tweet in processed_data:
    tweet["date"] = datetime.strptime(tweet["date"], "%b %d %Y")

But in Python, we often prefer to make new objects instead of mutating existing objects. So we could entirely remove that deepcopy usage above by making a new list of new dictionaries instead of deep-copying our old list-of-dictionaries.

1
2
3
4
5
# Parse date strings into datetime objects
processed_data = [
    {**tweet, "date": datetime.strptime(tweet["date"], "%b %d %Y")}
    for tweet in tweet_data
]

We tend to prefer shallow copies in Python. If you don’t mutate objects that don’t belong to you you usually won’t have any need for deepcopy.

The deepcopy function certainly has its uses, but it’s often unnecessary. “How to avoid using deepcopy” warrants a separate discussion in a future article.

Summary

Variables in Python are not buckets containing things; they’re pointers (they point to objects).

Python’s model of variables and objects boils down to two primary rules:

  1. Mutation changes an object
  2. Assignment points a variable to an object

As well as these corollary rules:

  1. Reassigning a variable points it to a different object, leaving the original object unchanged
  2. Assignments don’t copy anything, so it’s up to you to copy objects as needed

Furthermore, data structures work the same way: lists and dictionaries container pointers to objects rather than the objects themselves. And attributes work the same way: attributes point to objects (just like any variable points to an object). So objects cannot contain objects in Python (they can only point to objects).

And note that while mutations change objects (not variables), multiple variables can point to the same object. If two variables point to the same object changes to that object will be seen when accessing either variable (because they both point to the same object).

For more on this topic see:

This mental model of Python is tricky to internalize so it’s okay if it still feels confusing! Python’s features and best practices often nudge us toward “doing the right thing” automatically. But if your code is acting strangely, it might be due to changing an object you didn’t mean to change.

March 29, 2022 03:00 PM UTC


Real Python

Using Python's datetime Module

Python has several different modules to deal with dates and times. This course concentrates on the primary one, datetime. Dates and times are messy things! Shifts due to daylight savings time and time zones complicate any computing with dates and times.

In this course, you’ll tackle that messiness and learn:


[ 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 ]

March 29, 2022 02:00 PM UTC


Inspired Python

Make your own Tower Defense Game with PyGame

Make your own Tower Defense Game with PyGame

In this course you’ll learn how to write a 2d Tower Defense Game from scratch, using PyGame. Writing a game is easy; but writing one that is maintainable and easy to extend is not. A tower defense game is a perfect place to learn how to write a substantial game that will test your skills as a Python programmer. It is also a perfect template for many other 2d games.

Part of the challenge of writing a game is the many disparate discplines that rear their heads once you move beyond the truly basic. During the course you’ll learn the following skills:

What is a Game Loop?

How do games actually update and display things on the screen in a manner that is maintainable and easy to reason about for the budding game developer?

The Game Loop is a cornerstone of all games, big and small. You’ll learn how to create one, and how it’s used to handle keyboard and mouse input, graphics rendering, updating the physics of entities on the screen, and more.

State Machines and Transitions

Few games have just one screen, and thus one state. Most games have a main menu, a score board, the actual game, and possibly more states that a player interacts with during game play. Understanding how to transition your game’s state between these different concepts is critical to writing a game free of spaghetti code.

You’ll learn about finite-state machines, an important concept in Computer Science, and how it can easily make transform a complex set of confusing requirements into neat and tidy code.

Lazy evaluation, Generators and Iterables

Keeping track of the position of things – and calculating the next position of something, such as a flying bullet – is easily solved with a liberal use of Python’s itertools library and generators.

Master a part of Python that gets short thrift from most developers, as they’re harder to reason about than normal for-loops.

Drawing and manipulating graphics

Learn what a sprite is, how to manipulate it to move, rotate, scale it, and how to do it efficiently and in a manner that is clear and easy to reason about.

Level Editing

You’ll write a complete level editor capable of placing and editing all the entities that make up a tower defense game using a simple UI you will build yourself.

The level editor forms a core part of the game, and includes details on how to write a save and load feature, so you can share your levels with friends.

Path finding and Recursion

Learn about recursion, a powerful programming concept, to find valid paths through a map for the enemies to traverse. You’ll learn about basic graph theory and how Depth-First Search is used to traverse a map and find a route from start to end.

Vector Mathematics

Come to grips with the mathematics required to ensure a bullet travels in a straight line towards a target; that your enemies walk smoothly across the map; and how to do simple text-based animations using simple arithmetic.

You’ll learn about simple vector arithmetic, interpolation, and basic affine transformations (like scaling and rotating).

Object-Oriented Programming (OOP)

Improve your understanding of classes and objects and how to best leverage inheritance, the factory pattern, and Python’s dataclasses to succinctly describe your game world using simple classes.

Animation

Learn how to chain together frames of images into simple animations so the enemies walk across the screen and collapse when they’re struck by exploding projectiles.

Collision Detection

Important stuff: how does the turret know when to fire at an enemy that is in its crosshairs? What about when the bullet strikes an enemy?

Are you ready? Let’s code!



Read More ->

March 29, 2022 12:42 PM UTC

Tower Defense Game: Getting Started

Tower Defense Game: Getting Started

Python package management is not the easiest thing to wrap your head around. Nevertheless, it’s important that we capture the requirements our game has, properly, and commit them to a Python package.



Read More ->

March 29, 2022 12:42 PM UTC

Tower Defense Game: Game Loop and Initializing PyGame

Tower Defense Game: Game Loop and Initializing PyGame

How do you instruct your computer game to draw things to the screen consistently and to a drum-beat that ensures there’s no awkward pauses or jitter? What about listening to the keyboard and mouse inputs, or updating the score board for your game? The physics engine?

Get any one of these things wrong, or forget to do them, and your game misbehaves. Worse, it can misbehave in ways that you won’t necessarily catch on your own machine.

That’s why all computer games – big or small – has one (or more!) game loops that ensure the game carries out its most essential tasks in a repeatable and stable manner.

It’s time to write our skeleton game loop that will serve us throughout the course of the development of our tower defense game.



Read More ->

March 29, 2022 12:42 PM UTC

Tower Defense Game: Finite State Automata / State Machines

Tower Defense Game: Finite State Automata / State Machines

Complex codebases – and games are usually complex – tend to rely on a lot of state, usually captured in variables. Navigating from one screen in a game to another involves a lot of change: you need to render different things; the key bindings you use may change also; and perhaps you need to clear out old objects, like if you change from game play to the score screen.

But instead of having an ever-increasing number of variables to represent what your code is supposed to do – like, is_in_menu, has_won_game, is_in_level_editor, etc. – you should consider formalizing your stateful code using finite state automata, or commonly called a state machine.

To improve our code’s extensibility, it’s time to consider how we can effectively use a simple state machine to represent the state the game is in, and how OOP and inheritance can help with separation of concerns.



Read More ->

March 29, 2022 12:42 PM UTC


Mike Driscoll

Automating Excel with Python Video Overview

In this tutorial, I will show you an overview of using OpenPyXL and Python to read and write Excel documents. You will also learn how to:

The code in this tutorial is based on code from my book, Automating Excel with Python:

The post Automating Excel with Python Video Overview appeared first on Mouse Vs Python.

March 29, 2022 12:30 PM UTC


Kushal Das

Introducing Very Bad Web Application

I am planning to add a few chapters on securing services in my Linux Command Line book. But, to make it practical & hands on, I needed one real application which the readers can deploy and secure. I needed something simple, say one single binary so that it becomes easier to convert it into a proper systemd service.

I decided to write one in Rust :) This also helps to showcase that one can write totally insecure code even in Rust (or any other language). Let me introduce Very Bad Web application. The README contains the build instructions. The index page shows the available API.

Issues in the service

The service has the following 3 major issues:

I am currently updating the systemd (services) chapter in my book to show how to secure the service using only the features provided by the latest systemd. In future I will also have chapters on SELinux and AppArmor and learn how to secure the service using those two options.

If you think I should add some other nice security holes in this application, please feel free to suggest :)

March 29, 2022 07:15 AM UTC

March 28, 2022


Anarcat

What is going on with web servers

I stumbled upon this graph recently, which is w3techs.com graph of "Historical yearly trends in the usage statistics of web servers". It seems I hadn't looked at it in a long while because I was surprised at many levels:

  1. Apache is now second, behind Nginx, since ~2022 (so that's really new at least)

  2. Cloudflare "server" is third ahead of the traditional third (Microsoft IIS) - I somewhat knew that Cloudflare was hosting a lot of stuff, but I somehow didn't expect to see it there at all for some reason

  3. I had to lookup what LiteSpeed was (and it's not a bike company): it's a drop-in replacement (!?) of the Apache web server (not a fork, a bizarre idea but which seems to be gaining a lot of speed recently, possibly because of its support for QUIC and HTTP/3

So there. I'm surprised. I guess the stats should be taken with a grain of salt because they only partially correlate with Netcraft's numbers which barely mention LiteSpeed at all. (But they do point at a rising share as well.)

Netcraft also puts Nginx's first place earlier in time, around April 2019, which is about when Microsoft's IIS took a massive plunge down. That is another thing that doesn't map with w3techs' graphs at all either.

So it's all lies and statistics, basically. Moving on.

Oh and of course, the two first most popular web servers, regardless of the source, are package in Debian. So while we're working on statistics and just making stuff up, I'm going to go ahead and claim all of this stuff runs on Linux and that BSD is dead. Or something like that.

March 28, 2022 02:11 PM UTC