Skip to content

Commit 0038905

Browse files
Initial commit.
0 parents  commit 0038905

File tree

5 files changed

+225
-0
lines changed

5 files changed

+225
-0
lines changed

README.rst

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
.. -*- mode: rst -*-
2+
3+
Pickler2
4+
===================================
5+
6+
Small package allowing to decorate a function so that it automatically pickle its results
7+
and load it the next time it is called.
8+
9+
Example of usage
10+
===================================
11+
12+
With the following code, the first time long_function() will be called, it will execute and
13+
save the pickle processed_data. Next time it is run, it will load the pickled object without
14+
running the function.
15+
16+
.. code-block:: python
17+
18+
from pickler2 import pickled
19+
20+
@pickled("some_file_name.pck")
21+
def long_function():
22+
... long time to process data ...
23+
return processed_data
24+
25+
To explicitly re-run the function, you can call
26+
27+
.. code-block:: python
28+
29+
long_function(run=True)
30+
31+
When using this functionality at different places, within a project, you might want to save
32+
all the pickles at a same place. This can be done by given full name to the decorator
33+
34+
.. code-block:: python
35+
36+
@pickled("/some/long/path/some_file_name.pck")
37+
def long_function():
38+
... long time to process data ...
39+
return processed_data
40+
41+
Alternatively, the project pickling path can be registered as follow
42+
43+
.. code-block:: python
44+
45+
from pickler2 import register_project_path
46+
47+
register_project_path("/some/long/path/", "my_project")
48+
49+
and then use provide the project name to the constructor:
50+
51+
.. code-block:: python
52+
53+
@pickled("some_file_name.pck", project_name="my_project")
54+
def long_function():
55+
... long time to process data ...
56+
return processed_data
57+
58+
The registered pickled path are saved in "~/.pickler_conf.yaml". This allow to run the same code
59+
on different systems with differents path, as long as the corresponding path have been registered
60+
in their respective configuration path.
61+
62+
63+
Why the 2 in Piclker2?
64+
===================================
65+
Because Pickler was already taken on PyPi!
66+
67+
Licensing
68+
^^^^^^^^^
69+
70+
Pickler2 is **BSD-licenced** (3 clause):
71+
72+
This software is OSI Certified Open Source Software.
73+
OSI Certified is a certification mark of the Open Source Initiative.
74+
75+
Copyright (c) 2011-2019, authors of MNE-Python.
76+
All rights reserved.
77+
78+
Redistribution and use in source and binary forms, with or without
79+
modification, are permitted provided that the following conditions are met:
80+
81+
* Redistributions of source code must retain the above copyright notice,
82+
this list of conditions and the following disclaimer.
83+
84+
* Redistributions in binary form must reproduce the above copyright notice,
85+
this list of conditions and the following disclaimer in the documentation
86+
and/or other materials provided with the distribution.
87+
88+
* Neither the names of MNE-Python authors nor the names of any
89+
contributors may be used to endorse or promote products derived from
90+
this software without specific prior written permission.
91+
92+
**This software is provided by the copyright holders and contributors
93+
"as is" and any express or implied warranties, including, but not
94+
limited to, the implied warranties of merchantability and fitness for
95+
a particular purpose are disclaimed. In no event shall the copyright
96+
owner or contributors be liable for any direct, indirect, incidental,
97+
special, exemplary, or consequential damages (including, but not
98+
limited to, procurement of substitute goods or services; loss of use,
99+
data, or profits; or business interruption) however caused and on any
100+
theory of liability, whether in contract, strict liability, or tort
101+
(including negligence or otherwise) arising in any way out of the use
102+
of this software, even if advised of the possibility of such
103+
damage.**

pickler2/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .pickler import pickled, register_project_path

pickler2/pickler.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import functools
2+
import pickle
3+
from pathlib import Path
4+
from warnings import warn
5+
from yaml import load, dump
6+
try:
7+
from yaml import CLoader as Loader, CDumper as Dumper
8+
except ImportError:
9+
from yaml import Loader, Dumper
10+
11+
12+
config_path = Path.home() / ".pickler_conf.yaml"
13+
14+
15+
def register_project_path(project_name, pickling_path):
16+
config = load_config()
17+
18+
Path(pickling_path).mkdir(parents=True, exist_ok=True)
19+
config[project_name] = str(pickling_path)
20+
21+
with config_path.open("w") as stream:
22+
dump(config, stream, Dumper=Dumper)
23+
24+
25+
def load_config():
26+
if config_path.exists():
27+
with config_path.open("r") as stream:
28+
return load(stream, Loader=Loader)
29+
return {}
30+
31+
32+
def pickled(file_name, run=None, load=None, project_name=None):
33+
def pickler_decorator(func):
34+
@functools.wraps(func)
35+
def wrapper(file_name, *args, run=run, load=load, project_name=project_name, **kwargs):
36+
37+
if project_name is not None:
38+
config = load_config()
39+
if project_name not in config:
40+
raise ValueError("This project is unknown. Please register it first using " +
41+
"register_project_path(project_name, pickling_path).")
42+
43+
pickling_path = Path(config[project_name])
44+
else:
45+
pickling_path = None
46+
47+
if isinstance(file_name, str):
48+
file_name = Path(file_name)
49+
elif not isinstance(file_name, Path):
50+
raise TypeError("Unrecognized type ({}).".format(type(file_name)))
51+
52+
if not file_name.is_absolute():
53+
if pickling_path is not None:
54+
file_name = pickling_path / file_name
55+
else:
56+
if pickling_path is not None:
57+
warn("The file_name passed is absolute. The project pickling path has been ignored.")
58+
59+
if run or not file_name.exists():
60+
data = func(*args, **kwargs)
61+
with file_name.open("wb") as pickle_stream:
62+
pickle.dump(data, pickle_stream)
63+
return data
64+
65+
with file_name.open("rb") as pickle_stream:
66+
return pickle.load(pickle_stream)
67+
68+
return functools.partial(wrapper, file_name)
69+
return pickler_decorator

pickler2/tests/test_pickler.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from pathlib import Path
2+
from pickler2 import pickled, register_project_path
3+
4+
5+
def test_pickler(project_name=None, pickling_path=None):
6+
7+
if project_name is not None:
8+
register_project_path(project_name, pickling_path)
9+
10+
file_name = Path("test_pickler.pck")
11+
if pickling_path is None:
12+
if file_name.exists():
13+
file_name.unlink()
14+
else:
15+
if (Path(pickling_path) / file_name).exists():
16+
(Path(pickling_path) / file_name).unlink()
17+
18+
@pickled(file_name, project_name=project_name)
19+
def dummy_func1():
20+
return 1
21+
22+
dummy_func1()
23+
if pickling_path is None:
24+
assert(file_name.exists())
25+
else:
26+
assert((Path(pickling_path) / file_name).exists())
27+
28+
@pickled(file_name, project_name=project_name)
29+
def dummy_func2():
30+
return 2
31+
32+
assert(dummy_func2() == 1)
33+
34+
dummy_func2(run=True)
35+
assert(dummy_func2() == 2)
36+
37+
38+
def test_pickler_with_registration():
39+
test_pickler("test_project", str(Path(__file__).parent / "test"))

setup.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from setuptools import setup, find_packages
2+
3+
setup(
4+
name='pickler',
5+
version='0.0.1',
6+
url='https://github.com/christian-oreilly/pickler.git',
7+
author="Christian O'Reilly",
8+
author_email='[email protected]',
9+
description="Small package allowing to decorate a function so that it automatically pickle its results" +
10+
" and load it the next time it is called.",
11+
packages=find_packages(),
12+
install_requires=["numpy"],
13+
)

0 commit comments

Comments
 (0)