Variables
Last updated on 2025-04-16 | Edit this page
Estimated time: 20 minutes
Overview
Questions
- How can I eliminate redundancy in my SConscript files?
Objectives
- Use variables in SConscript files.
- Explain the benefits of decoupling configuration from computation.
Despite our efforts, our SConstruct still has repeated content,
i.e. the program we use to run our scripts, python
.
Additionally, if we renamed our scripts we’d have to hunt through our
SConstruct file in multiple places.
We can introduce Python variables after the import statements
in SConstruct
to hold our script name:
This is a variable assignment
- COUNT_SOURCE
is assigned the value
"countwords.py"
and behaves like (actually is) normal
Python variable assignment. The all capitals naming convention indicates
that the variable is intended for use as a setting or constant
value.
We can do the same thing with the interpreter language used to run the script:
Similar to the SCons special substitution variables, we can define
any number of per-task or per-builder substitution variables with
keyword arguments. The same ${...}
substitution syntax
tells SCons to replace a task action string variable with its value when
SCons is run.
Defining the variable LANGUAGE
in this way avoids
repeating python
in our SConstruct file, and allows us to
easily change how our script is run (e.g. we might want to use a
different version of Python and need to change python
to
python2
– or we might want to rewrite the script using
another language (e.g. switch from Python to R)).
In the count_words
pseudo-builder function we will
define optional arguments language
and
count_source
, which are defined to default as
LANGUAGE
and COUNT_SOURCE
respectively and
passed through as Command
task keyword arguments. This
tells SCons to replace the variable language
with its value
python
, and to replace the variable
count_source
with its value countwords.py
.
We will define and use the intermediate function keyword argument
variables instead of using the upper case variables directly to avoid
mixing up the function’s scope with the SConstruct
scope.
This is a detail of good practice in Python development, and since
SConscript files are Python code, you should follow the usual Python
practices and style guides wherever possible.
Use Variables
Update SConstruct
so that the .dat
rule
references the variable count_source
and
language
. Then do the same for the testzipf.py
script and the results.txt
rule, using
ZIPF_SOURCE
as the variable name.
This SConstruct file contains a solution to this challenge.
We place variables intended for use as configuration constants at the
top of an SConstruct file so they are easy to find and modify.
Alternatively, we can pull them out into a new file that just holds
variable definitions (i.e. delete them from the original SConstruct
file). Because SCons uses Python as the configuration language, we can
also move our custom builders and psuedo-builders. Let us create
scons_lesson_configuration.py
from the content below.
PYTHON
import pathlib
COUNT_SOURCE = "countwords.py"
LANGUAGE = "python"
ZIPF_SOURCE = "testzipf.py"
def count_words(env, data_file, language=LANGUAGE, count_source=COUNT_SOURCE):
"""Pseudo-builder to produce `.dat` targets from the `countwords.py` script
Assumes that the source text file is found in `books/{data_file}.txt`
:param env: SCons construction environment. Do not provide when using this
function with the `env.AddMethod` and `env.CountWords` access style.
:param data_file: String name of the data file to create.
"""
data_path = pathlib.Path(data_file)
text_file = pathlib.Path("books") / data_path.with_suffix(".txt")
target_nodes = env.Command(
target=[data_file],
source=[text_file, count_source],
action=["${language} ${count_source} ${SOURCES[0]} ${TARGET}"],
language=language,
count_source=count_source,
)
return target_nodes
An added benefit to moving our custom functions into a file with the
.py
extension is that we can use automated documentation
tools, such as Sphinx, to build
project documentation.
We can then import scons_lesson_configuration.py
into
the SConstruct file with a standard Python import:
Note that the above import statement merges the module namespace into
the SConstruct file namespace. We must be careful to avoid re-defining
variable and function names provided by
scons_lesson_configuration.py
in our
SConstruct
file, which would overwrite the names provided
by our module and lead to unexpected behavior.
We can re-run SCons to see that everything still works:
Where We Are
This SConstruct file and this Python module contain all of our work so far.
Key Points
- Define variables by assigning values to names with Python syntax
- Reference variables in action strings using SCons substitution
syntax
${...}
.