One place for hosting & domains

      How To Write Your First PHP Program


      The author selected Open Sourcing Mental Illness Ltd to receive a donation as part of the Write for DOnations program.

      Introduction

      The “Hello, World!” program is a classic and time-honored tradition in computer programming. Serving as a complete first program for beginners and a good program to test systems and programming environments, “Hello, World!” illustrates the basic syntax of programming languages.

      This tutorial will walk you through writing a “Hello, World!” program in PHP. You’ll also learn about opening and closing PHP code blocks within your code and using different types of comments in your code.

      Prerequisites

      You will need PHP installed as well as a local programming environment set up on your computer.

      To set this up, follow the How to Install PHP 7.4 and Set Up a Local Development Environment for your operating system.

      Writing the “Hello, World!” Program

      To write the “Hello, World!” program, start by opening a command-line text editor, such as nano, and create a new file:

      Once the text file opens up in the terminal window, type out the program:

      hello.php

      <?php
      echo "Hello, World!";
      ?>
      

      Let’s break down the different components of the code.

      All PHP code falls within a PHP Code Block, starting with <?php and ending with ?>.

      echo is a language construct. Its arguments are a list of expressions following the echo keyword, separated by commas and not delimited by parentheses. echo tells PHP to display or output whatever is included between echo and the ending semicolon ;.

      Between the echo and the ; is a sequence of characters — Hello, World! — that is enclosed in quotation marks. Any characters that are inside quotation marks are called a string.

      After writing the program, hold down the CTRL key and press the X key to exit nano. When prompted to save the file, press Y.

      Once you exit nano, you’ll return to your shell.

      With that, you have written your “Hello, World!” program.

      Running the “Hello, World!” Program

      With your “Hello, World!” program written, you’re ready to run the program. Use the php command along with the name of the program file as follows:

      Running the hello.php program that you just created will cause your terminal to produce the following output:

      Output

      Hello, World!

      Let’s go over what the program did in more detail.

      PHP executed the line echo "Hello, World!"; by calling the language construct echo. The string value of Hello, World! was passed to the construct.

      In this example, the string Hello, World! is also called an argument since it is a value that is passed to another part of the code, such as a construct or a function.

      The quotes that are on either side of Hello, World! were not output to the screen because they are used to tell PHP that this section of code contains a string. The quotation marks delineate where the string begins and ends.

      Since the program ran successfully, you can now confirm that PHP is properly installed and that the program is syntactically correct. Before going any further in the code itself, let’s take a closer look at the PHP Code Block.

      Working Outside the PHP Code Block

      Within a .php file, anything outside of the PHP tags is treated as HTML or plain text. The PHP language was originally written as a way to extend the functionality of HTML. With this in mind, you may include multiple PHP code blocks throughout a file. Anything outside the code block will render as HTML or plain text.

      Update your hello.php file:

      hello.php

      Hi Sammy
      <?php echo "Hello, World!"; ?>
      
      How are you doing?
      <?php echo "Swimmingly!";
      

      Save the file and rerun it:

      Output

      Hi Sammy Hello, World! How are you doing? Swimmingly!

      Diving into the code, you’ll notice that Hi Sammy and How are you doing? are both outside the PHP code blocks and therefore render as plain text when running the program.

      This file contains two PHP code blocks. The first code block includes both the starting and ending tags, while the second code block, being at the end of the file, leaves off the final closing tag.

      Including the closing block tag ?> is not required. When ending a file with a PHP code block, it is recommended to leave off the closing tag. Any character, even a blank space, which is rendered after the closing tag will be output to the screen as HTML or plain text. This can cause unexpected consequences with the function of your application because certain functionality, such as a redirect, will not process if anything has been output to the browser. When writing a file that contains only PHP code, never include the closing PHP tag.

      As code gets more complicated, like when splitting concepts over multiple code blocks, it can be beneficial to leave notes for ourselves and others. You can do this through the use of comments.

      A comment in code is a line that will not execute as a part of the program. Its only purpose is to be read by a human who is looking at the code. One thing that comes as a shock to many developers is how much time is spent reading code versus writing code. This means it’s essential to have code that is as easy to read as possible. You can accomplish this in a few ways:

      • Use coding standards. These are a collection of guidelines and best practices for organizing and formatting code clearly and consistently. In PHP, the most common coding standards are those developed by the PHP-FIG (Framework Interop Group).
      • Choose ease of reading over ease of writing. Use descriptive variables over short variables. It’s not about how many lines of code your write, but how long it will take someone to read those lines and understand what’s going on.
      • Comment for clarity. While it isn’t a hard and fast rule, if you’ve followed the previous two bullet points, your code should explain what is happening, while the comments explain why something is happening the way it is.

      When writing comments in PHP, there are two types of comments: single-line comments and multiline comments. Single line comments can start at any point on a line and end at either the end of the line or the end of the code block, whichever comes first.

      The most common way to start a single-line comment is with the double forward slash (//), although PHP also recognizes a hash sign (#) as a valid start to a single-line comment:

      hello.php

      Hi Sammy
      <?php echo "Hello"; //, World!"; ?>
      
      How are you doing?
      <?php echo "Swimmingly!";
      // other options: Floating along
      

      Save the file and run it again:

      Output

      Hi Sammy Hello How are you doing? Swimmingly!

      The first comment starts in the middle of a line. A closing quote and semicolon were added after "Hello" and the rest of the line was commented out. Commenting out one or more lines of code is often used in debugging to test how the code responds if certain elements are removed.

      You use a second comment to give a secondary option for an answer. The next step in your project may be to respond with one of several different options each time you execute the application. The comment is used as a reminder for other options that could be added.

      Multiline comments start with /* and end with */. The PHP interpreter will ignore any text or code within those characters. To provide more options, let’s change the last line to a multi-line comment:

      hello.php

      Hi Sammy
      <?php echo "Hello"; //, World!"; ?>
      
      How are you doing?
      <?php echo "Swimmingly!";
      /* When responding with one of a number of answers, here are some other options:
      * Floating along
      * Fin-tastic
      * Going with the flow
      * Treading water
      * Swamped
      */
      

      Using a multi-line comment gives more room to add detail or formatting to once again make the code, and the intention of the code, easier to understand. This multi-line comment includes line breaks and added * as a delineator for a list. The */ combination signifies the end of our comment block.

      Using DocBlocks for Documentation

      There is a special type of multi-line comment called a DocBlock. This is a unique way of documenting the functionality of a particular file, class, method, or other structural elements. Although a DocBlock starts and ends like any other multi-line comment /* */, they are designed to give specific detail for working with an element. Not only do these details provide an overview of the code for developers, but they may also be used by a code editor (or IDE) to provide suggestions and validation.

      A DocBlock consists of several parts. The first is a brief summary to introduce the element and a longer description if more context is needed.

      The final section that makes a DocBlock unique is for tags and annotations. These provide a way to succinctly and uniformly provide meta-information about the associated element. Tags can, for example, describe the type of information that is accepted or returned by a method or function. It may also provide details about the author or copyright of a file:

      hello.php

      <?php
      /**
       * DocBlock example
       *
       * @author Sammy <sammy@digitalocean.com>
       */
       ...
      

      While you should strive to write code that is clear and easy to follow, adding clarifying comments can add additional context that will increase the understanding of the code and the choices behind the code.

      Conclusion

      In this tutorial, you have written the “Hello, World!” program in PHP. You learned about opening and closing PHP code blocks within your code and using different comments to clarify and add context as your code gets more complicated. From here, you can continue learning by following the How To Work with Strings in PHP tutorial.



      Source link

      How To Use argparse to Write Command-Line Programs in Python


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Python’s argparse standard library module is a tool that helps you write command-line interfaces (CLI) over your Python code. You may already be familiar with CLIs: programs like git, ls, grep, and find all expose command-line interfaces that allow you to call an underlying program with specific inputs and options. argparse allows you to call your own custom Python code with command-line arguments similar to how you might invoke git, ls, grep, or find using the command line. You might find this useful if you want to allow other developers to run your code from the command line.

      In this tutorial, you’ll use some of the utilities exposed by Python’s argparse standard library module. You’ll write command-line interfaces that accept positional and optional arguments to control the underlying program’s behavior. You’ll also self-document a CLI by providing help text that can be displayed to other developers who are using your CLI.

      For this tutorial, you’ll write command-line interfaces for a program that keeps track of fish in a fictional aquarium.

      Prerequisites

      To get the most out of this tutorial, we recommend you have:

      • Some familiarity with programming in Python 3. You can review our How To Code in Python 3 tutorial series for background knowledge.

      Writing a Command-Line Program that Accepts a Positional Argument

      You can use the argparse module to write a command-line interface that accepts a positional argument. Positional arguments (as opposed to optional arguments, which we’ll explore in a subsequent section), are generally used to specify required inputs to your program.

      Let’s consider an example CLI that prints the fish in an aquarium tank identified by a positional tank argument.

      To create the CLI, open a file with your text editor:

      Then, add the following Python code:

      aquarium.py

      import argparse
      
      tank_to_fish = {
          "tank_a": "shark, tuna, herring",
          "tank_b": "cod, flounder",
      }
      
      parser = argparse.ArgumentParser(description="List fish in aquarium.")
      parser.add_argument("tank", type=str)
      args = parser.parse_args()
      
      fish = tank_to_fish.get(args.tank, "")
      print(fish)
      

      You can print out the fish in tank_a by running:

      • python3 aquarium.py tank_a

      After running that command, you will receive output like the following:

      Output

      shark, tuna, herring

      Similarly, if you ran aquarium.py to print out the fish in tank_b with:

      • python3 aquarium.py tank_b

      You would receive output like the following:

      Output

      cod, flounder

      Let’s break down the code in aquarium.py.

      First, you import the argparse module to make it available for use in your program. Next, you create a dictionary data structure tank_to_fish that maps tank names (like tank_a and tank_b) to string descriptions of fish held in those tanks.

      You instantiate an instance of the ArgumentParser class and bind it to the parser variable. You can think of parser as the main point of entry for configuring your command-line interface. The description string provided to parser is—as you’ll learn later—used in the automatically generated help text for the CLI exposed by aquarium.py.

      Calling add_argument on parser allows you to add arguments accepted by your command-line interface. In this case, you add a single argument named tank that is a string type. Calling parser.parse_args() instructs parser to process and validate the command-line input passed to aquarium.py (for example, something like tank_a). Accessing the args returned by parser.parse_args() allows you to retrieve the value of the passed in tank argument, and use it to print out the fish in that tank.

      At this point, you’ve written a command-line interface and executed your program to print fish. Now you need to describe how your CLI works to other developers. argparse has strong support for help text to document your CLIs. You’ll learn more about help text next.

      Viewing Help Text

      The aquarium.py file you just wrote in the previous section actually does more than print the fish in a specific tank. Since you’re using argparse, the command-line interface exposed by aquarium.py will automatically include help and usage messages that a user can consult to learn more about your program.

      Consider, for example, the usage message aquarium.py prints if you provide invalid arguments on the command line. Try invoking aquarium.py with the wrong arguments on the command line by running:

      • python3 aquarium.py --not-a-valid-argument

      If you run this command, you’ll receive output like this:

      Output

      usage: aquarium.py [-h] tank aquarium.py: error: the following arguments are required: tank

      The output printed on the command line indicates that there was an error trying to run aquarium.py. The output indicates that the user needs to invoke aquarium.py with a tank argument. Something else you might notice is the -h in-between [] characters. This denotes that -h is an optional argument that you can provide as well.

      Now you’ll find out what happens when you call aquarium.py with the -h option. Try invoking aquarium.py with the -h argument on the command line by running:

      If you run this command, you’ll receive output like this:

      Output

      usage: aquarium.py [-h] tank List fish in aquarium. positional arguments: tank optional arguments: -h, --help show this help message and exit

      As you may have guessed, the -h option is short for, “help.” Running python3 aquarium.py -h (or, equivalently, the longer variant python3 aquarium.py --help) prints out the help text. The help text, effectively, is a longer version of the usage text that was outputted in the previous example when you supplied invalid arguments. Notably, the help text also includes the custom description string of List fish in an aquarium that you instantiated the ArgumentParser with earlier on in this tutorial.

      By default, when you write a CLI using argparse you’ll automatically get help and usage text that you can use to document your CLI for other developers.

      So far, you’ve written a CLI that accepts a required positional argument. In the next section you’ll add an optional argument to your interface to expand on its capabilities.

      Adding an Optional Argument

      Sometimes, it’s helpful to include optional arguments in your command-line interface. These options are typically prefixed with two dash characters, for example --some-option. Let’s rewrite aquarium.py with the following adjusted content that adds an --upper-case option to your CLI:

      aquarium.py

      import argparse
      
      tank_to_fish = {
          "tank_a": "shark, tuna, herring",
          "tank_b": "cod, flounder",
      }
      
      parser = argparse.ArgumentParser(description="List fish in aquarium.")
      parser.add_argument("tank", type=str)
      parser.add_argument("--upper-case", default=False, action="store_true")
      args = parser.parse_args()
      
      fish = tank_to_fish.get(args.tank, "")
      
      if args.upper_case:
          fish = fish.upper()
      
      print(fish)
      

      Try invoking aquarium.py with the new --upper-case argument by running the following:

      • python3 aquarium.py --upper-case tank_a

      If you run this command, you’ll receive output like this:

      Output

      SHARK, TUNA, HERRING

      The fish in tank_a are now outputted in upper case. You accomplished this by adding a new --upper-case option when you called parser.add_argument("--upper-case", default=False, action="store_true"). The "--upper-case" string is the name of the argument you’d like to add.

      If the --upper-case option isn’t provided by the user of the CLI, default=False ensures that its value is set to False by default. action="store_true" controls what happens when the --upper-case option is provided by the CLI user. There are a number of different possible strings supported by the action parameter, but "store_true" stores the value True into the argument, if it is provided on the command line.

      Note that, although the argument is two words separated by a dash (upper-case), argparse makes it available to your code as args.upper_case (with an underscore separator) after you call parser.parse_args(). In general, argparse converts any dashes in the provided arguments into underscores so that you have valid Python identifiers to reference after you call parse_args().

      As before, argparse automatically creates a --help option and documents your command-line interface (including the --upper-case option you just added).

      Try invoking aquarium.py with the --help option again to receive the updated help text:

      • python3 aquarium.py --help

      Your output will be similar to:

      Output

      usage: aquarium.py [-h] [--upper-case] tank List fish in aquarium. positional arguments: tank optional arguments: -h, --help show this help message and exit --upper-case

      argparse automatically documented the positional tank argument, the optional --upper-case option, and the built-in --help option as well.

      This help text is useful, but you can improve it with additional information to help users better understand how they can invoke your program. You’ll explore how to enhance the help text in the next section.

      Exposing Additional Help Text to Your Users

      Developers use the help text provided by your command-line interfaces to understand what your program is capable of and how they should use it. Let’s revise aquarium.py again so it includes better help text. You can specify argument-level information by specifying help strings in the add_argument calls:

      aquarium.py

      import argparse
      
      tank_to_fish = {
          "tank_a": "shark, tuna, herring",
          "tank_b": "cod, flounder",
      }
      
      parser = argparse.ArgumentParser(description="List fish in aquarium.")
      parser.add_argument("tank", type=str, help="Tank to print fish from.")
      parser.add_argument(
          "--upper-case",
          default=False,
          action="store_true",
          help="Upper case the outputted fish.",
      )
      args = parser.parse_args()
      
      fish = tank_to_fish[args.tank]
      
      if args.upper_case:
          fish = fish.upper()
      
      print(fish)
      

      Try invoking aquarium.py with the --help option again to receive the updated help text:

      • python3 aquarium.py --help

      Your output will be the following:

      Output

      usage: aquarium.py [-h] [--upper-case] tank List fish in aquarium. positional arguments: tank Tank to print fish from. optional arguments: -h, --help show this help message and exit --upper-case Upper case the outputted fish.

      In this latest output, notice that the tank positional argument and the --upper-case optional argument both include custom help text. You provided this extra help text by supplying strings to the help part of add_argument. (For example, parser.add_argument("tank", type=str, help="Tank to print fish from.").) argparse takes these strings and renders them for you in the help text output.

      You can improve your help text further by having argparse print out any default values you have defined.

      Displaying Default Values in Help Text

      If you use a custom formatter_class when you instantiate your ArgumentParser instance, argparse will include default values in the help text output. Try adding argparse.ArgumentDefaultsHelpFormatter as your ArgumentParser formatter class:

      aquarium.py

      import argparse
      
      tank_to_fish = {
          "tank_a": "shark, tuna, herring",
          "tank_b": "cod, flounder",
      }
      
      parser = argparse.ArgumentParser(
          description="List fish in aquarium.",
          formatter_class=argparse.ArgumentDefaultsHelpFormatter,
      )
      parser.add_argument("tank", type=str, help="Tank to print fish from.")
      parser.add_argument(
          "--upper-case",
          default=False,
          action="store_true",
          help="Upper case the outputted fish.",
      )
      args = parser.parse_args()
      
      fish = tank_to_fish[args.tank]
      
      if args.upper_case:
          fish = fish.upper()
      
      print(fish)
      

      Now, try invoking aquarium.py with the --help option again to check the updated help text:

      • python3 aquarium.py --help

      After running this command, you’ll receive output like this:

      Output

      usage: aquarium.py [-h] [--upper-case] tank List fish in aquarium. positional arguments: tank Tank to print fish from. optional arguments: -h, --help show this help message and exit --upper-case Upper case the outputted fish. (default: False)

      In this latest output, notice that the documentation for --upper-case ends with an indication of the default value for the --upper-case option (default: False). By including argparse.ArgumentDefaultsHelpFormatter as the formatter_class of your ArgumentParser, argparse automatically started rendering default value information in its help text.

      Conclusion

      The argparse module is a powerful part of the Python standard library that allows you to write command-line interfaces for your code. This tutorial introduced you to the foundations of argparse: you wrote a command-line interface that accepted positional and optional arguments, and exposed help text to the user.

      argparse supports many more feautures that you can use to write command-line programs with sophisticated sets of inputs and validations. From here, you can use the argparse module’s documentation to learn more about other available classes and utilities.



      Source link

      How To Write Doctests in Python


      Introduction

      Documentation and testing are core components of every productive software development process. Ensuring that code is thoroughly documented and tested not only ensures that a program runs as expected, but also supports collaboration across programmers as well as user adoption. A programmer can be well served by first writing documentation and then tests, before finally writing code. Following a process like this will ensure that the function one is coding (for example) is well thought out and addresses possible edge cases.

      Python’s standard library comes equipped with a test framework module called doctest. The doctest module programmatically searches Python code for pieces of text within comments that look like interactive Python sessions. Then, the module executes those sessions to confirm that the code referenced by a doctest runs as expected.

      Additionally, doctest generates documentation for our code, providing input-output examples. Depending on how you approach writing doctests, this can either be closer to either “‘literate testing’ or ‘executable documentation,’” as the Python Standard Library documentation explains.

      Doctest Structure

      A Python doctest is written as though it is a comment, with a series of three quotation marks in a row — """ — at the top and bottom of the doctest.

      Sometimes, doctests are written with an example of the function and the expected output, but it may be preferable to also include a comment on what the function is intended to do. Including a comment will ensure that you as the programmer have sharpened your goals, and a future person reading the code understands it well. Remember, the future programmer reading the code may very well be you.

      The following is a mathematical example of a doctest for a function such as add(a, b) that adds two numbers together:

      """
      Given two integers, return the sum.
      
      >>> add(2, 3)
      5
      """
      

      In this example we have a line of explanation, and one example of the add() function with two integers for input values. If in the future you want the function to be able to add more than two integers, you would need to revise the doctest to match the function’s inputs.

      So far, this doctest is very readable to a human. You can further iterate on this docstring by including machine-readable parameters and a return description to explicate each variable coming in and out of the function.

      Here, we’ll add docstrings for the two arguments that are passed to the function and the value that is returned. The docstring will note the data types for each of the values — the parameter a, the parameter b, and the returned value — in this case they are all integers.

      """
      Given two integers, return the sum.
      
      :param a: int
      :param b: int
      :return: int
      
      >>> add(2, 3)
      5
      """
      

      This doctest is now ready to be incorporated into a function and tested.

      Incorporating a Doctest into a Function

      Doctests sit within a function after the def statement and before the code of the function. As this follows the initial definition of the function, it will be indented following Python’s conventions.

      This short function indicates how the doctest is incorporated.

      def add(a, b):
          """
          Given two integers, return the sum.
      
          :param a: int
          :param b: int
          :return: int
      
          >>> add(2, 3)
          5
          """
          return a + b
      
      

      In our short example, we only have this one function in our program, so now we will have to import the doctest module and have a call statement for the doctest to run.

      We’ll be adding the following lines before and after our function:

      import doctest 
      ...
      doctest.testmod()
      

      At this point, let’s test it on the Python shell rather than saving it to a program file right now. You can access a Python 3 shell on your command line terminal of choice (including IDE terminal) with the python3 command (or python if you’re using a virtual shell).

      If you go this route, once you press ENTER, you’ll receive output similar to the following:

      Output

      Type "help", "copyright", "credits" or "license" for more information. >>>

      You’ll be able to start typing code after the >>> prompt.

      Our complete example code, including the add() function with a doctest, docstrings, and a call to invoke the doctest follows. You can paste it into your Python interpreter to try it out:

      import doctest
      
      
      def add(a, b):
          """
          Given two integers, return the sum.
      
          :param a: int
          :param b: int
          :return: int
      
          >>> add(2, 3)
          5
          """
          return a + b
      
      doctest.testmod()
      
      

      Once you run the code, you’ll receive the following output:

      Output

      TestResults(failed=0, attempted=1)

      This means that our program ran as expected!

      If you modify the program above so that the return a + b line is instead return a * b, which modifies the function to multiply integers and return their product instead, you’ll receive a failure notice:

      Output

      ********************************************************************** File "__main__", line 9, in __main__.add Failed example: add(2, 3) Expected: 5 Got: 6 ********************************************************************** 1 items had failures: 1 of 1 in __main__.add ***Test Failed*** 1 failures. TestResults(failed=1, attempted=1)

      From the output above, you can begin to understand how useful the doctest module is as it fully describes what happened when a and b were multiplied instead of added, returning the product of 6 in the example case.

      You may want to experiment with more than one example. Let’s try with an example where both variables a and b contain the value of 0, and change the program back to addition with the + operator.

      import doctest
      
      
      def add(a, b):
          """
          Given two integers, return the sum.
      
          :param a: int
          :param b: int
          :return: int
      
          >>> add(2, 3)
          5
          >>> add(0, 0)
          0
          """
          return a + b
      
      doctest.testmod()
      
      

      Once we run this, we’ll receive the following feedback from the Python interpreter:

      Output

      TestResults(failed=0, attempted=2)

      Here, the output indicates that the doctest attempted two tests, on both lines of add(2, 3) and add(0, 0) and that both passed.

      If, again, we change the program to use the * operator for multiplication rather than the + operator, we can learn that edge cases are important when working with the doctest module, because the second example of add(0, 0) will return the same value whether it is addition or multiplication.

      import doctest
      
      
      def add(a, b):
          """
          Given two integers, return the sum.
      
          :param a: int
          :param b: int
          :return: int
      
          >>> add(2, 3)
          5
          >>> add(0, 0)
          0
          """
          return a * b
      
      doctest.testmod()
      
      

      The following output is returned:

      Output

      ********************************************************************** File "__main__", line 9, in __main__.add Failed example: add(2, 3) Expected: 5 Got: 6 ********************************************************************** 1 items had failures: 1 of 2 in __main__.add ***Test Failed*** 1 failures. TestResults(failed=1, attempted=2)

      When we modify the program, only one of the examples fails, but it is fully described as before. If we had started with the add(0, 0) example rather than the add(2, 3) example, we may not have noticed that there were opportunities for failure when small components of our program change.

      Doctests in Programming Files

      So far, we have used an example on the Python interactive terminal. Let’s now use it in a programming file that will count the number of vowels in a single word.

      In a program, we can import and call the doctest module in our if __name__ == "__main__": clause at the bottom of our programming file.

      We’ll create a new file — counting_vowels.py — in our text editor, you can use nano on the command line, like so:

      We can begin with defining our function count_vowels and passing the parameter of word to the function.

      counting_vowels.py

      def count_vowels(word):
      
      

      Before we write the body of the function, let’s explain what we want the function to do in our doctest.

      counting_vowels.py

      def count_vowels(word):
          """
          Given a single word, return the total number of vowels in that single word.
      
      

      So far so good, we are being pretty specific. Let’s flesh this out with the data type of the parameter word and the data type we want returned. In the first case it’s a string, in the second case it’s an integer.

      counting_vowels.py

      def count_vowels(word):
          """
          Given a single word, return the total number of vowels in that single word.
      
          :param word: str
          :return: int
      

      Next, let’s find examples. Think of a single word that has vowels, and then type it into the docstring.

      Let’s choose the word 'Cusco' for the city in Peru. How many vowels are in “Cusco”? In English, vowels are often considered to be a, e, i, o, and u. So here we will count u and o as the vowels.

      We’ll add the test for Cusco and the return of 2 as the integer into our program.

      counting_vowels.py

      def count_vowels(word):
          """
          Given a single word, return the total number of vowels in that single word.
      
          :param word: str
          :return: int
      
          >>> count_vowels('Cusco')
          2
      

      Again, it’s a good idea to have more than one example. Let’s have another example with more vowels. We’ll go with 'Manila' for the city in the Philippines.

      counting_vowels.py

      def count_vowels(word):
          """
          Given a single word, return the total number of vowels in that single word.
      
          :param word: str
          :return: int
      
          >>> count_vowels('Cusco')
          2
      
          >>> count_vowels('Manila')
          3
          """
      

      Those doctests look great, now we can code our program.

      We’ll start with initializing a variable — total_vowels to hold the vowel count. Next, we’ll create a for loop to iterate across the letters of the word string, and then include a conditional statement to check whether each letter is a vowel. We’ll increase the vowel count through the loop, then return the total number of vowels in the word to the total_values variable. Our program should be similar to this, without the doctest:

      def count_vowels(word):
          total_vowels = 0
          for letter in word:
              if letter in 'aeiou':
                  total_vowels += 1
          return total_vowels
      

      If you need more guidance on these topics, please check out our How To Code in Python book or complementary series.

      Next, we’ll add our main clause at the bottom of the program and import and run the doctest module:

      if __name__ == "__main__":
          import doctest
          doctest.testmod()
      

      At this point, here is our program:

      counting_vowels.py

      def count_vowels(word):
          """
          Given a single word, return the total number of vowels in that single word.
      
          :param word: str
          :return: int
      
          >>> count_vowels('Cusco')
          2
      
          >>> count_vowels('Manila')
          3
          """
          total_vowels = 0
          for letter in word:
              if letter in 'aeiou':
                  total_vowels += 1
          return total_vowels
      
      if __name__ == "__main__":
          import doctest
          doctest.testmod()
      
      

      We can run the program by using the python (or python3 depending on your virtual environment) command:

      • python counting_vowels.py

      If your program is identical to the above, all the tests should have passed and you will not receive any output. This means that the tests passed. This silent feature is useful when you are running programs for other purposes. If you are running specifically to test, you may want to use the -v flag, as below.

      • python counting_vowels.py -v

      When you do, you should receive this output:

      Output

      Trying: count_vowels('Cusco') Expecting: 2 ok Trying: count_vowels('Manila') Expecting: 3 ok 1 items had no tests: __main__ 1 items passed all tests: 2 tests in __main__.count_vowels 2 tests in 2 items. 2 passed and 0 failed. Test passed.

      Excellent! The test has passed. Still, our code may not be quite optimized for all edge cases yet. Let’s learn how to use doctests to strengthen our code.

      Using Doctests to Improve Code

      At this point, we have a working program. Maybe it is not the best program it can be yet, so let’s try to find an edge case. What if we add an upper-case vowel?

      Add another example in the doctest, this time let’s try 'Istanbul' for the city in Turkey. Like Manila, Istanbul also has three vowels.

      Below is our updated program with the new example.

      counting_vowels.py

      def count_vowels(word):
          """
          Given a single word, return the total number of vowels in that single word.
      
          :param word: str
          :return: int
      
          >>> count_vowels('Cusco')
          2
      
          >>> count_vowels('Manila')
          3
      
          >>> count_vowels('Istanbul')
          3
          """
          total_vowels = 0
          for letter in word:
              if letter in 'aeiou':
                  total_vowels += 1
          return total_vowels
      
      if __name__ == "__main__":
          import doctest
          doctest.testmod()
      
      

      Let’s run the program again.

      • python counting_vowels.py

      We have identified an edge case! Below is the output we have received:

      Output

      ********************************************************************** File "counting_vowels.py", line 14, in __main__.count_vowels Failed example: count_vowels('Istanbul') Expected: 3 Got: 2 ********************************************************************** 1 items had failures: 1 of 3 in __main__.count_vowels ***Test Failed*** 1 failures.

      The output above indicates that the test on 'Istanbul' is the one that failed. We told the program we were expecting three vowels to be counted, but instead the program counted only two. What went wrong here?

      In our line if letter in 'aeiou': we have only passed in lower-case vowels. We can modify our 'aeiou' string to be 'AEIOUaeiou' to count both upper- and lower-case vowels, or we can do something more elegant, and convert our value stored in word to lower-case with word.lower(). Let’s do the latter.

      counting_vowels.py

      def count_vowels(word):
          """
          Given a single word, return the total number of vowels in that single word.
      
          :param word: str
          :return: int
      
          >>> count_vowels('Cusco')
          2
      
          >>> count_vowels('Manila')
          3
      
          >>> count_vowels('Istanbul')
          3
          """
          total_vowels = 0
          for letter in word.lower():
              if letter in 'aeiou':
                  total_vowels += 1
          return total_vowels
      
      if __name__ == "__main__":
          import doctest
          doctest.testmod()
      
      

      Now, when we run the program, all tests should pass. You can confirm again by running python counting_vowels.py -v with the verbose flag.

      Still, this probably is not the best program it can be, and it may not be considering all edge cases.

      What if we pass the string value 'Sydney' — for the city in Australia — to word? Would we expect three vowels or one? In English, y is sometimes considered to be a vowel. Additionally, what would happen if you use the value 'Würzburg' — for the city in Germany — would the 'ü' count? Should it? How will you handle other non-English words? How will you handle words that use different character encodings, such as those available in UTF-16 or UTF-32?

      As a software developer, you will sometimes need to make tricky decisions like deciding which characters should be counted as vowels in the example program. Sometimes there may not be a right or wrong answer. In many cases, you will not consider the full scope of possibilities. The doctest module is therefore a good tool to start to think through possible edge cases and capture preliminary documentation, but ultimately you will need human user testing — and very likely collaborators — to build robust programs that serve everyone.

      Conclusion

      This tutorial introduced the doctest module as not only a method for testing and documenting software, but also as a way to think through programming before you begin, by first documenting it, then testing it, then writing the code.

      Not writing tests could lead not only to bugs but software failure. Getting in the habit of writing tests prior to writing code can support productive software that serves other developers and end users alike.

      If you would like to learn more about testing and debugging, check out our “Debugging Python Programs” series. We also have a free eBook on How To Code in Python and another on Python Machine Learning Projects.



      Source link