|
16 | 16 | """Method for saving arbitrary data to arbitrary destinations. |
17 | 17 |
|
18 | 18 | This module takes an object and URL, infers how to serialize and how to write |
19 | | -it out to the destination. |
| 19 | +it out to the destination. The intention is to preserve your work under most |
| 20 | +circumstances, so sometimes this will convert values by default and warn rather than |
| 21 | +error out immediately. This sometimes means less predictable behavior. |
20 | 22 |
|
21 | | -If an object could have multiple representations, this tries to infer the |
22 | | -intended representation from the URL's file extension. |
| 23 | +If an object could have multiple serializations, this tries to infer the |
| 24 | +intended serializations from the URL's file extension. |
23 | 25 |
|
24 | 26 | Possible extension: if not given a URL this could create one and return it? |
25 | 27 | """ |
26 | 28 |
|
27 | 29 | from __future__ import absolute_import, division, print_function |
28 | 30 |
|
29 | 31 | import logging |
| 32 | +import warnings |
30 | 33 | import os.path |
31 | 34 | import json |
32 | 35 | import numpy as np |
33 | 36 | import PIL.Image |
34 | 37 |
|
35 | | -from lucid.misc.io.writing import write, write_handle |
| 38 | +from lucid.misc.io.writing import write_handle |
36 | 39 | from lucid.misc.io.serialize_array import _normalize_array |
37 | 40 |
|
38 | 41 |
|
|
41 | 44 |
|
42 | 45 |
|
43 | 46 | def save_json(object, handle, indent=2): |
44 | | - """Save object as json on CNS.""" |
45 | | - obj_json = json.dumps(object, indent=indent) |
46 | | - handle.write(obj_json) |
| 47 | + """Save object as json on CNS.""" |
| 48 | + obj_json = json.dumps(object, indent=indent) |
| 49 | + handle.write(obj_json) |
47 | 50 |
|
48 | 51 |
|
49 | 52 | def save_npy(object, handle): |
50 | | - """Save numpy array as npy file.""" |
51 | | - np.save(handle, object) |
| 53 | + """Save numpy array as npy file.""" |
| 54 | + np.save(handle, object) |
52 | 55 |
|
53 | 56 |
|
54 | 57 | def save_npz(object, handle): |
55 | | - """Save dict of numpy array as npz file.""" |
56 | | - # there is a bug where savez doesn't actually accept a file handle. |
57 | | - log.warning("Saving npz files currently only works locally. :/") |
58 | | - path = handle.name |
59 | | - handle.close() |
60 | | - if type(object) is dict: |
61 | | - np.savez(path, **object) |
62 | | - elif type(object) is list: |
63 | | - np.savez(path, *object) |
64 | | - else: |
65 | | - log.warning("Saving non dict or list as npz file, did you maybe want npy?") |
66 | | - np.savez(path, object) |
| 58 | + """Save dict of numpy array as npz file.""" |
| 59 | + # there is a bug where savez doesn't actually accept a file handle. |
| 60 | + log.warning("Saving npz files currently only works locally. :/") |
| 61 | + path = handle.name |
| 62 | + handle.close() |
| 63 | + if type(object) is dict: |
| 64 | + np.savez(path, **object) |
| 65 | + elif type(object) is list: |
| 66 | + np.savez(path, *object) |
| 67 | + else: |
| 68 | + log.warning("Saving non dict or list as npz file, did you maybe want npy?") |
| 69 | + np.savez(path, object) |
67 | 70 |
|
68 | 71 |
|
69 | 72 | def save_img(object, handle, **kwargs): |
70 | | - """Save numpy array as image file on CNS.""" |
71 | | - if isinstance(object, np.ndarray): |
72 | | - normalized = _normalize_array(object) |
73 | | - image = PIL.Image.fromarray(normalized) |
74 | | - elif isinstance(object, PIL.Image): |
75 | | - image = object |
76 | | - else: |
77 | | - raise ValueError("Can only save_img for numpy arrays or PIL.Images!") |
78 | | - image.save(handle, **kwargs) # will infer format from handle's url ext. |
| 73 | + """Save numpy array as image file on CNS.""" |
| 74 | + |
| 75 | + if isinstance(object, np.ndarray): |
| 76 | + normalized = _normalize_array(object) |
| 77 | + object = PIL.Image.fromarray(normalized) |
| 78 | + |
| 79 | + if isinstance(object, PIL.Image.Image): |
| 80 | + object.save(handle, **kwargs) # will infer format from handle's url ext. |
| 81 | + else: |
| 82 | + raise ValueError("Can only save_img for numpy arrays or PIL.Images!") |
| 83 | + |
| 84 | + |
| 85 | +def save_txt(object, handle, **kwargs): |
| 86 | + if isinstance(object, str): |
| 87 | + handle.write(object) |
| 88 | + elif isinstance(object, list): |
| 89 | + for line in object: |
| 90 | + if not isinstance(line, str): |
| 91 | + line = repr(line) |
| 92 | + warnings.warn( |
| 93 | + "`save_txt` was called with an array containing objects other than " |
| 94 | + "strings; using `repr` to convert values to string." |
| 95 | + ) |
| 96 | + if not line.endswith("\n"): |
| 97 | + line += "\n" |
| 98 | + handle.write(line) |
79 | 99 |
|
80 | 100 |
|
81 | 101 | savers = { |
82 | | - ".png": save_img, |
83 | | - ".jpg": save_img, |
84 | | - ".jpeg": save_img, |
85 | | - ".npy": save_npy, |
86 | | - ".npz": save_npz, |
87 | | - ".json": save_json, |
| 102 | + ".png": save_img, |
| 103 | + ".jpg": save_img, |
| 104 | + ".jpeg": save_img, |
| 105 | + ".npy": save_npy, |
| 106 | + ".npz": save_npz, |
| 107 | + ".json": save_json, |
| 108 | + ".txt": save_txt, |
88 | 109 | } |
89 | 110 |
|
90 | 111 |
|
91 | 112 | def save(thing, url_or_handle, **kwargs): |
92 | | - """Save object to file on CNS. |
93 | | -
|
94 | | - File format is inferred from path. Use save_img(), save_npy(), or save_json() |
95 | | - if you need to force a particular format. |
96 | | -
|
97 | | - Args: |
98 | | - obj: object to save. |
99 | | - path: CNS path. |
100 | | -
|
101 | | - Raises: |
102 | | - RuntimeError: If file extension not supported. |
103 | | - """ |
104 | | - is_handle = hasattr(url_or_handle, 'write') and hasattr(url_or_handle, 'name') |
105 | | - if is_handle: |
106 | | - _, ext = os.path.splitext(url_or_handle.name) |
107 | | - else: |
108 | | - _, ext = os.path.splitext(url_or_handle) |
109 | | - if not ext: |
110 | | - raise RuntimeError("No extension in URL: " + url_or_handle) |
111 | | - |
112 | | - if ext in savers: |
113 | | - saver = savers[ext] |
| 113 | + """Save object to file on CNS. |
| 114 | +
|
| 115 | + File format is inferred from path. Use save_img(), save_npy(), or save_json() |
| 116 | + if you need to force a particular format. |
| 117 | +
|
| 118 | + Args: |
| 119 | + obj: object to save. |
| 120 | + path: CNS path. |
| 121 | +
|
| 122 | + Raises: |
| 123 | + RuntimeError: If file extension not supported. |
| 124 | + """ |
| 125 | + is_handle = hasattr(url_or_handle, "write") and hasattr(url_or_handle, "name") |
114 | 126 | if is_handle: |
115 | | - saver(thing, url_or_handle, **kwargs) |
| 127 | + _, ext = os.path.splitext(url_or_handle.name) |
| 128 | + else: |
| 129 | + _, ext = os.path.splitext(url_or_handle) |
| 130 | + if not ext: |
| 131 | + raise RuntimeError("No extension in URL: " + url_or_handle) |
| 132 | + |
| 133 | + if ext in savers: |
| 134 | + saver = savers[ext] |
| 135 | + if is_handle: |
| 136 | + saver(thing, url_or_handle, **kwargs) |
| 137 | + else: |
| 138 | + with write_handle(url_or_handle) as handle: |
| 139 | + saver(thing, handle, **kwargs) |
116 | 140 | else: |
117 | | - with write_handle(url_or_handle) as handle: |
118 | | - saver(thing, handle, **kwargs) |
119 | | - else: |
120 | | - saver_names = [(key, function.__name__) for (key, value) in savers.items()] |
121 | | - message = "Unknown extension '{}', supports {}." |
122 | | - raise RuntimeError(message.format(ext, saver_names)) |
| 141 | + saver_names = [(key, fn.__name__) for (key, fn) in savers.items()] |
| 142 | + message = "Unknown extension '{}', supports {}." |
| 143 | + raise ValueError(message.format(ext, saver_names)) |
0 commit comments