# data[].attributes.attachment_link.description
The official home of the Python Programming Language

# data[].attributes.attachment_link.provider_name
Educative: Interactive Courses for Software Developers

# data[].attributes.attachment_link.title
A Complete Guide to Web Development in Python

# data[].attributes.attachment_link.description
As Python continues its rise in popularity, developers are increasingly using web frameworks like Django and Flask. Let's look at how Python is used in web development, particularly in the backend.

# data[].attributes.attachment_link.readable_text
Everything you need to know about Django.

First steps

Are you new to Django or to programming? This is the place to start!

Getting help

Having trouble? We'd like to help!

How the documentation is organized

Django has a lot of documentation. A high-level overview of how it's organized will help you know where to look for certain things:

take you by the hand through a series of steps to create a Web application. Start here if you're new to Django or Web application development. Also look at the "First steps ".

Topic guides discuss key topics and concepts at a fairly high level and provide useful background information and explanation.

Reference guides contain technical reference for APIs and other aspects of Django's machinery. They describe how it works and how to use it but assume that you have a basic understanding of key concepts.

How-to guides are recipes. They guide you through the steps involved in addressing key problems and use-cases. They are more advanced than tutorials and assume some knowledge of how Django works.

The model layer

Django provides an abstraction layer (the "models") for structuring and manipulating the data of your Web application. Learn more about it below:

The view layer

Django has the concept of "views" to encapsulate the logic responsible for processing a user's request and for returning the response. Find all you need to know about views via the links below:

The template layer

The template layer provides a designer-friendly syntax for rendering the information to be presented to the user. Learn how this syntax can be used by designers and how it can be extended by programmers:

Forms

Django provides a rich framework to facilitate the creation of forms and the manipulation of form data.

The development process

Learn about the various components and tools to help you in the development and testing of Django applications:

The admin

Find all you need to know about the automated admin interface, one of Django's most popular features:

Security

Security is a topic of paramount importance in the development of Web applications and Django provides multiple protection tools and mechanisms:

Internationalization and localization

Django offers a robust internationalization and localization framework to assist you in the development of applications for multiple languages and world regions:

Performance and optimization

There are a variety of techniques and tools that can help get your code running more efficiently - faster, and using fewer system resources.

Geographic framework

intends to be a world-class geographic Web framework. Its goal is to make it as easy as possible to build GIS Web applications and harness the power of spatially enabled data.

Common Web application tools

Django offers multiple tools commonly needed in the development of Web applications:

Other core functionalities

Learn about some other core functionalities of the Django framework:

The Django open-source project

Learn about the development process for the Django project itself and about how you can contribute:

# data[].attributes.attachment_link.title
Django documentation | Django documentation | Django

# data[].attributes.attachment_link.description
Everything you need to know about Django. Are you new to Django or to programming? This is the place to start! Having trouble? We'd like to help! Django has a lot of documentation. A high-level overview of how it's organized will help you know where to look for certain things: take you by the hand through a series of steps to create a Web application.

# data[].attributes.attachment_link.readable_text
Welcome to Flask's documentation. Get started with Installation and then get an overview with the Quickstart . There is also a more detailed Tutorial that shows how to create a small but complete application with Flask. Common patterns are described in the Patterns for Flask section. The rest of the docs describe each component of Flask in detail, with a full reference in the API section.

Flask depends on the Jinja template engine and the Werkzeug WSGI toolkit. The documentation for these libraries can be found at:

User's Guide

Flask provides configuration and conventions, with sensible defaults, to get started. This section of the documentation explains the different parts of the Flask framework and how they can be used, customized, and extended. Beyond Flask itself, look for community-maintained extensions to add even more functionality.

API Reference

If you are looking for information on a specific function, class or method, this part of the documentation is for you.

Additional Notes

# data[].attributes.attachment_link.title
Welcome to Flask - Flask Documentation (2.2.x)

# data[].attributes.attachment_link.description
Welcome to Flask's documentation. Get started with Installation and then get an overview with the Quickstart . There is also a more detailed Tutorial that shows how to create a small but complete application with Flask. Common patterns are described in the Patterns for Flask section.

# data[].attributes.attachment_link.readable_text
Django is one of the most popular ways to create full-stack websites with Python.

We just published an 18-hour Django course on the freeCodeCamp.org YouTube channel.

Charles Severance (a.k.a. Dr. Chuck) created this course, along with the popular Python for Everybody course. He is a Clinical Professor at the University of Michigan School of Information, where he teaches various technology-oriented courses including programming, database design, and Web development.

This course introduces Python programmers to building websites using the Django library. As part of the course you will learn HTML, CSS, SQL, Django, JavaScript, jQuery, and JSON Web Services.

Here are all the sections in this comprehensive course:

Why Teach Django?

Web Applications and the Request/Response Cycle

Exploring the HyperText Transfer Protocol

Using Sockets to Make Network Connections in Python

Building a Simple Web Browser in Python

Building a Simple HTTP Server in Python

Understanding Browser Developer Mode

The Structure of a Django Application

Installing Django on PythonAnywhere

HTML - HyperText Markup Language (Part 1)

HTML - HyperText Markup Language (Part 2)

Code Walk Through HTML

CSS - Cascading Style Sheets - (Part 1)

CSS - Cascading Style Sheets - (Part 2)

CSS - Cascading Style Sheets - (Part 3)

CSS - Sample Code Walkthrough (Part 1)

CSS - Sample Code Walkthrough (Part 2)

Using GitHub With Django and PythonAnywhere

Using ngrok to Submit Assignments to DJ4E

How Databases Work

Introduction to Structured Query Language (SQL)

Demonstration: Basic SQL

Introduction to Django Models

Data Model Migration in Django

Demonstration: Django Single Table Models

Resetting your Django database on PythonAnywhere

Django Models - Many-To-One Relationships - Music Database

Model View Controller in Django

URL Routing in Django

Django Views

Inside Django Views and HTML Escaping in Django

Using Templates in Django

The Django Template Language (DTL)

Inheritance in Django Templates

Reversing Django Views and URLs

Understanding Django Generic Views

Forms, GET, POST, and HTTP

Building HTML Forms

Forms and Cross Site Request Forgery (CSRF)

CSRF Support in Django

The POST Refresh Pattern

Cookies in Browsers and Django

Using Sessions in Django

One-to-Many Models Overview

Removing Replication in One-to-Many Models

Storing Primary and Foreign Keys in a Database

Representing One-To-Many Models in Django

Using the Django Shell to Explore One-to-Many Models

Loading One-to-Many Data using a Django Batch Script

Creating and Managing Users in Django

Login and Logout URLs in Django

Using Django Login in Views

Using Django Forms Capabilities

Walkthrough of the DJ4E Autos Sample - Generic Django Edit Forms

Data Validation with Django Forms

Exploring the DIY Hello World Django Application on Localhost

Exploring the DIY Hello World Django Application on PythonAnywhere

Virtual Hosting of Django Applications

Owned Rows in Django - Overview

Owned Rows in Django - Generic Views Review

Owned Rows in Django - owner.py

Walking through the DJ4E My Articles (myarts) Sample Code

Walking through the DJ4E Bootstrap Menu (menu) Sample Code

Walking through the DJ4E Crispy Forms (crispy) Sample Code

Many-to-Many Overview

A Simple Many-To-Many Example in Django

Many-To-Many Data Models for Courses and Membership

Building a Django Batch Script to Load Data from CSV

JavaScript - Overview and History

JavaScript - In the Browser

JavaScript - The Language

JavaScript Functions and Arrays

JavaScript Conditional and Loops

JavaScript Object Oriented Concepts

JavaScript Object Oriented Classes

Walking through the DJ4E Pictures (pics) Sample Code

Walking through the DJ4E Forums (forums) Sample Code

jQuery and the Document Object Model

Using jQuery

Walking through the DJ4E jQuery DOM sample code

Walking through the DJ4E jQuery setup sample code

Walking through the DJ4E jQuery events sample code

Walking through the DJ4E jQuery DOM modification

JSON/AJAX Overview

Walking through the DJ4E JSON Sample Code

Building an AJAX Chat with Django

Walking through the DJ4E JSON (chat) Sample Code

Walking through the DJ4E Favicon Sample Code

Walking through the DJ4E Social Login Code

Walking through the DJ4E Favorites (favs) Sample Code

Walking through the DJ4E Search (well) Sample Code

Behind the Scenes: Welcome to Django for Everybody - Why Django?

Watch the full course below or on the freeCodeCamp.org YouTube channel (18-hour watch).

If you read this far, tweet to the author to show them you care. Tweet a thanks

# data[].attributes.attachment_link.title
Django for Everybody - Learn the Popular Python Framework from Dr. Chuck

# data[].attributes.attachment_link.description
Django is one of the most popular ways to create full-stack websites with Python. We just published an 18-hour Django course on the freeCodeCamp.org YouTube channel. Charles Severance (a.k.a. Dr. Chuck) created this course, along with the popular Python for Everybody course. He is a Clinical Professor

# data[].attributes.attachment_link.readable_text
Demo Video

On the main page of your diary, you'll have a list of entries. You can scroll through them and create new ones with a click of a button. The styling is provided in this tutorial, so you can focus on the Django part of the code. Here's a quick demo video of how it will look in action:

By the end of the tutorial, you'll be able to flawlessly navigate your diary to create, read, update, and delete entries on demand.

Project Overview

The tutorial is divided into multiple steps. That way, you can take breaks and continue at your own pace. In each step, you'll tackle a specific area of your diary project:

1. Setting up your Django diary project

2. Creating entries on the back end

3. Displaying entries on the front end

4. Adding styling

5. Managing entries on the front end

6. Improving your user experience

7. Implementing authentication

By following along, you'll explore the basics of web apps and how to add common features of a Django project. After finishing the tutorial, you'll have created your own personal diary app and will have a Django project blueprint to build upon.

Prerequisites

You don't need any previous knowledge of Django to complete this project. If you want to learn more about the topics you encounter in this tutorial, you'll find links to resources along the way.

However, you should be comfortable using the command line and have a basic knowledge of Python and classes. Although it helps to know about virtual environments and pip, you'll learn how to set everything up as you work through the tutorial.

Step 1: Setting Up Your Django Diary

Start the project by creating your project directory and setting up a virtual environment. This setup will keep your code isolated from any other projects on your machine. You can name your project folder and the virtual environment any way you want. In this tutorial, the project folder is named my-diary, and the virtual environment is named .venv:

$ mkdir my-diary $ cd my-diary $ python3 -m venv .venv $ source .venv/bin/activate

Your prompt now starts with the name of your virtual environment in parenthesis. This is an indicator that the virtual environment is activated. For the rest of the tutorial, your virtual environment must be activated. All of the following steps will take place inside this directory or its subdirectories.

Note: To activate your virtual environment on Windows, you might need to run this command:

For other platforms and shells, you might need to use a different command.

The only other requirement for your diary is Django itself. Install the specific version of this tutorial with pip:

(.venv) $ python -m pip install Django == 3.2.1

This command installs Django and some dependencies that Django requires. That's everything you need.

Initialize Django

With all the requirements in place, it's time to start the Django project itself. Use Django's command-line utility to create the base project structure:

Don't forget to add the dot (.) at the end of the command above. The dot prevents Django from creating another directory for your diary project.

Django just created a manage.py file and a folder named diary with five files. You don't have to understand what they do precisely. If you're curious, you can take a glimpse into the files. They all contain an explanation at the beginning that describes why they exist. During this tutorial, you'll only need to edit two of them:

File

Edited during tutorial?

From now on, the manage.py file will take over administrative tasks in the command line. You'll meet some of them during this tutorial.

Create the Database

Now that the basis of your Django diary project is prepared, you need a place to store the future contents of your diary. For this, you must create a database.

Django is shipped with support for multiple databases and works with an SQLite database by default if no other database configurations are provided. An SQLite database is all you need because you are the only user that connects to it, and your Django diary project will only run locally.

The best part is that you can create an SQLite database with a single command. By running migrations, you apply changes of a database schema into the database:

When you look in your project directory, you should see a db.sqlite3 file. Give yourself a pat on the shoulder: You just created a database!

To compare the current state of your project with the downloadable files for this tutorial, click the link below:

The files related to this section are in the source_code_step_1/ directory.

Be a Superuser

As the owner of your personal diary, you've earned yourself the role of the superuser. Claim it with this command:

(.venv) $ python manage.py createsuperuser Username (leave blank to use 'root'): admin Email address: admin@example.com Password: RealPyth0n Password (again): RealPyth0n Superuser created successfully.

You'll be prompted to choose a username, provide an email address, and set a password. This is the key to your diary, so make sure to remember it.

Run a Development Web Server

Another command that you'll use frequently is runserver. This command runs a lightweight development web server:

You can specify the IP address and the port of runserver. By default, the server runs on port 8000 on 127.0.0.1 and is only accessible on your computer. With the server running, you can visit your Django project in your browser by using either http://127.0.0.1:8000 or http://localhost:8000:

This is the home page of your diary. So far, there's only a rocket to see. That means the installation was successful.

Important: Every time you visit your diary project in the browser, you must start your local development web server first if it is not running already.

Finish the first step of this tutorial by visiting http://localhost:8000/admin and logging in with your credentials:

This is your very own Django admin site! It's one of the most powerful features of Django. With just a few adjustments, it gives you the ability to manage content and users right away. For now, there's not much to see in the Django admin site. Time to change that!

Step 2: Adding Your Diary Entries to the Back End

A Django project contains one or more applications. The scope of an app should be limited. In the beginning, differentiating between a project and apps can be confusing. But in big Django projects, this separation of concerns keeps the codebase clean. Another advantage of this structure is that you can reuse apps for other projects.

Connect the Entries App

In your terminal, the Django development web server may still be running. Stop it by pressing Ctrl+ C in the terminal.

Tip: Open a second terminal window to control the server in one window and run commands in the other:

After you've navigated to your project and activated the virtual environment, you can run the upcoming commands of this project in the second terminal window.

In this tutorial, you need only one additional app. The primary purpose of that app is to handle your diary entries, so let's call the app entries. Run the command to create the entries app:

This command creates an entries folder in your project with some predefined files. You'll need to edit only three of them later in this tutorial:

File

Edited during tutorial?

As you can see, some of them have the same name as files in the diary/ directory. By clicking the link below, you can compare your folder structure with the one in the source_code_step_2/ directory:

So far, Django doesn't know the app you just created. To connect the entries app to the Django diary project, add the path to the configuration class at the beginning of the INSTALLED_APPS list in diary/settings.py:

The entries app is plugged into the diary project now, and Django finds its configurations. One of these configurations is the model that describes how your diary entries should look in the database.

Create the Entries Model

You already created the database. Now it's time to define the database table where the entries of your diary will be stored. In Django, you do that with a model class. Just like conventional classes in Python, model names should be singular and uppercase. While your app is called entries, your model will be called Entry.

The fields of the Entry model are the elements that a diary entry will have. In the front, those fields will be displayed as a form. In the back, they will be the columns of your Entry database table. A diary entry in this tutorial contains three fields:

1. title is the headline.

2. content is the main text body.

3. date_created is the creation-date and -time.

In entries/models.py, first import timezone from django.utils. Then create the Entry class in the same file as shown below:

In addition to title, content, and date_created, Django will automatically add id as a unique primary key. The string representation of an entry with the primary key 1 would be Entry object (1) by default. When you add .__str__(), you can customize what is shown instead. For a diary entry, the title is a better string representation.

Register the Entry Model

To also see the Entry model in the Django admin site, you need to register it in entries/admin.py:

Django won't throw an error when you forget to register a model in the admin site. After all, not every model needs to be managed in a user interface. But for the minimal viable product of your diary, you will take advantage of the built-in Django admin site.

Migrate the Entry Model

After adding the new class and registering it on the admin site, you need to make migration files for Django and run them. With makemigrations, you create the migration files, which contain Django's instructions for building a database. With migrate, you implement them:

(.venv) $ python manage.py makemigrations (.venv) $ python manage.py migrate

Once the migrations are finished, run the development web server, move to the browser, and visit the Django admin site at http://localhost:8000/admin:

Currently, there are no entries listed. Finish this step by creating at least one entry to your diary by clicking Add Entry. Not sure what to write? Maybe reflect on how awesome it is that you have a fully functional back end for your Django diary project!

Step 3: Displaying Your Diary Entries on the Front End

You're now able to add new entries in the Django admin site. But when you visit the homepage of your diary in the browser, it still shows the jittering rocket. In this step, you'll learn how to display your diary entries on the front end.

If you want to see how the code looks at the end of this step, click the link below:

The files related to this step are in the source_code_step_3/ directory.

Create List and Detail Views

There are two kinds of views in Django: function-based views and class-based views. Both take a web request and return a web response. Generally speaking, function-based views give you more control but also more work. Class-based views give you less control but also less work.

Less work sounds good. But that's not the only reason you'll use class-based views for your diary. Your Django diary will use views that are typical for a web app, such as showing a list of database items or their details. That's why class-based views are a good choice for your diary views.

Django provides many generic views out of the box. In this case, you'll create subclasses of a DetailView and a ListView and connect them to your Entry model in entries/views.py:

As promised, there's not much code for you to write at this time. The query Entry.objects.all() on line 12 would return all entries ordered by their primary key. Enhancing it with .order_by("-date_created") will return your entries in ascending order, with the newest entry on top of the list.

When you write a view like this, Django will make assumptions in the background, such as the name and location of the template to render by the view.

Create Your Templates

With templates, you can generate HTML dynamically. Django expects the templates for the class-based views you just created to be in a specific location with a particular name. Create the subfolders for your templates:

In Django templates, you can even reference CSS classes dynamically. When you take a look at <h2> on line 5, you can see that class="{{ entry.date_created|date:'l' }}" is added to it. This shows the timestamp with a special formatting. That way, the <h2> element has the weekday as a class, and you can give every weekday a unique color in the CSS later.

A detail template expects exactly one entry object. That's why you can access it directly here without a loop.

Add Routes to Your Views

To see the templates in action, you need to connect your views to URLs. Django works with a urls.py file to dispatch the incoming requests from users in the browser. A file like this already exists in the diary project folder. For the entries app, you must create it first at entries/urls.py and add the paths to EntryListView and EntryDetailView:

The path() function on lines 8 and 13 must have at least two arguments:

1. A route string pattern, which contains a URL pattern

2. The reference to a view, which is an as_view() function for class-based views

Additionally, you can pass arguments as kwargs and provide a name. With a name, you can reference views easily in your Django project. So even if you decide to change the URL pattern, you don't have to update your templates.

Now that the URLs for the entries app are in place, you need to connect them to the urlpatterns list of diary. When you open diary/urls.py, you'll see the urlpatterns your Django Diary project uses. So far, there's only the route to "admin/", which is added by default so you can reach the Django admin site. To show the entries of your diary when you visit http://localhost:8000, you first need to dispatch the root URL to the entries app:

After you create new templates, manually restart the Django development web server. Then visit http://localhost:8000 and enjoy your views:

Now everything is in place to see your entries in the front end. Your diary still looks a bit unstyled, though. Let's change that in the next step!

Step 4: Making Your Django Diary Look Nice

In this step, you'll add some style to your diary. If you want to take a look at the finished code of this step, then click the link below and check out the source_code_step_4/ directory:

While your writing is creative, the design of the diary is currently a bit basic. You can start spicing things up by creating a base template at entries/templates/entries/base.html with this content:

<!-- entries/templates/entries/base.html --> {% load static %} <!DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" > < title >My Diary</ title > < link rel = "stylesheet" href = " {% static 'css/diary.css' %} " > </ head > < body > < h1 >< a href = "/" >Dear diary ...</ a ></ h1 > {% block content %}{% endblock %} </ body > </ html >

With template inheritance, you don't have to repeat markup in your templates. Instead, you extend your child templates. Django then automatically merges them together when serving them in the view.

Add a Stylesheet

By inserting {% load static %} at the beginning of a template file, you can reference static files with the {% static %} template tag and a relative path to the CSS file. Create diary.css in entries/static/css/ and expand the box below to view the CSS code you'll add to the file.

Extend Child Templates

Now it's time to connect the child templates with the base.html parent template. Update entries/templates/entries/entry_list.html so it looks like this:

The {% block %} template tag defines a part of the document that a child template can override. To enable this, you must declare that your child template extends the parent template and define a block element with the same name. With the {% extends %} template tag on line 3, you define entries/base.html as the parent. {% block %} and {% endblock %} on lines 5 and 18 wrap the markup that will be placed in the content block of the parent.

Do the same for entries/templates/entries/entries_detail.html:

You can now read your entries with style. However, when you want to create, update, or delete an entry, you have to go to the Django admin site. That's way too many clicks for when you want to quickly jot down some thoughts. In the next step, you'll improve your workflow by adding this functionality to the front end.

Step 5: Managing Your Diary Entries on the Front End

When you build and use web apps, there are four basic operations that you perform all the time. These operations are so common that they are frequently just referred to by the CRUD acronym:

Create

Read

Update

Delete

In the Django admin site, you can perform all these actions already. In the front end, you only can read your entries so far. To resemble the functionality of the Django admin site, you'll repeat what you've done already for the EntryDetail and EntryList, adding a view, a template, and a URL.

Add the Views

Add your three subclasses at the bottom of entries/views.py:

Additionally, you need to define where the user should be redirected after submitting the form of the view. By default, .get_success_url() simply returns the value of success_url. In EntryUpdateView, you need to overwrite this method.

By providing the entry.id as a keyword argument in line 15, you stay on the entry detail page after editing it. Instead of using URLs, you use reverse_lazy to refer to them by name, just like you did in your templates.

Create the Templates

Like before, Django looks for templates with a specific name:

For EntryDeleteView, it's entry_confirm_delete.html.

For EntryCreateView, it's entry_form.html.

For EntryUpdateView it would be entry_update_form.html.

When this template is loaded by CreateView, the form will be empty, and canceling it will bring you to the list of entries again. When loaded by CreateUpdateView, it will be prefilled with the current title and content of the entry. Canceling will bring you to the entry detail page.

There are multiple ways to render a form in a template. With {{ form.as_p }} in line 7, Django will display the fields you defined in the view wrapped in paragraphs. Whenever you post content in Django forms, you must also include the {% csrf_token %} template tag in line 6 to it. This is a security measure to prevent cross-site request forgery.

This template will show up when you are about to delete an entry. Mentioning "{{ entry.title }}" and {{ entry.created_date|date:'Y-m-d' }} in the form reminds you which entry you would delete by pressing Confirm.

Create the URLs

After you've created the views and their templates, create their routes to access them in the front end. Add three additional paths to urlpatterns in entries/urls.py:

For entry-create, you only need a basic create path. Like the entry-detail created earlier, entry-update and entry-delete need a primary key to identify which entry should be updated or deleted.

Now you can create, update, and delete entries for your diary directly in the front end. Start the development web server and visit http://localhost:8000/create to test it. If you want to compare your code with the one from this tutorial, then click the link below:

You can find the files related to this step in the source_code_step_5/ directory.

Step 6: Improving Your User Experience

Using your diary, you may have stumbled over a few quirks that make navigating around a bit annoying. In this step, you'll tackle them one by one. You'll see that small changes in the interface can have a big impact on your diary user experience.

Handle Your Success

It's always nice to get some feedback, especially when it's positive. Using the messages framework, you can quickly define one-time flash messages that display after you submit your forms. To make use of this feature, import messages and SuccessMessageMixin to entries/views.py:

# entries/views.py from django.contrib import messages from django.contrib.messages.views import SuccessMessageMixin

EntryListView and EntryDetailView are read views and don't handle a form. They may display a message in the template, but they don't send one. That means you don't need to subclass SuccessMessageMixin for them. EntryCreateView, EntryUpdateView and EntryDeleteView, on the other hand, do add a notification to the message storage, so you need to adjust their functionality:

After inheriting SuccessMessageMixin in EntryCreateView on line 3 and in EntryUpdateView on line 9, you define a success_message for them on lines 7 and 12. Especially when you perform a destructive action like deleting an entry, it's crucial to give feedback that everything went fine. To show a message in a DeleteView, you have to add a custom .delete() method and add your custom success_message to the messages framework manually:

Click the link below to see the full content of entries/views.py. You can find it in source_code_step_6/ directory:

Now that the views send messages, you need to enhance the templates to display them.

Get the Message

Messages are stored in the message storage. By looping through it, you show all messages that are currently in the message storage. In Django templates, you accomplish this by using the messages template tag. The way you built the diary and structured the templates, you only need to add it to entries/base.html:

By wrapping the messages list in {% if messages %} on lines 4 and 12, you ensure it will only be shown conditionally, if there are any messages in the storage.

Style Your Messages

Stop the development web server by pressing Ctrl+ C in the terminal and restart it. Then visit http://localhost:8000 to see your changes in action. If the messages look unstyled, you may have to clear the cache of your browser so it reloads the stylesheet changes:

You now own a fully usable diary web app built entirely in Django. Although your diary is only stored locally, it is a good idea to make sure only you can access it. Let's add that restriction before you start journaling.

Step 7: Putting a Lock on Your Django Diary

What a small, golden lock is for a physical diary, a login form is for your digital one. The key to this lock is the name and password combination that you're already using to log in to the Django admin site.

Reuse Your Django Admin Login

The authentication system that Django provides is quite basic. For other projects with regular users, you may consider customizing it. But for your Django diary, it's sufficient to reuse the login of the Django admin site. You'll see that this works just fine in a moment. First, let's take care of logging out.

Restrict Access to Your Views

In Django, you can define who is allowed to see which views. By default, they are open to everyone who visits your website. To make sure that the views require an authenticated user to be accessed, import LoginRequiredMixin at the top of entries/views.py:

Once a view class uses the LoginRequiredMixin, it requires a successful login first. Additionally, you must define a login_url so Django knows where to redirect you when you're not logged in. Instead of doing this for all classes individually, you can create a base class that inherits LoginRequiredMixin and defines the login_url in entries/views.py:

# entries/views.py class LockedView ( LoginRequiredMixin ): login_url = "admin:login"

Now you can inherit LockedView in all your other views that should only be accessible for authenticated users. Edit entries/views.py so your classes look like this:

Now when you visit http://localhost:8000, you'll be redirected to the Django login form. After logging in, you'll be redirected to the entries list and see everything that is for your eyes only:

And that's it! You've successfully built your very own personal diary in Django. If you want to compare your code with the final code of the project, then click the link below:

The complete code for the project is in the source_code_step_final/ directory.

Next Steps

The diary you built is fully functional and ready to use. But it is also an excellent foundation to build upon. Maybe you already have some ideas as to how you can improve your diary even more. Or you can try out one of the ideas below:

Add a page for your most recent entries.

Give every weekday a color.

Show the creation date in the HTML <title> tag.

Create an emoji of the day selection for an entry.

Paginate the list of entries.

Throughout this tutorial, you were introduced to some base concepts and patterns that Django provides. Hopefully, it sparked some interest to dive deeper into this Python web framework. If you want to continue your journey and learn more, you can check out these tutorials:

You can apply the knowledge you gained by completing your diary project to other Django tutorials and take your web app skills to the next level!

Conclusion

In this tutorial, you created a personal diary from scratch using Django. You learned how to build a full-featured web application that you can use day to day. It takes advantage of Django's many strengths, including the admin site, class-based views, the message framework, and the templating system.

This Django project is also a perfect basis for your future Django projects. The steps you finished in this diary project are basically the same for other projects, like a blog or a to-do app.
In this tutorial, you learned how to:

Set up a Django project

Work with the standard SQLite database

Make use of the Django admin site

Create models and class-based views

Nest and style templates

Secure your diary with authentication

You can download the complete source code for the Django diary by clicking the link below:

Get a short & sweet Python Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

# data[].attributes.attachment_link.title
Build a Personal Diary With Django and Python - Real Python

# data[].attributes.attachment_link.description
On the main page of your diary, you'll have a list of entries. You can scroll through them and create new ones with a click of a button. The styling is provided in this tutorial, so you can focus on the Django part of the code.

# data[].attributes.attachment_link.readable_text
Most Python frameworks typically fall into one of two categories: minimalist frameworks like Flask and FastAPI or more robust frameworks that have batteries included, like the popular Django framework.

Django, however, has a pretty steep learning curve, primarily because its patterns are very different from almost all other Python frameworks. For example, what other frameworks would call controller actions are known as views in Django.

Django projects are organized into apps, which can sometimes muddle the typical MVC organization but provides benefits in modularity. Out of the box, it can be quite complicated to build a REST API without using a library like the DjangoRestFramework. Django's folder structure often results in multiple folders and subfolders with the same name, causing confusion among teams.

While many developers still use and love Django, there is a recognized need for a framework that follows more conventional framework patterns, similar to those used by Ruby's Rails, PHP's Laravel, or NestJS. The Masonite framework allows us to use Python with a more robust, familiar convention.

In this tutorial, we'll become familiar with Masonite by building a simple to-do list. To follow along with this tutorial, you'll need to have Python ≥v3.6 installed. First, let's review some of Masonite's features and perks.

Masonite features

Inbuilt email support with the MailProvider, SMTP, and MailgunDriver

IOC container and auto-resolving dependency injection

Service Providers that easily add functionality to the framework

Extremely simple static files configuration

Masonite ORM: drop-in replacement for Orator similar to Active Record

Includes a useful command line tool called the craft command

Send WebSocket requests from your server with the Broadcast Provider, pusher, ably, and pubnub drivers

Extremely extendable

Masonite terminology

Masonite's terminology is familiar to that of Rails, Laravel, and most other MVC web frameworks:

Controllers: classes that contain several methods called actions

Actions: controller method that determines how to respond to particular request

Routes: rules that determine which URL or method combinations should be directed to which controller actions

Creating a new Masonite project

First, let's create a new virtual environment:

python -m venv venv

To activate your virtual environment, run source ./venv/bin/activate. To deactivate, use the command deactivate. Next, install Masonite as follows:

pip install masonite

Create a new project with the command below:

craft new firstproject

Next, navigate to the firstproject directory. Run the following code to install additional dependencies:

craft install

In essence, craft is a command that acts as a prefix when working in Masonite, similar to the rails and artisan commands in Rails and Laravel.

Masonite controllers

Now, we'll create our first controller:

craft controller First

The command above will create the app/http/FirstController.py file, which contains the following code:

"""A FirstController Module."""

from masonite.request import Request
from masonite.view import View
from masonite.controllers import Controller


class FirstController(Controller):
    """FirstController Controller Class."""

    def __init__(self, request: Request):
        """FirstController Initializer

        Arguments:
            request {masonite.request.Request} -- The Masonite Request class.
        """
        self.request = request

    def show(self, view: View):
        pass

In the __init__ function definition, we declare a parameter called request. request uses dependency injection to make the request object, which contains URL params, queries, etc., available to all our routes. Alternately, you could declare parameters on an action by action basis, like the view parameter on the show method.

Rendering templates and views

Let's say you're building a full-stack application that will render a template; we'll use the view parameter that we passed into the show method, rendering the desired template:

def show(self, view: View):
return view.render("helloworld", {"phrase": "Hello World"})

The code above tells Masonite to look in /resources/templates for a template called helloworld.html and render it using the data in the dictionary that is passed as the second argument.

To create the accompanying view, run the command craft view helloworld. Finally, add the code below to the newly created resources/templates/helloworld.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello World</title>
</head>
<body>
    <h1>{{ phrase }}</h1>
</body>
</html>

Creating Masonite routes

To see the code above at work, we just need to connect the controller to a route so that it can render the view. Open up routes/web.py, and let's add our route as follows:

"""Web Routes."""

from masonite.routes import Get, Post

ROUTES = [
    Get("/", "[email protected]").name("welcome"), Get("/first", "[email protected]").name("first") ]

We use the Get function to declare a route for a GET request. The first string denotes the URL of the route, and the second string denotes the action from the controller that should be invoked. In our case, we invoke show from FirstController. The name method allows us to give our route a name, which we'll use to refer to it in links within our templates.

Now, you can run the server with the command craft serve. We can see our page rendered on localhost:8000/first. That was pretty easy!

Sending JSON data

Sending JSON data is quite straightforward. We'll return either a dictionary or an array in our action. If we modify our show action in FirstController as follows, when you check the page in the browser, you'll be greeted by JSON data:

def show(self, view: View):
return {"Hello": "World"}

Working with migrations

To set up our application for migrations, first, we'll configure the database details in the .env file:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=masonite
DB_USERNAME=root
DB_PASSWORD=root
DB_LOG=True

The DB_CONNECTION field corresponds with the different presets in config/database.py. For now, let's comment all of these out, causing Masonite to default to SQLite3:

#DB_CONNECTION=mysql
#DB_HOST=127.0.0.1
#DB_PORT=3306
#DB_DATABASE=masonite
#DB_USERNAME=root
#DB_PASSWORD=root
#DB_LOG=True

Now, let's run a migration to migrate out the default user auth migration that comes out of the box and create our sqlite3 database:

craft migrate

Next, let's create a migration to create a todos table:

craft migration create_todos_table --create todos

The code above generates a new file in databases/migrations that looks like the code below:

"""CreatePostsTable Migration."""

from masoniteorm.migrations import Migration


class CreatePostsTable(Migration):
    def up(self):
        """
        Run the migrations.
        """
        with self.schema.create("todos") as table:
            table.increments("id")

            table.timestamps()

    def down(self):
        """
        Revert the migrations.
        """
        self.schema.drop("todos")

The up function represents what happens when we run the migration, while the down migration represents what happens when we rollback the migration or drop the table. Next, let's add some fields for the todo table in the up function:

    def up(self):
        """
        Run the migrations.
        """
        with self.schema.create("todos") as table:
            table.increments("id")

            table.string("detail")
            table.boolean("completed")

            table.timestamps()

In the code block above, we added a string field called detail and a boolean field called completed. Now, let's run the migration and create the table in our database:

craft migrate

Creating a model

Now, we have a table for our Todos. All we need now is a model that allows us to interact with the data:

craft model Todo

Let's create the file app/Todo.py with our model class:

"""Todo Model."""

from masoniteorm.models import Model


class Todo(Model):
    """Todo Model."""
    pass

Currently, all of our model methods are attained in the Todo class by inheriting them from Masonite's Model class.

Usually, the table name is the plural version of the class name, ie. Dog and Dogs. While the table should work automatically in our case, let's explicitly declare the database table that it should connect to:

"""Todo Model."""

from masoniteorm.models import Model


class Todo(Model):
    """Todo Model."""
    __table__ = 'todos'

Adding to-do items in the Python shell

Run the command below to open up a Python shell with a preloaded Masonite container:

craft tinker

Then, we'll import our todo model as follows:

from app.Todo import Todo

Now, let's create a few to-do list items by adding the following code snippet several times:

Todo.create(detail="Hello", completed=False)

After you've added a few to-do list items, exit the Python shell with quit(). Now, let's import the model into our controller and send back all those to-do list items as JSON. Add the code below in your app/http/controllers/FirstController.py file:

"""A FirstController Module."""

from masonite.request import Request
from masonite.view import View
from masonite.controllers import Controller
from app.Todo import Todo


class FirstController(Controller):
    """FirstController Controller Class."""

    def __init__(self, request: Request):
        """FirstController Initializer

        Arguments:
            request {masonite.request.Request} -- The Masonite Request class.
        """
        self.request = request

    def show(self, view: View):
        return Todo.all()

Turn on the server with craft serve and check out the result on localhost:8000/first.

CORS

Before we deploy our application, we need to set up our CORS headers in case we receive external requests to our application.

Let's import and add the CORSProvider, a built-in provider that helps configure the CORS middleware, into the Providers array. Add the following code block to /config/providers.py:

"""Providers Configuration File."""

from masonite.providers import (
    AppProvider,
    CorsProvider, # ADD THIS
    RequestHelpersProvider,
    AuthenticationProvider,
    BroadcastProvider,
    CacheProvider,
    CsrfProvider,
    HelpersProvider,
    MailProvider,
    QueueProvider,
    RouteProvider,
    SessionProvider,
    StatusCodeProvider,
    UploadProvider,
    ViewProvider,
    WhitenoiseProvider,

)
from masonite.logging.providers import LoggingProvider
from masonite.validation.providers import ValidationProvider
from masoniteorm.providers import ORMProvider

"""Providers List
Providers are a simple way to remove or add functionality for Masonite
The providers in this list are either ran on server start or when a
request is made depending on the provider. Take some time to can
learn more more about Service Providers in our documentation
"""

PROVIDERS = [
    # Framework Providers
    AppProvider,
    CorsProvider, # ADD THIS
    RequestHelpersProvider,
    CsrfProvider,
    AuthenticationProvider,
    SessionProvider,
    RouteProvider,
    StatusCodeProvider,
    WhitenoiseProvider,
    ViewProvider,
    # Optional Framework Providers
    MailProvider,
    UploadProvider,
    QueueProvider,
    CacheProvider,
    BroadcastProvider,
    HelpersProvider,
    ValidationProvider,
    # Third Party Providers
    LoggingProvider,
    ValidationProvider,
    ORMProvider,
    # Application Providers
]

Configure CORS middleware

To configure our CORS middleware, first, let's declare a CORS variable and assign it a dictionary of our cors headers. Add the code below to your /config/middleware.py file:

"""Middleware Configuration Settings."""

from masonite.middleware import (
    ResponseMiddleware,
    MaintenanceModeMiddleware,
    GuardMiddleware,
)

from app.http.middleware.AuthenticationMiddleware import AuthenticationMiddleware
from app.http.middleware.CsrfMiddleware import CsrfMiddleware
from app.http.middleware.LoadUserMiddleware import LoadUserMiddleware
from app.http.middleware.VerifyEmailMiddleware import VerifyEmailMiddleware

"""HTTP Middleware
HTTP middleware is middleware that will be ran on every request. Middleware
is only ran when a HTTP call is successful (a 200 response). This list
should contain a simple aggregate of middleware classes.
"""

HTTP_MIDDLEWARE = [
    LoadUserMiddleware,
    CsrfMiddleware,
    ResponseMiddleware,
    MaintenanceModeMiddleware,
]

"""Route Middleware
Specify a dictionary of middleware to be used on a per route basis here. The key will
be the alias to use on routes and the value can be any middleware class or a list
of middleware (middleware stacks).
"""

ROUTE_MIDDLEWARE = {
    "auth": AuthenticationMiddleware,
    "verified": VerifyEmailMiddleware,
    "guard": GuardMiddleware,
}


## ADD THIS
CORS = {
    'Access-Control-Allow-Origin': "*",
    "Access-Control-Allow-Methods": "*",
    "Access-Control-Allow-Headers": "*",
    "Access-Control-Max-Age": "3600",
    "Access-Control-Allow-Credentials": "true"
}

Deploying our Masonite app to Heroku

To deploy our Masonite to-do list application to Heroku, let's install G unicorn and P sycopg2. If you're aren't using Heroku Postgres, be sure to install the right drivers for your deployment platform:

pip install psycopg2
pip install gunicorn

Create a file called Procfile in the project root and run the following command:

web: gunicorn wsgi

Next, create a runtime.txt file:

python-3.9.0

To create list of current dependencies, run pip freeze > requirements.txt:

backpack==0.1
bcrypt==3.1.7
certifi==2021.5.30
cffi==1.14.6
charset-normalizer==2.0.5
cleo==0.8.1
clikit==0.6.2
crashtest==0.3.1
cryptography==3.4.8
exceptionite==1.0.1
Faker==4.18.0
gunicorn==20.1.0
hfilesize==0.1.0
hupper==1.9.1
idna==3.2
inflection==0.3.1
Jinja2==2.11.3
MarkupSafe==2.0.1
masonite==3.0.12
masonite-dot==0.0.5
masonite-logging==1.0.1
masonite-orm==1.0.55
masonite-validation==3.0.14
passlib==1.7.4
pastel==0.2.1
pendulum==2.1.2
psutil==5.6.7
psycopg2==2.9.1
pycparser==2.20
pylev==1.4.0
python-dateutil==2.8.2
python-dotenv==0.10.5
pytzdata==2020.1
requests==2.26.0
requests-file==1.5.1
simplejson==3.17.5
six==1.16.0
tabulate==0.8.9
text-unidecode==1.3
tldextract==2.2.3
urllib3==1.26.6
whitenoise==4.1.4

If you're missing any of the dependencies above, or you run into issues with Psycopg2, you can easily copy the missing dependency to your requirements.txt file.

Next, we'll push our project up to GitHub and deploy to a new Heroku project. Go to the Resources tab and provision a Heroku Postgres database. Get the credentials for the database and add the following variables to Heroku config vars:

DB_CONNECTION=postgres
DB_HOST=<get value from herok>
DB_PORT=<get value from herok>
DB_DATABASE=<get value from herok>
DB_USERNAME=<get value from herok>
DB_PASSWORD=<get value from herok>
DB_LOG=True

Finally, copy the key variable from your local .env to your Heroku config vars and set a variable. The code for our final application looks like the image below:

Conclusion

In this tutorial, we built a simple to-do list application with Masonite, a web framework for Python that uses the MVC model. Masonite offers many of the best features of modern frameworks like PHP, Ruby, and NestJS, bringing them to Python in a slick package.

The CLI is powerful by helping you generate the components of your application while still providing a simple enough workflow to maximize productivity. Now that you are familiar with the fundamentals of Masonite, you should be able to build your own, complex applications. Happy coding!

Try it for free.

# data[].attributes.attachment_link.title
Masonite: MVC web framework for Python - LogRocket Blog

# data[].attributes.attachment_link.description
Most Python frameworks typically fall into one of two categories: minimalist frameworks like Flask and FastAPI or more robust frameworks that have batteries included, like the popular Django framework. Django, however, has a pretty steep learning curve, primarily because its patterns are very different from almost all other Python frameworks.

# data[].attributes.attachment_link.title
GitHub - django/django: The Web framework for perfectionists with deadlines.

# data[].attributes.attachment_link.description
Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. Thanks for checking it out. All documentation is in the " docs" directory and online at https://docs.djangoproject.com/en/stable/. If you're just getting started, here's how we recommend you read the docs: First, read docs/intro/install.txt for instructions on installing Django.

# data[].attributes.attachment_link.readable_text
Si vous demandez aux programmeurs Python ce qu'ils aiment le plus à propos de Python, ils citeront souvent sa grande lisibilité. En effet, un haut niveau de lisibilité est au cœur de la conception du langage Python, après le fait reconnu que le code est lu beaucoup plus souvent que ce qui est écrit.

Une raison de la haute lisibilité du code Python est son jeu relativement complet d'instructions de style de code et ses idiomes "Pythoniques".

Quand un développeur Python vétéran (un Pythoniste) appelle des portions de code non "Pythoniques", il veut dire généralement que ces lignes de code ne suivent pas les instructions communes et ne parviennent pas à exprimer leur intention dans ce qui est considéré comme la meilleure façon (entendez: la manière la plus lisible).

Dans certains cas limites, aucune meilleure pratique n'a été convenue sur la façon d'exprimer une intention en code Python, mais ces cas sont rares.

Concepts généraux
Mauvais Bon Mauvais Bon
Idiomes

Un idiome de langage, dit simplement, est un moyen d'écrire du code. La notion d'idiomes de programmation est discutée amplement sur c2 et sur Stack Overflow.

Du code Python idiomatique est souvent désigné comme étant Pythonique.

Bien qu'il y ait habituellement une - et de préférence une seule - manière évidente de le faire; la façon d'écrire du code Python idiomatique peut être non évidente pour les débutants Python. Donc, les bons idiomes doivent être consciemment acquis.

Certains idiomes Python communs suivent:

L'unpacking imbriqué marche aussi:

Le Zen de Python

Aussi connu comme PEP 20, les principes directeurs pour la conception de Python.

Pour quelques exemples de bons styles Python, voir ces diapositives d'un groupe d'utilisateurs Python.

PEP 8

PEP 8 est le guide de fait du style de code pour Python. Une version de haute qualité, facile à lire de la PEP 8 est également disponible sur pep8.org.

C'est une lecture fortement recommandée. La communauté Python entière fait de son mieux pour adhérer aux instructions énoncées dans le présent document. Certains projets peuvent osciller autour d'elles de temps à autre, tandis que d'autres peuvent modifier ses recommandations.

Cela étant dit, conformer votre code Python à PEP 8 est généralement une bonne idée et contribue à rendre le code plus consistant lorsque vous travaillez sur des projets avec d'autres développeurs. Il existe un programme en ligne de commande, pep8, qui peut vérifier la conformité de votre code. Installez-le en exécutant la commande suivante dans votre terminal:

Ensuite, exécutez-le sur un fichier ou une série de fichiers pour avoir un rapport de toutes violations.

Le programme autopep8 peut être utilisé pour reformater automatiquement le code dans le style PEP 8. Installez le programme avec:

Utilisez-le pour formater un fichier sur place avec:

Exclure l'option va mener le programme à renvoyer en sortie le code modifié directement dans la console pour examen. L'option --aggressive effectuera des changements plus importants et peut être appliquée à plusieurs reprises pour plus d'effet.

Conventions

Voici quelques conventions que vous devriez suivre pour rendre votre code plus facile à lire.

Mauvais:

Bon:

Mauvais:

Bon:

Mauvais:

Bon:

Mauvais:

Bon:

Mauvais:

Bon:

Mauvais:

Bon:

# data[].attributes.attachment_link.title
Style de code - The Hitchhiker's Guide to Python

# data[].attributes.attachment_link.description
Si vous demandez aux programmeurs Python ce qu'ils aiment le plus à propos de Python, ils citeront souvent sa grande lisibilité. En effet, un haut niveau de lisibilité est au cœur de la conception du langage Python, après le fait reconnu que le code est lu beaucoup plus souvent que ce qui est écrit.

# data[].attributes.attachment_link.readable_text
This document gives coding conventions for the Python code comprising the standard library in the main Python distribution. Please see the companion informational PEP describing style guidelines for the C code in the C implementation of Python .

This document and PEP 257 (Docstring Conventions) were adapted from Guido's original Python Style Guide essay, with some additions from Barry's style guide .

This style guide evolves over time as additional conventions are identified and past conventions are rendered obsolete by changes in the language itself.

Many projects have their own coding style guidelines. In the event of any conflicts, such project-specific guides take precedence for that project.

One of Guido's key insights is that code is read much more often than it is written. The guidelines provided here are intended to improve the readability of code and make it consistent across the wide spectrum of Python code. As PEP 20 says, "Readability counts".

A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is the most important.

However, know when to be inconsistent-sometimes style guide recommendations just aren't applicable. When in doubt, use your best judgment. Look at other examples and decide what looks best. And don't hesitate to ask!

In particular: do not break backwards compatibility just to comply with this PEP!

Some other good reasons to ignore a particular guideline:

Indentation

Use 4 spaces per indentation level.

Continuation lines should align wrapped elements either vertically using Python's implicit line joining inside parentheses, brackets and braces, or using a hanging indent . When using a hanging indent the following should be considered; there should be no arguments on the first line and further indentation should be used to clearly distinguish itself as a continuation line.

# Aligned with opening delimiter. foo = long_function_name(var_one, var_two, var_three, var_four) # More indentation included to distinguish this from the rest. def long_function_name( var_one, var_two, var_three, var_four): print(var_one) # Hanging indents should add a level. foo = long_function_name( var_one, var_two, var_three, var_four)

# Arguments on first line forbidden when not using vertical alignment. foo = long_function_name(var_one, var_two, var_three, var_four) # Further indentation required as indentation is not distinguishable. def long_function_name( var_one, var_two, var_three, var_four): print(var_one)

The 4-space rule is optional for continuation lines.

Optional:

# Hanging indents *may* be indented to other than 4 spaces. foo = long_function_name( var_one, var_two, var_three, var_four)

When the conditional part of an if-statement is long enough to require that it be written across multiple lines, it's worth noting that the combination of a two character keyword (i.e. if), plus a single space, plus an opening parenthesis creates a natural 4-space indent for the subsequent lines of the multiline conditional. This can produce a visual conflict with the indented suite of code nested inside the if-statement, which would also naturally be indented to 4 spaces. This PEP takes no explicit position on how (or whether) to further visually distinguish such conditional lines from the nested suite inside the if-statement. Acceptable options in this situation include, but are not limited to:

# No extra indentation. if (this_is_one_thing and that_is_another_thing): do_something() # Add a comment, which will provide some distinction in editors # supporting syntax highlighting. if (this_is_one_thing and that_is_another_thing): # Since both conditions are true, we can frobnicate. do_something() # Add some extra indentation on the conditional continuation line. if (this_is_one_thing and that_is_another_thing): do_something()

(Also see the discussion of whether to break before or after binary operators below.)

The closing brace/bracket/parenthesis on multi-line constructs may either line up under the first non-whitespace character of the last line of list, as in:

my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', )

or it may be lined up under the first character of the line that starts the multi-line construct, as in:

my_list = [ 1, 2, 3, 4, 5, 6, ] result = some_function_that_takes_arguments( 'a', 'b', 'c', 'd', 'e', 'f', )

Tabs or Spaces?

Spaces are the preferred indentation method.

Tabs should be used solely to remain consistent with code that is already indented with tabs.

Python 3 disallows mixing the use of tabs and spaces for indentation.

Python 2 code indented with a mixture of tabs and spaces should be converted to using spaces exclusively.

When invoking the Python 2 command line interpreter with the -t option, it issues warnings about code that illegally mixes tabs and spaces. When using -tt these warnings become errors. These options are highly recommended!

Maximum Line Length

Limit all lines to a maximum of 79 characters.

For flowing long blocks of text with fewer structural restrictions (docstrings or comments), the line length should be limited to 72 characters.

Limiting the required editor window width makes it possible to have several files open side-by-side, and works well when using code review tools that present the two versions in adjacent columns.

The default wrapping in most tools disrupts the visual structure of the code, making it more difficult to understand. The limits are chosen to avoid wrapping in editors with the window width set to 80, even if the tool places a marker glyph in the final column when wrapping lines. Some web based tools may not offer dynamic line wrapping at all.

Some teams strongly prefer a longer line length. For code maintained exclusively or primarily by a team that can reach agreement on this issue, it is okay to increase the nominal line length from 80 to 100 characters (effectively increasing the maximum length to 99 characters), provided that comments and docstrings are still wrapped at 72 characters.

The Python standard library is conservative and requires limiting lines to 79 characters (and docstrings/comments to 72).

The preferred way of wrapping long lines is by using Python's implied line continuation inside parentheses, brackets and braces. Long lines can be broken over multiple lines by wrapping expressions in parentheses. These should be used in preference to using a backslash for line continuation.

Backslashes may still be appropriate at times. For example, long, multiple with-statements cannot use implicit continuation, so backslashes are acceptable:

with open('/path/to/some/file/you/want/to/read') as file_1, \ open('/path/to/some/file/being/written', 'w') as file_2: file_2.write(file_1.read())

(See the previous discussion on multiline if-statements for further thoughts on the indentation of such multiline with-statements.)

Another such case is with assert statements.

Make sure to indent the continued line appropriately.

Should a line break before or after a binary operator?

For decades the recommended style was to break after binary operators. But this can hurt readability in two ways: the operators tend to get scattered across different columns on the screen, and each operator is moved away from its operand and onto the previous line. Here, the eye has to do extra work to tell which items are added and which are subtracted:

# No: operators sit far away from their operands income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)

To solve this readability problem, mathematicians and their publishers follow the opposite convention. Donald Knuth explains the traditional rule in his Computers and Typesetting series:

"Although formulas within a paragraph always break after binary operations and relations, displayed formulas always break before binary operations" .

Following the tradition from mathematics usually results in more readable code:

# Yes: easy to match operators with operands income = (gross_wages + taxable_interest + (dividends - qualified_dividends) - ira_deduction - student_loan_interest)

In Python code, it is permissible to break before or after a binary operator, as long as the convention is consistent locally. For new code Knuth's style is suggested.

Blank Lines

Surround top-level function and class definitions with two blank lines.

Method definitions inside a class are surrounded by a single blank line.

Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations).

Use blank lines in functions, sparingly, to indicate logical sections.

Python accepts the control-L (i.e. ^L) form feed character as whitespace; Many tools treat these characters as page separators, so you may use them to separate pages of related sections of your file. Note, some editors and web-based code viewers may not recognize control-L as a form feed and will show another glyph in its place.

Source File Encoding

Code in the core Python distribution should always use UTF-8 (or ASCII in Python 2).

Files using ASCII (in Python 2) or UTF-8 (in Python 3) should not have an encoding declaration.

In the standard library, non-default encodings should be used only for test purposes or when a comment or docstring needs to mention an author name that contains non-ASCII characters; otherwise, using \x, \u, \U, or \N escapes is the preferred way to include non-ASCII data in string literals.

For Python 3.0 and beyond, the following policy is prescribed for the standard library (see PEP 3131): All identifiers in the Python standard library MUST use ASCII-only identifiers, and SHOULD use English words wherever feasible (in many cases, abbreviations and technical terms are used which aren't English). In addition, string literals and comments must also be in ASCII. The only exceptions are (a) test cases testing the non-ASCII features, and (b) names of authors. Authors whose names are not based on the latin alphabet MUST provide a latin transliteration of their names.

Open source projects with a global audience are encouraged to adopt a similar policy.

Imports

Imports should usually be on separate lines, e.g.:

import os import sys

import os, sys

It's okay to say this though:

from subprocess import Popen, PIPE

Imports are always put at the top of the file, just after any module comments and docstrings, and before module globals and constants.

Imports should be grouped in the following order:

1. standard library imports

2. related third party imports

3. local application/library specific imports

You should put a blank line between each group of imports.

Absolute imports are recommended, as they are usually more readable and tend to be better behaved (or at least give better error messages) if the import system is incorrectly configured (such as when a directory inside a package ends up on sys.path):

import mypkg.sibling from mypkg import sibling from mypkg.sibling import example

However, explicit relative imports are an acceptable alternative to absolute imports, especially when dealing with complex package layouts where using absolute imports would be unnecessarily verbose:

from . import sibling from .sibling import example

Standard library code should avoid complex package layouts and always use absolute imports.

Implicit relative imports should never be used and have been removed in Python 3.

When importing a class from a class-containing module, it's usually okay to spell this:

from myclass import MyClass from foo.bar.yourclass import YourClass

If this spelling causes local name clashes, then spell them :

import myclass import foo.bar.yourclass

and use myclass.MyClass and foo.bar.yourclass.YourClass.

Wildcard imports ( from <module> import *) should be avoided, as they make it unclear which names are present in the namespace, confusing both readers and many automated tools. There is one defensible use case for a wildcard import, which is to republish an internal interface as part of a public API (for example, overwriting a pure Python implementation of an interface with the definitions from an optional accelerator module and exactly which definitions will be overwritten isn't known in advance).

When republishing names this way, the guidelines below regarding public and internal interfaces still apply.

Module level dunder names

Module level "dunders" (i.e. names with two leading and two trailing underscores) such as __all__, __author__, __version__, etc. should be placed after the module docstring but before any import statements except from __future__ imports. Python mandates that future-imports must appear in the module before any other code except docstrings.

For example:

"""This is the example module. This module does stuff. """ from __future__ import barry_as_FLUFL __all__ = ['a', 'b', 'c'] __version__ = '0.1' __author__ = 'Cardinal Biggles' import os import sys

In Python, single-quoted strings and double-quoted strings are the same. This PEP does not make a recommendation for this. Pick a rule and stick to it. When a string contains single or double quote characters, however, use the other one to avoid backslashes in the string. It improves readability.

For triple-quoted strings, always use double quote characters to be consistent with the docstring convention in PEP 257.

Pet Peeves

Avoid extraneous whitespace in the following situations:

Other Recommendations

Avoid trailing whitespace anywhere. Because it's usually invisible, it can be confusing: e.g. a backslash followed by a space and a newline does not count as a line continuation marker. Some editors don't preserve it and many projects (like CPython itself) have pre-commit hooks that reject it.

Function annotations should use the normal rules for colons and always have spaces around the -> arrow if present. (See Function Annotations below for more about function annotations.)

def munge(input: AnyStr): ... def munge() -> AnyStr: ...

def munge(input:AnyStr): ... def munge()->PosInt: ...

Trailing commas are usually optional, except they are mandatory when making a tuple of one element (and in Python 2 they have semantics for the print statement). For clarity, it is recommended to surround the latter in (technically redundant) parentheses.

FILES = ('setup.cfg',)

OK, but confusing:

FILES = 'setup.cfg',

When trailing commas are redundant, they are often helpful when a version control system is used, when a list of values, arguments or imported items is expected to be extended over time. The pattern is to put each value (etc.) on a line by itself, always adding a trailing comma, and add the close parenthesis/bracket/brace on the next line. However it does not make sense to have a trailing comma on the same line as the closing delimiter (except in the above case of singleton tuples).

FILES = [ 'setup.cfg', 'tox.ini', ] initialize(FILES, error=True, )

FILES = ['setup.cfg', 'tox.ini',] initialize(FILES, error=True,)

Comments that contradict the code are worse than no comments. Always make a priority of keeping the comments up-to-date when the code changes!

Comments should be complete sentences. If a comment is a phrase or sentence, its first word should be capitalized, unless it is an identifier that begins with a lower case letter (never alter the case of identifiers!).

If a comment is short, the period at the end can be omitted. Block comments generally consist of one or more paragraphs built out of complete sentences, and each sentence should end in a period.

You should use two spaces after a sentence-ending period.

When writing English, follow Strunk and White.

Python coders from non-English speaking countries: please write your comments in English, unless you are 120% sure that the code will never be read by people who don't speak your language.

Block comments generally apply to some (or all) code that follows them, and are indented to the same level as that code. Each line of a block comment starts with a # and a single space (unless it is indented text inside the comment).

Paragraphs inside a block comment are separated by a line containing a single #.

Use inline comments sparingly.

An inline comment is a comment on the same line as a statement. Inline comments should be separated by at least two spaces from the statement. They should start with a # and a single space.

Inline comments are unnecessary and in fact distracting if they state the obvious.

Don't do this:

x = x + 1 # Increment x

But sometimes, this is useful:

x = x + 1 # Compensate for border

Documentation Strings

Conventions for writing good documentation strings (a.k.a. "docstrings") are immortalized in PEP 257.

Write docstrings for all public modules, functions, classes, and methods. Docstrings are not necessary for non-public methods, but you should have a comment that describes what the method does. This comment should appear after the def line.

PEP 257 describes good docstring conventions. Note that most importantly, the """ that ends a multiline docstring should be on a line by itself, e.g.:

"""Return a foobang Optional plotz says to frobnicate the bizbaz first. """

For one liner docstrings, please keep the closing """ on the same line.

The naming conventions of Python's library are a bit of a mess, so we'll never get this completely consistent - nevertheless, here are the currently recommended naming standards. New modules and packages (including third party frameworks) should be written to these standards, but where an existing library has a different style, internal consistency is preferred.

Overriding Principle

Names that are visible to the user as public parts of the API should follow conventions that reflect usage rather than implementation.

Descriptive: Naming Styles

There are a lot of different naming styles. It helps to be able to recognize what naming style is being used, independently from what they are used for.

The following naming styles are commonly distinguished:

b (single lowercase letter)

B (single uppercase letter)

lower_case_with_underscores

UPPER_CASE_WITH_UNDERSCORES

CapitalizedWords (or CapWords, CamelCase, StudlyCaps)

mixedCase (differs from CapitalizedWords by initial lowercase character!)

Capitalized_Words_With_Underscores (ugly!)

There's also the style of using a short unique prefix to group related names together. This is not used much in Python, but it is mentioned for completeness. For example, the os.stat() function returns a tuple whose items traditionally have names like st_mode, st_size, st_mtime and so on. (This is done to emphasize the correspondence with the fields of the POSIX system call struct, which helps programmers familiar with that.)

The X11 library uses a leading X for all its public functions. In Python, this style is generally deemed unnecessary because attribute and method names are prefixed with an object, and function names are prefixed with a module name.

In addition, the following special forms using leading or trailing underscores are recognized (these can generally be combined with any case convention):

_single_leading_underscore: weak "internal use" indicator. E.g. from M import * does not import objects whose name starts with an underscore.

single_trailing_underscore_: used by convention to avoid conflicts with Python keyword, e.g.:

Tkinter.Toplevel(master, class_='ClassName')

__double_leading_underscore: when naming a class attribute, invokes name mangling (inside class FooBar, __boo becomes _FooBar__boo; see below).

__double_leading_and_trailing_underscore__: "magic" objects or attributes that live in user-controlled namespaces. E.g. __init__, __import__ or __file__. Never invent such names; only use them as documented.

Prescriptive: Naming Conventions

Names to Avoid

Never use the characters 'l' (lowercase letter el), 'O' (uppercase letter oh), or 'I' (uppercase letter eye) as single character variable names.

In some fonts, these characters are indistinguishable from the numerals one and zero. When tempted to use 'l', use 'L' instead.

ASCII Compatibility

Identifiers used in the standard library must be ASCII compatible as described in the policy section of PEP 3131.

Package and Module Names

Modules should have short, all-lowercase names. Underscores can be used in the module name if it improves readability. Python packages should also have short, all-lowercase names, although the use of underscores is discouraged.

When an extension module written in C or C++ has an accompanying Python module that provides a higher level (e.g. more object oriented) interface, the C/C++ module has a leading underscore (e.g. _socket).

Class Names

Class names should normally use the CapWords convention.

The naming convention for functions may be used instead in cases where the interface is documented and used primarily as a callable.

Note that there is a separate convention for builtin names: most builtin names are single words (or two words run together), with the CapWords convention used only for exception names and builtin constants.

Type variable names

Names of type variables introduced in PEP 484 should normally use CapWords preferring short names: T, AnyStr, Num. It is recommended to add suffixes_co or _contra to the variables used to declare covariant or contravariant behavior correspondingly. Examples

from typing import TypeVar VT_co = TypeVar('VT_co', covariant=True) KT_contra = TypeVar('KT_contra', contravariant=True)

Exception Names

Because exceptions should be classes, the class naming convention applies here. However, you should use the suffix "Error" on your exception names (if the exception actually is an error).

Global Variable Names

(Let's hope that these variables are meant for use inside one module only.) The conventions are about the same as those for functions.

Modules that are designed for use via from M import * should use the __all__ mechanism to prevent exporting globals, or use the older convention of prefixing such globals with an underscore (which you might want to do to indicate these globals are "module non-public").

Function Names

Function names should be lowercase, with words separated by underscores as necessary to improve readability.

mixedCase is allowed only in contexts where that's already the prevailing style (e.g. threading.py), to retain backwards compatibility.

Function and method arguments

Always use self for the first argument to instance methods.

Always use cls for the first argument to class methods.

If a function argument's name clashes with a reserved keyword, it is generally better to append a single trailing underscore rather than use an abbreviation or spelling corruption. Thus class_ is better than clss. (Perhaps better is to avoid such clashes by using a synonym.)

Method Names and Instance Variables

Use the function naming rules: lowercase with words separated by underscores as necessary to improve readability.

Use one leading underscore only for non-public methods and instance variables.

To avoid name clashes with subclasses, use two leading underscores to invoke Python's name mangling rules.

Python mangles these names with the class name: if class Foo has an attribute named __a, it cannot be accessed by Foo.__a. (An insistent user could still gain access by calling Foo._Foo__a.) Generally, double leading underscores should be used only to avoid name conflicts with attributes in classes designed to be subclassed.

Note: there is some controversy about the use of __names (see below).

Constants

Constants are usually defined on a module level and written in all capital letters with underscores separating words. Examples include MAX_OVERFLOW and TOTAL.

Designing for inheritance

Always decide whether a class's methods and instance variables (collectively: "attributes") should be public or non-public. If in doubt, choose non-public; it's easier to make it public later than to make a public attribute non-public.

Public attributes are those that you expect unrelated clients of your class to use, with your commitment to avoid backward incompatible changes. Non-public attributes are those that are not intended to be used by third parties; you make no guarantees that non-public attributes won't change or even be removed.

We don't use the term "private" here, since no attribute is really private in Python (without a generally unnecessary amount of work).

Another category of attributes are those that are part of the "subclass API" (often called "protected" in other languages). Some classes are designed to be inherited from, either to extend or modify aspects of the class's behavior. When designing such a class, take care to make explicit decisions about which attributes are public, which are part of the subclass API, and which are truly only to be used by your base class.

With this in mind, here are the Pythonic guidelines:

Public attributes should have no leading underscores.

If your public attribute name collides with a reserved keyword, append a single trailing underscore to your attribute name. This is preferable to an abbreviation or corrupted spelling. (However, notwithstanding this rule, 'cls' is the preferred spelling for any variable or argument which is known to be a class, especially the first argument to a class method.)

Note 1: See the argument name recommendation above for class methods.

For simple public data attributes, it is best to expose just the attribute name, without complicated accessor/mutator methods. Keep in mind that Python provides an easy path to future enhancement, should you find that a simple data attribute needs to grow functional behavior. In that case, use properties to hide functional implementation behind simple data attribute access syntax.

Note 1: Properties only work on new-style classes.

Note 2: Try to keep the functional behavior side-effect free, although side-effects such as caching are generally fine.

Note 3: Avoid using properties for computationally expensive operations; the attribute notation makes the caller believe that access is (relatively) cheap.

If your class is intended to be subclassed, and you have attributes that you do not want subclasses to use, consider naming them with double leading underscores and no trailing underscores. This invokes Python's name mangling algorithm, where the name of the class is mangled into the attribute name. This helps avoid attribute name collisions should subclasses inadvertently contain attributes with the same name.

Note 1: Note that only the simple class name is used in the mangled name, so if a subclass chooses both the same class name and attribute name, you can still get name collisions.

Note 2: Name mangling can make certain uses, such as debugging and __getattr__(), less convenient. However the name mangling algorithm is well documented and easy to perform manually.

Note 3: Not everyone likes name mangling. Try to balance the need to avoid accidental name clashes with potential use by advanced callers.

Public and internal interfaces

Any backwards compatibility guarantees apply only to public interfaces. Accordingly, it is important that users be able to clearly distinguish between public and internal interfaces.

Documented interfaces are considered public, unless the documentation explicitly declares them to be provisional or internal interfaces exempt from the usual backwards compatibility guarantees. All undocumented interfaces should be assumed to be internal.

To better support introspection, modules should explicitly declare the names in their public API using the __all__ attribute. Setting __all__ to an empty list indicates that the module has no public API.

Even with __all__ set appropriately, internal interfaces (packages, modules, classes, functions, attributes or other names) should still be prefixed with a single leading underscore.

An interface is also considered internal if any containing namespace (package, module or class) is considered internal.

Imported names should always be considered an implementation detail. Other modules must not rely on indirect access to such imported names unless they are an explicitly documented part of the containing module's API, such as os.path or a package's __init__ module that exposes functionality from submodules.

Code should be written in a way that does not disadvantage other implementations of Python (PyPy, Jython, IronPython, Cython, Psyco, and such).

For example, do not rely on CPython's efficient implementation of in-place string concatenation for statements in the form a += b or a = a + b. This optimization is fragile even in CPython (it only works for some types) and isn't present at all in implementations that don't use refcounting. In performance sensitive parts of the library, the ''.join() form should be used instead. This will ensure that concatenation occurs in linear time across various implementations.

Comparisons to singletons like None should always be done with is or is not, never the equality operators.

Also, beware of writing if x when you really mean if x is not None - e.g. when testing whether a variable or argument that defaults to None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context!

Use is not operator rather than not ... is. While both expressions are functionally identical, the former is more readable and preferred.

if foo is not None:

if not foo is None:

When implementing ordering operations with rich comparisons, it is best to implement all six operations (__eq__, __ne__, __lt__, __le__, __gt__, __ge__) rather than relying on other code to only exercise a particular comparison.

To minimize the effort involved, the functools.total_ordering() decorator provides a tool to generate missing comparison methods.

PEP 207 indicates that reflexivity rules are assumed by Python. Thus, the interpreter may swap y > x with x < y, y >= x with x <= y, and may swap the arguments of x == y and x != y. The sort() and min() operations are guaranteed to use the < operator and the max() function uses the > operator. However, it is best to implement all six operations so that confusion doesn't arise in other contexts.

Always use a def statement instead of an assignment statement that binds a lambda expression directly to an identifier.

def f(x): return 2*x

f = lambda x: 2*x

The first form means that the name of the resulting function object is specifically 'f' instead of the generic '<lambda>'. This is more useful for tracebacks and string representations in general. The use of the assignment statement eliminates the sole benefit a lambda expression can offer over an explicit def statement (i.e. that it can be embedded inside a larger expression)

Derive exceptions from Exception rather than BaseException. Direct inheritance from BaseException is reserved for exceptions where catching them is almost always the wrong thing to do.

Design exception hierarchies based on the distinctions that code catching the exceptions is likely to need, rather than the locations where the exceptions are raised. Aim to answer the question "What went wrong?" programmatically, rather than only stating that "A problem occurred" (see PEP 3151 for an example of this lesson being learned for the builtin exception hierarchy)

Class naming conventions apply here, although you should add the suffix "Error" to your exception classes if the exception is an error. Non-error exceptions that are used for non-local flow control or other forms of signaling need no special suffix.

Use exception chaining appropriately. In Python 3, "raise X from Y" should be used to indicate explicit replacement without losing the original traceback.

When deliberately replacing an inner exception (using "raise X" in Python 2 or "raise X from None" in Python 3.3+), ensure that relevant details are transferred to the new exception (such as preserving the attribute name when converting KeyError to AttributeError, or embedding the text of the original exception in the new exception message).

When raising an exception in Python 2, use raise ValueError('message') instead of the older form raise ValueError, 'message'.

The latter form is not legal Python 3 syntax.

The paren-using form also means that when the exception arguments are long or include string formatting, you don't need to use line continuation characters thanks to the containing parentheses.

When catching exceptions, mention specific exceptions whenever possible instead of using a bare except: clause.

For example, use:

try: import platform_specific_module except ImportError: platform_specific_module = None

A bare except: clause will catch SystemExit and KeyboardInterrupt exceptions, making it harder to interrupt a program with Control-C, and can disguise other problems. If you want to catch all exceptions that signal program errors, use except Exception: (bare except is equivalent to except BaseException:).

A good rule of thumb is to limit use of bare 'except' clauses to two cases:

1. If the exception handler will be printing out or logging the traceback; at least the user will be aware that an error has occurred.

2. If the code needs to do some cleanup work, but then lets the exception propagate upwards with raise. try...finally can be a better way to handle this case.

When binding caught exceptions to a name, prefer the explicit name binding syntax added in Python 2.6:

try: process_data() except Exception as exc: raise DataProcessingFailedError(str(exc))

This is the only syntax supported in Python 3, and avoids the ambiguity problems associated with the older comma-based syntax.

When catching operating system errors, prefer the explicit exception hierarchy introduced in Python 3.3 over introspection of errno values.

When a resource is local to a particular section of code, use a with statement to ensure it is cleaned up promptly and reliably after use. A try/finally statement is also acceptable.

Context managers should be invoked through separate functions or methods whenever they do something other than acquire and release resources. For example:

with conn.begin_transaction(): do_stuff_in_transaction(conn)

with conn: do_stuff_in_transaction(conn)

The latter example doesn't provide any information to indicate that the __enter__ and __exit__ methods are doing something other than closing the connection after a transaction. Being explicit is important in this case.

Use string methods instead of the string module.

String methods are always much faster and share the same API with unicode strings. Override this rule if backward compatibility with Pythons older than 2.0 is required.

Use ''.startswith() and ''.endswith() instead of string slicing to check for prefixes or suffixes.

startswith() and endswith() are cleaner and less error prone. For example:

if foo.startswith('bar'):

Object type comparisons should always use isinstance() instead of comparing types directly:

if isinstance(obj, int):

if type(obj) is type(1):

When checking if an object is a string, keep in mind that it might be a unicode string too! In Python 2, str and unicode have a common base class, basestring, so you can do:

if isinstance(obj, basestring):

Note that in Python 3, unicode and basestring no longer exist (there is only str) and a bytes object is no longer a kind of string (it is a sequence of integers instead)

Don't write string literals that rely on significant trailing whitespace. Such trailing whitespace is visually indistinguishable and some editors (or more recently, reindent.py) will trim them.

Function Annotations

With the acceptance of PEP 484, the style rules for function annotations are changing.

In order to be forward compatible, function annotations in Python 3 code should preferably use PEP 484 syntax. (There are some formatting recommendations for annotations in the previous section.)

The experimentation with annotation styles that was recommended previously in this PEP is no longer encouraged.

However, outside the stdlib, experiments within the rules of PEP 484 are now encouraged. For example, marking up a large third party library or application with PEP 484 style type annotations, reviewing how easy it was to add those annotations, and observing whether their presence increases code understandability.

The Python standard library should be conservative in adopting such annotations, but their use is allowed for new code and for big refactorings.

For code that wants to make a different use of function annotations it is recommended to put a comment of the form:

# type: ignore

near the top of the file; this tells type checker to ignore all annotations. (More fine-grained ways of disabling complaints from type checkers can be found in PEP 484.)

Like linters, type checkers are optional, separate tools. Python interpreters by default should not issue any messages due to type checking and should not alter their behavior based on annotations.

Users who don't want to use type checkers are free to ignore them. However, it is expected that users of third party library packages may want to run type checkers over those packages. For this purpose PEP 484 recommends the use of stub files: .pyi files that are read by the type checker in preference of the corresponding .py files. Stub files can be distributed with a library, or separately (with the library author's permission) through the typeshed repo .

For code that needs to be backwards compatible, type annotations can be added in the form of comments. See the relevant section of PEP 484 .

This document has been placed in the public domain.

# data[].attributes.attachment_link.description
This document gives coding conventions for the Python code comprising the standard library in the main Python distribution. Please see the companion informational PEP describing style guidelines for the C code in the C implementation of Python . This document and PEP 257 (Docstring Conventions) were adapted from Guido's original Python Style Guide essay, with some additions from Barry's style guide .

# data[].attributes.attachment_link.description
What's New in Python- What's New In Python 3.11- Summary - Release highlights, New Features- PEP 657: Fine-grained error locations in tracebacks, PEP 654: Exception Groups and except*, PEP 678: Exc...

# data[].attributes.attachment_link.description
Checkout new Python Example tutorials and guides