1

I have previously done the following to fetch a version string:

>>> filepath = './somemodule/__init__.py'
>>> name = 'dummy'
>>> module_source = imp.load_source(name, filepath)
>>> module_source.__version__
1.0.0

Now, when imp has been deprecated in Python 3 (I'm on 3.7.1), what would be a nice replacement for load_source which will just require the std library?

This seems a little convoluted to me AND load_module is actually deprecated:

>>> from importlib.machinery import SourceFileLoader
>>> loaded = SourceFileLoader(name, filepath).load_module()
>>> loaded.__version__
1.0.0

EDIT #1:

This is discussed in Import arbitrary python source file. (Python 3.3+) and one of the solutions which came up there is:

>>> loader = importlib.machinery.SourceFileLoader(name, filepath)
>>> mod = types.ModuleType(loader.name)
>>> loader.exec_module(mod)
>>> mod.__version__
1.0.0
fredrik
  • 7,730
  • 10
  • 56
  • 104

1 Answers1

0

The "comfortable" way to import a module is to use importlib.import_module(). This is, for all practical purposes, just like using the import statement. However, it cannot import arbitrary files which are not in sys.path, so it doesn't work for your use case.

To import a file directly, the importlib documentation provides this recipe:

import importlib.util
import sys

# For illustrative purposes.
import tokenize
file_path = tokenize.__file__
module_name = tokenize.__name__

spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# Optional; only necessary if you want to be able to import the module
# by name later.
sys.modules[module_name] = module

Finally, a bit of advice: I recommend putting the version string in a separate text file called (say) __version__.txt, and loading it from __init__.py and anything else which needs to know the version. That way, you don't need to execute Python code to read the version number. You can load data from such files by using pkgutil.get_data(__package__, '__version__.txt') in __init__.py, and replace __package__ with a suitable value when calling from another module, or open the file directly. get_data() returns bytes; you may want to call .decode() on the return value to convert it into a string.

(pkgutil.get_data() may seem a roundabout way to do it, but it's required if your code is imported from a ZIP file or installed in some other exotic way. If your code is in a namespace package or was installed in a particularly unusual fashion, get_data() may fail to work and return None, so you should check for that case and avoid crashing on it.)

Kevin
  • 25,251
  • 7
  • 54
  • 78