Friday, May 3, 2024
HomePythonExecute Dynamically Generated Code – Actual Python

Execute Dynamically Generated Code – Actual Python


Python’s built-in exec() perform lets you execute arbitrary Python code from a string or compiled code enter.

The exec() perform could be useful when you must run dynamically generated Python code, however it may be fairly harmful if you happen to use it carelessly. On this tutorial, you’ll study not solely the way to use exec(), however simply as importantly, when it’s okay to make use of this perform in your code.

On this tutorial, you’ll learn to:

  • Work with Python’s built-in exec() perform
  • Use exec() to execute code that comes as strings or compiled code objects
  • Assess and reduce the safety dangers related to utilizing exec() in your code

Moreover, you’ll write just a few examples of utilizing exec() to resolve completely different issues associated to dynamic code execution.

To get essentially the most out of this tutorial, you have to be aware of Python’s namespaces and scope, and strings. You also needs to be aware of a few of Python’s built-in capabilities.

Attending to Know Python’s exec()

Python’s built-in exec() perform lets you execute any piece of Python code. With this perform, you possibly can execute dynamically generated code. That’s the code that you simply learn, auto-generate, or acquire throughout your program’s execution. Usually, it’s a string.

The exec() perform takes a chunk of code and executes it as your Python interpreter would. Python’s exec() is like eval() however much more highly effective and susceptible to safety points. Whereas eval() can solely consider expressions, exec() can execute sequences of statements, in addition to imports, perform calls and definitions, class definitions and instantiations, and extra. Primarily, exec() can execute a whole absolutely featured Python program.

The signature of exec() has the next type:

exec(code [, globals [, locals]])

The perform executes code, which could be both a string containing legitimate Python code or a compiled code object.

If code is a string, then it’s parsed as a collection of Python statements, which is then internally compiled into bytecode, and at last executed, until a syntax error happens throughout the parsing or compilation step. If code holds a compiled code object, then it’s executed immediately, making the method a bit extra environment friendly.

The globals and locals arguments let you present dictionaries representing the world and native namespaces through which exec() will run the goal code.

The exec() perform’s return worth is None, most likely as a result of not each piece of code has a last, distinctive, and concrete end result. It could simply have some unwanted effects. This habits notably differs from eval(), which returns the results of the evaluated expression.

To get an preliminary feeling of how exec() works, you possibly can create a rudimentary Python interpreter with two strains of code:

>>>

>>> whereas True:
...     exec(enter("->> "))
...

->> print("Howdy, World!")
Howdy, World!

->> import this
The Zen of Python, by Tim Peters

Stunning is best than ugly.
Express is best than implicit.
Easy is best than complicated.
    ...

->> x = 10
->> if 1 <= x <= 10: print(f"{x} is between 1 and 10")
10 is between 1 and 10

On this instance, you utilize an infinite whereas loop to imitate the habits of a Python interpreter or REPL. Contained in the loop, you utilize enter() to get the consumer’s enter on the command line. You then use exec() to course of and run the enter.

This instance showcases what’s arguably the primary use case of exec(): executing code that involves you as a string.

You’ll generally use exec() when you must dynamically run code that comes as a string. For instance, you possibly can write a program that generates strings containing legitimate Python code. You’ll be able to construct these strings from elements that you simply acquire at completely different moments in your program’s execution. You can even use the consumer’s enter or another enter supply to assemble these strings.

When you’ve constructed the goal code as strings, then you should use exec() to execute them as you’ll execute any Python code.

On this state of affairs, you possibly can not often be sure of what your strings will comprise. That’s one cause why exec() implies severe safety dangers. That is notably true if you happen to’re utilizing untrusted enter sources, like a consumer’s direct enter, in constructing your code.

In programming, a perform like exec() is an extremely highly effective software as a result of it lets you write packages that generate and execute new code dynamically. To generate this new code, your packages will use info out there at runtime solely. To run the code, your packages will use exec().

Nevertheless, with nice energy comes nice accountability. The exec() perform implies severe safety dangers, as you’ll study quickly. So, you need to keep away from utilizing exec() more often than not.

Within the following sections, you’ll learn the way exec() works and the way to use this perform for executing code that comes as strings or as compiled code objects.

Operating Code From a String Enter

The most typical strategy to name exec() is with code that comes from a string-based enter. To construct this string-based enter, you should use:

  • Single strains of code or one-liner code snippets
  • A number of strains of code separated by semicolons
  • A number of strains of code separated by newline characters
  • A number of strains of code inside triple-quoted strings and with correct indentation

A one-liner program consists of a single line of code that performs a number of actions directly. Say that you’ve got a sequence of numbers, and also you need to construct a brand new sequence containing the sum of squares of all of the even numbers in an enter sequence.

To resolve this downside, you should use the next one-liner code:

>>>

>>> numbers = [2, 3, 7, 4, 8]

>>> sum(quantity**2 for quantity in numbers if quantity % 2 == 0)
84

Within the highlighted line, you utilize a generator expression to compute the sq. worth of all of the even numbers within the enter sequence of values. You then use sum() to compute the entire sum of squares.

To run this code with exec(), you simply want to rework your one-liner code right into a single-line string:

>>>

>>> exec("end result = sum(quantity**2 for quantity in numbers if quantity % 2 == 0)")
>>> end result
84

On this instance, you specific the one-liner code as a string. You then feed this string into exec() for execution. The one distinction between your authentic code and the string is that the latter shops the computation lead to a variable for later entry. Keep in mind that exec() returns None slightly than a concrete execution end result. Why? As a result of not each piece of code has a last distinctive end result.

Python lets you write a number of statements in a single line of code, utilizing semicolons to separate them. Regardless that this apply is discouraged, nothing will cease you from doing one thing like this:

>>>

>>> title = enter("Your title: "); print(f"Howdy, {title}!")
Your title: Leodanis
Howdy, Leodanis!

You should use semicolons to separate a number of statements and construct a single-line string that serves as an argument to exec(). Right here’s how:

>>>

>>> exec("title = enter('Your title: '); print(f'Howdy, {title}!')")
Your title: Leodanis
Howdy, Leodanis!

The concept of this instance is that you may mix a number of Python statements right into a single-line string through the use of semicolons to separate them. Within the instance, the primary assertion takes the consumer’s enter, whereas the second assertion prints a greeting message to the display.

You can even combination a number of statements in a single-line string utilizing the newline character, n:

>>>

>>> exec("title = enter('Your title: ')nprint(f'Howdy, {title}!')")
Your title: Leodanis
Howdy, Leodanis!

The newline character makes exec() perceive your single-line string as a multiline set of Python statements. Then exec() runs the aggregated statements in a row, which works like a multiline code file.

The ultimate strategy to constructing a string-based enter for feeding exec() is to make use of triple-quoted strings. This strategy is arguably extra versatile and lets you generate string-based enter that appears and works like regular Python code.

It’s essential to notice that this strategy requires you to make use of correct indentation and code formatting. Contemplate the next instance:

>>>

>>> code = """
... numbers = [2, 3, 7, 4, 8]
...
... def is_even(quantity):
...     return quantity % 2 == 0
...
... even_numbers = [number for number in numbers if is_even(number)]
...
... squares = [number**2 for number in even_numbers]
...
... end result = sum(squares)
...
... print("Unique information:", numbers)
... print("Even numbers:", even_numbers)
... print("Sq. values:", squares)
... print("Sum of squares:", end result)
... """

>>> exec(code)
Unique information: [2, 3, 7, 4, 8]
Even numbers: [2, 4, 8]
Sq. values: [4, 16, 64]
Sum of squares: 84

On this instance, you utilize a triple-quoted string to offer the enter to exec(). Observe that this string appears to be like like all common piece of Python code. It makes use of acceptable indentation, naming fashion, and formatting. The exec() perform will perceive and execute this string as an everyday Python code file.

It is best to notice that whenever you go a string with code to exec(), the perform will parse and compile the goal code into Python bytecode. In all instances, the enter string ought to comprise legitimate Python code.

If exec() finds any invalid syntax throughout the parsing and compilation steps, then the enter code received’t run:

>>>

>>> exec("print('Howdy, World!)")
Traceback (most up-to-date name final):
  File "<stdin>", line 1, in <module>
    File "<string>", line 1
      print('Howdy, World!)
            ^
SyntaxError: unterminated string literal (detected at line 1)

On this instance, the goal code comprises a name to print() that takes a string as an argument. This string isn’t correctly ended with a closing single citation mark, so exec() raises a SyntaxError declaring the problem and doesn’t run the enter code. Observe that Python pinpoints the error at first of the string slightly than on the finish, the place the closing single citation mark ought to go.

Operating code that comes as a string, such as you did within the instance above, is arguably the pure means of utilizing exec(). Nevertheless, if you must run the enter code many instances, then utilizing a string as an argument will make the perform run the parsing and compilation steps each time. This habits could make your code inefficient when it comes to execution pace.

On this state of affairs, essentially the most handy strategy is to compile the goal code beforehand after which run the ensuing bytecode with exec() as many instances as wanted. Within the following part, you’ll learn to use exec() with compiled code objects.

Executing Compiled Code

In apply, exec() could be fairly gradual whenever you use it to course of strings containing code. For those who ever must dynamically run a given piece of code greater than as soon as, then compiling it beforehand would be the most performant and really useful strategy. Why? Since you’ll be working the parsing and compilation steps solely as soon as after which reusing the compiled code.

To compile a chunk of Python code, you should use compile(). This built-in perform takes a string as an argument and runs a one-time bytecode compilation on it, producing a code object that you may then go to exec() for execution.

The signature of compile() has the next type:

compile(supply, filename, mode, flags=0, dont_inherit=False, optimize=-1)

On this tutorial, you’ll solely use the three first arguments to compile(). The supply argument holds the code that you must compile into bytecode. The filename argument will maintain the file from which the code was learn. To learn from a string object, you’ll need to set filename to the "<string>" worth.

Lastly, compile() can generate code objects that you may execute utilizing both exec() or eval(), relying on the mode argument’s worth. This argument must be set to "exec" or "eval", relying on the goal execution perform:

>>>

>>> string_input = """
... def sum_of_even_squares(numbers):
...     return sum(quantity**2 for quantity in numbers if quantity % 2 == 0)
...
... print(sum_of_even_squares(numbers))
... """

>>> compiled_code = compile(string_input, "<string>", "exec")
>>> exec(compiled_code)

>>> numbers = [2, 3, 7, 4, 8]
>>> exec(compiled_code)
84

>>> numbers = [5, 3, 9, 6, 1]
>>> exec(compiled_code)
36

Compiling often-repeated code up entrance with compile() can assist you barely enhance your code’s efficiency by skipping the parsing and bytecode compilation steps on every name to exec().

Operating Code From Python Supply Recordsdata

You can even use exec() to run code that you simply’ve learn from a dependable .py file in your file system or some other place. To do that, you should use the built-in open() perform to learn the file’s content material as a string, which you’ll be able to then go as an argument to exec().

For instance, say that you’ve got a Python file named hiya.py containing the next code:

# hiya.py

print("Howdy, Pythonista!")
print("Welcome to Actual Python!")

def greet(title="World"):
    print(f"Howdy, {title}!")

This pattern script prints a greeting and a welcome message to the display. It additionally defines a pattern greet() perform for testing functions. The perform takes a reputation as an argument and prints a personalized greeting to the display.

Now get again to your Python interactive session and run the next code:

>>>

>>> with open("hiya.py", mode="r", encoding="utf-8") as hiya:
...     code = hiya.learn()
...

>>> exec(code)
Howdy, Pythonista!
Welcome to Actual Python!

>>> greet()
Howdy, World!

>>> greet("Pythonista")
Howdy, Pythonista!

On this instance, you first open the goal .py file as an everyday textual content file utilizing the built-in open() perform in a with assertion. You then name .learn() on the file object to learn the file’s content material into the code variable. This name to .learn() returns the file’s content material as a string. The ultimate step is to name exec() with this string as an argument.

This instance runs the code and makes the greet() perform and objects that dwell in hiya.py out there in your present namespace. That’s why you should use greet() immediately. The key behind this habits has to do with the globals and locals arguments, which you’ll study within the subsequent part.

Utilizing the method within the above instance, you possibly can open, learn, and execute any file containing Python code. This method may fit whenever you don’t know up entrance which supply information you’ll be working. So, you possibly can’t write import module, since you don’t know the module’s title whenever you’re writing the code.

For those who ever select to make use of this method, then just remember to solely execute code from trusted supply information. Ideally, essentially the most dependable supply information are people who you have consciously created to run dynamically. You should by no means run code information that come from exterior sources, together with your customers, with out inspecting the code first.

Utilizing the globals and locals Arguments

You’ll be able to go an execution context to exec() utilizing the globals and locals arguments. These arguments can settle for dictionary objects that’ll work as the worldwide and native namespaces that exec() will use to run the goal code.

These arguments are optionally available. For those who omit them, then exec() will execute the enter code within the present scope, and all of the names and objects on this scope will likely be out there to exec(). Likewise, all of the names and objects that you simply outline within the enter code will likely be out there within the present scope after the decision to exec().

Contemplate the next instance:

>>>

>>> code = """
... z = x + y
... """

>>> # International names are accessible from exec()
>>> x = 42
>>> y = 21

>>> z
Traceback (most up-to-date name final):
    ...
NameError: title 'z' just isn't outlined

>>> exec(code)
>>> # Names in code can be found within the present scope
>>> z
63

This instance reveals that if you happen to name exec() with out offering particular values to the globals and locals arguments, then the perform runs the enter code within the present scope. On this case, the present scope is the worldwide one.

Observe that after you name exec(), the names outlined within the enter code are additionally out there within the present scope. That’s why you possibly can entry z within the last line of code.

For those who solely present a price to globals, then that worth have to be a dictionary. The exec() perform will use this dictionary for each world and native names. This habits will prohibit entry to most names within the present scope:

>>>

>>> code = """
... z = x + y
... """

>>> x = 42
>>> y = 21

>>> exec(code, {"x": x})
Traceback (most up-to-date name final):
    ...
NameError: title 'y' just isn't outlined

>>> exec(code, {"x": x, "y": y})

>>> z
Traceback (most up-to-date name final):
    ...
NameError: title 'z' just isn't outlined

Within the first name to exec(), you utilize a dictionary because the globals argument. As a result of your dictionary doesn’t present a key holding the y title, the decision to exec() doesn’t have entry to this title and raises a NameError exception.

Within the second name to exec(), you present a special dictionary to globals. On this case, the dictionary comprises each variables, x and y, which permits the perform to work accurately. Nevertheless, this time you don’t have entry to z after the decision to exec(). Why? Since you’re utilizing a customized dictionary to offer an execution scope to exec() slightly than falling again to your present scope.

For those who name exec() with a globals dictionary that doesn’t comprise the __builtins__ key explicitly, then Python will routinely insert a reference to the built-in scope or namespace beneath that key. So, all of the built-in objects will likely be accessible out of your goal code:

>>>

>>> code = """
... print(__builtins__)
... """

>>> exec(code, {})
{'__name__': 'builtins', '__doc__': "Constructed-in capabilities, ...}

On this instance, you’ve supplied an empty dictionary to the globals argument. Observe that exec() nonetheless has entry to the built-in namespace as a result of this namespace is routinely inserted into the supplied dictionary beneath the __builtins__ key.

For those who present a price for the locals argument, then it may be any mapping object. This mapping object will maintain the native namespace when exec() is working your goal code:

>>>

>>> code = """
... z = x + y
... print(f"{z=}")
... """

>>> x = 42  # International title

>>> def func():
...     y = 21  # Native title
...     exec(code, {"x": x}, {"y": y})
...

>>> func()
z=63

>>> z
Traceback (most up-to-date name final):
    ...
NameError: title 'z' just isn't outlined

The decision to exec() is embedded in a perform on this instance. Subsequently, you could have a worldwide (module-level) scope and a neighborhood (function-level) scope. The globals argument offers the x title from the worldwide scope, and the locals argument offers the y title from the native scope.

Observe that after working func(), you don’t have entry to z as a result of this title was created beneath the native scope of exec(), which isn’t out there from the surface.

With the globals and locals arguments, you possibly can tweak the context through which exec() runs your code. These arguments are fairly useful in terms of minimizing the safety dangers related to exec(), however you need to nonetheless just remember to’re working code from trusted sources solely. Within the following part, you’ll study these safety dangers and the way to take care of them.

Uncovering and Minimizing the Safety Dangers Behind exec()

As you’ve realized up to now, exec() is a robust software that lets you execute arbitrary code that involves you as strings. It is best to use exec() with excessive care and warning due to its skill to run any piece of code.

Usually, the code that feeds exec() is dynamically generated at runtime. This code could have many sources of enter, which may embody your program consumer, different packages, a database, a stream of information, and a community connection, amongst others.

On this situation, you possibly can’t be solely certain what the enter string will comprise. So, the chance of dealing with an untrusted and malicious supply of enter code is fairly excessive.

The safety points related to exec() are the commonest cause why many Python builders advocate avoiding this perform altogether. Discovering a greater, sooner, extra sturdy, and safer answer is sort of at all times doable.

Nevertheless, if you happen to should use exec() in your code, then the widely really useful strategy is to make use of it with specific globals and locals dictionaries.

One other essential concern with exec() is that it breaks a elementary assumption in programming: the code that you simply’re at present studying or writing is the code that you simply’ll be executing whenever you fireplace up your program. How does exec() break this assumption? It makes your packages run new and unknown code that’s dynamically generated. This new code could be onerous to comply with, keep, and even management.

Within the following sections, you’ll dive into just a few suggestions, strategies, and practices that you need to apply if you happen to ever want to make use of exec() in your code.

Avoiding Enter From Untrusted Sources

In case your customers can present your packages with arbitrary Python code at runtime, then points can come up in the event that they enter code that violates or breaks your safety guidelines. For instance this downside, get again to the Python interpreter instance that makes use of exec() for code execution:

>>>

>>> whereas True:
...     exec(enter("->> "))
...
->> print("Howdy, World!")
Howdy, World!

Now say that you simply need to use this method to implement an interactive Python interpreter on one among your Linux net servers. For those who permit your customers to go arbitrary code into your program immediately, then a malicious consumer may present one thing like "import os; os.system('rm -rf *')". This code snippet will most likely take away all of the content material of your server’s disk, so don’t run it.

To stop this threat, you possibly can prohibit entry to the import system by making the most of the globals dictionary:

>>>

>>> exec("import os", {"__builtins__": {}}, {})
Traceback (most up-to-date name final):
    ...
ImportError: __import__ not discovered

The import system internally makes use of the built-in __import__() perform. So, if you happen to forbid entry to the built-in namespace, then the import system received’t work.

Regardless that you possibly can tweak the globals dictionary as proven within the above instance, one factor that it’s essential to by no means do is to make use of exec() for working exterior and probably unsafe code by yourself laptop. Even if you happen to rigorously clear up and validate the enter, you’ll threat being hacked. So, you’re finest avoiding this apply.

Proscribing globals and locals to Decrease Dangers

You’ll be able to present customized dictionaries because the globals and locals arguments if you wish to fine-tune the entry to world and native names when working code with exec(). For instance, if you happen to go empty dictionaries to each globals and locals, then exec() received’t have entry to your present world and native namespaces:

>>>

>>> x = 42
>>> y = 21

>>> exec("print(x + y)", {}, {})
Traceback (most up-to-date name final):
    ...
NameError: title 'x' just isn't outlined

For those who name exec() with empty dictionaries for globals and locals, then you definately forbid entry to world and native names. This tweak lets you prohibit the names and objects out there whenever you’re working code with exec().

Nevertheless, this method doesn’t assure a secure use of exec(). Why? As a result of the perform nonetheless has entry to all of Python’s built-in names, as you realized within the part concerning the globals and locals arguments:

>>>

>>> exec("print(min([2, 3, 7, 4, 8]))", {}, {})
2

>>> exec("print(len([2, 3, 7, 4, 8]))", {}, {})
5

In these examples, you utilize empty dictionaries for globals and locals, however exec() can nonetheless entry built-in capabilities like min(), len(), and print(). How would you forestall exec() from accessing built-in names? That’s the following part’s matter.

Deciding on Allowed Constructed-in Names

As you’ve already realized, if you happen to go a customized dictionary to globals with no __builtins__ key, then Python will routinely replace that dictionary with all of the names within the built-in scope beneath a brand new __builtins__ key. To limit this implicit habits, you should use a globals dictionary containing a __builtins__ key with an acceptable worth.

For instance, if you wish to forbid entry to built-in names utterly, then you possibly can name exec() like within the following instance:

>>>

>>> exec("print(min([2, 3, 7, 4, 8]))", {"__builtins__": {}}, {})
Traceback (most up-to-date name final):
    ...
NameError: title 'print' just isn't outlined

On this instance, you set globals to a customized dictionary containing a __builtins__ key with an empty dictionary as its related worth. This apply prevents Python from inserting a reference to the built-in namespace into globals. This manner, you make sure that exec() received’t have entry to built-in names whereas executing your code.

You can even tweak your __builtins__ key if you happen to want exec() to entry sure built-in names solely:

>>>

>>> allowed_builtins = {"__builtins__": {"min": min, "print": print}}
>>> exec("print(min([2, 3, 7, 4, 8]))", allowed_builtins, {})
2

>>> exec("print(len([2, 3, 7, 4, 8]))", allowed_builtins, {})
Traceback (most up-to-date name final):
    ...
NameError: title 'len' just isn't outlined

Within the first instance, exec() efficiently runs your enter code as a result of min() and print() are current within the dictionary related to the __builtins__ key. Within the second instance, exec() raises a NameError and doesn’t run your enter code as a result of len() isn’t current within the supplied allowed_builtins.

The strategies within the above examples let you reduce the safety implications of utilizing exec(). Nevertheless, these strategies aren’t solely foolproof. So, everytime you really feel that you must use exec(), strive to consider one other answer that doesn’t use the perform.

Placing exec() Into Motion

Up up to now, you’ve realized how the built-in exec() perform works. You realize that you should use exec() to run both string-based or compiled-code enter. You additionally realized that this perform can take two optionally available arguments, globals and locals, which let you tweak the execution namespace for exec().

Moreover, you’ve realized that utilizing exec() implies some severe safety points, together with permitting customers to run arbitrary Python code in your laptop. You studied some really useful coding practices that assist reduce the safety dangers related to exec() in your code.

Within the following sections, you’ll code just a few sensible examples that’ll enable you spot these use instances through which utilizing exec() could be acceptable.

Operating Code From Exterior Sources

Utilizing exec() to execute code that comes as strings from both your customers or another supply might be the commonest and harmful use case of exec(). This perform is the quickest means so that you can settle for code as strings and run it as common Python code within the context of a given program.

You should by no means use exec() to run arbitrary exterior code in your machine, as a result of there’s no safe strategy to do it. For those who’re going to make use of exec(), then use it as a strategy to let your customers run their very own code on their very own machines.

The usual library has just a few modules that use exec() for executing code supplied by the consumer as a string. A superb instance is the timeit module, which Guido van Rossum initially wrote himself.

The timeit module offers a fast strategy to time small items of Python code that come as strings. Take a look at the next instance from the module’s documentation:

>>>

>>> from timeit import timeit

>>> timeit("'-'.be part of(str(n) for n in vary(100))", quantity=10000)
0.1282792080000945

The timeit() perform takes a code snippet as a string, runs the code, and returns a measurement of the execution time. The perform additionally takes a number of different arguments. For instance, quantity lets you present the variety of instances that you simply need to execute the goal code.

On the coronary heart of this perform, you’ll discover the Timer class. Timer makes use of exec() to run the supplied code. For those who examine the supply code of Timer within the timeit module, then you definately’ll discover that the category’s initializer, .__init__(), consists of the next code:

# timeit.py

# ...

class Timer:
    """Class for timing execution pace of small code snippets."""

    def __init__(
        self,
        stmt="go",
        setup="go",
        timer=default_timer,
        globals=None
    ):
        """Constructor.  See class doc string."""
        self.timer = timer
        local_ns = {}
        global_ns = _globals() if globals is None else globals
        # ...
        src = template.format(stmt=stmt, setup=setup, init=init)
        self.src = src  # Save for traceback show
        code = compile(src, dummy_src_name, "exec")
        exec(code, global_ns, local_ns)
        self.internal = local_ns["inner"]

    # ...

The decision to exec() within the highlighted line executes the consumer’s code utilizing global_ns and local_ns as its world and native namespaces.

This manner of utilizing exec() is acceptable whenever you’re offering a software on your customers, who must present their very own goal code. This code will run on the customers’ machines, so that they’ll be chargeable for guaranteeing that the enter code is safe to run.

One other instance of utilizing exec() to run code that comes as a string is the doctest module. This module inspects your docstrings seeking textual content that appears like a Python interactive session. If doctest finds any interactive-session-like textual content, then it executes that textual content as Python code to test if it really works as anticipated.

For instance, say that you’ve got the next perform for including two numbers collectively:

# calculations.py

def add(a, b):
    """Return the sum of two numbers.

    Exams:
        >>> add(5, 6)
        11
        >>> add(2.3, 5.4)
        7.7
        >>> add("2", 3)
        Traceback (most up-to-date name final):
        TypeError: numeric sort anticipated for "a" and "b"
    """
    if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
        increase TypeError('numeric sort anticipated for "a" and "b"')
    return a + b

# ...

On this code snippet, add() defines a docstring with a number of assessments that test how the perform ought to work. Observe that the assessments symbolize calls to add() in a hypothetical interactive session utilizing legitimate and invalid argument varieties.

After you have these interactive assessments and their anticipated outputs in your docstrings, then you should use doctest to run them and test in the event that they concern the anticipated end result.

Go to your command line and run the next command within the listing containing your calculations.py file:

$ python -m doctest calculations.py

This command received’t concern any output if all of the check works as anticipated. If no less than one check fails, then you definately’ll get an exception declaring the issue. To substantiate this, you possibly can change one of many anticipated outputs within the perform’s docstring and run the above command once more.

The doctest module makes use of exec() to execute any interactive docstring-embedded code, as you possibly can verify within the module’s supply code:

# doctest.py

class DocTestRunner:
    # ...

    def __run(self, check, compileflags, out):
        # ...
        strive:
            # Do not blink!  That is the place the consumer's code will get run.
            exec(
                compile(instance.supply, filename, "single", compileflags, True),
                check.globs
            )
            self.debugger.set_continue() # ==== Instance Completed ====
            exception = None
        besides KeyboardInterrupt:
        # ...

As you possibly can verify on this code snippet, the consumer’s code runs in an exec() name, which makes use of compile() to compile the goal code. To run this code, exec() makes use of check.globs as its globals argument. Observe that the remark proper earlier than the decision to exec() jokingly states that that is the place the place the consumer’s code runs.

Once more, on this use case of exec(), the accountability of offering safe code examples is on the customers. The doctest maintainers aren’t chargeable for making certain that the decision to exec() doesn’t trigger any harm.

It’s essential to notice that doctest doesn’t forestall the safety dangers related to exec(). In different phrases, doctest will run any Python code. For instance, somebody may modify your add() perform to incorporate the next code within the docstring:

# calculations.py

def add(a, b):
    """Return the sum of two numbers.

    Exams:
        >>> import os; os.system("ls -l")
        0
    """
    if not (isinstance(a, (int, float)) and isinstance(b, (int, float))):
        increase TypeError('numeric sort anticipated for "a" and "b"')
    return a + b

For those who run doctest on this file, then the ls -l command will efficiently run. On this instance, the embedded command is generally innocent. Nevertheless, a malicious consumer may modify your docstring and embed one thing like os.system("rm -rf *") or another harmful command.

Once more, you at all times need to watch out with exec() and with instruments that use this perform, like doctest does. Within the particular case of doctest, so long as you recognize the place your embedded check code is coming from, this software will likely be fairly secure and helpful.

Utilizing Python for Configuration Recordsdata

One other state of affairs through which you should use exec() to run code is when you could have a configuration file that makes use of legitimate Python syntax. Your file can outline a number of configuration parameters with particular values. Then you possibly can learn the file and course of its content material with exec() to construct a dictionary object containing all of your configuration parameters and their values.

For instance, say that you’ve got the next configuration file for a textual content editor app that you simply’re engaged on:

# settings.conf

font_face = ""
font_size = 10
line_numbers = True
tab_size = 4
auto_indent = True

This file has legitimate Python syntax, so you possibly can execute its content material utilizing exec() as you’d do with an everyday .py file.

The beneath perform reads your settings.conf file and builds a configuration dictionary:

>>>

>>> from pathlib import Path

>>> def load_config(config_file):
...     config_file = Path(config_file)
...     code = compile(config_file.read_text(), config_file.title, "exec")
...     config_dict = {}
...     exec(code, {"__builtins__": {}}, config_dict)
...     return config_dict
...

>>> load_config("settings.conf")
{
    'font_face': '',
    'font_size': 10,
    'line_numbers': True,
    'tab_size': 4,
    'auto_indent': True
}

The load_config() perform takes the trail to a configuration file. Then it reads the goal file as textual content and passes that textual content into exec() for execution. Through the exec() run, the perform injects the configuration parameters into the locals dictionary, which is later returned to the caller code.

That’s it! Now you possibly can learn all of your configuration parameters and their corresponding values from the ensuing dictionary and use the parameters to arrange your editor venture.

Conclusion

You’ve realized the way to use the built-in exec() perform to execute Python code from a string or bytecode enter. This perform offers a fast software for executing dynamically generated Python code. You additionally realized the way to reduce the safety dangers related to exec() and when it’s okay to make use of the perform in your code.

On this tutorial, you’ve realized the way to:

  • Work with Python’s built-in exec() perform
  • Use Python’s exec() to run string-based and compiled-code enter
  • Assess and reduce the safety dangers related to utilizing exec()

Moreover, you’ve coded just a few sensible examples that helped you higher perceive when and the way to use exec() in your Python code.



RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments