Skip to content

Commit ec16a65

Browse files
committed
Chapter 8: Production Item 54: Consider module-scoped code to configure deployment environments
1 parent 0c4831f commit ec16a65

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed

item_54_consider_module_scoped_code.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,112 @@
1515

1616
# Item 54: Consider module-scoped code to configure deployment environments
1717

18+
# A deployment environment is a configuration in which your program runs.
19+
# Every program has at least one deployment environment, the production
20+
# environment. The goal of writing a program in the first place is to put it
21+
# to work in the production environment and achieve some kind of outcome.
22+
23+
# Writing or modifying a program requires being able to run it on the computer
24+
# you use for developing. The configuration of your development may be much
25+
# different from your production environments have the same Python packages
26+
# installed. The trouble is that production environment often require many
27+
# external assumptions that are hard to reproduce in development environments.
28+
29+
# For example, say you want to run your program in a web server container and
30+
# give it access to a database. This means that very time you want to modify
31+
# your program's code, you need to run a server container, the database must
32+
# be set up properly, and your program needs to password for access. That's a
33+
# very high cost if all you're trying to do is verify that a one-line change
34+
# to your program works correctly.
35+
36+
# The best way to work around these issues is to override parts of your
37+
# program at startup time to provide different functionality depending on the
38+
# deployment environment. For example, you could have two different __main__
39+
# files, one for production and one for development.
40+
41+
# # dev_main.py
42+
# TESTING = True
43+
# import db_connection
44+
# db = db_connection.Database()
45+
46+
# # prod_main.py
47+
# TESTING = False
48+
# import db_connection
49+
# db = db_connection.Database()
50+
51+
# The only difference between the two files is the value of the TESTING
52+
# constant. Other modules in your program can then import the __main__ module
53+
# and use the value of TESTING to decide how they define their own attributes.
54+
55+
# # db_connection.py
56+
# import __main__
1857
#
58+
#
59+
# class TestingDatabase(object):
60+
# #...
61+
# pass
62+
#
63+
#
64+
# class RealDatabase(object):
65+
# #...
66+
# pass
67+
#
68+
#
69+
# if __main__.TESTING:
70+
# Database = TestingDatabase
71+
# else:
72+
# Database = RealDatabase
73+
74+
# The key behavior to notice here is that code running in module scope--not
75+
# inside any function or method--is just normal Python code. You can use an
76+
# if statement at the module level to decide how the module will define names.
77+
# This makes it easy to tailor modules to your various deployment
78+
# environments. You avoid having to reproduce costly assumptions like
79+
# database configurations when they aren't needed. You can inject fake or mock
80+
# implementations that ease interactive development and testing (see Item 56:
81+
# "Test everything with unittest")
82+
83+
# Note
84+
# Once your deployment environments get complicated, you should consider moving
85+
# them out of Python constants (like TESTING) and into dedicated configuration
86+
# files. Tools like the configparser built-in module let you maintain
87+
# production configurations separate from code, a distinction that's crucial for
88+
# collaborating with an operations team.
89+
90+
# This approach can be used for more than working around external assumptions.
91+
# For example, if you know that your program must work differently based on its
92+
# host platform, you can inspect the sys module before defining top-level
93+
# constructs in a module.
94+
95+
# db_connection.py
96+
import sys
97+
98+
99+
class Win32Database(object):
100+
#...
101+
pass
102+
103+
104+
class PosixDatabase(object):
105+
#...
106+
pass
107+
108+
109+
if sys.platform.startswith('win32'):
110+
Database = Win32Database
111+
else:
112+
Database = PosixDatabase
113+
114+
115+
# Similarly, you can use environment variable from os.environ to guide your
116+
# module definitions.
117+
118+
119+
# Things to remember
120+
121+
# 1. Programs often need to run in multiple deployment environments that each
122+
# have unique assumptions and configurations.
123+
# 2. You can tailor a module's contents to different deployment environments
124+
# by using normal Python statements in module scope.
125+
# 3. Module contents can be the product of any external condition, including
126+
# host introspection through the sys and os modules.

0 commit comments

Comments
 (0)