pickle-debug 1.2__tar.gz → 2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pickle_debug
3
- Version: 1.2
3
+ Version: 2.0
4
4
  Summary: Store expensive objects from debugging
5
5
  Author: Gerard
6
6
  Author-email: gweatherby@uchc.edu
@@ -59,6 +59,35 @@ slow.invoke = pickle_debug.cached(slow.invoke) # one instance
59
59
  Slow.invoke = pickle_debug.cached(Slow.invoke) # all instances
60
60
  ```
61
61
 
62
+ The decorator also works inside a class body. For a classmethod, put `@cached` below `@classmethod`:
63
+
64
+ ```python
65
+ class Slow:
66
+ @pickle_debug.cached
67
+ def invoke(self, x):
68
+ ...
69
+
70
+ @classmethod
71
+ @pickle_debug.cached
72
+ def build(cls, x):
73
+ ...
74
+ ```
75
+
76
+ A wrapper may be needed to cache some functions, e.g. a method returning a generator:
77
+
78
+ ```python
79
+ def something(x,y,z)->Generator:
80
+
81
+ @pickle_debug.cached
82
+ def cache_something(x,y,z)->list:
83
+ return list(something(x,y,z))
84
+ ```
85
+
86
+ When a cached function is called as a method, `self` (or `cls`) is keyed by its class's name
87
+ (`module.QualifiedName`), not its current value. All instances of a class share cache entries, and the
88
+ instance does not need to be picklable. Subclasses get their own entries. Static methods are keyed like
89
+ plain functions.
90
+
62
91
  ## API
63
92
 
64
93
  ### `enable(directory)`
@@ -70,7 +99,8 @@ Decorator. Returns a wrapper (using `functools.wraps`) that:
70
99
 
71
100
  1. calls `func` directly if caching is not enabled;
72
101
  2. otherwise builds a key from `func.__module__`, `func.__qualname__`, the positional arguments and
73
- the sorted keyword arguments, and hashes it with SHA-256;
102
+ the sorted keyword arguments, and hashes it with SHA-256. If `func` is called as a method, the
103
+ first argument (`self` or `cls`) is replaced by its class's name (see [Methods](#methods));
74
104
  3. returns the pickled result from `<directory>/<hash>.pickle` if present;
75
105
  4. otherwise calls `func`, writes the result to the cache, and returns it.
76
106
 
@@ -101,13 +131,15 @@ wrong place. It does nothing if caching is not enabled or the directory does not
101
131
  [Unpicklable attributes](#unpicklable-attributes)). The arguments are pickled to build the key, and the
102
132
  result is pickled to store it. Because dropped attributes are not part of the key, two arguments that
103
133
  differ only in unpicklable attributes share a cache entry.
134
+ - **Wrappers may be required** for methods that return generators.
104
135
  - **The key is the pickled arguments.** Equal arguments that pickle differently (for example sets with a
105
136
  different iteration order) may miss the cache. Objects whose pickle changes between calls never hit it.
106
137
  - **The key does not include the function's code or any files it reads.** If you change the function or
107
138
  its input data, call `reset()` or use a new cache directory, or you will get stale results.
108
- - **Bound methods and `self`.** A wrapped bound method (`slow.invoke`) does not include `self` in the key,
109
- so all instances share entries. A wrapped class function (`Slow.invoke`) includes `self`, so the
110
- instance must be picklable and its state is part of the key.
139
+ - **Instance state is not part of the key.** Methods are keyed by the class of `self` or `cls`, not by its
140
+ value, so all instances of a class share entries. If a method's result depends on instance attributes,
141
+ instances with different state will get each other's cached results. Pass that state as an argument
142
+ instead, or call `reset()` between runs.
111
143
  - This is a debugging aid. Do not use it as a production cache. Cached files are loaded with `pickle`,
112
144
  so only use a directory you trust.
113
145
 
@@ -47,6 +47,35 @@ slow.invoke = pickle_debug.cached(slow.invoke) # one instance
47
47
  Slow.invoke = pickle_debug.cached(Slow.invoke) # all instances
48
48
  ```
49
49
 
50
+ The decorator also works inside a class body. For a classmethod, put `@cached` below `@classmethod`:
51
+
52
+ ```python
53
+ class Slow:
54
+ @pickle_debug.cached
55
+ def invoke(self, x):
56
+ ...
57
+
58
+ @classmethod
59
+ @pickle_debug.cached
60
+ def build(cls, x):
61
+ ...
62
+ ```
63
+
64
+ A wrapper may be needed to cache some functions, e.g. a method returning a generator:
65
+
66
+ ```python
67
+ def something(x,y,z)->Generator:
68
+
69
+ @pickle_debug.cached
70
+ def cache_something(x,y,z)->list:
71
+ return list(something(x,y,z))
72
+ ```
73
+
74
+ When a cached function is called as a method, `self` (or `cls`) is keyed by its class's name
75
+ (`module.QualifiedName`), not its current value. All instances of a class share cache entries, and the
76
+ instance does not need to be picklable. Subclasses get their own entries. Static methods are keyed like
77
+ plain functions.
78
+
50
79
  ## API
51
80
 
52
81
  ### `enable(directory)`
@@ -58,7 +87,8 @@ Decorator. Returns a wrapper (using `functools.wraps`) that:
58
87
 
59
88
  1. calls `func` directly if caching is not enabled;
60
89
  2. otherwise builds a key from `func.__module__`, `func.__qualname__`, the positional arguments and
61
- the sorted keyword arguments, and hashes it with SHA-256;
90
+ the sorted keyword arguments, and hashes it with SHA-256. If `func` is called as a method, the
91
+ first argument (`self` or `cls`) is replaced by its class's name (see [Methods](#methods));
62
92
  3. returns the pickled result from `<directory>/<hash>.pickle` if present;
63
93
  4. otherwise calls `func`, writes the result to the cache, and returns it.
64
94
 
@@ -89,13 +119,15 @@ wrong place. It does nothing if caching is not enabled or the directory does not
89
119
  [Unpicklable attributes](#unpicklable-attributes)). The arguments are pickled to build the key, and the
90
120
  result is pickled to store it. Because dropped attributes are not part of the key, two arguments that
91
121
  differ only in unpicklable attributes share a cache entry.
122
+ - **Wrappers may be required** for methods that return generators.
92
123
  - **The key is the pickled arguments.** Equal arguments that pickle differently (for example sets with a
93
124
  different iteration order) may miss the cache. Objects whose pickle changes between calls never hit it.
94
125
  - **The key does not include the function's code or any files it reads.** If you change the function or
95
126
  its input data, call `reset()` or use a new cache directory, or you will get stale results.
96
- - **Bound methods and `self`.** A wrapped bound method (`slow.invoke`) does not include `self` in the key,
97
- so all instances share entries. A wrapped class function (`Slow.invoke`) includes `self`, so the
98
- instance must be picklable and its state is part of the key.
127
+ - **Instance state is not part of the key.** Methods are keyed by the class of `self` or `cls`, not by its
128
+ value, so all instances of a class share entries. If a method's result depends on instance attributes,
129
+ instances with different state will get each other's cached results. Pass that state as an argument
130
+ instead, or call `reset()` between runs.
99
131
  - This is a debugging aid. Do not use it as a production cache. Cached files are loaded with `pickle`,
100
132
  so only use a directory you trust.
101
133
 
@@ -2,7 +2,7 @@
2
2
  name = "pickle_debug"
3
3
  description = "Store expensive objects from debugging"
4
4
  license = {text = "MIT" }
5
- version = "1.2"
5
+ version = "2.0"
6
6
  dependencies = []
7
7
  requires-python= ">= 3.12"
8
8
  readme = "README.md"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pickle_debug
3
- Version: 1.2
3
+ Version: 2.0
4
4
  Summary: Store expensive objects from debugging
5
5
  Author: Gerard
6
6
  Author-email: gweatherby@uchc.edu
@@ -59,6 +59,35 @@ slow.invoke = pickle_debug.cached(slow.invoke) # one instance
59
59
  Slow.invoke = pickle_debug.cached(Slow.invoke) # all instances
60
60
  ```
61
61
 
62
+ The decorator also works inside a class body. For a classmethod, put `@cached` below `@classmethod`:
63
+
64
+ ```python
65
+ class Slow:
66
+ @pickle_debug.cached
67
+ def invoke(self, x):
68
+ ...
69
+
70
+ @classmethod
71
+ @pickle_debug.cached
72
+ def build(cls, x):
73
+ ...
74
+ ```
75
+
76
+ A wrapper may be needed to cache some functions, e.g. a method returning a generator:
77
+
78
+ ```python
79
+ def something(x,y,z)->Generator:
80
+
81
+ @pickle_debug.cached
82
+ def cache_something(x,y,z)->list:
83
+ return list(something(x,y,z))
84
+ ```
85
+
86
+ When a cached function is called as a method, `self` (or `cls`) is keyed by its class's name
87
+ (`module.QualifiedName`), not its current value. All instances of a class share cache entries, and the
88
+ instance does not need to be picklable. Subclasses get their own entries. Static methods are keyed like
89
+ plain functions.
90
+
62
91
  ## API
63
92
 
64
93
  ### `enable(directory)`
@@ -70,7 +99,8 @@ Decorator. Returns a wrapper (using `functools.wraps`) that:
70
99
 
71
100
  1. calls `func` directly if caching is not enabled;
72
101
  2. otherwise builds a key from `func.__module__`, `func.__qualname__`, the positional arguments and
73
- the sorted keyword arguments, and hashes it with SHA-256;
102
+ the sorted keyword arguments, and hashes it with SHA-256. If `func` is called as a method, the
103
+ first argument (`self` or `cls`) is replaced by its class's name (see [Methods](#methods));
74
104
  3. returns the pickled result from `<directory>/<hash>.pickle` if present;
75
105
  4. otherwise calls `func`, writes the result to the cache, and returns it.
76
106
 
@@ -101,13 +131,15 @@ wrong place. It does nothing if caching is not enabled or the directory does not
101
131
  [Unpicklable attributes](#unpicklable-attributes)). The arguments are pickled to build the key, and the
102
132
  result is pickled to store it. Because dropped attributes are not part of the key, two arguments that
103
133
  differ only in unpicklable attributes share a cache entry.
134
+ - **Wrappers may be required** for methods that return generators.
104
135
  - **The key is the pickled arguments.** Equal arguments that pickle differently (for example sets with a
105
136
  different iteration order) may miss the cache. Objects whose pickle changes between calls never hit it.
106
137
  - **The key does not include the function's code or any files it reads.** If you change the function or
107
138
  its input data, call `reset()` or use a new cache directory, or you will get stale results.
108
- - **Bound methods and `self`.** A wrapped bound method (`slow.invoke`) does not include `self` in the key,
109
- so all instances share entries. A wrapped class function (`Slow.invoke`) includes `self`, so the
110
- instance must be picklable and its state is part of the key.
139
+ - **Instance state is not part of the key.** Methods are keyed by the class of `self` or `cls`, not by its
140
+ value, so all instances of a class share entries. If a method's result depends on instance attributes,
141
+ instances with different state will get each other's cached results. Pass that state as an argument
142
+ instead, or call `reset()` between runs.
111
143
  - This is a debugging aid. Do not use it as a production cache. Cached files are loaded with `pickle`,
112
144
  so only use a directory you trust.
113
145
 
@@ -8,6 +8,4 @@ src/pickle_debug.egg-info/dependency_links.txt
8
8
  src/pickle_debug.egg-info/top_level.txt
9
9
  src/test/patch_test.py
10
10
  src/test/pd_test.py
11
- src/test/record.py
12
- src/test/ttest.py
13
- src/test/unpicklable_test.py
11
+ src/test/ttest.py
@@ -1,11 +0,0 @@
1
- import pickle_debug
2
-
3
- def sum(x,y):
4
- print(f"{x} + {y}")
5
- return x + y
6
-
7
- pickle_debug.enable("/tmp/sum")
8
- ans = pickle_debug.record("sum",sum,3,4)
9
- print(ans)
10
- ans = pickle_debug.record("sum",sum,3,4)
11
- print(ans)
@@ -1,46 +0,0 @@
1
- import pathlib
2
- import tempfile
3
- import threading
4
-
5
- from pickle_debug import cached, enable
6
-
7
-
8
- class Holder:
9
- def __init__(self, value):
10
- self.value = value
11
- self.lock = threading.Lock()
12
- self.inner = Inner()
13
- self.me = self
14
-
15
-
16
- class Inner:
17
- def __init__(self):
18
- self.ok = 'inner'
19
- self.callback = lambda: None
20
-
21
-
22
- class Slotted:
23
- __slots__ = ('a', 'lock')
24
-
25
- def __init__(self):
26
- self.a = 1
27
- self.lock = threading.Lock()
28
-
29
-
30
- calls = []
31
-
32
-
33
- @cached
34
- def make(x):
35
- calls.append(x)
36
- return Holder(x), Slotted()
37
-
38
-
39
- enable(pathlib.Path(tempfile.mkdtemp()) / 'cache')
40
- make(5)
41
- h, s = make(5)
42
- assert calls == [5], calls
43
- assert h.value == 5 and not hasattr(h, 'lock') and h.me is h
44
- assert h.inner.ok == 'inner' and not hasattr(h.inner, 'callback')
45
- assert s.a == 1 and not hasattr(s, 'lock')
46
- print('ok')
File without changes
File without changes
File without changes