PKRL1{{h5glance/__init__.py"""Explore HDF5 files in an HTML view""" from .generate import H5Glance, install_ipython_h5py_display __version__ = "0.1" PKRLkh5glance/__main__.pyfrom .cli import main main() PKRL&th5glance/cli.pyimport argparse import h5py from http.server import HTTPServer, BaseHTTPRequestHandler from pathlib import Path import sys import threading import webbrowser from .generate import make_document def main(argv=None): ap = argparse.ArgumentParser(prog="h5glance", description="View HDF5 file structure in HTML") ap.add_argument("input", help="HDF5 file to view", type=Path) ap.add_argument("-w", "--write", metavar="HTML_FILE", help="Write output to HTML file.") args = ap.parse_args(argv) if not args.input.is_file(): print("Not a file:", args.input) sys.exit(2) elif not h5py.is_hdf5(args.input): print("Not an HDF5 file:", args.input) sys.exit(2) if args.write: with open("test_output.html", 'w') as f: f.write(str(make_document(args.input))) return serve(args.input) def serve(h5path): class H5ViewHandler(BaseHTTPRequestHandler): def do_GET(self): if self.path != "/": return self.send_error(404) self.send_response(200) self.end_headers() self.wfile.write(str(make_document(h5path)).encode('utf-8')) server = HTTPServer(('localhost', 0), H5ViewHandler) url = "http://{}:{}/".format(server.server_name, server.server_port) print("Serving on", url) t = threading.Timer(0.5, webbrowser.open_new_tab, args=(url,)) t.start() try: server.serve_forever() except KeyboardInterrupt: pass PKRLȢg g h5glance/copypath.js// Based on code by Stackoverflow user Dean Taylor // https://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript/30810322 // Used under Stackoverflow's CC-BY-SA 3.0 license (function() { function copyTextToClipboard(text) { let textArea = document.createElement("textarea"); // // *** This styling is an extra step which is likely not required. *** // // Why is it here? To ensure: // 1. the element is able to have focus and selection. // 2. if element was to flash render it has minimal visual impact. // 3. less flakyness with selection and copying which **might** occur if // the textarea element is not visible. // // The likelihood is the element won't even render, not even a flash, // so some of these are just precautions. However in IE the element // is visible whilst the popup box asking the user for permission for // the web page to copy to the clipboard. // // Place in top-left corner of screen regardless of scroll position. textArea.style.position = 'fixed'; textArea.style.top = 0; textArea.style.left = 0; // Ensure it has a small width and height. Setting to 1px / 1em // doesn't work as this gives a negative w/h on some browsers. textArea.style.width = '2em'; textArea.style.height = '2em'; // We don't need padding, reducing the size if it does flash render. textArea.style.padding = 0; // Clean up any borders. textArea.style.border = 'none'; textArea.style.outline = 'none'; textArea.style.boxShadow = 'none'; // Avoid flash of white box if rendered for any reason. textArea.style.background = 'transparent'; textArea.value = text; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { if (!document.execCommand('copy')) { console.log("Unable to copy text with document.execCommand()"); } } finally { document.body.removeChild(textArea); } } function copy_event_handler(event) { copyTextToClipboard(event.target.dataset.hdf5Path); event.preventDefault(); } function enable_copylinks(parent) { let links = parent.querySelectorAll(".h5glance-dataset-copylink") links.forEach(function (link) { link.addEventListener("click", copy_event_handler); }); } // The code to actually trigger this is substituted below. //ACTIVATE })(); PKRLvGuuh5glance/generate.pyimport h5py from htmlgen import (Document, Element, Division, UnorderedList, Checkbox, Label, ListItem, html_attribute, Span, Link, Script, ) from pathlib import Path _PKGDIR = Path(__file__).parent class Style(Element): def __init__(self, styles): super().__init__('style') self.children.append_raw(styles) class Abbreviation(Element): def __init__(self, *content, title=""): super().__init__("abbr") self.extend(content) self.title = title title = html_attribute('title') class Code(Element): """An HTML in-line element. """ def __init__(self, *content): super().__init__("code") self.extend(content) def id_generator(template): n = 0 while True: yield template % n n += 1 dtype_kind_to_descr = { "u": "unsigned integer", "i": "signed integer", "f": "floating point", "c": "complex floating point", "b": "boolean", "O": "object (e.g. string)", } def make_dtype_abbr(dtobj): desc = "" if dtobj.kind in "uifc": desc += "{}-bit ".format(dtobj.itemsize * 8) try: desc += dtype_kind_to_descr[dtobj.kind] except KeyError: pass code = Code(dtobj.str) if desc: return Abbreviation(code, title=desc) return code def make_list(*items): ul = UnorderedList() ul.extend(items) return ul checkbox_ids = id_generator("h5glance-expand-switch-%d") def checkbox_w_label(label_content): c = Checkbox() c.id = next(checkbox_ids) l = Label(label_content) l.for_ = c.id return [c, l] def item_for_dataset(name, ds): shape = " × ".join(str(n) for n in ds.shape) namespan = Span(name) namespan.add_css_classes("h5glance-dataset-name") copylink = Link("#", "[📋]") copylink.set_attribute("data-hdf5-path", ds.name) copylink.add_css_classes("h5glance-dataset-copylink") li = ListItem( namespan, " ", copylink, ": ", shape, " entries, dtype: ", make_dtype_abbr(ds.dtype) ) li.add_css_classes("h5glance-dataset") return li def item_for_group(gname, grp): subgroups, datasets = [], [] for name, obj in sorted(grp.items()): if isinstance(obj, h5py.Group): subgroups.append((name, obj)) else: datasets.append((name, obj)) return ListItem(*checkbox_w_label(gname), make_list( *[item_for_group(n, g) for n, g in subgroups], *[item_for_dataset(n, d) for n, d in datasets], )) def file_or_grp_name(obj): if isinstance(obj, h5py.File): return obj.filename elif isinstance(obj, h5py.Group): return obj.name return obj treeview_ids = id_generator("h5glance-container-%d") def make_fragment(obj): if isinstance(obj, h5py.Group): name = file_or_grp_name(obj) ct = make_list(item_for_group(name, obj)) elif isinstance(obj, (str, Path)) and h5py.is_hdf5(obj): with h5py.File(obj, 'r') as f: return make_fragment(f) else: raise TypeError("Unknown object type: {!r}".format(obj)) # Expand first level first_chkbx = ct.children.children[0].children.children[0] # Yuck first_chkbx.checked = True tv = Division(ct) tv.add_css_classes("h5glance-css-treeview") tv.id = next(treeview_ids) return tv def get_treeview_css(): with (_PKGDIR / 'treeview.css').open() as f: return Style(f.read()) JS_ACTIVATE_COPYLINKS_DOC = """ window.addEventListener("load", function(event) { enable_copylinks(document); }); """ JS_ACTIVATE_COPYLINKS_FRAG = """ enable_copylinks(document.getElementById("TREEVIEW-ID")); """ def get_copylinks_js(activation): with (_PKGDIR / "copypath.js").open() as f: return f.read().replace("//ACTIVATE", activation) def make_document(obj): d = Document() d.append_head(get_treeview_css()) d.append_head(Script(script=get_copylinks_js(JS_ACTIVATE_COPYLINKS_DOC))) d.title = "{} - H5Glance".format(file_or_grp_name(obj)) d.append_body(make_fragment(obj)) return d def h5obj_to_html(obj): treeview = make_fragment(obj) js_activate = JS_ACTIVATE_COPYLINKS_FRAG.replace("TREEVIEW-ID", treeview.id) div = Division( get_treeview_css(), treeview, Script(script=get_copylinks_js(js_activate)), ) return str(div) class H5Glance: """View an HDF5 object in a Jupyter notebook""" def __init__(self, obj): self.obj = obj def _repr_html_(self): return h5obj_to_html(self.obj) def install_ipython_h5py_display(): """Call inside IPython to install HTML views for h5py groups and files""" from IPython import get_ipython ip = get_ipython() if ip is None: raise EnvironmentError("This function is to be called in IPython") html_formatter = ip.display_formatter.formatters['text/html'] html_formatter.for_type(h5py.Group, h5obj_to_html) PKRL77h5glance/treeview.css/* Based on https://css-tricks.com/snippets/css/nested-expandable-folders/ Reused with permission from https://css-tricks.com/license/ */ .h5glance-css-treeview ul, .h5glance-css-treeview li { padding: 0; margin: 0; margin-left: 12px; list-style: none; } /* Override padding from Jupyter CSS */ .rendered_html .h5glance-css-treeview ul { padding: 0; } /* Make the checkbox itself invisible */ .h5glance-css-treeview input { position: absolute; opacity: 0; } .h5glance-css-treeview { font-family: sans-serif; -moz-user-select: none; -webkit-user-select: none; user-select: none; } .h5glance-css-treeview code { font-size: 12pt; font-family: "courier", monospace; } .h5glance-css-treeview a.h5glance-dataset-copylink { text-decoration: none; } /* These next two bits drive the expanding/collapsing behaviour */ .h5glance-css-treeview input + label + ul { display: none; } .h5glance-css-treeview input:checked:not(:disabled) + label + ul { display: block; } .h5glance-css-treeview label, .h5glance-css-treeview label::before { display: inline-block; height: 16px; line-height: 16px;, vertical-align: middle; } .h5glance-css-treeview label { margin-left: -1.5em; } .h5glance-css-treeview label::before { content: "+"; width: 1.5em; vertical-align: middle; } .h5glance-css-treeview input:checked + label::before { content: "–"; } /* webkit adjacent element selector bugfix */ @media screen and (-webkit-min-device-pixel-ratio:0) { .h5glance-css-treeview { -webkit-animation: webkit-adjacent-element-selector-bugfix infinite 1s; } @-webkit-keyframes webkit-adjacent-element-selector-bugfix { from { padding: 0; } to { padding: 0; } } } .h5glance-css-treeview li.h5glance-dataset { margin-bottom: 3px; } .h5glance-dataset-name { font-weight: bold; } PK!H ).'h5glance-0.1.dist-info/entry_points.txtN+I/N.,()0MIKN1s2r3PKRL}  h5glance-0.1.dist-info/LICENSEBSD 3-Clause License Copyright (c) 2017, European X-Ray Free-Electron Laser Facility GmbH All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK!HNOh5glance-0.1.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,zd&Y)r$[)T&UrPK!HMh5glance-0.1.dist-info/METADATA}Rr@WI<D#Zci؝u (.Ri{[Hr)If1Dl 򗢖KCq^06O0a VJceAZ-`Q̜~'FcY!yLArӸPT62[}[?U32qF*]w`+N*`̾o_SQadZgK-,DZƨV 0%'?ԎunBrG›cAU1z.Xj>3eS=F **T#iip'NmPK!HCFh5glance-0.1.dist-info/RECORDuv@} T.zAbP2H(O߽1w{n!uҤ=˦qhgf($Ih.HGlYμ!!x&Wz/J?.B]HHbDn[;:J.n>u55:}'Q,ybOðvkm,M<ySR0:lJv!/;Fma$kØ'(4ç0oPKRL1{{h5glance/__init__.pyPKRLkh5glance/__main__.pyPKRL&th5glance/cli.pyPKRLȢg g ?h5glance/copypath.jsPKRLvGuuh5glance/generate.pyPKRL77%h5glance/treeview.cssPK!H ).',h5glance-0.1.dist-info/entry_points.txtPKRL}  W-h5glance-0.1.dist-info/LICENSEPK!HNO3h5glance-0.1.dist-info/WHEELPK!HM'4h5glance-0.1.dist-info/METADATAPK!HCF6h5glance-0.1.dist-info/RECORDPK V8