I have a class MyClass defined in my_module. MyClass has a method pickle_myself which pickles the instance of the class in question:
def pickle_myself(self, pkl_file_path):
with open(pkl_file_path, 'w+') as f:
pkl.dump(self, f, protocol=2)
I have made sure that my_module is in PYTHONPATH. In the interpreter, executing __import__('my_module') works fine:
>>> __import__('my_module')
<module 'my_module' from 'A:\my_stuff\my_module.pyc'>
However, when eventually loading the file, I get:
File "A:\Anaconda\lib\pickle.py", line 1128, in find_class
__import__(module)
ImportError: No module named my_module
Some things I have made sure of:
I have not changed the location of my_module.py (Python pickling after changing a module's directory)
I have tried to use dill instead, but still get the same error (More on python ImportError No module named)
EDIT -- A toy example that reproduces the error:
The example itself is spread over a bunch of files.
First, we have the module ball (stored in a file called ball.py):
class Ball():
def __init__(self, ball_radius):
self.ball_radius = ball_radius
def say_hello(self):
print "Hi, I'm a ball with radius {}!".format(self.ball_radius)
Then, we have the module test_environment:
import os
import ball
#import dill as pkl
import pickle as pkl
class Environment():
def __init__(self, store_dir, num_balls, default_ball_radius):
self.store_dir = store_dir
self.balls_in_environment = [ball.Ball(default_ball_radius) for x in range(num_balls)]
def persist(self):
pkl_file_path = os.path.join(self.store_dir, "test_stored_env.pkl")
with open(pkl_file_path, 'w+') as f:
pkl.dump(self, f, protocol=2)
Then, we have a module that has functions to make environments, persist them, and load them, called make_persist_load:
import os
import test_environment
#import pickle as pkl
import dill as pkl
def make_env_and_persist():
cwd = os.getcwd()
my_env = test_environment.Environment(cwd, 5, 5)
my_env.persist()
def load_env(store_path):
stored_env = None
with open(store_path, 'rb') as pkl_f:
stored_env = pkl.load(pkl_f)
return stored_env
Then we have a script to put it all together, in test_serialization.py:
import os
import make_persist_load
MAKE_AND_PERSIST = True
LOAD = (not MAKE_AND_PERSIST)
cwd = os.getcwd()
store_path = os.path.join(cwd, "test_stored_env.pkl")
if MAKE_AND_PERSIST == True:
make_persist_load.make_env_and_persist()
if LOAD == True:
loaded_env = make_persist_load.load_env(store_path)
In order to make it easy to use this toy example, I have put it all up on in a Github repository that simply needs to be cloned into your directory of choice.. Please see the README containing instructions, which I also reproduce here:
Instructions:
1) Clone repository into a directory.
2) Add repository directory to PYTHONPATH.
3) Open up test_serialization.py, and set the variable MAKE_AND_PERSIST to True. Run the script in an interpreter.
4) Close the previous interpreter instance, and start up a new one. In test_serialization.py, change MAKE_AND_PERSIST to False, and this will programmatically set LOAD to True. Run the script in an interpreter, causing ImportError: No module named test_environment.
5) By default, the test is set to use dill, instead of pickle. In order to change this, go into test_environment.py and make_persist_load.py, to change imports as required.
EDIT: after switching to dill '0.2.5.dev0', dill.detect.trace(True) output
C2: test_environment.Environment
# C2
D2: <dict object at 0x000000000A9BDAE8>
C2: ball.Ball
# C2
D2: <dict object at 0x000000000AA25048>
# D2
D2: <dict object at 0x000000000AA25268>
# D2
D2: <dict object at 0x000000000A9BD598>
# D2
D2: <dict object at 0x000000000A9BD9D8>
# D2
D2: <dict object at 0x000000000A9B0BF8>
# D2
# D2
EDIT: the toy example works perfectly well when run on Mac/Ubuntu (i.e. Unix-like systems?). It only fails on Windows.
I can tell from your question that you are probably doing something like this, with a class method that is attempting to pickle the instance of the class. It's ill-advised to do that, if you are doing that… it's much more sane to use pkl.dump external to the class instead (where pkl is pickle or dill etc). However, it can still work with this design, see below:
>>> class Thing(object):
... def pickle_myself(self, pkl_file_path):
... with open(pkl_file_path, 'w+') as f:
... pkl.dump(self, f, protocol=2)
...
>>> import dill as pkl
>>>
>>> t = Thing()
>>> t.pickle_myself('foo.pkl')
Then restarting...
Python 2.7.10 (default, Sep 2 2015, 17:36:25)
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> f = open('foo.pkl', 'r')
>>> t = dill.load(f)
>>> t
<__main__.Thing object at 0x1060ff410>
If you have a much more complicated class, which I'm sure you do, then you are likely to run into trouble, especially if that class uses another file that is sitting in the same directory.
>>> import dill
>>> from bar import Zap
>>> print dill.source.getsource(Zap)
class Zap(object):
x = 1
def __init__(self, y):
self.y = y
>>>
>>> class Thing2(Zap):
... def pickle_myself(self, pkl_file_path):
... with open(pkl_file_path, 'w+') as f:
... dill.dump(self, f, protocol=2)
...
>>> t = Thing2(2)
>>> t.pickle_myself('foo2.pkl')
Then restarting…
Python 2.7.10 (default, Sep 2 2015, 17:36:25)
[GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import dill
>>> f = open('foo2.pkl', 'r')
>>> t = dill.load(f)
>>> t
<__main__.Thing2 object at 0x10eca8090>
>>> t.y
2
>>>
Well… shoot, that works too. You'll have to post your code, so we can see what pattern you are using that dill (and pickle) fails for. I know having one module import another that is not "installed" (i.e. in some local directory) and expecting the serialization to "just work" doesn't for all cases.
See dill issues:
https://github.com/uqfoundation/dill/issues/128
https://github.com/uqfoundation/dill/issues/129
and this SO question:
Why dill dumps external classes by reference, no matter what?
for some examples of failure and potential workarounds.
EDIT with regard to updated question:
I don't see your issue. Running from the command line, importing from the interpreter (import test_serialization), and running the script in the interpreter (as below, and indicated in your steps 3-5) all work. That leads me to think you might be using an older version of dill?
>>> import os
>>> import make_persist_load
>>>
>>> MAKE_AND_PERSIST = False #True
>>> LOAD = (not MAKE_AND_PERSIST)
>>>
>>> cwd = os.getcwd()
>>> store_path = os.path.join(cwd, "test_stored_env.pkl")
>>>
>>> if MAKE_AND_PERSIST == True:
... make_persist_load.make_env_and_persist()
...
>>> if LOAD == True:
... loaded_env = make_persist_load.load_env(store_path)
...
>>>
EDIT based on discussion in comments:
Looks like it's probably an issue with Windows, as that seems to be the only OS the error appears.
EDIT after some work (see: https://github.com/uqfoundation/dill/issues/140):
Using this minimal example, I can reproduce the same error on Windows, while on MacOSX it still works…
# test.py
class Environment():
def __init__(self):
pass
and
# doit.py
import test
import dill
env = test.Environment()
path = "test.pkl"
with open(path, 'w+') as f:
dill.dump(env, f)
with open(path, 'rb') as _f:
_env = dill.load(_f)
print _env
However, if you use open(path, 'r') as _f, it works on both Windows and MacOSX. So it looks like the __import__ on Windows is more sensitive to file type than on non-Windows systems. Still, throwing an ImportError is weird… but this one small change should make it work.
In case someone is having same problem, I had the same problem running Python 2.7 and the problem was the pickle file created on windows while I am running Linux, what I had to do is running dos2unix which has to be downloaded first using
sudo yum install dos2unix
And then you need to convert the pickle file example
dos2unix data.p
Related
I have a code which I cannot alter named temp.py which contains
if __name__ == "__main__":
*some pieces of code not a function call*
I want to import temp into an other file which I can edit but importing doesnot run the above part. I know I can use subprocess to run temp.py but that is not what I want. I want to import the module entirely but cannot alter the code.
I have heard of a module called imp but is depreceated now.
EDIT:
I am aware that code under the if statement is not meant to be excecuted when imported, but lets just assume temp.py is written in a really worse way and I cannot alter it.
You are trying to go against the import system. Pretty much any thing you end up doing to achieve this will be a hack.
Consider we have a file:
(py39) Juans-MacBook-Pro:~ juan$ cat subvert.py
def foo(x):
print("Hello, ", x)
if __name__ == "__main__":
foo("Goodbye")
Note, if I open up a REPL, I'm in __main__, so at your top-level script, you could just do:
(py39) Juans-MacBook-Pro:~ juan$ python
Python 3.9.5 (default, May 18 2021, 12:31:01)
[Clang 10.0.0 ] :: Anaconda, Inc. on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> __name__
'__main__'
>>> exec(open("subvert.py").read())
Hello, Goodbye
Of course, just execing the source code directly in the same namespace is probably not what you want...
Note, we can pass our own namespace:
>>> namespace = {"__name__": "__main__"}
>>> exec(open("subvert.py").read(), namespace)
Hello, Goodbye
So our module's namespace won't be clobbered.
Now, if you want an actual module after it, you could hack together something like:
>>> import types
>>> module = types.ModuleType("__main__")
>>> module
<module '__main__'>
>>> exec(open("subvert.py").read(), module.__dict__)
Hello, Goodbye
>>> module.foo('bar')
Hello, bar
I wouldn't expect any of this to work well. It won't have all the attributes of a fully loaded module, look at importlib if you find you need to flesh it out more.
something like this is really bad practice but will work:
file1.py has the "main" part:
def foo1():
print("foo1")
if __name__ == '__main__':
print("main function started")
foo1()
print("main function finished")
file2.py is an intermediary file:
from file1 import *
with open('file1.py') as f:
lines = f.readlines()
a = "if __name__ == '__main__':\n"
new_main = ["def main():\n"] + lines[lines.index(a)+1:]
exec("".join(new_main))
file3.py used to import file2 as if it is the file1 module:
import file2
file2.main()
file2.foo1()
output when running file3.py:
main function started
foo1
main function finished
foo1
Python programmers use following mechanism for avoiding importing a main script on another scripts.
if __name__ == "__main__":
# TODO
Read its philosophia here.
When some code used if __name__ == "__main__": means to you shouldn't import its code and this work isn't true.
But you can use this mechanism :
def main():
# TODO
if __name__ == "__main__":
main()
In this case everyone can import your main script and use main function.
I am new to Python and am playing with Pickle and don't understand how this works
I define a defaultdict, write it to pickle. Then in a different script I read it and it still behaves like a defaultdict even without importing collections
script1:
import pickle
from collections import defaultdict
x = defaultdict(list)
x['a'].append(1)
print(x)
with open('pick','wb') as f:
pickle.dump( x, f )
script2:
import pickle
with open('pick','rb') as f:
x = pickle.load( f )
x['b'].append(2)
print(x)
y = dict()
try:
y['b'].append(2)
print(y)
except KeyError:
print("Can't append to y")
running:
$ python3 pick2.py
defaultdict(<class 'list'>, {'a': [1], 'b': [2]})
Can't append to y
So, the 2nd script doesn't import defaultdict but the pickled x still acts like one. I'm confused :)
How does this work in Python? Thanks for any info :)
First of all, if you look at the pickle docs, specifically:
pickle can save and restore class instances transparently, however the class definition must be importable and live in the same module as when the object was stored
So what this is telling us is that pickle will import the module that defines the object you are unpickling.
We can show this with a small example, consider the following folder structure:
parent/
|-- a.py
|-- sub/
sub is an empty sub-folder
a.py holds an example class
# a.py
class ExampleClass:
def __init__(self):
self.var = 'This is a string'
Now starting the python console in the parent directory:
alex#toaster:parent$ python3
>>> import pickle
>>> from a import ExampleClass
>>> x = ExampleClass()
>>> x.var
'This is a string'
>>> with open('eg.p', 'wb') as f:
... pickle.dump(x, f)
Exit the shell. Move to the sub directory and try to load the pickled ExampleClass object.
alex#toaster:sub$ python3
>>> import pickle
>>> with open('../eg.p', 'rb') as f:
... x = pickle.load(f)
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ModuleNotFoundError: No module named 'a'
We get a ModuleNotFoundError as pickle cannot load the class definition from the module a (it's in a different directory). In your case, python can load the collections.defaultdict class as this module is on the PYTHONPATH. However, to continue to use the module(s) imported by pickle you will still need to import them yourself; eg you want to create another defaultdict in script2.py.
To find out more about modules look here, specifically 6.1.2 The Module Search Path.
I would like to know if it is possible to obtain the module name and directory path from a class instance of that module.
I need that because I save a class instance with pickle and I need to know from where this class come from when I load it from another software (on the same computer).
update
So I tried the aswer of #albar
>>> from Models import Model_physio_moyen
>>> b = Model_physio_moyen.pop_Sigmoid_outside_noise
>>> module_name = sys.modules[b.__module__].__name__
>>> module_name
'Models.Model_physio_moyen'
So that working except if the class has been compiled with jitclassof numba:
>>> from Models.Pops_Stim import ATN
>>> a = ATN.pop_ATN()
>>> module_name = sys.modules[a.__module__].__name__
>>> module_name
'numba.jitclass.boxing'
In this condition, I wish I could find Models.Pops_Stim but I'm getting 'numba.jitclass.boxing'instead.
Assuming your instance is called a:
import sys
module_name = sys.modules[a.__module__].__name__
module_file = sys.modules[a.__module__].__file__
In short, how can this happen?
cternus#astarael:~⟫ python
Python 2.7.12 (default, Jun 29 2016, 14:05:02)
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import backports
>>> import imp
>>> imp.find_module('backports')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named backports
The imp module claims to be "an interface to the mechanisms used to implement the import statement." If this is so, why can the import statement find backports, but imp.find_module() can't?
For some background: backports claims to be a "namespace package," not a package in its own right; other modules, such as backports.shutil_get_terminal_size, are situated in this namespace. This formed the basis of an ultimately-rejected PEP. I'm asking this question because I'm having a variant of this issue and am trying to track down the cause.
For more weirdness:
>>> backports.__file__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__file__'
>>> dir(backports)
['__doc__', '__name__', '__path__']
>>> backports.__path__
['/Library/Python/2.7/site-packages/backports']
>>> import os; os.path.exists(backports.__path__[0])
False
(And no, I have no files or directories named backports or backports.py anywhere on my system.)
Edited to clarify: I am aware that this probably represents a strange configuration state of my system. My question is not "how can I fix this" but "how is it possible?"
This module can be brought by python-configparser APT package.
how is it possible?
This is possible because python-configparser uses path configuration file (.pth file):
root#ubuntu18.10:/# dpkg -L python-configparser | head | tail -n 1
/usr/lib/python2.7/dist-packages/configparser-3.5.0b2-nspkg.pth
This file is automatically picked up by python's site module on interpreter startup because it is located in /usr/lib/python2.7/dist-packages/ and has .pth extension. As docs say:
A path configuration file is a file whose name has the form name.pth and exists in one of the four directories mentioned above...
Lines starting with import (followed by space or tab) are executed.
The file /usr/lib/python2.7/dist-packages/configparser-3.5.0b2-nspkg.pth contains the following:
import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('backports',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('backports', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('backports', [os.path.dirname(p)])));m = m or sys.modules.setdefault('backports', types.ModuleType('backports'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p)
So, this code is auto-executed on python startup. Slightly beautified, it looks like this:
import sys, types, os
has_mfs = sys.version_info > (3, 5)
p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('backports',))
importlib = has_mfs and __import__('importlib.util')
has_mfs and __import__('importlib.machinery')
m = has_mfs and sys.modules.setdefault('backports', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('backports', [os.path.dirname(p)])))
m = m or sys.modules.setdefault('backports', types.ModuleType('backports'))
mp = (m or []) and m.__dict__.setdefault('__path__',[])
(p not in mp) and mp.append(p)
What it does (at least on python 2) is: it manually creates a module object by invoking types.ModuleType constructor (that's why it looks like <module 'backports' (built-in)>) and puts it to sys.modules with sys.modules.setdefault('backports', types.ModuleType('backports')). After it was added to sys.modules, import backports will just return that object.
__path__ gives a hint
root#ubuntu18.10:/# python -c 'import backports; print backports.__path__'
['/usr/lib/python2.7/dist-packages/backports']
root#ubuntu18.10:/# dpkg -S /usr/lib/python2.7/dist-packages/backports
python-configparser: /usr/lib/python2.7/dist-packages/backports
I have no files or directories named backports
On Ubuntu, there is /usr/lib/python2.7/dist-packages/backports brought by this package as shown above, so I'm not sure why you don't have it. Maybe it's another package behaving similarly/variant of it for MacOS is different/you just deleted that dir while debugging the issue?
I have a trusted remote server that stores many custom Python modules. I can fetch them via HTTP (e.g. using urllib2.urlopen) as text/plain, but I cannot save the fetched module code to the local hard disk. How can I import the code as a fully operable Python module, including its global variables and imports?
I suppose I have to use some combination of exec and imp module's functions, but I've been unable to make it work yet.
It looks like this should do the trick: importing a dynamically generated module
>>> import imp
>>> foo = imp.new_module("foo")
>>> foo_code = """
... class Foo:
... pass
... """
>>> exec foo_code in foo.__dict__
>>> foo.Foo.__module__
'foo'
>>>
Also, as suggested in the ActiveState article, you might want to add your new module to sys.modules:
>>> import sys
>>> sys.modules["foo"] = foo
>>> from foo import Foo
<class 'Foo' …>
>>>
Here's something I bookmarked a while back that covers something similar:
Customizing the Python Import System
It's a bit beyond what you want, but the basic idea is there.
Python3 version
(attempted to edit other answer but the edit que is full)
import imp
my_dynamic_module = imp.new_module("my_dynamic_module")
exec("""
class Foo:
pass
""", my_dynamic_module.__dict__)
Foo = my_dynamic_module.Foo
foo_object = Foo()
# register it on sys
import sys
sys.modules[my_dynamic_module.__name__] = my_dynamic_module
I recently encountered trying to do this while trying to write unit tests for source code examples I put into a project's readme (I wanted to avoid just linking to small files or duplicating the text in a way that could get out of sync).
I came up with the following
import sys
import types
from importlib import import_module
def compile_and_install_module(module_name: str, source_code: str) -> types.ModuleType:
"""Compile source code and install it as a module.
End result is that `import <module_name>` and `from <module_name> import ...` should work.
"""
module = types.ModuleType(module_name, "Module created from source code")
# Execute source in context of empty/fake module
exec(source_code, module.__dict__)
# Insert fake module into sys.modules. It's now a real module
sys.modules[module_name] = module
# Imports should work now
return import_module(module_name)
And a quick example of how you can use it
$ cat hello.py
def foo():
print("Hello world")
bar = 42
$ python
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC v.1928 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from compile import compile_and_install_module
>>> compile_and_install_module("hello", open("hello.py").read())
<module 'hello'>
>>> import hello
>>> hello.foo()
Hello world
>>> from hello import bar
>>> bar
42
You can remove the return value and import_lib import if you