Introducing Python (2014)

Chapter 10. Systems

One thing a computer can do that most humans can’t is be sealed up in a cardboard box and sit in a warehouse.

— Jack Handey

In your everyday use of a computer, you do such things as list the contents of a folder or directory, create and remove files, and other housekeeping that’s necessary if not particularly exciting. You can also carry out these tasks, and more, within your own Python programs. Will this power drive you mad or cure your insomnia? We’ll see.

Python provides many system functions through a module named os (for “operating system”), which we’ll import for all the programs in this chapter.

Files

Python, like many other languages, patterned its file operations after Unix. Some functions, such as chown() and chmod(), have the same names, but there are a few new ones.

Create with open()

File Input/Output introduced you to the open() function and explains how you can use it to open a file or create one if it doesn’t already exist. Let’s create a text file called oops.txt:

>>> fout = open('oops.txt', 'wt')

>>> print('Oops, I created a file.', file=fout)

>>> fout.close()

With that done, let’s perform some tests with it.

Check Existence with exists()

To verify whether the file or directory is really there or you just imagined it, you can provide exists(), with a relative or absolute pathname, as demonstrated here:

>>> import os

>>> os.path.exists('oops.txt')

True

>>> os.path.exists('./oops.txt')

True

>>> os.path.exists('waffles')

False

>>> os.path.exists('.')

True

>>> os.path.exists('..')

True

Check Type with isfile()

The functions in this section check whether a name refers to a file, directory, or symbolic link (see the examples that follow for a discussion of links).

The first function we’ll look at, isfile, asks a simple question: is it a plain old law-abiding file?

>>> name = 'oops.txt'

>>> os.path.isfile(name)

True

Here’s how you determine a directory:

>>> os.path.isdir(name)

False

A single dot (.) is shorthand for the current directory, and two dots (..) stands for the parent directory. These always exist, so a statement such as the following will always report True:

>>> os.path.isdir('.')

True

The os module contains many functions dealing with pathnames (fully qualified filenames, starting with / and including all parents). One such function, isabs(), determines whether its argument is an absolute pathname. The argument doesn’t need to be the name of a real file:

>>> os.path.isabs(name)

False

>>> os.path.isabs('/big/fake/name')

True

>>> os.path.isabs('big/fake/name/without/a/leading/slash')

False

Copy with copy()

The copy() function comes from another module, shutil. This example copies the file oops.txt to the file ohno.txt:

>>> import shutil

>>> shutil.copy('oops.txt', 'ohno.txt')

The shutil.move() function copies a file and then removes the original.

Change Name with rename()

This function does exactly what it says. In the example here, it renames ohno.txt to ohwell.txt:

>>> import os

>>> os.rename('ohno.txt', 'ohwell.txt')

Link with link() or symlink()

In Unix, a file exists in one place, but it can have multiple names, called links. In low-level hard links, it’s not easy to find all the names for a given file. A symbolic link is an alternative method that stores the new name as its own file, making it possible for you to get both the original and new names at once. The link() call creates a hard link, and symlink() makes a symbolic link. The islink() function checks whether the file is a symbolic link.

Here’s how to make a hard link to the existing file oops.txt from the new file yikes.txt:

>>> os.link('oops.txt', 'yikes.txt')

>>> os.path.isfile('yikes.txt')

True

To create a symbolic link to the existing file oops.txt from the new file jeepers.txt, use the following:

>>> os.path.islink('yikes.txt')

False

>>> os.symlink('oops.txt', 'jeepers.txt')

>>> os.path.islink('jeepers.txt')

True

Change Permissions with chmod()

On a Unix system, chmod() changes file permissions.There are read, write, and execute permissions for the user (that’s usually you, if you created the file), the main group that the user is in, and the rest of the world. The command takes an intensely compressed octal (base 8) value that combines user, group, and other permissions. For instance, to make oops.txt only readable by its owner, type the following:

>>> os.chmod('oops.txt', 0o400)

If you don’t want to deal with cryptic octal values and would rather deal with (slightly) obscure cryptic symbols, you can import some constants from the stat module and use a statement such as the following:

>>> import stat

>>> os.chmod('oops.txt', stat.S_IRUSR)

Change Ownership with chown()

This function is also Unix/Linux/Mac–specific. You can change the owner and/or group ownership of a file by specifying the numeric user ID (uid) and group ID (gid):

>>> uid = 5

>>> gid = 22

>>> os.chown('oops', uid, gid)

Get a Pathname with abspath()

This function expands a relative name to an absolute one. If your current directory is /usr/gaberlunzie and the file oops.txt is there, also, you can type the following:

>>> os.path.abspath('oops.txt')

'/usr/gaberlunzie/oops.txt'

Get a symlink Pathname with realpath()

In one of the earlier sections, we made a symbolic link to oops.txt from the new file jeepers.txt. In circumstances such as this, you can get the name of oops.txt from jeepers.txt by using the realpath() function, as shown here:

>>> os.path.realpath('jeepers.txt')

'/usr/gaberlunzie/oops.txt'

Delete a File with remove()

In this snippet, we use the remove() function and say farewell to oops.txt:

>>> os.remove('oops.txt')

>>> os.path.exists('oops.txt')

False

Directories

In most operating systems, files exist in a hierarchy of directories (more often called folders these days). The container of all of these files and directories is a file system (sometimes called a volume). The standard os module deals with operating specifics such as these and provides the following functions with which you can manipulate them.

Create with mkdir()

This example shows how to create a directory called poems to store that precious verse:

>>> os.mkdir('poems')

>>> os.path.exists('poems')

True

Delete with rmdir()

Upon second thought, you decide you don’t need that directory after all. Here’s how to delete it:

>>> os.rmdir('poems')

>>> os.path.exists('poems')

False

List Contents with listdir()

Okay, take two; let’s make poems again, with some contents:

>>> os.mkdir('poems')

Now, get a list of its contents (none so far):

>>> os.listdir('poems')

[]

Next, make a subdirectory:

>>> os.mkdir('poems/mcintyre')

>>> os.listdir('poems')

['mcintyre']

Create a file in this subdirectory (don’t type all these lines unless you really feel poetic; just make sure you begin and end with matching quotes, either single or tripled):

>>> fout = open('poems/mcintyre/the_good_man', 'wt')

>>> fout.write('''Cheerful and happy was his mood,

... He to the poor was kind and good,

... And he oft' times did find them food,

... Also supplies of coal and wood,

... He never spake a word was rude,

... And cheer'd those did o'er sorrows brood,

... He passed away not understood,

... Because no poet in his lays

... Had penned a sonnet in his praise,

... 'Tis sad, but such is world's ways.

... ''')

344

>>> fout.close()

Finally, let’s see what we have. It had better be there:

>>> os.listdir('poems/mcintyre')

['the_good_man']

Change Current Directory with chdir()

With this function, you can go from one directory to another. Let’s leave the current directory and spend a little time in poems:

>>> import os

>>> os.chdir('poems')

>>> os.listdir('.')

['mcintyre']

List Matching Files with glob()

The glob() function matches file or directory names by using Unix shell rules rather than the more complete regular expression syntax. Here are those rules:

§  * matches everything (re would expect .*)

§  ? matches a single character

§  [abc] matches character a, b, or c

§  [!abc] matches any character except a, b, or c

Try getting all files or directories that begin with m:

>>> import glob

>>> glob.glob('m*')

['mcintyre']

How about any two-letter files or directories?

>>> glob.glob('??')

[]

I’m thinking of an eight-letter word that begins with m and ends with e:

>>> glob.glob('m??????e')

['mcintyre']

What about anything that begins with a k, l, or m, and ends with e?

>>> glob.glob('[klm]*e')

['mcintyre']

Programs and Processes

When you run an individual program, your operating system creates a single process. It uses system resources (CPU, memory, disk space) and data structures in the operating system’s kernel (file and network connections, usage statistics, and so on). A process is isolated from other processes—it can’t see what other processes are doing or interfere with them.

The operating system keeps track of all the running processes, giving each a little time to run and then switching to another, with the twin goals of spreading the work around fairly and being responsive to the user. You can see the state of your processes with graphical interfaces such as the Mac’s Activity Monitor (OS X), or Task Manager on Windows-based computers.

You can also access process data from your own programs. The standard library’s os module provides a common way of accessing some system information. For instance, the following functions get the process ID and the current working directory of the running Python interpreter:

>>> import os

>>> os.getpid()

76051

>>> os.getcwd()

'/Users/williamlubanovic'

And these get my user ID and group ID:

>>> os.getuid()

501

>>> os.getgid()

20

Create a Process with subprocess

All of the programs that you’ve seen here so far have been individual processes. You can start and stop other existing programs from Python by using the standard library’s subprocess module. If you just want to run another program in a shell and grab whatever output it created (both standard output and standard error output), use the getoutput() function. Here, we’ll get the output of the Unix date program:

>>> import subprocess

>>> ret = subprocess.getoutput('date')

>>> ret

'Sun Mar 30 22:54:37 CDT 2014'

You won’t get anything back until the process ends. If you need to call something that might take a lot of time, see the discussion on concurrency in Concurrency. Because the argument to getoutput() is a string representing a complete shell command, you can include arguments, pipes, <and > I/O redirection, and so on:

>>> ret = subprocess.getoutput('date -u')

>>> ret

'Mon Mar 31 03:55:01 UTC 2014'

Piping that output string to the wc command counts one line, six “words,” and 29 characters:

>>> ret = subprocess.getoutput('date -u | wc')

>>> ret

'       1       6      29'

A variant method called check_output() takes a list of the command and arguments. By default it only returns standard output as type bytes rather than a string and does not use the shell:

>>> ret = subprocess.check_output(['date', '-u'])

>>> ret

b'Mon Mar 31 04:01:50 UTC 2014\n'

To show the exit status of the other program, getstatusoutput() returns a tuple with the status code and output:

>>> ret = subprocess.getstatusoutput('date')

>>> ret

(0, 'Sat Jan 18 21:36:23 CST 2014')

If you don’t want to capture the output but might want to know its exit status, use call():

>>> ret = subprocess.call('date')

Sat Jan 18 21:33:11 CST 2014

>>> ret

0

(In Unix-like systems, 0 is usually the exit status for success.)

That date and time was printed to output but not captured within our program. So, we saved the return code as ret.

You can run programs with arguments in two ways. The first is to specify them in a single string. Our sample command is date -u, which prints the current date and time in UTC (you’ll read more about UTC in a few pages):

>>> ret = subprocess.call('date -u', shell=True)

Tue Jan 21 04:40:04 UTC 2014

You need that shell=True to recognize the command line date -u, splitting it into separate strings and possibly expanding any wildcard characters such as * (we didn’t use any in this example).

The second method makes a list of the arguments, so it doesn’t need to call the shell:

>>> ret = subprocess.call(['date', '-u'])

Tue Jan 21 04:41:59 UTC 2014

Create a Process with multiprocessing

You can run a Python function as a separate process or even run multiple independent processes in a single program with the multiprocessing module. Here’s a short example that does nothing useful; save it as mp.py and then run it by typing python mp.py:

import multiprocessing

import os

def do_this(what):

    whoami(what)

def whoami(what):

    print("Process %s says: %s" % (os.getpid(), what))

if __name__ == "__main__":

    whoami("I'm the main program")

    for n inrange(4):

        p = multiprocessing.Process(target=do_this,

          args=("I'm function %s" % n,))

        p.start()

When I run this, my output looks like this:

Process 6224 says: I'm the main program

Process 6225 says: I'm function 0

Process 6226 says: I'm function 1

Process 6227 says: I'm function 2

Process 6228 says: I'm function 3

The Process() function spawned a new process and ran the do_this() function in it. Because we did this in a loop that had four passes, we generated four new processes that executed do_this() and then exited.

The multiprocessing module has more bells and whistles than a clown on a calliope. It’s really intended for those times when you need to farm out some task to multiple processes to save overall time; for example, downloading web pages for scraping, resizing images, and so on. It includes ways to queue tasks, enable intercommunication among processes, and wait for all the processes to finish. Concurrency delves into some of these details.

Kill a Process with terminate()

If you created one or more processes and want to terminate one for some reason (perhaps it’s stuck in a loop, or maybe you’re bored, or you want to be an evil overlord), use terminate(). In the example that follows, our process would count to a million, sleeping at each step for a second, and printing an irritating message. However, our main program runs out of patience in five seconds and nukes it from orbit:

import multiprocessing

import time

import os

def whoami(name):

    print("I'm %s, in process %s" % (name, os.getpid()))

def loopy(name):

    whoami(name)

    start = 1

    stop = 1000000

    for num inrange(start, stop):

        print("\tNumber %s of %s. Honk!" % (num, stop))

        time.sleep(1)

if __name__ == "__main__":

    whoami("main")

    p = multiprocessing.Process(target=loopy, args=("loopy",))

    p.start()

    time.sleep(5)

    p.terminate()

When I run this program, I get the following:

I'm main, in process 97080

I'm loopy, in process 97081

    Number 1 of 1000000. Honk!

    Number 2 of 1000000. Honk!

    Number 3 of 1000000. Honk!

    Number 4 of 1000000. Honk!

    Number 5 of 1000000. Honk!

Calendars and Clocks

Programmers devote a surprising amount of effort to dates and times. Let’s talk about some of the problems they encounter, and then get to some best practices and tricks to make the situation a little less messy.

Dates can be represented in many ways—too many ways, actually. Even in English with the Roman calendar, you’ll see many variants of a simple date:

§  July 29 1984

§  29 Jul 1984

§  29/7/1984

§  7/29/1984

Among other problems, date representations can be ambiguous. In the previous examples, it’s easy to determine that 7 stands for the month and 29 is the day of the month, largely because months don’t go to 29. But how about 1/6/2012? Is that referring to January 6 or June 1?

The month name varies by language within the Roman calendar. Even the year and month can have a different definition in other cultures.

Leap years are another wrinkle. You probably know that every four years is a leap year (and the summer Olympics and the American presidential election). Did you also know that every 100 years is not a leap year, but that every 400 years is? Here’s code to test various years for leapiness:

>>> import calendar

>>> calendar.isleap(1900)

False

>>> calendar.isleap(1996)

True

>>> calendar.isleap(1999)

False

>>> calendar.isleap(2000)

True

>>> calendar.isleap(2002)

False

>>> calendar.isleap(2004)

True

Times have their own sources of grief, especially because of time zones and daylight savings time. If you look at a time zone map, the zones follow political and historic boundaries rather than every 15 degrees (360 degrees / 24) of longitude. And countries start and end daylight saving times on different days of the year. In fact, countries in the southern hemisphere advance their clocks when the northern hemisphere is winding them back, and vice versa. (If you think about it a bit, you will see why.)

Python’s standard library has many date and time modules: datetime, time, calendar, dateutil, and others. There’s some overlap, and it’s a bit confusing.

The datetime Module

Let’s begin by investigating the standard datetime module. It defines four main objects, each with many methods:

§  date for years, months, and days

§  time for hours, minutes, seconds, and fractions

§  datetime for dates and times together

§  timedelta for date and/or time intervals

You can make a date object by specifying a year, month, and day. Those values are then available as attributes:

>>> from datetime import date

>>> halloween = date(2014, 10, 31)

>>> halloween

datetime.date(2014, 10, 31)

>>> halloween.day

31

>>> halloween.month

10

>>> halloween.year

2014

You can print a date with its isoformat() method:

>>> halloween.isoformat()

'2014-10-31'

The iso refers to ISO 8601, an international standard for representing dates and times. It goes from most general (year) to most specific (day). It also sorts correctly: by year, then month, then day. I usually pick this format for date representation in programs, and for filenames that save data by date. The next section describes the more complex strptime() and strftime() methods for parsing and formatting dates.

This example uses the today() method to generate today’s date:

>>> from datetime import date

>>> now = date.today()

>>> now

datetime.date(2014, 2, 2)

This one makes use of a timedelta object to add some time interval to a date:

>>> from datetime import timedelta

>>> one_day = timedelta(days=1)

>>> tomorrow = now + one_day

>>> tomorrow

datetime.date(2014, 2, 3)

>>> now + 17*one_day

datetime.date(2014, 2, 19)

>>> yesterday = now - one_day

>>> yesterday

datetime.date(2014, 2, 1)

The range of date is from date.min (year=1, month=1, day=1) to date.max (year=9999, month=12, day=31). As a result, you can’t use it for historic or astronomical calculations.

The datetime module’s time object is used to represent a time of day:

>>> from datetime import time

>>> noon = time(12, 0, 0)

>>> noon

datetime.time(12, 0)

>>> noon.hour

12

>>> noon.minute

0

>>> noon.second

0

>>> noon.microsecond

0

The arguments go from the largest time unit (hours) to the smallest (microseconds). If you don’t provide all the arguments, time assumes all the rest are zero. By the way, just because you can store and retrieve microseconds doesn’t mean you can retrieve time from your computer to the exact microsecond. The accuracy of subsecond measurements depends on many factors in the hardware and operating system.

The datetime object includes both the date and time of day. You can create one directly, such as the one that follows, which is for January 2, 2014, at 3:04 A.M., plus 5 seconds and 6 microseconds:

>>> from datetime import datetime

>>> some_day = datetime(2014, 1, 2, 3, 4, 5, 6)

>>> some_day

datetime.datetime(2014, 1, 2, 3, 4, 5, 6)

The datetime object also has an isoformat() method:

>>> some_day.isoformat()

'2014-01-02T03:04:05.000006'

That middle T separates the date and time parts.

datetime has a now() method with which you can get the current date and time:

>>> from datetime import datetime

>>> now = datetime.now()

>>> now

datetime.datetime(2014, 2, 2, 23, 15, 34, 694988)

14

>>> now.month

2

>>> now.day

2

>>> now.hour

23

>>> now.minute

15

>>> now.second

34

>>> now.microsecond

694988

You can merge a date object and a time object into a datetime object by using combine():

>>> from datetime import datetime, time, date

>>> noon = time(12)

>>> this_day = date.today()

>>> noon_today = datetime.combine(this_day, noon)

>>> noon_today

datetime.datetime(2014, 2, 2, 12, 0)

You can yank the date and time from a datetime by using the date() and time() methods:

>>> noon_today.date()

datetime.date(2014, 2, 2)

>>> noon_today.time()

datetime.time(12, 0)

Using the time Module

It is confusing that Python has a datetime module with a time object, and a separate time module. Furthermore, the time module has a function called—wait for it—time().

One way to represent an absolute time is to count the number of seconds since some starting point. Unix time uses the number of seconds since midnight on January 1, 1970.[8] This value is often called the epoch, and it is often the simplest way to exchange dates and times among systems.

The time module’s time() function returns the current time as an epoch value:

>>> import time

>>> now = time.time()

>>> now

1391488263.664645

If you do the math, you’ll see that it has been over one billion seconds since New Year’s, 1970. Where did the time go?

You can convert an epoch value to a string by using ctime():

>>> time.ctime(now)

'Mon Feb  3 22:31:03 2014'

In the next section, you’ll see how to produce more attractive formats for dates and times.

Epoch values are a useful least-common denominator for date and time exchange with different systems, such as JavaScript. Sometimes, though, you need actual days, hours, and so forth, which time provides as struct_time objects. localtime() provides the time in your system’s time zone, and gmtime() provides it in UTC:

>>> time.localtime(now)

time.struct_time(tm_year=2014, tm_mon=2, tm_mday=3, tm_hour=22, tm_min=31,

tm_sec=3, tm_wday=0, tm_yday=34, tm_isdst=0)

>>> time.gmtime(now)

time.struct_time(tm_year=2014, tm_mon=2, tm_mday=4, tm_hour=4, tm_min=31,

tm_sec=3, tm_wday=1, tm_yday=35, tm_isdst=0)

In my (Central) time zone, 22:31 was 04:31 of the next day in UTC (formerly called Greenwich time or Zulu time). If you omit the argument to localtime() or gmtime(), they assume the current time.

The opposite of these is mktime(), which converts a struct_time object to epoch seconds:

>>> tm = time.localtime(now)

>>> time.mktime(tm)

1391488263.0

This doesn’t exactly match our earlier epoch value of now() because the struct_time object preserves time only to the second.

Some advice: wherever possible, use UTC instead of time zones. UTC is an absolute time, independent of time zones. If you have a server, set its time to UTC; do not use local time.

Here’s some more advice (free of charge, no less): never use daylight savings time if you can avoid it. If you use daylight savings time, an hour disappears at one time of year (“spring ahead”) and occurs twice at another time (“fall back”). For some reason, many organizations use daylight savings in their computer systems, but are mystified every year by data duplicates and dropouts. It all ends in tears.

NOTE

Remember, your friends are UTC for times, and UTF-8 for strings (for more about UTF-8, see Chapter 7).

Read and Write Dates and Times

isoformat() is not the only way to write dates and times. You already saw the ctime() function in the time module, which you can use to convert epochs to strings:

>>> import time

>>> now = time.time()

>>> time.ctime(now)

'Mon Feb  3 21:14:36 2014'

You can also convert dates and times to strings by using strftime(). This is provided as a method in the datetime, date, and time objects, and as a function in the time module. strftime() uses format strings to specify the output, which you can see in Table 10-1.

Table 10-1. Outut specifiers for strftime()

Format string

Date/time unit

Range

%Y

year

1900-…

%m

month

01-12

%B

month name

January, …

%b

month abbrev

Jan, …

%d

day of month

01-31

%A

weekday name

Sunday, …

a

weekday abbrev

Sun, …

%H

hour (24 hr)

00-23

%I

hour (12 hr)

01-12

%p

AM/PM

AM, PM

%M

minute

00-59

%S

second

00-59

Numbers are zero-padded on the left.

Here’s the strftime() function provided by the time module. It converts a struct_time object to a string. We’ll first define the format string fmt and use it again later:

>>> import time

>>> fmt = "It's %A, %B %d, %Y, local time %I:%M:%S%p"

>>> t = time.localtime()

>>> t

time.struct_time(tm_year=2014, tm_mon=2, tm_mday=4, tm_hour=19,

tm_min=28, tm_sec=38, tm_wday=1, tm_yday=35, tm_isdst=0)

>>> time.strftime(fmt, t)

"It's Tuesday, February 04, 2014, local time 07:28:38PM"

If we try this with a date object, only the date parts will work, and the time defaults to midnight:

>>> from datetime import date

>>> some_day = date(2014, 7, 4)

>>> fmt = "It's %B %d, %Y, local time %I:%M:%S%p"

>>> some_day.strftime(fmt)

"It's Friday, July 04, 2014, local time 12:00:00AM"

For a time object, only the time parts are converted:

>>> from datetime import time

>>> some_time = time(10, 35)

>>> some_time.strftime(fmt)

"It's Monday, January 01, 1900, local time 10:35:00AM"

Clearly, you won’t want to use the day parts from a time object, because they’re meaningless.

To go the other way and convert a string to a date or time, use strptime() with the same format string. There’s no regular expression pattern matching; the nonformat parts of the string (without %) need to match exactly. Let’s specify a format that matches year-month-day, such as 2012-01-29. What happens if the date string you want to parse has spaces instead of dashes?

>>> import time

>>> fmt = "%Y-%m-%d"

>>> time.strptime("2012 01 29", fmt)

Traceback (most recent call last):

  File "<stdin>", line 1, in<module>

  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/

  python3.3/_strptime.py", line 494, in _strptime_time

    tt = _strptime(data_string, format)[0]

  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/

  python3.3/_strptime.py", line 337, in _strptime

    (data_string, format))

ValueError: time data '2012 01 29' does notmatch format '%Y-%m-%d'

If we feed strptime() some dashes, is it happy now?

>>> time.strptime("2012-01-29", fmt)

time.struct_time(tm_year=2012, tm_mon=1, tm_mday=29, tm_hour=0, tm_min=0,

tm_sec=0, tm_wday=6, tm_yday=29, tm_isdst=-1)

Yes.

Even if the string seems to match its format, an exception is raised if a value is out of range:

>>> time.strptime("2012-13-29", fmt)

Traceback (most recent call last):

  File "<stdin>", line 1, in<module>

  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/

  python3.3/_strptime.py", line 494, in _strptime_time

    tt = _strptime(data_string, format)[0]

  File "/Library/Frameworks/Python.framework/Versions/3.3/lib/

  python3.3/_strptime.py", line 337, in _strptime

    (data_string, format))

ValueError: time data '2012-13-29' does notmatch format '%Y-%m-%d'

Names are specific to your locale—internationalization settings for your operating system. To print different month and day names, change your locale by using setlocale(); its first argument is locale.LC_TIME for dates and times, and the second is a string combining the language and country abbreviation. Let’s invite some international friends to a Halloween party. We’ll print the month, day, and day of week in US English, French, German, Spanish, and Icelandic. (What? You think Icelanders don’t enjoy a good party as much as anyone else? They even have real elves.)

>>> import locale

>>> from datetime import date

>>> halloween = date(2014, 10, 31)

>>> for lang_country in ['en_us', 'fr_fr', 'de_de', 'es_es', 'is_is',]:

...     locale.setlocale(locale.LC_TIME, lang_country)

...     halloween.strftime('%A, %B %d')

...

'en_us'

'Friday, October 31'

'fr_fr'

'Vendredi, octobre 31'

'de_de'

'Freitag, Oktober 31'

'es_es'

'viernes, octubre 31'

'is_is'

'föstudagur, október 31'

>>> 

Where do you find these magic values for lang_country? This is a bit wonky, but you can try this to get all of them (there are a few hundred):

>>> import locale

>>> names = locale.locale_alias.keys()

From names, let’s get just locale names that seem to work with setlocale(), such as the ones we used in the preceding example—a two-character language code followed by an underscore and a two-character country code:

>>> good_names = [name for name innames if \

len(name) == 5 andname[2] == '_']

What do the first five look like?

>>> good_names[:5]

['sr_cs', 'de_at', 'nl_nl', 'es_ni', 'sp_yu']

So, if you wanted all the German language locales, try this:

>>> de = [name for name ingood_names if name.startswith('de')]

>>> de

['de_at', 'de_de', 'de_ch', 'de_lu', 'de_be']

Alternative Modules

If you find the standard library modules confusing, or lacking a particular conversion that you want, there are many third-party alternatives. Here are just a few of them:

arrow

This combines many date and time functions with a simple API.

dateutil

This module parses almost any date format and handles relative dates and times well.

iso8601

This fills in gaps in the standard library for the ISO8601 format.

fleming

This module offers many time zone functions.

Things to Do

10.1 Write the current date as a string to the text file today.txt.

10.2 Read the text file today.txt into the string today_string.

10.3 Parse the date from today_string.

10.4 List the files in your current directory.

10.5 List the files in your parent directory.

10.6 Use multiprocessing to create three separate processes. Make each one wait a random number of seconds between one and five, print the current time, and then exit.

10.7 Create a date object of your day of birth.

10.8 What day of the week was your day of birth?

10.9 When will you be (or when were you) 10,000 days old?


[8This starting point is roughly when Unix was born.