load python code at runtime - python

I would like to load a .py file at runtime. This .py file is basically a config file with the following format:
var1=value
var2=value
predicate_function=func line : <return true or false>
Once this file is loaded, I would like to be able to access var1, var2 and predicate_function. For each line, I'll pass it to the predicate function, and if it returns false, I'll ignore it.
In any case, I'm not sure how to load a python file at runtime and access its variables.
Clarification: there may be any number of these config files that I need to pass to the main program and I won't know their names until runtime. Google tells me I should use __import__. I'm not sure how to correctly use that method and then access the variables of the imported file.

As written in the python official documentation, if you just want to import a module by name, you can look it up in the sys.modules dictionary after using __import__.
Supposing your configuration is in myproject.mymodule, you would do like that :
module_name = 'myproject.mymodule'
import sys
__import__(module_name)
mymodule = sys.modules[module_name]
# Then you can just access your variables and functions
print mymodule.var1
print mymodule.var2
# etc...
You can also use the return value of __import__ statement but you will have to understand fully how python works with namespaces and scopes.

You just need to be able to dynamically specify the imports and then dynamically get at the variables.
Let's say your config file is bar.py and looks like this:
x = 3
y = 4
def f(x): return (x<4)
Then your code should look like this:
import sys
# somehow modnames should be a list of strings that are the names of config files
#
# you can do this more dynamically depending on what you're doing
modnames = ['bar']
for modname in modnames:
exec('import %s' % modname)
for modname in modnames:
mod = sys.modules[modname]
for k in mod.__dict__:
if k[:2] != '__':
print modname, k, mod.__dict__[k]
I get this output:
bar f <function f at 0x7f2354eb4cf8>
bar x 3
bar y 4
Then you at least have all the variables and functions. I didn't quite get what you wanted from the predicate functions, but maybe you can get that on your own now.

To access another Python module, you import it. execfile has been mentioned by a couple people, but it is messy and dangerous. execfile clutters your namespace, possibly even messing up the code you are running. When you want to access another Python source file, use the import statement.
Even better would be not to use a Python file for configuration at all, but rather to use the builtin module ConfigParser or a serialization format like JSON. This way your configuration files don't allow execution of arbitrary (possibly malicious) code, doesn't require people to know Python to configure your program, and can easily be altered programatically.

If the imported module is on the regular search path, you can use __import__.
If you need to load the module from an arbitrary path in the filesystem, use imp.load_module.
Be sure to consider the security implications of loading arbitrary user-specified code.

In Python 2.*, execfile works (I recommend passing a specific dictionary and accessing the variables from there -- as the note in the docs says, execfile can't affect the calling function's locals() dictionary).
In Python 3.*, execfile has been removed, so do, instead:
with open('thefile.py') as f:
exec(f.read(), somedict)

Since the Python version hasn't been clearly mentioned, it is worth pointing out that the imp module has been deprecated in newer Python versions in favor of the importlib module. Example here.

I'm kinda late to the party, but I want to present an alternative answer nonetheless.
If you want to import code without affecting the global module namespace, you can create an anonymous module (using types.ModuleType) and load arbitrary code in it (using compile and exec). For instance, like this:
import types
filename = "/path/to/your/file.py"
with open(filename) as fp:
code = compile(fp.read(), filename, "exec")
config_module = types.ModuleType("<config>")
exec code in config_module.__dict__
You can then access the variables as config_module.var1, &c.

If you want to have a configuration file that will only be edited by the user when the program isn't running, just import it as a normal python file
ie.
main.py:
import config
print config.var1
config.py:
var="var12"
var2 = 100.5

try the imp module : http://docs.python.org/library/imp.html

Related

How can I call a function that is stored as a variable from a python file that is also stored as a variable?

I can import a python script using import_module. But, how can I call a function stored as a variable from that script? I've previously used getattr to work with dictionaries stored as variables, but I don't think this same method works with functions. Here's an example that does not currently work:
from importlib import import_module
file_list = ['file1','file2']
func_list = ['func1','func2']
for file in file_list:
test_file = import_module(file)
for func in func_list:
from test_file import func
file1:
def func1():
...
def func2():
...
file2:
def func1():
...
def func2():
...
I can import a python script using import_module.
When you do this, the result is a module object - just the same as an import statement provides.
from test_file import func
The reason this doesn't work is because it is looking for a test_file module - and it cares about module names as they appear in sys.path, not about your local variable names.
Fortunately, since you already have the module object, you presumably realized you could access the contents normally, as attributes, e.g. test_file.func.
I've previously used getattr to work with dictionaries stored as variables, but I don't think this same method works with functions
I'm not quite sure what you mean here. Attributes are attributes, whether they're plain data, functions, classes or anything else. test_file is a thing that has a func attribute, therefore getattr(test_file, 'func') gets that attribute.
The remaining issue is the variable-variables problem - you don't really want to be creating a name for that result dynamically. So yes, you can store that in a dict, if you want. But frankly it's easier to just use the module object. Unless perhaps for some reason you need/want to "trim" the contents and only expose a limited interface (for some other client); but you can't avoid loading the whole module. from X import Y does that anyway.
The module object that you got from the dynamic import is already working as a namespace, which you need here anyway because you're importing multiple modules that have overlapping attribute names.
tl;dr: if you want to call a function from that imported module, just do it the same way that you would have if you had imported the module (not a name from that module) normally. We can, for example, put the imported modules in a list:
modules = [import_module(f) for f in filenames]
and then call the appropriate method by looking it up within the appropriate module object:
modules[desired_module_id].desired_func()
basically You would run this code in a separate file and where it says the_file_where_this_is_needed.py You would insert the file where You want these import statement to be. (also probably You can run this code in the very file). it will be sort of like hardcoding but automatic
file_list = ['file1', 'file2']
func_list = ['func1', 'func2']
with open('the_file_where_this_is_needed.py', 'r') as file:
data = file.read()
string = ''
for file in file_list:
for func in func_list:
string += f'from {file} import {func}\n'
data = string + data
with open('the_file_where_this_is_needed.py', 'w') as file:
file.write(data)

Partial Imports: Import everything up to a error

Is there a way to perform a python import which is not atomic?
For instance, I have a file as follows:
# Filename: a.py
myvariable = 1
mylist = [1, 2, 3]
raise ImportError
donotimportthis = 5
I then have a separate file which does the following:
import a
a.myvariable == 1 # This is okay as it imported it
a.donotimportthis # <-- raise an exception as this is not imported.
I have a file which contains some python code, this follows the format of:
...variables...
import X
I do not have X installed nor do I want it however I do want the variables.
Note: This file is autogenerated not by me but by a tool whose version is frozen.
Two choices, in descending order of preference:
Change the autogeneration process. Instead of invoking proprietary_autogen_process, invoke custom_autogen_wrapper. This wrapper in turn first invokes the proprietary third-party tool, and then modifies the produced module source code by searching for the code that imports module X, and deletes everything after it.
This is relatively straightforward. You just need to take some care to not introduce false positives or false negatives by performing too loose (or too strict) matching of the import code. Ideally you’d use an AST rewriter but that’s probably overkill; a regular expression search for import X might work, although it will yield wrong results if this text appears inside a comment, string literal or inside a method which isn’t executed.
Generate a stub module X in a location where it will be found by the autogenerated module when importing the latter. I don’t recommend this because it’s tedious: You probably can’t just generate an empty module, since the autogenerated module will want to use X. You need to generate meaningful method stubs.
You can do specific imports with
from a import myvariable
EDIT: The above won't work if anything that is flat in the file raises an error. If you have no way to edit the imported file then I don't know if there is a (resonable) solution to this. Sorry didn't realise.
(an unreasonable solution would be to read in the file as text, slice it, and then run eval on it).
Or, as mentioned in the comments, put the stuff you don't want under
if __name__=="__main__":
<here>
Then it will only be invoked if you run the file directly.
What you can do is removing the donotimportthis variable at the end of the module, as follows: del donotimportthis. I hope it helps

use raw_input instead of input in python3

I am in the habit of using raw_input(...) for certain debugging. However, in python3 this has changed to input(...). Is there a way to define an alias at the top of my project, such as:
# __init__.py
raw_input = input
I tried the above, but it only worked in the file I added it to, and not any other files in that directory. I'd like this to work basically in every file within my python repository.
You can define all aliases in a separate file (e.g. aliases.py) then import said file where needed (i.e. import aliases).
The con with this method that you'll be referencing the alias through aliases.alias unless you make the import stricter (i.e. from aliases import raw_input) or if you don't care about avoiding a wildcard import (i.e. from aliases import *).
Additionally, if you don't mind another import in the aliases file you can use the builtins namespace:
import builtins
builtins.raw_input = input
You still have to define all aliases separate file (e.g. aliases.py) then import said file where needed (i.e. import aliases) but the advantage of using the builtins namespace is that you can use that import exactly as given.
You can do it by creating a module for creating the renaming function and then importing it to every file you want to like this:
First the module function declaration in alias.py
def raw_input(a):
return input(a)
Secondly, import to another file:
from alias import raw_input
x = raw_input("hello world")
print(x)
Sadly, you will have to make the import of the module to every file you want to use the renamed function.
Hope it works for you!
Put this at the top, and you will get exactly what you want.
import builtins
builtins.raw_input = builtins.input
It is guaranteed to work, but generally considered a bad practice (everybody will be confused with where is that raw_input defined)

Using exec to load module variables, using a file path

A textbook suggests I should be able to do this:
d = {}
exec("C://Users//Dave//Desktop//Bot//bot_config_data.py", globals(), d)
File "<string>", line 1 C://Users//Dave//Desktop//Bot//bot_config_data.py
^
SyntaxError: invalid syntax
I can do this:
d = {}
exec('from bot_config_data import price_data', globals(), d)
But, I would like to do the former.
I'm trying to write a method which overrides config data from various files.
Am I completely off base here?
Update
The book was quite misleading. It posted part of the code, the result of the complete code block, and then gave the remainder. As I was referencing, rather than working through it cover to cover, I tripped over myself.
This is the code I now have:
data = {}
file = 'C:\\Users\\Dave\\Desktop\\Bot\\bot_config_data.py'
with open(file) as f:
code = compile(f.read(), file, "exec")
exec(code, globals(), data)
price_data = data["price_data"]
Update2
Using Mad Physicist's answer below, my code would be:
from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader
filepath = 'C:\\Users\\Dave\\Desktop\\Bot\\bot_config_data.py'
module_data = os.path.basename(filepath)
spec = spec_from_loader(module_data, SourceFileLoader(module_data, filepath))
bot_config_data = module_from_spec(spec)
spec.loader.exec_module(bot_config_data)
price_data = bot_config_data.price_data
You are completely off-base and the textbook is giving you some very bad advice.
exec runs Python code. As in Python statements, not the name of a file. From the docs:
exec(object[, globals[, locals]])
This function supports dynamic execution of Python code. object must be either a string or a code object. If it is a string, the string is parsed as a suite of Python statements which is then executed (unless a syntax error occurs)
That is why your second statement works fine. If you want to run a Python module, import it. If you need to import using a dynamic name instead of a hard-coded import statement, you can use importlib.import_module:
price_data = importlib.import_module('bot_config_data').price_data
This will run the whole import machinery for you, including ensuring that bot_config_data ends up in sys.modules.
If you really need something even fancier, you can use the __import__ machinery. __import__ is the under-the-hood implementation of the import statement:
d = {}
bot_config_data = __import__('bot_config_data', locals=d, from_list=['price_data'])
price_data = bot_config_data.price_data
If you want full control over the process, you can use the low-level machinery described here: How to import a module given the full path?. In particular see my answer to that question, since it describes how to load random text files as Python scripts: https://stackoverflow.com/a/43602557/2988730:
from importlib.util import spec_from_loader, module_from_spec
from importlib.machinery import SourceFileLoader
spec = spec_from_loader("bot_config_data", SourceFileLoader("bot_config_data", "C:/Users/Dave/Desktop/Bot/bot_config_data.py"))
bot_config_data = module_from_spec(spec)
spec.loader.exec_module(bot_config_data)
price_data = bot_config_data.price_data
Final note: It looks like you blindly converted all the \\ to // in your path. Forward slashes do not need to be escaped, so you only need single forward slashes. If you want to avoid escaping your backslashes, put r in front of your string to make it a raw string.
If you need to import using a dynamic name instead of a hard-coded
import statement, you can use importlib.import_module:
price_data = importlib.import_module('bot_config_data').price_data
Huh? How is that "dynamic"? 'bot_config_data' is literally a string literal.
If you really want to import modules dynamically, contrary to what is widely believed, the only way to do this is with exec(). I challenge anyone to find or submit an example of importlib.import_module or __import__ being used to truly dynamically import modules.
To be clear: __import__ allows for the dynamic handling of imports, which is not the same as dynamically importing, as that is a chicken/egg type problem only exec() can solve.
The problem is that importlib.import_module() returns an object, which needs an assignment. (It doesn't just add to the global namespace as is often implied.) But you can't use the same argument on both sides of the assignment. So while it allows you to do handle imports in a more dynamic way, it provides no way of dynamically importing modules.

Injecting Locals into Dynamically Loaded Modules Before Execution

I'm trying to build a sort of script system in python that will allow small snippets of code to be selected and executed at runtime inside python.
Essentially I want to be able to load a small python file like
for i in Foo: #not in a function.
print i
Where somewhere else in the program I assign what Foo will be. As if Foo served as a function argument to the entire loaded python file instead of a single function
So somewhere else
FooToPass = GetAFoo ()
TempModule = __import__ ("TheSnippit",<Somehow put {'Foo' : FooToPass} in the locals>)
It is considered bad style to have code with side effects at module level. If you want your module to do something, put that code in a function, make Foo a parameter of this function and call it with the desired value.
Python's import mechanism does not allow to preinitialise a module namespace. If you want to do this anyway (which is, in my opinion, confusing and unnecessary), you have to fiddle around with details of the import mechanism. Example implementation (untested):
import imp
import sys
def my_import(module_name, globals):
if module_name in sys.modules:
return sys.modules[module_name]
module = imp.new_module(module_name)
vars(module).update(globals)
f, module.__file__, options = imp.find_module(module_name)
exec f.read() in vars(module)
f.close()
sys.modules[module_name] = module
return module

Categories