Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Session: improvements to documentation + handle unpickleable objects #527

Open
wants to merge 33 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ab13325
code formatting changes
leogama Jul 16, 2022
33ca2ed
remove unnecessary '_main_modified' attribute from pickler
leogama Jul 16, 2022
ad8db21
new _open() function to handle file names and file-like objects
leogama Jul 16, 2022
da4cc07
merge function _make_peekable() with _open()
leogama Jul 16, 2022
1732e3d
new function is_module_pickle()
leogama Jul 16, 2022
2fdd31d
move session-related code to session.py submodule
leogama Jul 16, 2022
6b55755
session: deal with modules with unpickleable objects
leogama Jul 19, 2022
aac47b5
disable framing when using the 'refonfail' option
leogama Jul 19, 2022
5e4d912
rename is_module_pickle() to is_pickled_module(); fix _dill's __all__
leogama Jul 21, 2022
699f30a
sync with branch 'session-excludes'
leogama Jul 22, 2022
04968f3
refonfail: save modules by reference using save_reduce()
leogama Jul 22, 2022
a596126
unpickleable ctypes objects raise ValueError...
leogama Jul 22, 2022
d3837cf
Merge branch 'master' into document-session
leogama Jul 23, 2022
f46d399
move common autodoc options to conf.py and exclude some special members
leogama Jul 24, 2022
bef5795
don't document trace() twice
leogama Jul 24, 2022
953b5e0
fix is_pickled_module()
leogama Jul 26, 2022
e5da1c8
deteail the effects of 'module' argument in load_module() and rename …
leogama Jul 30, 2022
2e4887c
Better describe the side effects and the usage of the returned value …
leogama Jul 30, 2022
be319c8
describe session module behavior and use cases
leogama Jul 30, 2022
a9ea883
add Python License and copyright notice for modified code as specifie…
leogama Aug 1, 2022
b722431
revert addition of PSF license; add link to license
leogama Aug 2, 2022
2a7e984
_open(): cover all the possible file opening modes
leogama Aug 3, 2022
fa4fa85
grammar
leogama Aug 3, 2022
92318a7
better document Pickler.save() and Pickler._save_module_dict()
leogama Aug 3, 2022
4fc2f5f
Merge branch 'master' into document-session
leogama Aug 4, 2022
0e365f5
move session settings to session.py; changes to refonfail
leogama Aug 4, 2022
9c54e34
add _TruncatableWriter to handle 'refonfail' with non-seekable streams
leogama Aug 4, 2022
ffdd180
update 'refonfail' example
leogama Aug 4, 2022
d5b1701
Merge branch 'master' into document-session
leogama Aug 13, 2022
f60d239
merge the two save() methods and save_module_dict() with _save_module…
leogama Aug 14, 2022
4fe577b
minor
leogama Aug 14, 2022
d059d84
grammar; keep __weakref__ attribute in docs
leogama Aug 14, 2022
8bf6157
Add option 'refonfail' for dump_module (enabled by default)
leogama Oct 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions dill/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@
dump_module, load_module, load_module_asdict,
dump_session, load_session # backward compatibility
)
from . import detect, logger, session, source, temp
from . import detect, logging, session, source, temp

# get global settings
from .settings import settings

# make sure "trace" is turned off
logger.trace(False)
logging.trace(False)

from importlib import reload

Expand Down
340 changes: 296 additions & 44 deletions dill/_dill.py

Large diffs are not rendered by default.

122 changes: 122 additions & 0 deletions dill/_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/usr/bin/env python
#
# Author: Leonardo Gama (@leogama)
# Copyright (c) 2022 The Uncertainty Quantification Foundation.
# License: 3-clause BSD. The full license text is available at:
# - https://github.com/uqfoundation/dill/blob/master/LICENSE
"""
Auxiliary classes and functions used in more than one module, defined here to
avoid circular import problems.
"""

import contextlib
import io
import math
from contextlib import suppress

#NOTE: dill._dill is not completely loaded at this point, can't import from it.
from dill import _dill

# Type hints.
from typing import Tuple, Union

def _format_bytes_size(size: Union[int, float]) -> Tuple[int, str]:
"""Return bytes size text representation in human-redable form."""
unit = "B"
power_of_2 = math.trunc(size).bit_length() - 1
magnitude = min(power_of_2 - power_of_2 % 10, 80) # 2**80 == 1 YiB
if magnitude:
# Rounding trick: 1535 (1024 + 511) -> 1K; 1536 -> 2K
size = ((size >> magnitude-1) + 1) >> 1
unit = "%siB" % "KMGTPEZY"[(magnitude // 10) - 1]
return size, unit


## File-related utilities ##

class _PeekableReader(contextlib.AbstractContextManager):
"""lightweight readable stream wrapper that implements peek()"""
def __init__(self, stream, closing=True):
self.stream = stream
self.closing = closing
def __exit__(self, *exc_info):
if self.closing:
self.stream.close()
def read(self, n):
return self.stream.read(n)
def readline(self):
return self.stream.readline()
def tell(self):
return self.stream.tell()
def close(self):
return self.stream.close()
def peek(self, n):
stream = self.stream
try:
if hasattr(stream, 'flush'):
stream.flush()
position = stream.tell()
stream.seek(position) # assert seek() works before reading
chunk = stream.read(n)
stream.seek(position)
return chunk
except (AttributeError, OSError):
raise NotImplementedError("stream is not peekable: %r", stream) from None

class _SeekableWriter(io.BytesIO, contextlib.AbstractContextManager):
"""works as an unlimited buffer, writes to file on close"""
def __init__(self, stream, closing=True, *args, **kwds):
super().__init__(*args, **kwds)
self.stream = stream
self.closing = closing
def __exit__(self, *exc_info):
self.close()
def close(self):
self.stream.write(self.getvalue())
with suppress(AttributeError):
self.stream.flush()
super().close()
if self.closing:
self.stream.close()

def _open(file, mode, *, peekable=False, seekable=False):
"""return a context manager with an opened file-like object"""
readonly = ('r' in mode and '+' not in mode)
if not readonly and peekable:
raise ValueError("the 'peekable' option is invalid for writable files")
if readonly and seekable:
raise ValueError("the 'seekable' option is invalid for read-only files")
should_close = not hasattr(file, 'read' if readonly else 'write')
if should_close:
file = open(file, mode)
# Wrap stream in a helper class if necessary.
if peekable and not hasattr(file, 'peek'):
# Try our best to return it as an object with a peek() method.
if hasattr(file, 'seekable'):
file_seekable = file.seekable()
elif hasattr(file, 'seek') and hasattr(file, 'tell'):
try:
file.seek(file.tell())
file_seekable = True
except Exception:
file_seekable = False
else:
file_seekable = False
if file_seekable:
file = _PeekableReader(file, closing=should_close)
else:
try:
file = io.BufferedReader(file)
except Exception:
# It won't be peekable, but will fail gracefully in _identify_module().
file = _PeekableReader(file, closing=should_close)
elif seekable and (
not hasattr(file, 'seek')
or not hasattr(file, 'truncate')
or (hasattr(file, 'seekable') and not file.seekable())
):
file = _SeekableWriter(file, closing=should_close)
if should_close or isinstance(file, (_PeekableReader, _SeekableWriter)):
return file
else:
return contextlib.nullcontext(file)
2 changes: 1 addition & 1 deletion dill/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from inspect import ismethod, isfunction, istraceback, isframe, iscode

from .pointers import parent, reference, at, parents, children
from .logger import trace
from .logging import trace

__all__ = ['baditems','badobjects','badtypes','code','errors','freevars',
'getmodule','globalvars','nestedcode','nestedglobals','outermost',
Expand Down
Loading