The property of certain operations in mathematics and computer science whereby they can be applied multiple times without changing the result beyond the initial application.
In other words, in the context of REST APIs, the result of making a request shouldn’t change the state of the application more than once. That is, you make the same request multiple times, there should be no side effects such that the same result is sent each time.
Or, perhaps even more simply, if the same request is sent more than once, it’ll only be processed once.
Secondly, the crux of the article has a great breakdown of the various methods that should exist in a REST API:
GET Method: The GET method is used to retrieve a resource from the server. It is an idempotent method because retrieving the same resource multiple times will not change the resource itself or cause any side effects on the server.
PUT Method: The PUT method is used to update a resource on the server. It is idempotent because sending the same request multiple times will result in the same resource state as if the request had only been sent once. For example, if you send a PUT request to update a user’s email address with the same new email address multiple times, the user’s email address will only be updated once.
DELETE Method: The DELETE method is used to delete a resource from the server. It is idempotent because deleting a resource multiple times will have the same result as deleting it only once. If the resource has already been deleted, sending a DELETE request for the same resource will not result in any changes.
POST Method: The POST method is used to create a new resource on the server or to submit data to be processed. It is not idempotent because sending the same request multiple times will create multiple resources or submit the same data multiple times, resulting in different outcomes.
And keeping this in mind is very helpful when designing an API.
Ultimately, I share this because the article does a great job of breaking down the concepts of idempotency in REST API design and the supporting application. And it’s a good reminder to have on hand whenever I’m – or you, whoever you are ::s
If you’re to go through the archives of the content here, you’re going to find majority of articles dealing with PHP, JavaScript, HTML, CSS, and variations thereof and all of which will likely be around building solutions on top of WordPress.
But a couple of decades(!) ago, I was working with Python – and other languages – when I was in college. And given some side projects that I’m working on right now, I’m using it again.
This time, though, I’ve got an environment set up that I really don’t want to leave and I want to be able to easily go back and forth between working with PHP and related tooling to Python and similar tooling with as little context switching as possible.
having proper sniffers and fixers for coding standards,
easily generating docblocks for the code,
and more.
Though it’s obviously outside the content I normally write but given that I’m doing this more and more, I thought I’d share everything I’ve done to set up a Python development environment as a PHP developer.
Summary
If you’re curious about everything covered in this article, here’s a list:
Installing Python via Homebrew
Setting Up Visual Studio Code
Installing the Python Extension
Configuration the Python Environment
Python Coding Standards
Debugging Python Code
GitHub Copilot
Installing the Extension
Generating DocBlocks
Explaining Code
Conclusion
Resources
Visual Studio Code and Other Tools for Python Development
Installing Python via Homebrew
The majority of the software I install is through Homebrew and I’ve covered it more extensively in the article I wrote when I first set up the machine I use each day.
You can check to see if Python is already installed on your system by running $ which python on your machine. At this point, though, you’ll likely see something like python not found. This is because the Python binary is, at the time of this writing, is python3.
So run $ which python3 and if you’re given a path, then you’ve got it installed. If not, run the following:
$ brew install python
After this, you should be able to run:
$ python3 --version
And you’ll see the current version you’re running. For this article, I’m running 3.9.6.
Don’t Forget pip
pip is a Python package manager much like Composer is a PHP package manager so I’ve not found a reason not to install it.
It should be installed by default if you’ve installed Python via Homebrew. Make sure you’re running the latest version. To do this, enter:
$ pip3 install --upgrade pip
Once the process is done, the initial work for installing and setting up Python is done.
Setting Up Visual Studio Code
My goal is to make sure I can swap between PHP development and Python development as quickly as possible by just opening a new instance of the IDE.
Installing the Python Extension
To install the Python extension for Visual Studio Code:
Navigate to the Extensions you have installed (you can use cmd+shift+x on macOS to do this.
If Python is not already installed, search for python and make sure it’s the extension that’s authored by Microsoft. Install it and, if requested, restart Visual Studio Code.
Before getting started with Python in Visual Studio Code, there are still a few things to do.
Configuring the Python Environment
If you’re like me, then whenever you restart Visual Studio Code, it’s going to have Python installed but still look and function much like it would any other time you open it. That is, it’s going to act like it’s a standard PHP editor.
Things change whenever you want to start a new Python-based project (be a single file or something more complex).
Select the Python Interpreter you want to use in your projects. To do this, open the command palette by first pressed cmd+p then typing “Python: Select Interpreter“. Essentially this is asking which version or which binary, of all those that may exist on your system, do you want to use when running your code. Depending on your set up, you’ll see a number of different options (one will also be Global and one will also be Recommended). I favor the Recommended option because it should be the one provided by Homebrew (though it’s possible to have both Global and Recommended be the same, too).
You can now create a new Python file or open an existing one. The Python extension provides various features, including code highlighting, code completion, linting, debugging, and more.
If you opt to create a new file, you’ll need to choose an environment type in the current workspace. There are two types, Venv and Conda. What you opt to use depends on your needs. For the purposes of this article, I’m using Venv.
At this point, Visual Studio Code is ready for Python-based projects along with everything that comes with the environment. This includes IntelliSense, debugging, and running scripts.
But if you’re coming from another development background, you know there’s more to do such as debugging code, coding standards, sniffing and fixing code, docblocks, and and using modern tools like GitHub Copilot to help with any, all, and more than the above.
Python Coding Standards
As is the same with other programming languages, Python has it’s own set of coding standards. When it comes to using Python, all of this is already built into the extension out-of-the-box so there’s very little that we have to do.
PHP Coding Standards, DocStrings, etc.
First though, it’s worth knowing where to look should you need to reference something just in case. At least this is what I find to be the case whenever I’m curious about some standard or decision the plugin makes when it formats my code. Here’s a concise list of resources I have available:
Assuming you’ve got a file set up in Visual Studio Code, call it hello.py or whatever you’d like, and you’ve selected Venv (or Conda, should you so choose) as your environment of choice, then you’re ready to start writing code, formatting it, and adding inline code to it.
First, in your settings.json file, make sure that you’ve added the following directives:
This will ensure that even if you miss any formatting in the document, it will format it whenever you save it.
Secondly, start by writing a basic function that’s essentially the classic Hello World except it will accept a string and say Hello to whatever you pass to it.
def hello(str):
return "Hello " + str + "!"
Obviously this function won’t do anything until it’s invoked. So it’s possible to either just invoke the function by using print or we can get a bit more involved with Python and look at how to accept command line arguments while building out this functionality.
Let’s do the latter. To do this, we’ll need to import the sys package and then look at the arguments that are passed to the script. A very rudimentary way of doing this is to look at the arguments and blindly accept the first argument as the the name.
Now when you run the script, you can run $ python3 hello.py Tom and the output should simply be “Hello Tom!”
But, as mentioned, there are a lot of problems with this implementation the least of which is not blindly accepting output. I’ll come back to this point later in the article.
Before finishing, let’s add one more function that’s formatted like this:
def goodbye(str):
return "Goodbye " + str + "!"
Functionally, this looks fine but it’s ill-formatted per the Python coding standards and if you’re editor is properly set up, then you should see something like the following image:
When you hover over the underlined code, you’ll see a message that reads Expected indented block. So indent the block per the coding standards and you should be good to go.
Finally, update the main file to say goodbye so that it looks like this:
When working with PHP, it’s a bit of work to get Xdebug set up and working and configured in Visual Studio Code. It’s not has bad as it once was, but it’s also more of an involved process than it is in Python.
Out-of-the-box, the Python extension for Visual Studio Code handles the majority of this for us. Instead of having to configure any third-party module, enable it in a configuration file, then set up a configuration in the IDE, we simply set breakpoints, add watches, and all of the usual actual debugging tasks in Visual Studio code. Then we just tell the editor to Run and Debug.
But given the code above, we can’t simply press a button and run because it’s expecting input from the terminal. There are two ways to handle this.
We can set up an else so that it defaults to an empty string,
We can edit the code inline during debugging.
Each of these are useful in their own context.
Set Up a Conditional
The easiest route is to first set up an else case. The block of code may not look like this
Now if you run the script from the command line, you should see it output Hello World! It should also output Goodbye World!
Now if you set a breakpoint on either of the function calls, let’s say print(hello("World")) and start the debugger, you’ll see the familiar interface that you see when debugging PHP.
From here, you can do the usual debugging behavior. If you’ve set the same breakpoint as I’ve set above, then click on Run and Debug and you should see see the debugger hit breakpoints, add variables to the Watch list, and all of the usual inspections that you can do to variables, function calls, and so on.
But if you’re debugging arguments that come in via the command-line, what then?
Edit Values Inline
I don’t know how often you’ve done this in the past but I’ve find when working with web applications, this can be extremely useful especially with data coming in from another source (like a POST request).
To do this in Visual Studio Code, simply set a breakpoint as you normally would. Assuming you’ve got a variable name set in the Watch list and the breakpoint is on the line where the variable is invoked, then you can modify it by two-finger Set Value.
This will allow you to change the value in real time and then see the output of the result once done. And if you want to do this with command-line arguments, then set a breakpoint and a watch when args is set and change the value to ['...', 'Tom'] and you’ll find that you’ve now simulated updating command line arguments.
This particular extension has made it easier to more quickly understand code when I’m lacking context, generate docblocks faster and more concisely than I normally can, and even recommend suggestions for making sure I’m following best practices regardless of what I’m doing.
But in the context of using Python especially as someone who’s more rusty on it than other languages, I’ve found it extremely helpful when bringing me up to speed when I want to achieve something (such as how to retrieve command line arguments).
To do this, though, we need to install and configure the extension.
Installing The Extension
First, I recommend installing both of the following:
GitHub Copilot. This utility is primarily focused on helping developers write code more efficiently by providing intelligent code suggestions and autocompletions based on the context of their code. It is designed to enhance the developer’s coding experience.
GitHub Copilot Chat. GitHub Copilot Chat beta provides a natural language chat interface to engage with GitHub Copilot, an AI developer tool or “AI pair programmer,” and get coding information and support for tasks from explaining code to finding issues and fixing them.
Note this is free for open source projects. If you’re working on anything that’s licensed as such, then there’s no need to pay; however, if you’re working on anything that’s closed source, it costs money.
Further, note that whatever license you use, make sure you pay attention to the settings GitHub asks as it will ask if it can use your source code to training their language model. For some, it’s not a big deal; for others, this isn’t something you want to do; however, if you’re working in open source, it’s probably expected to share it.
If you’re ensure and you’re not self-employed or you’re using it for an official project for your employer or for someone else, then be sure to ask them.
Generating DocBlocks
Assuming you’ve got GitHub Copilot and Copilot Chat installed, here’s how you can use it to quickly generate aspects of your code that are generally mundane (such as DocBlocks).
Looking at the following function:
def hello(str):
return "Hello " + str + "!"
Let’s ask Copilot to generate a docblock for it. There are two ways to do this:
You can ask Copilot to generate it inline using a menu item and then ‘accept’ the results to have them automatically inserted.
You can ask Copilot Chat to generate it and then copy and paste the results into the editor.
We’ll look at an example of the latter in the next section so let’s use the first option to generate a docblock.
First, two-finger click or right-click on the function signature and select Copilot > Generate Docs.
Once it’s done processing the code, you’ll likely see something like this:
For the purposes of this acrticle, I’ll accept it. Once done, your function should look something like this:
def hello(str):
"""
Returns a greeting message with the provided string.
Args:
str (str): The name or string to be greeted.
Returns:
str: A greeting message with the provided string.
"""
return "Hello " + str + "!"
You can do this for the rest of the functions in this file or whatever file on which you’re working. In addition to making this much quicker, it also follows the coding conventions of Python so you don’t have to tweak it after the fact.
Explaining Code
Now let’s use Copilot Chat to explain a section of code in the chat window provided by the extension. Since we know how to generate documentation, let’s use the feature to explain the conditional we have.
Select the block of code in Code, then click on the Copilot Chat icon in the Activity Bar. Next, you can either prompt it with something like “Explain this code” or you can use one of the built in commands it offers such as /explain.
If you use the latter, it should return a lengthy response. Something like this, perhaps:
This Python code reads command-line arguments using the sys.argv list. If there is more than one argument, it calls the hello and goodbye functions with the first argument and prints the results. If there is only one argument, it calls the same functions with the string “World” and prints the results.
The hello function takes a string argument and returns a greeting message that includes the argument. The goodbye function takes a string argument and returns a farewell message that includes the argument.
This code demonstrates how to use command-line arguments in Python and how to call functions with arguments. It also shows how to use conditional statements to handle different cases based on the number of arguments passed in.
To improve the readability of this code, we could add comments to explain what each part of the code does. We could also use more descriptive function and variable names to make the code easier to understand. Additionally, we could use f-strings to format the greeting and farewell messages instead of concatenating strings.
If you want to make the code more succinct, leave the block of code highlighted then prompt Chat with “Make this code more succinct.” It should return something like this:
And for good measure, let’s ask it to generate a single-line comment to explain what this block of code is doing. We can use a more specific prompt such as “Explain this in a single line comment.”
The first result may be something like this:
This Python code reads a command-line argument and calls the hello and goodbye functions with the argument, or “World” if no argument is provided, and prints the results.
But given that I’m coming from a PHP background, I’m used to inline comments being no more than 120 characters so I’ll prompt Chat with the following: “Shorten the last answer to no more than 120 characters.”
Then it returned this:
Python code reads command-line arg, calls hello and goodbye with arg or “World” and prints results.
And since we’re in Python, we can remove the “Python code” phrase and just go with:
Reads command-line arg, calls hello and goodbye with arg or “World” and prints results.
The final version of your IDE may look something like this.
And that’s it for the initial primer on how to use Visual Studio Code and Other Tools for Python Development.
Conclusion
I obviously contend that AI is more of an assistant to writing software than anything else, but I found this to be a unique case to document. This covers how to use things such as GitHub Copilot and Copilot chat, but also how to set up a new language in Visual Studio Code and leverage modern tools to get easily onboarded or get brought up to speed on how to do certain things in the language.
It’s easy to feel utility to technology fatigue with so much happening in our space. Each month, it seems as if there’s some new pre-processor, linter, JavaScript utility, security analyzer, or something else that promises to make us more product or write better code or get things done in such a way it’s more maintainable.
Perhaps some or all of that is true.
But in all of the years of writing web applications, I’ve yet to really find anything that’s increased productivity and learning so much as I have with using GPT-based tools. And though it’s useful in my day-to-day work in writing PHP and related code, it’s also been extremely useful in getting up to speed with writing, debugging, documenting, and being brought up to speed on Python, too.
There’s a lot to be said for using a traditional debugger such as Xdebug (and I’m still a fan), but lately, I’ve been writing more generalized PHP utilities that have no front-end and aren’t plugged into any specific application.
That is, I’m using native PHP libraries to run queries against incoming data and help analyze certain aspects of it and output a report.
There’s been a learning curve for a lot of it, but part of the process has included plenty of debugging. And if you’ve read my series on Ray in WordPress, then you know I’m a fan of Ray.
So here’s how you can use Ray to debug vanilla PHP utilities.
In your composer.json file, make sure the following directive is present:
"require-dev": {
"spatie/ray": "^1.29.0"
},
Then in the terminal, run the following:
$ composer update
You may need to run $ composer install if you haven’t already, but I’m assuming you’re adding to the file you already have set up.
After that’s done, make sure the following is in your PHP code:
<?php
require 'vendor/autoload.php';
// ...
use Spatie/Ray;
At this point, add the following line just to make sure Ray is loading (and make sure the Ray application is actually running). Add the following line:
ray('Loaded...');
Then in your terminal run $ php script.php. Assuming all has gone well, you should see the following:
From there, you can use the rest of the tools outlined in the Ray for WordPress series for doing whatever it is you want. Except at this point, you’re just running Ray for debug vanilla PHP utilities.
As much as I enjoy working with Laravel Valet and using Homebrew to manage different versions of PHP, there are still times in which my system has been set up in such a way that my shell is using one version of PHP and Laravel is using a different version.
At the time of this post, I see the following whenever which I run $ which php:
/opt/homebrew/opt/php@7.4/bin/php
And when I run $ php -v, I’ll see the following output:
PHP 7.4.33 (cli) (built: Jul 13 2023 17:41:11) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Xdebug v3.1.2, Copyright (c) 2002-2021, by Derick Rethans
with Zend OPcache v7.4.33, Copyright (c), by Zend Technologies
But if I was to run phpinfo(); within a page hosted via Laravel, I’d get PHP8. This is problematic because if I’m working on a function that I’m eventually going to incorporate in an app that runs in the browser, there may be functionality that doesn’t work as I expect.
As such, it’s important to make sure we have parity between Laravel and our system’s binary. To do this, issue the following command in the terminal:
$ valet use php@7.4
$ valet restart
Obviously, you’ll change the value of php@7.4 to whatever you want to use on your machine. Whatever it is, though, make sure there’s parity between the two. This way, whenever you run a script independently, then you’ll have the same results in the browser as you do in your terminal.
Assume you’re working on a custom WordPress plugin that includes its own template for rendering data. The difference in this template is that it’s not one that you’ll apply to a page or custom post type in WordPress, but it will instead be its own page with access to the WordPress core functions.
This works well enough for a plugin, right? 🙂. Credit.
For example, say you’re generating a report for a given user ID and you want the page to incorporate fonts from Google, perhaps a third-party library, and it look as if it’s not even part of the WordPress core application.
The head element of the page may look something like this:
You want to access the report through a pretty link that includes the $userId (perhaps something like /information/42
You want to use core functions for getting user metadata through functions like get_user_meta.
To do this, you need to have custom templates with rewrite rules in your WordPress plugin. Again, this is not a custom template in the WordPress sense and you’ll only have as many rewrite rules as you do templates.
Custom Templates with Rewrite Rules
1. The Plugin Structure
This can be structured however you want, but I find it easiest to have the rules set in their own file that’s included in the plugin’s bootstrap file or in the core plugin’s file.
I also think it’s easiest to keep the template in its own template directory where you can also house other templates. The main reason for this being that if you have custom rewrite rules, each rule can map to its own template.
In the example I’m going to give throughout this article, I’m going to have the rules located in the plugin bootstrap file which I’m calling plugin.php and I’m going to have the template in a directory called templates. The template will be called information.php so that it maps to the permalink structure I’m going to set up.
So aim to have:
plugin.php in the root of your plugin
templates/information.php in the root of your plugin
Note that the information.php template does not need to include the standard WordPress template headers because this isn’t that type of template.
2. The Template
This template can be as involved and complicated or as simple as you like. For the purposes of this post and to keep the code easy to follow, I’m just going to render user’s first name and last name from the user metadata table.
Obviously, though, you can mark this up however you find necessary. You can even incorporate additional functions that are in plugin.php or whatever file you want to incorporate additional logic to add functionality to the template.
You may see that I have a getAsset function in the head element of this page. This is what that function looks like in plugin.php:
function getAsset(string $filename)
{
if (0 < stripos($filename, '.css')) {
return trailingslashit(plugins_url('acme-info')) . "/assets/css/$filename";
}
if (0 < stripos($filename, '.js')) {
return trailingslashit(plugins_url('acme-info')) . "/assets/js/$filename";
}
}
This is useful for that I’m doing in a template like this but additional functionality and the reason behind doing this is outside the scope of this article. I’m showing that it can be done.
3. The Rewrite Rules
To set up the custom rewrite rules, you’ll need three functions:
and one for redirecting the request to the new template whenever a request is made to the new rewrite rule.
First, set up the rewrite rule whenever the plugin is activated so that it persists as long as the plugin is activated. To do this, set up a function that looks like this:
This will add a rewrite rule whenever the plugin is activated to that you can use information/42 and have it passed to the WordPress core application with the digit representing the ID for the user account you want to retrieve.
Next, set up the deactivation hook so the custom rewrite rule is removed and is flushed from the WordPress application so no lingering functionality from the plugin works:
register_deactivation_hook(__FILE__, function () {
flush_rewrite_rules();
});
Finally, set up the functionality so that it does the following:
add_action('template_redirect', function () {
$requestUri = filter_var($_SERVER['REQUEST_URI'], FILTER_SANITIZE_URL);
$requestUriParts = array_values(array_filter(explode('/', $requestUri)));
if (
count($requestUriParts) === 2 &&
strtolower($requestUriParts[0]) === 'report' &&
ctype_digit($requestUriParts[1])
) {
$userId = intval($requestUriParts[1]);
// Validate $userId and ensure it's within an appropriate range
if ($userId > 0 && $userId <= 1000000) { // Adjust the upper limit as needed
$templatePath = plugin_dir_path(__FILE__) . 'templates/information.php';
// Check if the template file exists before including it
if (file_exists($templatePath)) {
include $templatePath;
exit;
} else {
// Handle the case where the template file is missing
die('Report template not found.');
}
} else {
// Handle invalid user IDs
die('Invalid user ID.');
}
} else {
// Handle invalid URLs
die('Invalid URL.');
}
});
At this point, assuming you’re trying to access https://yourEnvironment.com/information/42 then you’ll be redirected to the custom template outlined in the second step above.
Notice that you’re still able to read the userId and you’re able to use built-in WordPress functions to render whatever information you deem necessary for your solution.
Kinsta offers premium managed WordPress hosting for everyone, small or large. Powered by Google Platform and strengthened by Cloudflare, they take performance to the next level. All hosting plans include 24/7/365 support from their team of WordPress engineers. Get startedwith a free migration today.