From 86033fa77620b86a336430860ad546cf5c4e2414 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 2 Feb 2020 10:11:51 -0500 Subject: [PATCH 01/80] refactored qm._tm_cache to use a resources.path instead of __file__ (needs cleanup). Should require an __init__ to nmrsim/bin, which was added. --- dev_notes/github_actions/README.txt | 6 +++++- dev_notes/infrequent_operations.txt | 4 ++++ nmrsim/bin/__init__.py | 2 ++ nmrsim/qm.py | 23 +++++++++++++++++++++-- 4 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 nmrsim/bin/__init__.py diff --git a/dev_notes/github_actions/README.txt b/dev_notes/github_actions/README.txt index f6413a4..e21215a 100644 --- a/dev_notes/github_actions/README.txt +++ b/dev_notes/github_actions/README.txt @@ -1,4 +1,8 @@ Considering using GitHub Actions. This folder contains examples to possibly modify. pythonpackage.yml is GH's demo for setting up and testing a python package. -pythonpublish.ym, is GH's demo for uploading a package to PyPI. \ No newline at end of file +pythonpublish.ym, is GH's demo for uploading a package to PyPI. + +This link may be very useful: +https://dan.yeaw.me/posts/github-actions-automate-your-python-development-workflow/ +https://medium.com/swlh/automate-python-testing-with-github-actions-7926b5d8a865 \ No newline at end of file diff --git a/dev_notes/infrequent_operations.txt b/dev_notes/infrequent_operations.txt index 403f079..f49422e 100644 --- a/dev_notes/infrequent_operations.txt +++ b/dev_notes/infrequent_operations.txt @@ -21,6 +21,10 @@ interactive ipywidgets not appearing in Jupyter: conda install nodejs and may also require an npm install. +Recognizing conda envs in Jupyter: + conda install ipykernel + python -m ipykernel install --user --name {EnvironmentName} + virtual environments: python -m venv venv # second venv is name of dir source venv/bin/activate (linux/macOS), or diff --git a/nmrsim/bin/__init__.py b/nmrsim/bin/__init__.py new file mode 100644 index 0000000..ea79640 --- /dev/null +++ b/nmrsim/bin/__init__.py @@ -0,0 +1,2 @@ +# bin may require an __init__ file for importlib.resources. See: +# https://importlib-resources.readthedocs.io/en/latest/using.html \ No newline at end of file diff --git a/nmrsim/qm.py b/nmrsim/qm.py index bd25fd6..cc72746 100644 --- a/nmrsim/qm.py +++ b/nmrsim/qm.py @@ -40,7 +40,9 @@ calculating second-order spectra: one using pydata/sparse and caching, and the other using neither. """ +from importlib import resources import os +import sys import numpy as np import sparse @@ -327,7 +329,25 @@ def _tm_cache(nspins): # provides a modest speed improvement on larger spin systems. filename = f'T{nspins}.npz' bin_dir = os.path.join(os.path.dirname(__file__), 'bin') - path = os.path.join(bin_dir, filename) + print('BIN DIR: ', bin_dir) + # path = os.path.join(bin_dir, filename) + + import nmrsim.bin + # newpath = resources.Path(nmrsim.bin, filename) # returns a context manager + # print('NEWPATH', newpath, type(newpath)) + + # path = Path('..', 'bin', filename) + # path = s2 + # bin_contents = resources.contents(nmrsim.bin) + # print(nmrsim.bin.__name__) + # print('BIN CONTAINS:') + # for name in bin_contents: + # print(name) + # assert resources.is_resource(nmrsim.bin, name) + newpath = resources.path(nmrsim.bin, filename) + with newpath as p: + print('NEW PATH: ', p) + path = p try: T_sparse = sparse.load_npz(path) return T_sparse @@ -338,7 +358,6 @@ def _tm_cache(nspins): sparse.save_npz(path, T_sparse) return T_sparse - def _intensity_and_energy(H, nspins): """ Calculate intensity matrix and energies (eigenvalues) from Hamiltonian. From 2405ec8d3cac04b1a886632381f9242e0cf8eb8f Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 2 Feb 2020 14:52:58 -0500 Subject: [PATCH 02/80] refactored qm._tm_cache to use a resources.path instead of __file__ (needs cleanup). Should require an __init__ to nmrsim/bin, which was added. --- CHANGELOG.rst | 10 ++++++++++ nmrsim/qm.py | 36 ++++++++++-------------------------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 792cde9..12fba36 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -24,6 +24,16 @@ Working towards a Version 1.0.0 release, the author interprets the terms below a * **Version 1.0.0 release**: API is stable. The package is available on PyPI (and perhaps conda). +Unreleased +---------- +Changed +^^^^^^^ +nmrsim.qp now uses importlib.resources.path to find the nmrsim/bin folder, +instead of relying on the use of __file__. +For users, this means that if your application uses nmrsim as a dependency, +and you want to freeze the application (e.g. with PyInstaller or PyOxidizer), +the frozen app should now find the bin folder and contents. + 0.3.0 - 2019-11-08 (beta release) --------------------------------- Added diff --git a/nmrsim/qm.py b/nmrsim/qm.py index cc72746..fdfe318 100644 --- a/nmrsim/qm.py +++ b/nmrsim/qm.py @@ -41,12 +41,11 @@ and the other using neither. """ from importlib import resources -import os -import sys import numpy as np import sparse +import nmrsim.bin from nmrsim.math import normalize_peaklist CACHE = True # saving of partial solutions is allowed @@ -137,16 +136,18 @@ def _so_sparse(nspins): # for user? filename_Lz = f'Lz{nspins}.npz' filename_Lproduct = f'Lproduct{nspins}.npz' - bin_dir = os.path.join(os.path.dirname(__file__), 'bin') - path_Lz = os.path.join(bin_dir, filename_Lz) - path_Lproduct = os.path.join(bin_dir, filename_Lproduct) - + path_context_Lz = resources.path(nmrsim.bin, filename_Lz) + path_context_Lproduct = resources.path(nmrsim.bin, filename_Lproduct) + with path_context_Lz as p: + path_Lz = p + with path_context_Lproduct as p: + path_Lproduct = p try: Lz = sparse.load_npz(path_Lz) Lproduct = sparse.load_npz(path_Lproduct) return Lz, Lproduct except FileNotFoundError: - print('no SO file ', filename_Lz, ' found in: ', bin_dir) + print('no SO file ', path_Lz, ' found.') print(f'creating {filename_Lz} and {filename_Lproduct}') Lz, Lproduct = _so_dense(nspins) Lz_sparse = sparse.COO(Lz) @@ -328,25 +329,8 @@ def _tm_cache(nspins): # Speed tests indicated that using sparse-array transition matrices # provides a modest speed improvement on larger spin systems. filename = f'T{nspins}.npz' - bin_dir = os.path.join(os.path.dirname(__file__), 'bin') - print('BIN DIR: ', bin_dir) - # path = os.path.join(bin_dir, filename) - - import nmrsim.bin - # newpath = resources.Path(nmrsim.bin, filename) # returns a context manager - # print('NEWPATH', newpath, type(newpath)) - - # path = Path('..', 'bin', filename) - # path = s2 - # bin_contents = resources.contents(nmrsim.bin) - # print(nmrsim.bin.__name__) - # print('BIN CONTAINS:') - # for name in bin_contents: - # print(name) - # assert resources.is_resource(nmrsim.bin, name) - newpath = resources.path(nmrsim.bin, filename) - with newpath as p: - print('NEW PATH: ', p) + path_context = resources.path(nmrsim.bin, filename) + with path_context as p: path = p try: T_sparse = sparse.load_npz(path) From 2e17a879c9c4c1ac20cf752b896e8591f50c85cf Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 2 Feb 2020 15:16:18 -0500 Subject: [PATCH 03/80] Added to discrete module's docstring, and fixed errors in ABX docstring. --- nmrsim/discrete.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/nmrsim/discrete.py b/nmrsim/discrete.py index bcb085b..ec7353d 100644 --- a/nmrsim/discrete.py +++ b/nmrsim/discrete.py @@ -13,6 +13,12 @@ * ABX: simulates an ABX system. +* ABX3: simulates an ABX3 system. + +* AAXX: simulates an AA'XX' system. + +* AABB: simulates an AA'BB' system. + References ---------- .. [1] WINDNMR-Pro home page: https://www.chem.wisc.edu/areas/reich/plt/windnmr.htm @@ -157,10 +163,10 @@ def ABX(Jab, Jax, Jbx, Vab, Vcentr, vx, normalize=True): --------- Jab : float The Ha-Hb coupling constant (Hz). - Jbx : float - The Ha-Hb coupling constant (Hz). Jax : float - The Ha-Hb coupling constant (Hz). + The Ha-Hx coupling constant (Hz). + Jbx : float + The Hb-Hx coupling constant (Hz). Vab : float The difference in the frequencies (in the absence of coupling) of Ha and Hb (Hz). From 6795656be5cd03debe6080a3750054aeb9e843a2 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 2 Feb 2020 16:02:50 -0500 Subject: [PATCH 04/80] Fixed Issue #4 --- CHANGELOG.rst | 14 +++++++++----- dev_notes/infrequent_operations.txt | 5 ----- docs/source/jupyter/qm_explanation.ipynb | 23 +++++++++++++++-------- jupyter/qm_explanation.ipynb | 17 ++++++++++++----- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 12fba36..c9f93a9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -28,11 +28,15 @@ Unreleased ---------- Changed ^^^^^^^ -nmrsim.qp now uses importlib.resources.path to find the nmrsim/bin folder, -instead of relying on the use of __file__. -For users, this means that if your application uses nmrsim as a dependency, -and you want to freeze the application (e.g. with PyInstaller or PyOxidizer), -the frozen app should now find the bin folder and contents. +* nmrsim.qp now uses importlib.resources.path to find the nmrsim/bin folder, + instead of relying on the use of __file__. + For users, this means that if your application uses nmrsim as a dependency, + and you want to freeze the application (e.g. with PyInstaller or PyOxidizer), + the frozen app should now find the bin folder and contents. + +Fixed +^^^^^ +* Documentation errors (Issues #2, #4) 0.3.0 - 2019-11-08 (beta release) --------------------------------- diff --git a/dev_notes/infrequent_operations.txt b/dev_notes/infrequent_operations.txt index f49422e..ced0c35 100644 --- a/dev_notes/infrequent_operations.txt +++ b/dev_notes/infrequent_operations.txt @@ -4,11 +4,6 @@ If 'make html' isn't working for some reason, do this from the 'docs' directory: sphinx-apidoc -f -o source/ ../nmrsim/ -When using pipenv, matplotlib on OSX gives an error. The solution is to edit the - matplotlib configuration so as not to use the Mac backend, but TkAgg: - - https://stackoverflow.com/questions/49367013/pipenv-install-matplotlib# - Exporting conda env to a .yml: conda env export --no-builds > environment.yml diff --git a/docs/source/jupyter/qm_explanation.ipynb b/docs/source/jupyter/qm_explanation.ipynb index 8c296eb..392a829 100644 --- a/docs/source/jupyter/qm_explanation.ipynb +++ b/docs/source/jupyter/qm_explanation.ipynb @@ -19,12 +19,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Disclaimer\n", + "A description of the algorithms for computing second-order spectra follows. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Disclaimer\n", "\n", "The author is not an NMR spectroscopist by training. Ultimately, I would like to understand, and be able to explain, the quantum mechanics behind the entire process of simulating an NMR spectrum. For now, here is a \"recipe\" of the steps to arrive at the spin Hamiltonian, and how its eigensolution can be used to calculate frequencies and intensities.\n", "\n", "Two sources in particular enabled this:\n", - "1. Materials by Ilya Kuprov at SpinDynamics.org, particularly [Module I, Lecture 5](http://spindynamics.org/documents/sd_m1_lecture_05.pdf) and the Matlab code of [Module II, Lecture 05](http://spindynamics.org/documents/sd_m2_lecture_05.pdf) and [06](http://spindynamics.org/documents/sd_m2_lecture_06.pdf).\n", + "1. Materials by Ilya Kuprov at SpinDynamics.org, particularly [Module I, Lecture 5](http://spindynamics.org/Spin-Dynamics---Part-I---Lecture-05.php) and the Matlab code of [Module II, Lecture 05](http://spindynamics.org/Spin-Dynamics---Part-II---Lecture-05.php) and [06](http://spindynamics.org/Spin-Dynamics---Part-II---Lecture-06.php).\n", "2. [Materials](http://www.users.csbsju.edu/~frioux/workinprogress.html#Spectroscopy) by Frank Rioux at St. John's University and College of St. Benedict. In particular, [*ABC Proton NMR Using Tensor Algebra*](http://www.users.csbsju.edu/~frioux/nmr/ABC-NMR-Tensor.pdf) was very helpful." ] }, @@ -75,11 +82,11 @@ "metadata": {}, "outputs": [], "source": [ - "home_path = os.path.abspath(os.path.join('..', '..', '..'))\n", + "home_path = os.path.abspath(os.path.join('..'))\n", "if home_path not in sys.path:\n", " sys.path.append(home_path)\n", "\n", - "tests_path = os.path.abspath(os.path.join('..', '..', '..', 'tests'))\n", + "tests_path = os.path.abspath(os.path.join('..', 'tests'))\n", "if tests_path not in sys.path:\n", " sys.path.append(tests_path)" ] @@ -548,12 +555,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "[]\n" + "[]\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -571,9 +578,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "nmrsim", "language": "python", - "name": "python3" + "name": "nmrsim" }, "language_info": { "codemirror_mode": { diff --git a/jupyter/qm_explanation.ipynb b/jupyter/qm_explanation.ipynb index acbb9ce..f1630ee 100644 --- a/jupyter/qm_explanation.ipynb +++ b/jupyter/qm_explanation.ipynb @@ -19,7 +19,14 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Disclaimer\n", + "A description of the algorithms for computing second-order spectra follows. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Disclaimer\n", "\n", "The author is not an NMR spectroscopist by training. Ultimately, I would like to understand, and be able to explain, the quantum mechanics behind the entire process of simulating an NMR spectrum. For now, here is a \"recipe\" of the steps to arrive at the spin Hamiltonian, and how its eigensolution can be used to calculate frequencies and intensities.\n", "\n", @@ -548,12 +555,12 @@ "name": "stdout", "output_type": "stream", "text": [ - "[]\n" + "[]\n" ] }, { "data": { - "image/png": "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\n", + "image/png": "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\n", "text/plain": [ "
" ] @@ -571,9 +578,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "nmrsim", "language": "python", - "name": "python3" + "name": "nmrsim" }, "language_info": { "codemirror_mode": { From 0242372cc8489296270726dcdc1f467f8051966b Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 2 Feb 2020 16:41:10 -0500 Subject: [PATCH 05/80] Fixed Issue #3: interactive demo now uses Bokeh. --- docs/source/jupyter/2-Interactive-Demo.ipynb | 505 ++++++++++++++++--- jupyter/2-Interactive-Demo.ipynb | 503 +++++++++++++++--- 2 files changed, 865 insertions(+), 143 deletions(-) diff --git a/docs/source/jupyter/2-Interactive-Demo.ipynb b/docs/source/jupyter/2-Interactive-Demo.ipynb index 8b47ffb..a6c39d9 100644 --- a/docs/source/jupyter/2-Interactive-Demo.ipynb +++ b/docs/source/jupyter/2-Interactive-Demo.ipynb @@ -9,71 +9,25 @@ "\n", "There are many ways to create interactive plots in a Juypter notebook, and the visualization ecosystem is constantly changing. For example the Holoviz tool suite (http://holoviz.org/) looks promising (especially the possibility of creating a web application using Panel). Another interesting option is nbinteract (https://www.nbinteract.com/).\n", "\n", - "This notebook currently uses ipywidgets and plotly to create some simple NMR demonstrations." + "This notebook currently uses ipywidgets and bokeh to create some simple NMR demonstrations." ] }, { "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - " \n", - " " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib as mpl\n", - "import matplotlib.pyplot as plt\n", - "\n", - "#bokeh + jupyter lab requires \"jupyter labextension install jupyterlab_bokeh\" installation, \n", - "# but 2018-07-28 there is a dependency conflict\n", - "\n", - "# import bokeh.io\n", - "# import bokeh.plotting\n", - "# bokeh.io.output_notebook()\n", - "import plotly\n", - "import plotly.graph_objs as go\n", - "from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot\n", - "init_notebook_mode(connected=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "import os\n", "import sys\n", - "module_path = os.path.abspath(os.path.join('..', '..', '..'))\n", + "module_path = os.path.abspath(os.path.join('..'))\n", "if module_path not in sys.path:\n", " sys.path.append(module_path)" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -125,7 +79,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -140,36 +94,439 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ - "def interactive_ab(va=110, vb=100, J=10, k=0.1, w=0.5):\n", - " args = (va, vb, J, k, w)\n", - " x, y = dnmr_AB(*args)\n", - " obj = go.Scatter(x=x, y=y)\n", - " data = [obj]\n", - " iplot(data)" + "from ipywidgets import interact" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " Loading BokehJS ...\n", + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + "(function(root) {\n", + " function now() {\n", + " return new Date();\n", + " }\n", + "\n", + " var force = true;\n", + "\n", + " if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n", + " root._bokeh_onload_callbacks = [];\n", + " root._bokeh_is_loading = undefined;\n", + " }\n", + "\n", + " var JS_MIME_TYPE = 'application/javascript';\n", + " var HTML_MIME_TYPE = 'text/html';\n", + " var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n", + " var CLASS_NAME = 'output_bokeh rendered_html';\n", + "\n", + " /**\n", + " * Render data to the DOM node\n", + " */\n", + " function render(props, node) {\n", + " var script = document.createElement(\"script\");\n", + " node.appendChild(script);\n", + " }\n", + "\n", + " /**\n", + " * Handle when an output is cleared or removed\n", + " */\n", + " function handleClearOutput(event, handle) {\n", + " var cell = handle.cell;\n", + "\n", + " var id = cell.output_area._bokeh_element_id;\n", + " var server_id = cell.output_area._bokeh_server_id;\n", + " // Clean up Bokeh references\n", + " if (id != null && id in Bokeh.index) {\n", + " Bokeh.index[id].model.document.clear();\n", + " delete Bokeh.index[id];\n", + " }\n", + "\n", + " if (server_id !== undefined) {\n", + " // Clean up Bokeh references\n", + " var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n", + " cell.notebook.kernel.execute(cmd, {\n", + " iopub: {\n", + " output: function(msg) {\n", + " var id = msg.content.text.trim();\n", + " if (id in Bokeh.index) {\n", + " Bokeh.index[id].model.document.clear();\n", + " delete Bokeh.index[id];\n", + " }\n", + " }\n", + " }\n", + " });\n", + " // Destroy server and session\n", + " var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n", + " cell.notebook.kernel.execute(cmd);\n", + " }\n", + " }\n", + "\n", + " /**\n", + " * Handle when a new output is added\n", + " */\n", + " function handleAddOutput(event, handle) {\n", + " var output_area = handle.output_area;\n", + " var output = handle.output;\n", + "\n", + " // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n", + " if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", + " return\n", + " }\n", + "\n", + " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", + "\n", + " if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n", + " toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n", + " // store reference to embed id on output_area\n", + " output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", + " }\n", + " if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", + " var bk_div = document.createElement(\"div\");\n", + " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var script_attrs = bk_div.children[0].attributes;\n", + " for (var i = 0; i < script_attrs.length; i++) {\n", + " toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n", + " }\n", + " // store reference to server id on output_area\n", + " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", + " }\n", + " }\n", + "\n", + " function register_renderer(events, OutputArea) {\n", + "\n", + " function append_mime(data, metadata, element) {\n", + " // create a DOM node to render to\n", + " var toinsert = this.create_output_subarea(\n", + " metadata,\n", + " CLASS_NAME,\n", + " EXEC_MIME_TYPE\n", + " );\n", + " this.keyboard_manager.register_events(toinsert);\n", + " // Render to node\n", + " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", + " render(props, toinsert[toinsert.length - 1]);\n", + " element.append(toinsert);\n", + " return toinsert\n", + " }\n", + "\n", + " /* Handle when an output is cleared or removed */\n", + " events.on('clear_output.CodeCell', handleClearOutput);\n", + " events.on('delete.Cell', handleClearOutput);\n", + "\n", + " /* Handle when a new output is added */\n", + " events.on('output_added.OutputArea', handleAddOutput);\n", + "\n", + " /**\n", + " * Register the mime type and append_mime function with output_area\n", + " */\n", + " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", + " /* Is output safe? */\n", + " safe: true,\n", + " /* Index of renderer in `output_area.display_order` */\n", + " index: 0\n", + " });\n", + " }\n", + "\n", + " // register the mime type if in Jupyter Notebook environment and previously unregistered\n", + " if (root.Jupyter !== undefined) {\n", + " var events = require('base/js/events');\n", + " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", + "\n", + " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", + " register_renderer(events, OutputArea);\n", + " }\n", + " }\n", + "\n", + " \n", + " if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n", + " root._bokeh_timeout = Date.now() + 5000;\n", + " root._bokeh_failed_load = false;\n", + " }\n", + "\n", + " var NB_LOAD_WARNING = {'data': {'text/html':\n", + " \"
\\n\"+\n", + " \"

\\n\"+\n", + " \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n", + " \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n", + " \"

\\n\"+\n", + " \"
    \\n\"+\n", + " \"
  • re-rerun `output_notebook()` to attempt to load from CDN again, or
  • \\n\"+\n", + " \"
  • use INLINE resources instead, as so:
  • \\n\"+\n", + " \"
\\n\"+\n", + " \"\\n\"+\n", + " \"from bokeh.resources import INLINE\\n\"+\n", + " \"output_notebook(resources=INLINE)\\n\"+\n", + " \"\\n\"+\n", + " \"
\"}};\n", + "\n", + " function display_loaded() {\n", + " var el = document.getElementById(\"1819\");\n", + " if (el != null) {\n", + " el.textContent = \"BokehJS is loading...\";\n", + " }\n", + " if (root.Bokeh !== undefined) {\n", + " if (el != null) {\n", + " el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n", + " }\n", + " } else if (Date.now() < root._bokeh_timeout) {\n", + " setTimeout(display_loaded, 100)\n", + " }\n", + " }\n", + "\n", + "\n", + " function run_callbacks() {\n", + " try {\n", + " root._bokeh_onload_callbacks.forEach(function(callback) {\n", + " if (callback != null)\n", + " callback();\n", + " });\n", + " } finally {\n", + " delete root._bokeh_onload_callbacks\n", + " }\n", + " console.debug(\"Bokeh: all callbacks have finished\");\n", + " }\n", + "\n", + " function load_libs(css_urls, js_urls, callback) {\n", + " if (css_urls == null) css_urls = [];\n", + " if (js_urls == null) js_urls = [];\n", + "\n", + " root._bokeh_onload_callbacks.push(callback);\n", + " if (root._bokeh_is_loading > 0) {\n", + " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", + " return null;\n", + " }\n", + " if (js_urls == null || js_urls.length === 0) {\n", + " run_callbacks();\n", + " return null;\n", + " }\n", + " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", + " root._bokeh_is_loading = css_urls.length + js_urls.length;\n", + "\n", + " function on_load() {\n", + " root._bokeh_is_loading--;\n", + " if (root._bokeh_is_loading === 0) {\n", + " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", + " run_callbacks()\n", + " }\n", + " }\n", + "\n", + " function on_error() {\n", + " console.error(\"failed to load \" + url);\n", + " }\n", + "\n", + " for (var i = 0; i < css_urls.length; i++) {\n", + " var url = css_urls[i];\n", + " const element = document.createElement(\"link\");\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.rel = \"stylesheet\";\n", + " element.type = \"text/css\";\n", + " element.href = url;\n", + " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", + " document.body.appendChild(element);\n", + " }\n", + "\n", + " for (var i = 0; i < js_urls.length; i++) {\n", + " var url = js_urls[i];\n", + " var element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " };var element = document.getElementById(\"1819\");\n", + " if (element == null) {\n", + " console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1819' but no matching script tag was found. \")\n", + " return false;\n", + " }\n", + "\n", + " function inject_raw_css(css) {\n", + " const element = document.createElement(\"style\");\n", + " element.appendChild(document.createTextNode(css));\n", + " document.body.appendChild(element);\n", + " }\n", + "\n", + " \n", + " var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-1.4.0.min.js\"];\n", + " var css_urls = [];\n", + " \n", + "\n", + " var inline_js = [\n", + " function(Bokeh) {\n", + " Bokeh.set_log_level(\"info\");\n", + " },\n", + " function(Bokeh) {\n", + " \n", + " \n", + " }\n", + " ];\n", + "\n", + " function run_inline_js() {\n", + " \n", + " if (root.Bokeh !== undefined || force === true) {\n", + " \n", + " for (var i = 0; i < inline_js.length; i++) {\n", + " inline_js[i].call(root, root.Bokeh);\n", + " }\n", + " if (force === true) {\n", + " display_loaded();\n", + " }} else if (Date.now() < root._bokeh_timeout) {\n", + " setTimeout(run_inline_js, 100);\n", + " } else if (!root._bokeh_failed_load) {\n", + " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", + " root._bokeh_failed_load = true;\n", + " } else if (force !== true) {\n", + " var cell = $(document.getElementById(\"1819\")).parents('.cell').data().cell;\n", + " cell.output_area.append_execute_result(NB_LOAD_WARNING)\n", + " }\n", + "\n", + " }\n", + "\n", + " if (root._bokeh_is_loading === 0) {\n", + " console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", + " run_inline_js();\n", + " } else {\n", + " load_libs(css_urls, js_urls, function() {\n", + " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + " run_inline_js();\n", + " });\n", + " }\n", + "}(window));" + ], + "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\n \n\n \n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n var NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"
    \\n\"+\n \"
  • re-rerun `output_notebook()` to attempt to load from CDN again, or
  • \\n\"+\n \"
  • use INLINE resources instead, as so:
  • \\n\"+\n \"
\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n var el = document.getElementById(\"1819\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };var element = document.getElementById(\"1819\");\n if (element == null) {\n console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1819' but no matching script tag was found. \")\n return false;\n }\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n \n var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-1.4.0.min.js\"];\n var css_urls = [];\n \n\n var inline_js = [\n function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\n function(Bokeh) {\n \n \n }\n ];\n\n function run_inline_js() {\n \n if (root.Bokeh !== undefined || force === true) {\n \n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n if (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n var cell = $(document.getElementById(\"1819\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n\n }\n\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(css_urls, js_urls, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from bokeh.io import push_notebook, show, output_notebook\n", + "from bokeh.plotting import figure\n", + "output_notebook()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "# get initial xy data\n", + "x, y = dnmr_AB(*args)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "p = figure(title = 'DNMR AB Interactive Plot',\n", + " plot_height=300,\n", + " plot_width=600)\n", + "r = p.line(x, y)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ - "from ipywidgets import interactive" + "def interactive_ab(va=110, vb=100, J=10, k=0.1, w=0.5):\n", + " args = (va, vb, J, k, w)\n", + " x, y = dnmr_AB(*args)\n", + " r.data_source.data['y'] = y\n", + " r.data_source.data['x'] = x\n", + " push_notebook()\n" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 28, "metadata": {}, "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "(function(root) {\n", + " function embed_document(root) {\n", + " \n", + " var docs_json = {\"abb7f6e8-d85a-45ad-9129-6083fee87ca5\":{\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1831\",\"type\":\"LinearAxis\"}],\"center\":[{\"id\":\"1835\",\"type\":\"Grid\"},{\"id\":\"1840\",\"type\":\"Grid\"}],\"left\":[{\"id\":\"1836\",\"type\":\"LinearAxis\"}],\"plot_height\":300,\"renderers\":[{\"id\":\"1857\",\"type\":\"GlyphRenderer\"}],\"title\":{\"id\":\"1821\",\"type\":\"Title\"},\"toolbar\":{\"id\":\"1847\",\"type\":\"Toolbar\"},\"x_range\":{\"id\":\"1823\",\"type\":\"DataRange1d\"},\"x_scale\":{\"id\":\"1827\",\"type\":\"LinearScale\"},\"y_range\":{\"id\":\"1825\",\"type\":\"DataRange1d\"},\"y_scale\":{\"id\":\"1829\",\"type\":\"LinearScale\"}},\"id\":\"1820\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1845\",\"type\":\"ResetTool\"},{\"attributes\":{\"ticker\":{\"id\":\"1832\",\"type\":\"BasicTicker\"}},\"id\":\"1835\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1844\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"1881\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"text\":\"DNMR AB Interactive Plot\"},\"id\":\"1821\",\"type\":\"Title\"},{\"attributes\":{\"callback\":null},\"id\":\"1825\",\"type\":\"DataRange1d\"},{\"attributes\":{\"formatter\":{\"id\":\"1878\",\"type\":\"BasicTickFormatter\"},\"ticker\":{\"id\":\"1832\",\"type\":\"BasicTicker\"}},\"id\":\"1831\",\"type\":\"LinearAxis\"},{\"attributes\":{\"callback\":null},\"id\":\"1823\",\"type\":\"DataRange1d\"},{\"attributes\":{\"overlay\":{\"id\":\"1880\",\"type\":\"BoxAnnotation\"}},\"id\":\"1843\",\"type\":\"BoxZoomTool\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1856\",\"type\":\"Line\"},{\"attributes\":{\"dimension\":1,\"ticker\":{\"id\":\"1837\",\"type\":\"BasicTicker\"}},\"id\":\"1840\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1882\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1827\",\"type\":\"LinearScale\"},{\"attributes\":{\"data_source\":{\"id\":\"1854\",\"type\":\"ColumnDataSource\"},\"glyph\":{\"id\":\"1855\",\"type\":\"Line\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1856\",\"type\":\"Line\"},\"selection_glyph\":null,\"view\":{\"id\":\"1858\",\"type\":\"CDSView\"}},\"id\":\"1857\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1837\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1876\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1841\",\"type\":\"PanTool\"},{\"id\":\"1842\",\"type\":\"WheelZoomTool\"},{\"id\":\"1843\",\"type\":\"BoxZoomTool\"},{\"id\":\"1844\",\"type\":\"SaveTool\"},{\"id\":\"1845\",\"type\":\"ResetTool\"},{\"id\":\"1846\",\"type\":\"HelpTool\"}]},\"id\":\"1847\",\"type\":\"Toolbar\"},{\"attributes\":{\"formatter\":{\"id\":\"1876\",\"type\":\"BasicTickFormatter\"},\"ticker\":{\"id\":\"1837\",\"type\":\"BasicTicker\"}},\"id\":\"1836\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1829\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1878\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1846\",\"type\":\"HelpTool\"},{\"attributes\":{},\"id\":\"1841\",\"type\":\"PanTool\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"lightgrey\"},\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":{\"value\":1.0},\"line_color\":{\"value\":\"black\"},\"line_dash\":[4,4],\"line_width\":{\"value\":2},\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"1880\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"source\":{\"id\":\"1854\",\"type\":\"ColumnDataSource\"}},\"id\":\"1858\",\"type\":\"CDSView\"},{\"attributes\":{\"callback\":null,\"data\":{\"x\":{\"__ndarray__\":\"AAAAAAAASUD6jLhACiBJQPQZcYEUQElA7qYpwh5gSUDoM+ICKYBJQOLAmkMzoElA3E1ThD3ASUDV2gvFR+BJQM9nxAVSAEpAyfR8RlwgSkDDgTWHZkBKQL0O7sdwYEpAt5umCHuASkCxKF9JhaBKQKu1F4qPwEpApULQypngSkCfz4gLpABLQJhcQUyuIEtAkun5jLhAS0CMdrLNwmBLQIYDaw7NgEtAgJAjT9egS0B6HdyP4cBLQHSqlNDr4EtAbjdNEfYATEBoxAVSACFMQGJRvpIKQUxAXN520xRhTEBWay8UH4FMQFD451QpoUxASYWglTPBTEBDElnWPeFMQD2fERdIAU1ANyzKV1IhTUAxuYKYXEFNQCtGO9lmYU1AJdPzGXGBTUAfYKxae6FNQBntZJuFwU1AE3od3I/hTUAMB9YcmgFOQAaUjl2kIU5AACFHnq5BTkD6rf/euGFOQPQ6uB/DgU5A7sdwYM2hTkDoVCmh18FOQOLh4eHh4U5A3G6aIuwBT0DW+1Jj9iFPQNCIC6QAQk9AyhXE5ApiT0DEonwlFYJPQL4vNWYfok9At7ztpinCT0CxSabnM+JPQFZrLxQfAVBA0rGLNCQRUEBQ+OdUKSFQQMw+RHUuMVBASYWglTNBUEDGy/y1OFFQQEMSWdY9YVBAwFi19kJxUEA9nxEXSIFQQLrlbTdNkVBANyzKV1KhUEC0ciZ4V7FQQDG5gphcwVBArv/euGHRUEArRjvZZuFQQKiMl/lr8VBAJdPzGXEBUUCiGVA6dhFRQB9grFp7IVFAnKYIe4AxUUAZ7WSbhUFRQJYzwbuKUVFAE3od3I9hUUCQwHn8lHFRQAwH1hyagVFAik0yPZ+RUUAGlI5dpKFRQITa6n2psVFAACFHnq7BUUB9Z6O+s9FRQPqt/9644VFAd/Rb/73xUUD0OrgfwwFSQHGBFEDIEVJA7sdwYM0hUkBrDs2A0jFSQOhUKaHXQVJAZZuFwdxRUkDi4eHh4WFSQF8oPgLncVJA3G6aIuyBUkBZtfZC8ZFSQNb7UmP2oVJAU0Kvg/uxUkDQiAukAMJSQE3PZ8QF0lJAyhXE5AriUkBGXCAFEPJSQMSifCUVAlNAQOnYRRoSU0C+LzVmHyJTQDp2kYYkMlNAt7ztpilCU0A0A0rHLlJTQLFJpuczYlNALpACCDlyU0Cr1l4oPoJTQCgdu0hDklNApWMXaUiiU0AiqnOJTbJTQJ/wz6lSwlNAHDcsylfSU0CZfYjqXOJTQBbE5Api8lNAkwpBK2cCVEAQUZ1LbBJUQI2X+WtxIlRACt5VjHYyVECHJLKse0JUQARrDs2AUlRAgLFq7YViVED+98YNi3JUQHo+Iy6QglRA+IR/TpWSVEB0y9tumqJUQPIROI+fslRAbliUr6TCVEDsnvDPqdJUQGjlTPCu4lRA5SupELTyVEBicgUxuQJVQN+4YVG+ElVAXP+9ccMiVUDZRRqSyDJVQFaMdrLNQlVA09LS0tJSVUBQGS/z12JVQM1fixPdclVASqbnM+KCVUDH7ENU55JVQEQzoHTsolVAwHn8lPGyVUA+wFi19sJVQLoGtdX70lVAOE0R9gDjVUC0k20WBvNVQDLayTYLA1ZAriAmVxATVkAsZ4J3FSNWQKit3pcaM1ZAJvQ6uB9DVkCiOpfYJFNWQB+B8/gpY1ZAnMdPGS9zVkAZDqw5NINWQJZUCFo5k1ZAE5tkej6jVkCQ4cCaQ7NWQA0oHbtIw1ZAim55203TVkAHtdX7UuNWQIT7MRxY81ZAAUKOPF0DV0B+iOpcYhNXQPrORn1nI1dAeBWjnWwzV0D0W/+9cUNXQHKiW952U1dA7ui3/ntjV0BsLxQfgXNXQOh1cD+Gg1dAZrzMX4uTV0DiAimAkKNXQGBJhaCVs1dA3I/hwJrDV0BZ1j3hn9NXQNYcmgGl41dAU2P2IarzV0DQqVJCrwNYQE3wrmK0E1hAyjYLg7kjWEBHfWejvjNYQMTDw8PDQ1hAQQog5MhTWEC+UHwEzmNYQDuX2CTTc1hAuN00RdiDWEA0JJFl3ZNYQLJq7YXio1hALrFJpuezWECs96XG7MNYQCg+Aufx01hApoReB/fjWEAiy7on/PNYQKARF0gBBFlAHFhzaAYUWUCans+ICyRZQBblK6kQNFlAkyuIyRVEWUAQcuTpGlRZQI24QAogZFlACv+cKiV0WUCHRflKKoRZQASMVWsvlFlAgdKxizSkWUD+GA6sObRZQHtfasw+xFlA+KXG7EPUWUB17CINSeRZQPIyfy1O9FlAbnnbTVMEWkDsvzduWBRaQGgGlI5dJFpA5kzwrmI0WkBik0zPZ0RaQODZqO9sVFpAXCAFEHJkWkDaZmEwd3RaQFatvVB8hFpA1PMZcYGUWkBQOnaRhqRaQM2A0rGLtFpASscu0pDEWkDHDYvyldRaQERU5xKb5FpAwZpDM6D0WkA+4Z9TpQRbQLsn/HOqFFtAOG5YlK8kW0C1tLS0tDRbQDL7ENW5RFtAr0Ft9b5UW0AsiMkVxGRbQKjOJTbJdFtAJhWCVs6EW0CiW95205RbQCCiOpfYpFtAnOiWt920W0AaL/PX4sRbQJZ1T/jn1FtAFLyrGO3kW0CQAgg58vRbQA5JZFn3BFxAio/AefwUXEAI1hyaASVcQIQceboGNVxAAWPV2gtFXEB+qTH7EFVcQPvvjRsWZVxAeDbqOxt1XED1fEZcIIVcQHLDonwllVxA7wn/nCqlXEBsUFu9L7VcQOmWt900xVxAZt0T/jnVXEDjI3AeP+VcQGBqzD5E9VxA3bAoX0kFXUBa94R/ThVdQNc94Z9TJV1AVIQ9wFg1XUDQypngXUVdQE0R9gBjVV1AyldSIWhlXUBHnq5BbXVdQMTkCmJyhV1AQStngneVXUC+ccOifKVdQDu4H8OBtV1AuP5744bFXUA1RdgDjNVdQLKLNCSR5V1AL9KQRJb1XUCsGO1kmwVeQClfSYWgFV5ApqWlpaUlXkAj7AHGqjVeQKAyXuavRV5AHXm6BrVVXkCavxYnumVeQBcGc0e/dV5AlEzPZ8SFXkARkyuIyZVeQI7Zh6jOpV5ACiDkyNO1XkCHZkDp2MVeQAStnAne1V5AgfP4KePlXkD+OVVK6PVeQHuAsWrtBV9A+MYNi/IVX0B1DWqr9yVfQPJTxsv8NV9Ab5oi7AFGX0Ds4H4MB1ZfQGkn2ywMZl9A5m03TRF2X0BjtJNtFoZfQOD6740bll9AXUFMriCmX0Dah6jOJbZfQFfOBO8qxl9A1BRhDzDWX0BRW70vNeZfQM6hGVA69l9AJvQ6uB8DYEBkF2lIIgtgQKI6l9gkE2BA4F3FaCcbYEAfgfP4KSNgQF6kIYksK2BAnMdPGS8zYEDa6n2pMTtgQBkOrDk0Q2BAWDHayTZLYECWVAhaOVNgQNR3Nuo7W2BAE5tkej5jYEBSvpIKQWtgQJDhwJpDc2BAzgTvKkZ7YEANKB27SINgQExLS0tLi2BAim55202TYEDIkadrUJtgQAe11ftSo2BARtgDjFWrYECE+zEcWLNgQMIeYKxau2BAAUKOPF3DYEBAZbzMX8tgQH6I6lxi02BAvKsY7WTbYED6zkZ9Z+NgQDnydA1q62BAeBWjnWzzYEC2ONEtb/tgQPRb/71xA2FAM38tTnQLYUByolvedhNhQLDFiW55G2FA7ui3/nsjYUAtDOaOfithQGwvFB+BM2FAqlJCr4M7YUDodXA/hkNhQCeZns+IS2FAZrzMX4tTYUCk3/rvjVthQOICKYCQY2FAISZXEJNrYUBgSYWglXNhQJ5sszCYe2FA3I/hwJqDYUAasw9RnYthQFnWPeGfk2FAmPlrcaKbYUDWHJoBpaNhQBRAyJGnq2FAU2P2IaqzYUCShiSyrLthQNCpUkKvw2FADs2A0rHLYUBN8K5itNNhQIwT3fK222FAyjYLg7njYUAIWjkTvOthQEd9Z6O+82FAhqCVM8H7YUDEw8PDwwNiQALn8VPGC2JAQQog5MgTYkCALU50yxtiQL5QfATOI2JA/HOqlNArYkA7l9gk0zNiQHq6BrXVO2JAuN00RdhDYkD2AGPV2ktiQDQkkWXdU2JAc0e/9d9bYkCyau2F4mNiQPCNGxbla2JALrFJpudzYkBt1Hc26ntiQKz3pcbsg2JA6hrUVu+LYkAoPgLn8ZNiQGdhMHf0m2JApoReB/ejYkDkp4yX+atiQCLLuif8s2JAYe7ot/67YkCgERdIAcRiQN40RdgDzGJAHFhzaAbUYkBbe6H4CNxiQJqez4gL5GJA2MH9GA7sYkAW5SupEPRiQFUIWjkT/GJAkyuIyRUEY0DSTrZZGAxjQBBy5OkaFGNATpUSeh0cY0CNuEAKICRjQMzbbpoiLGNACv+cKiU0Y0BIIsu6JzxjQIdF+UoqRGNAxmgn2yxMY0AEjFVrL1RjQEKvg/sxXGNAgdKxizRkY0DA9d8bN2xjQP4YDqw5dGNAPDw8PDx8Y0B7X2rMPoRjQLqCmFxBjGNA+KXG7EOUY0A2yfR8RpxjQHXsIg1JpGNAtA9RnUusY0DyMn8tTrRjQDBWrb1QvGNAbnnbTVPEY0CtnAneVcxjQOy/N25Y1GNAKuNl/lrcY0BoBpSOXeRjQKcpwh5g7GNA5kzwrmL0Y0AkcB4/ZfxjQGKTTM9nBGRAobZ6X2oMZEDg2ajvbBRkQB791n9vHGRAXCAFEHIkZECbQzOgdCxkQNpmYTB3NGRAGIqPwHk8ZEBWrb1QfERkQJXQ6+B+TGRA1PMZcYFUZEASF0gBhFxkQFA6dpGGZGRAj12kIYlsZEDNgNKxi3RkQAykAEKOfGRASscu0pCEZECI6lxik4xkQMcNi/KVlGRABjG5gpicZEBEVOcSm6RkQIJ3FaOdrGRAwZpDM6C0ZEAAvnHDorxkQD7hn1OlxGRAfATO46fMZEC7J/xzqtRkQPpKKgSt3GRAOG5YlK/kZEB2kYYksuxkQLW0tLS09GRA9NfiRLf8ZEAy+xDVuQRlQHAeP2W8DGVAr0Ft9b4UZUDuZJuFwRxlQCyIyRXEJGVAaqv3pcYsZUCoziU2yTRlQOfxU8bLPGVAJhWCVs5EZUBkOLDm0ExlQKJb3nbTVGVA4X4MB9ZcZUAgojqX2GRlQF7FaCfbbGVAnOiWt910ZUDbC8VH4HxlQBov89fihGVAWFIhaOWMZUCWdU/455RlQNWYfYjqnGVAFLyrGO2kZUBS39mo76xlQJACCDnytGVAzyU2yfS8ZUAOSWRZ98RlQExskun5zGVAio/AefzUZUDJsu4J/9xlQAjWHJoB5WVARvlKKgTtZUCEHHm6BvVlQMI/p0oJ/WVAAWPV2gsFZkBAhgNrDg1mQH6pMfsQFWZAvMxfixMdZkD7740bFiVmQDoTvKsYLWZAeDbqOxs1ZkC2WRjMHT1mQPV8RlwgRWZAM6B07CJNZkByw6J8JVVmQLDm0AwoXWZA7wn/nCplZkAtLS0tLW1mQGxQW70vdWZAqnOJTTJ9ZkDplrfdNIVmQCe65W03jWZAZt0T/jmVZkCkAEKOPJ1mQOMjcB4/pWZAIUeerkGtZkBgasw+RLVmQJ6N+s5GvWZA3bAoX0nFZkAb1FbvS81mQFr3hH9O1WZAmBqzD1HdZkDXPeGfU+VmQBVhDzBW7WZAVIQ9wFj1ZkCSp2tQW/1mQNDKmeBdBWdAD+7HcGANZ0BNEfYAYxVnQIw0JJFlHWdAyldSIWglZ0AJe4Cxai1nQEeerkFtNWdAhsHc0W89Z0DE5ApickVnQAMIOfJ0TWdAQStngndVZ0CATpUSel1nQL5xw6J8ZWdA/ZTxMn9tZ0A7uB/DgXVnQHrbTVOEfWdAuP5744aFZ0D3IapziY1nQDVF2AOMlWdAdGgGlI6dZ0CyizQkkaVnQPGuYrSTrWdAL9KQRJa1Z0Bt9b7UmL1nQKwY7WSbxWdA6jsb9Z3NZ0ApX0mFoNVnQGeCdxWj3WdApqWlpaXlZ0DkyNM1qO1nQCPsAcaq9WdAYQ8wVq39Z0CgMl7mrwVoQN5VjHayDWhAHXm6BrUVaEBbnOiWtx1oQJq/Fie6JWhA2OJEt7wtaEAXBnNHvzVoQFUpodfBPWhAlEzPZ8RFaEDSb/33xk1oQBGTK4jJVWhAT7ZZGMxdaECO2YeozmVoQMz8tTjRbWhACiDkyNN1aEBJQxJZ1n1oQIdmQOnYhWhAxolueduNaEAErZwJ3pVoQEPQypngnWhAgfP4KeOlaEDAFie65a1oQP45VUrotWhAPV2D2uq9aEB7gLFq7cVoQLqj3/rvzWhA+MYNi/LVaEA36jsb9d1oQHUNaqv35WhAtDCYO/rtaEDyU8bL/PVoQDF39Fv//WhAb5oi7AEGaUCuvVB8BA5pQOzgfgwHFmlAKwStnAkeaUBpJ9ssDCZpQKhKCb0OLmlA5m03TRE2aUAkkWXdEz5pQGO0k20WRmlAodfB/RhOaUDg+u+NG1ZpQB4eHh4eXmlAXUFMriBmaUCbZHo+I25pQNqHqM4ldmlAGKvWXih+aUBXzgTvKoZpQJXxMn8tjmlA1BRhDzCWaUASOI+fMp5pQFFbvS81pmlAj37rvzeuaUDOoRlQOrZpQAzFR+A8vmlAS+h1cD/GaUCJC6QAQs5pQMgu0pBE1mlABlIAIUfeaUBFdS6xSeZpQIOYXEFM7mlAwbuK0U72aUAA37hhUf5pQD4C5/FTBmpAfSUVglYOakC7SEMSWRZqQPprcaJbHmpAOI+fMl4makB3ss3CYC5qQLXV+1JjNmpA9Pgp42U+akAyHFhzaEZqQHE/hgNrTmpAr2K0k21WakDuheIjcF5qQCypELRyZmpAa8w+RHVuakCp72zUd3ZqQOgSm2R6fmpAJjbJ9HyGakBlWfeEf45qQKN8JRWClmpA4p9TpYSeakAgw4E1h6ZqQF7mr8WJrmpAnQneVYy2akDbLAzmjr5qQBpQOnaRxmpAWHNoBpTOakCXlpaWltZqQNW5xCaZ3mpAFN3ytpvmakBSACFHnu5qQJEjT9eg9mpAz0Z9Z6P+akAOaqv3pQZrQEyN2YeoDmtAi7AHGKsWa0DJ0zWorR5rQAj3YziwJmtARhqSyLIua0CFPcBYtTZrQMNg7ui3PmtAAoQcebpGa0BAp0oJvU5rQH/KeJm/VmtAve2mKcJea0D7ENW5xGZrQDo0A0rHbmtAeFcx2sl2a0C3el9qzH5rQPWdjfrOhmtANMG7itGOa0By5Oka1JZrQLEHGKvWnmtA7ypGO9mma0AuTnTL265rQGxxolvetmtAq5TQ6+C+a0Dpt/5748ZrQCjbLAzmzmtAZv5anOjWa0ClIYks695rQONEt7zt5mtAImjlTPDua0BgixPd8vZrQJ+uQW31/mtA3dFv/fcGbEAc9Z2N+g5sQFoYzB39FmxAmDv6rf8ebEDXXig+AidsQBWCVs4EL2xAVKWEXgc3bECSyLLuCT9sQNHr4H4MR2xADw8PDw9PbEBOMj2fEVdsQIxVay8UX2xAy3iZvxZnbEAJnMdPGW9sQEi/9d8bd2xAhuIjcB5/bEDFBVIAIYdsQAMpgJAjj2xAQkyuICaXbECAb9ywKJ9sQL+SCkErp2xA/bU40S2vbEA82WZhMLdsQHr8lPEyv2xAuR/DgTXHbED3QvEROM9sQDVmH6I612xAdIlNMj3fbECyrHvCP+dsQPHPqVJC72xAL/PX4kT3bEBuFgZzR/9sQKw5NANKB21A61xik0wPbUApgJAjTxdtQGijvrNRH21ApsbsQ1QnbUDl6RrUVi9tQCMNSWRZN21AYjB39Fs/bUCgU6WEXkdtQN920xRhT21AHZoBpWNXbUBcvS81Zl9tQJrgXcVoZ21A2QOMVWtvbUAXJ7rlbXdtQFZK6HVwf21AlG0WBnOHbUDSkESWdY9tQBG0ciZ4l21AT9egtnqfbUCO+s5GfadtQMwd/dZ/r21AC0ErZ4K3bUBJZFn3hL9tQIiHh4eHx21Axqq1F4rPbUAFzuOnjNdtQEPxETiP321AghRAyJHnbUDAN25YlO9tQP9anOiW921APX7KeJn/bUB8ofgInAduQLrEJpmeD25A+edUKaEXbkA3C4O5ox9uQHYusUmmJ25AtFHf2agvbkDzdA1qqzduQDGYO/qtP25AcLtpirBHbkCu3pcas09uQOwBxqq1V25AKyX0OrhfbkBpSCLLumduQKhrUFu9b25A5o5+6793bkAlsqx7wn9uQGPV2gvFh25AovgInMePbkDgGzcsypduQB8/ZbzMn25AXWKTTM+nbkCchcHc0a9uQNqo72zUt25AGcwd/da/bkBX70uN2cduQJYSeh3cz25A1DWord7XbkATWdY94d9uQFF8BM7j525AkJ8yXubvbkDOwmDu6PduQA3mjn7r/25ASwm9Du4Hb0CJLOue8A9vQMhPGS/zF29ABnNHv/Ufb0BFlnVP+CdvQIO5o9/6L29AwtzRb/03b0AAAAAAAEBvQA==\",\"dtype\":\"float64\",\"shape\":[800]},\"y\":{\"__ndarray__\":\"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\",\"dtype\":\"float64\",\"shape\":[800]}},\"selected\":{\"id\":\"1882\",\"type\":\"Selection\"},\"selection_policy\":{\"id\":\"1881\",\"type\":\"UnionRenderers\"}},\"id\":\"1854\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1855\",\"type\":\"Line\"},{\"attributes\":{},\"id\":\"1832\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1842\",\"type\":\"WheelZoomTool\"}],\"root_ids\":[\"1820\"]},\"title\":\"Bokeh Application\",\"version\":\"1.4.0\"}};\n", + " var render_items = [{\"docid\":\"abb7f6e8-d85a-45ad-9129-6083fee87ca5\",\"notebook_comms_target\":\"1883\",\"roots\":{\"1820\":\"3d3d6923-a0bf-469a-97d2-64f7cc0df7a2\"}}];\n", + " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", + "\n", + " }\n", + " if (root.Bokeh !== undefined) {\n", + " embed_document(root);\n", + " } else {\n", + " var attempts = 0;\n", + " var timer = setInterval(function(root) {\n", + " if (root.Bokeh !== undefined) {\n", + " clearInterval(timer);\n", + " embed_document(root);\n", + " } else {\n", + " attempts++;\n", + " if (attempts > 100) {\n", + " clearInterval(timer);\n", + " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", + " }\n", + " }\n", + " }, 10, root)\n", + " }\n", + "})(window);" + ], + "application/vnd.bokehjs_exec.v0+json": "" + }, + "metadata": { + "application/vnd.bokehjs_exec.v0+json": { + "id": "1820" + } + }, + "output_type": "display_data" + }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "9ef42603b8af4f66aa2b6be3cefd8243", + "model_id": "19659e1b293f4ca2a4e0535ec3f56f23", "version_major": 2, "version_minor": 0 }, @@ -179,25 +536,29 @@ }, "metadata": {}, "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "interactive(interactive_ab, k=(0.1, 100))" + "show(p, notebook_handle=True)\n", + "interact(interactive_ab, k=(0.1, 100))" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "nmrsim", "language": "python", - "name": "python3" + "name": "nmrsim" }, "language_info": { "codemirror_mode": { diff --git a/jupyter/2-Interactive-Demo.ipynb b/jupyter/2-Interactive-Demo.ipynb index d759936..a6c39d9 100644 --- a/jupyter/2-Interactive-Demo.ipynb +++ b/jupyter/2-Interactive-Demo.ipynb @@ -9,58 +9,12 @@ "\n", "There are many ways to create interactive plots in a Juypter notebook, and the visualization ecosystem is constantly changing. For example the Holoviz tool suite (http://holoviz.org/) looks promising (especially the possibility of creating a web application using Panel). Another interesting option is nbinteract (https://www.nbinteract.com/).\n", "\n", - "This notebook currently uses ipywidgets and plotly to create some simple NMR demonstrations." + "This notebook currently uses ipywidgets and bokeh to create some simple NMR demonstrations." ] }, { "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - " \n", - " " - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib as mpl\n", - "import matplotlib.pyplot as plt\n", - "\n", - "#bokeh + jupyter lab requires \"jupyter labextension install jupyterlab_bokeh\" installation, \n", - "# but 2018-07-28 there is a dependency conflict\n", - "\n", - "# import bokeh.io\n", - "# import bokeh.plotting\n", - "# bokeh.io.output_notebook()\n", - "import plotly\n", - "import plotly.graph_objs as go\n", - "from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot\n", - "init_notebook_mode(connected=True)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -73,7 +27,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -125,7 +79,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -140,36 +94,439 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ - "def interactive_ab(va=110, vb=100, J=10, k=0.1, w=0.5):\n", - " args = (va, vb, J, k, w)\n", - " x, y = dnmr_AB(*args)\n", - " obj = go.Scatter(x=x, y=y)\n", - " data = [obj]\n", - " iplot(data)" + "from ipywidgets import interact" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " Loading BokehJS ...\n", + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + "(function(root) {\n", + " function now() {\n", + " return new Date();\n", + " }\n", + "\n", + " var force = true;\n", + "\n", + " if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n", + " root._bokeh_onload_callbacks = [];\n", + " root._bokeh_is_loading = undefined;\n", + " }\n", + "\n", + " var JS_MIME_TYPE = 'application/javascript';\n", + " var HTML_MIME_TYPE = 'text/html';\n", + " var EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n", + " var CLASS_NAME = 'output_bokeh rendered_html';\n", + "\n", + " /**\n", + " * Render data to the DOM node\n", + " */\n", + " function render(props, node) {\n", + " var script = document.createElement(\"script\");\n", + " node.appendChild(script);\n", + " }\n", + "\n", + " /**\n", + " * Handle when an output is cleared or removed\n", + " */\n", + " function handleClearOutput(event, handle) {\n", + " var cell = handle.cell;\n", + "\n", + " var id = cell.output_area._bokeh_element_id;\n", + " var server_id = cell.output_area._bokeh_server_id;\n", + " // Clean up Bokeh references\n", + " if (id != null && id in Bokeh.index) {\n", + " Bokeh.index[id].model.document.clear();\n", + " delete Bokeh.index[id];\n", + " }\n", + "\n", + " if (server_id !== undefined) {\n", + " // Clean up Bokeh references\n", + " var cmd = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n", + " cell.notebook.kernel.execute(cmd, {\n", + " iopub: {\n", + " output: function(msg) {\n", + " var id = msg.content.text.trim();\n", + " if (id in Bokeh.index) {\n", + " Bokeh.index[id].model.document.clear();\n", + " delete Bokeh.index[id];\n", + " }\n", + " }\n", + " }\n", + " });\n", + " // Destroy server and session\n", + " var cmd = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n", + " cell.notebook.kernel.execute(cmd);\n", + " }\n", + " }\n", + "\n", + " /**\n", + " * Handle when a new output is added\n", + " */\n", + " function handleAddOutput(event, handle) {\n", + " var output_area = handle.output_area;\n", + " var output = handle.output;\n", + "\n", + " // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n", + " if ((output.output_type != \"display_data\") || (!output.data.hasOwnProperty(EXEC_MIME_TYPE))) {\n", + " return\n", + " }\n", + "\n", + " var toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n", + "\n", + " if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n", + " toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n", + " // store reference to embed id on output_area\n", + " output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n", + " }\n", + " if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n", + " var bk_div = document.createElement(\"div\");\n", + " bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n", + " var script_attrs = bk_div.children[0].attributes;\n", + " for (var i = 0; i < script_attrs.length; i++) {\n", + " toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n", + " }\n", + " // store reference to server id on output_area\n", + " output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n", + " }\n", + " }\n", + "\n", + " function register_renderer(events, OutputArea) {\n", + "\n", + " function append_mime(data, metadata, element) {\n", + " // create a DOM node to render to\n", + " var toinsert = this.create_output_subarea(\n", + " metadata,\n", + " CLASS_NAME,\n", + " EXEC_MIME_TYPE\n", + " );\n", + " this.keyboard_manager.register_events(toinsert);\n", + " // Render to node\n", + " var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n", + " render(props, toinsert[toinsert.length - 1]);\n", + " element.append(toinsert);\n", + " return toinsert\n", + " }\n", + "\n", + " /* Handle when an output is cleared or removed */\n", + " events.on('clear_output.CodeCell', handleClearOutput);\n", + " events.on('delete.Cell', handleClearOutput);\n", + "\n", + " /* Handle when a new output is added */\n", + " events.on('output_added.OutputArea', handleAddOutput);\n", + "\n", + " /**\n", + " * Register the mime type and append_mime function with output_area\n", + " */\n", + " OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n", + " /* Is output safe? */\n", + " safe: true,\n", + " /* Index of renderer in `output_area.display_order` */\n", + " index: 0\n", + " });\n", + " }\n", + "\n", + " // register the mime type if in Jupyter Notebook environment and previously unregistered\n", + " if (root.Jupyter !== undefined) {\n", + " var events = require('base/js/events');\n", + " var OutputArea = require('notebook/js/outputarea').OutputArea;\n", + "\n", + " if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n", + " register_renderer(events, OutputArea);\n", + " }\n", + " }\n", + "\n", + " \n", + " if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n", + " root._bokeh_timeout = Date.now() + 5000;\n", + " root._bokeh_failed_load = false;\n", + " }\n", + "\n", + " var NB_LOAD_WARNING = {'data': {'text/html':\n", + " \"
\\n\"+\n", + " \"

\\n\"+\n", + " \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n", + " \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n", + " \"

\\n\"+\n", + " \"
    \\n\"+\n", + " \"
  • re-rerun `output_notebook()` to attempt to load from CDN again, or
  • \\n\"+\n", + " \"
  • use INLINE resources instead, as so:
  • \\n\"+\n", + " \"
\\n\"+\n", + " \"\\n\"+\n", + " \"from bokeh.resources import INLINE\\n\"+\n", + " \"output_notebook(resources=INLINE)\\n\"+\n", + " \"\\n\"+\n", + " \"
\"}};\n", + "\n", + " function display_loaded() {\n", + " var el = document.getElementById(\"1819\");\n", + " if (el != null) {\n", + " el.textContent = \"BokehJS is loading...\";\n", + " }\n", + " if (root.Bokeh !== undefined) {\n", + " if (el != null) {\n", + " el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n", + " }\n", + " } else if (Date.now() < root._bokeh_timeout) {\n", + " setTimeout(display_loaded, 100)\n", + " }\n", + " }\n", + "\n", + "\n", + " function run_callbacks() {\n", + " try {\n", + " root._bokeh_onload_callbacks.forEach(function(callback) {\n", + " if (callback != null)\n", + " callback();\n", + " });\n", + " } finally {\n", + " delete root._bokeh_onload_callbacks\n", + " }\n", + " console.debug(\"Bokeh: all callbacks have finished\");\n", + " }\n", + "\n", + " function load_libs(css_urls, js_urls, callback) {\n", + " if (css_urls == null) css_urls = [];\n", + " if (js_urls == null) js_urls = [];\n", + "\n", + " root._bokeh_onload_callbacks.push(callback);\n", + " if (root._bokeh_is_loading > 0) {\n", + " console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", + " return null;\n", + " }\n", + " if (js_urls == null || js_urls.length === 0) {\n", + " run_callbacks();\n", + " return null;\n", + " }\n", + " console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", + " root._bokeh_is_loading = css_urls.length + js_urls.length;\n", + "\n", + " function on_load() {\n", + " root._bokeh_is_loading--;\n", + " if (root._bokeh_is_loading === 0) {\n", + " console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n", + " run_callbacks()\n", + " }\n", + " }\n", + "\n", + " function on_error() {\n", + " console.error(\"failed to load \" + url);\n", + " }\n", + "\n", + " for (var i = 0; i < css_urls.length; i++) {\n", + " var url = css_urls[i];\n", + " const element = document.createElement(\"link\");\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.rel = \"stylesheet\";\n", + " element.type = \"text/css\";\n", + " element.href = url;\n", + " console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n", + " document.body.appendChild(element);\n", + " }\n", + "\n", + " for (var i = 0; i < js_urls.length; i++) {\n", + " var url = js_urls[i];\n", + " var element = document.createElement('script');\n", + " element.onload = on_load;\n", + " element.onerror = on_error;\n", + " element.async = false;\n", + " element.src = url;\n", + " console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.head.appendChild(element);\n", + " }\n", + " };var element = document.getElementById(\"1819\");\n", + " if (element == null) {\n", + " console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1819' but no matching script tag was found. \")\n", + " return false;\n", + " }\n", + "\n", + " function inject_raw_css(css) {\n", + " const element = document.createElement(\"style\");\n", + " element.appendChild(document.createTextNode(css));\n", + " document.body.appendChild(element);\n", + " }\n", + "\n", + " \n", + " var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-1.4.0.min.js\"];\n", + " var css_urls = [];\n", + " \n", + "\n", + " var inline_js = [\n", + " function(Bokeh) {\n", + " Bokeh.set_log_level(\"info\");\n", + " },\n", + " function(Bokeh) {\n", + " \n", + " \n", + " }\n", + " ];\n", + "\n", + " function run_inline_js() {\n", + " \n", + " if (root.Bokeh !== undefined || force === true) {\n", + " \n", + " for (var i = 0; i < inline_js.length; i++) {\n", + " inline_js[i].call(root, root.Bokeh);\n", + " }\n", + " if (force === true) {\n", + " display_loaded();\n", + " }} else if (Date.now() < root._bokeh_timeout) {\n", + " setTimeout(run_inline_js, 100);\n", + " } else if (!root._bokeh_failed_load) {\n", + " console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n", + " root._bokeh_failed_load = true;\n", + " } else if (force !== true) {\n", + " var cell = $(document.getElementById(\"1819\")).parents('.cell').data().cell;\n", + " cell.output_area.append_execute_result(NB_LOAD_WARNING)\n", + " }\n", + "\n", + " }\n", + "\n", + " if (root._bokeh_is_loading === 0) {\n", + " console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", + " run_inline_js();\n", + " } else {\n", + " load_libs(css_urls, js_urls, function() {\n", + " console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n", + " run_inline_js();\n", + " });\n", + " }\n", + "}(window));" + ], + "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n function now() {\n return new Date();\n }\n\n var force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\n \n\n \n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n var NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"
    \\n\"+\n \"
  • re-rerun `output_notebook()` to attempt to load from CDN again, or
  • \\n\"+\n \"
  • use INLINE resources instead, as so:
  • \\n\"+\n \"
\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n var el = document.getElementById(\"1819\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error() {\n console.error(\"failed to load \" + url);\n }\n\n for (var i = 0; i < css_urls.length; i++) {\n var url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error;\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n for (var i = 0; i < js_urls.length; i++) {\n var url = js_urls[i];\n var element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error;\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };var element = document.getElementById(\"1819\");\n if (element == null) {\n console.error(\"Bokeh: ERROR: autoload.js configured with elementid '1819' but no matching script tag was found. \")\n return false;\n }\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n \n var js_urls = [\"https://cdn.pydata.org/bokeh/release/bokeh-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-widgets-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-tables-1.4.0.min.js\", \"https://cdn.pydata.org/bokeh/release/bokeh-gl-1.4.0.min.js\"];\n var css_urls = [];\n \n\n var inline_js = [\n function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\n function(Bokeh) {\n \n \n }\n ];\n\n function run_inline_js() {\n \n if (root.Bokeh !== undefined || force === true) {\n \n for (var i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\n if (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n var cell = $(document.getElementById(\"1819\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n\n }\n\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(css_urls, js_urls, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from bokeh.io import push_notebook, show, output_notebook\n", + "from bokeh.plotting import figure\n", + "output_notebook()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "# get initial xy data\n", + "x, y = dnmr_AB(*args)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "p = figure(title = 'DNMR AB Interactive Plot',\n", + " plot_height=300,\n", + " plot_width=600)\n", + "r = p.line(x, y)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ - "from ipywidgets import interactive" + "def interactive_ab(va=110, vb=100, J=10, k=0.1, w=0.5):\n", + " args = (va, vb, J, k, w)\n", + " x, y = dnmr_AB(*args)\n", + " r.data_source.data['y'] = y\n", + " r.data_source.data['x'] = x\n", + " push_notebook()\n" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 28, "metadata": {}, "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "(function(root) {\n", + " function embed_document(root) {\n", + " \n", + " var docs_json = {\"abb7f6e8-d85a-45ad-9129-6083fee87ca5\":{\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1831\",\"type\":\"LinearAxis\"}],\"center\":[{\"id\":\"1835\",\"type\":\"Grid\"},{\"id\":\"1840\",\"type\":\"Grid\"}],\"left\":[{\"id\":\"1836\",\"type\":\"LinearAxis\"}],\"plot_height\":300,\"renderers\":[{\"id\":\"1857\",\"type\":\"GlyphRenderer\"}],\"title\":{\"id\":\"1821\",\"type\":\"Title\"},\"toolbar\":{\"id\":\"1847\",\"type\":\"Toolbar\"},\"x_range\":{\"id\":\"1823\",\"type\":\"DataRange1d\"},\"x_scale\":{\"id\":\"1827\",\"type\":\"LinearScale\"},\"y_range\":{\"id\":\"1825\",\"type\":\"DataRange1d\"},\"y_scale\":{\"id\":\"1829\",\"type\":\"LinearScale\"}},\"id\":\"1820\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1845\",\"type\":\"ResetTool\"},{\"attributes\":{\"ticker\":{\"id\":\"1832\",\"type\":\"BasicTicker\"}},\"id\":\"1835\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1844\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"1881\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"text\":\"DNMR AB Interactive Plot\"},\"id\":\"1821\",\"type\":\"Title\"},{\"attributes\":{\"callback\":null},\"id\":\"1825\",\"type\":\"DataRange1d\"},{\"attributes\":{\"formatter\":{\"id\":\"1878\",\"type\":\"BasicTickFormatter\"},\"ticker\":{\"id\":\"1832\",\"type\":\"BasicTicker\"}},\"id\":\"1831\",\"type\":\"LinearAxis\"},{\"attributes\":{\"callback\":null},\"id\":\"1823\",\"type\":\"DataRange1d\"},{\"attributes\":{\"overlay\":{\"id\":\"1880\",\"type\":\"BoxAnnotation\"}},\"id\":\"1843\",\"type\":\"BoxZoomTool\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1856\",\"type\":\"Line\"},{\"attributes\":{\"dimension\":1,\"ticker\":{\"id\":\"1837\",\"type\":\"BasicTicker\"}},\"id\":\"1840\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1882\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1827\",\"type\":\"LinearScale\"},{\"attributes\":{\"data_source\":{\"id\":\"1854\",\"type\":\"ColumnDataSource\"},\"glyph\":{\"id\":\"1855\",\"type\":\"Line\"},\"hover_glyph\":null,\"muted_glyph\":null,\"nonselection_glyph\":{\"id\":\"1856\",\"type\":\"Line\"},\"selection_glyph\":null,\"view\":{\"id\":\"1858\",\"type\":\"CDSView\"}},\"id\":\"1857\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1837\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1876\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"active_drag\":\"auto\",\"active_inspect\":\"auto\",\"active_multi\":null,\"active_scroll\":\"auto\",\"active_tap\":\"auto\",\"tools\":[{\"id\":\"1841\",\"type\":\"PanTool\"},{\"id\":\"1842\",\"type\":\"WheelZoomTool\"},{\"id\":\"1843\",\"type\":\"BoxZoomTool\"},{\"id\":\"1844\",\"type\":\"SaveTool\"},{\"id\":\"1845\",\"type\":\"ResetTool\"},{\"id\":\"1846\",\"type\":\"HelpTool\"}]},\"id\":\"1847\",\"type\":\"Toolbar\"},{\"attributes\":{\"formatter\":{\"id\":\"1876\",\"type\":\"BasicTickFormatter\"},\"ticker\":{\"id\":\"1837\",\"type\":\"BasicTicker\"}},\"id\":\"1836\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1829\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1878\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1846\",\"type\":\"HelpTool\"},{\"attributes\":{},\"id\":\"1841\",\"type\":\"PanTool\"},{\"attributes\":{\"bottom_units\":\"screen\",\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"value\":\"lightgrey\"},\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":{\"value\":1.0},\"line_color\":{\"value\":\"black\"},\"line_dash\":[4,4],\"line_width\":{\"value\":2},\"render_mode\":\"css\",\"right_units\":\"screen\",\"top_units\":\"screen\"},\"id\":\"1880\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"source\":{\"id\":\"1854\",\"type\":\"ColumnDataSource\"}},\"id\":\"1858\",\"type\":\"CDSView\"},{\"attributes\":{\"callback\":null,\"data\":{\"x\":{\"__ndarray__\":\"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\",\"dtype\":\"float64\",\"shape\":[800]},\"y\":{\"__ndarray__\":\"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\",\"dtype\":\"float64\",\"shape\":[800]}},\"selected\":{\"id\":\"1882\",\"type\":\"Selection\"},\"selection_policy\":{\"id\":\"1881\",\"type\":\"UnionRenderers\"}},\"id\":\"1854\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1855\",\"type\":\"Line\"},{\"attributes\":{},\"id\":\"1832\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1842\",\"type\":\"WheelZoomTool\"}],\"root_ids\":[\"1820\"]},\"title\":\"Bokeh Application\",\"version\":\"1.4.0\"}};\n", + " var render_items = [{\"docid\":\"abb7f6e8-d85a-45ad-9129-6083fee87ca5\",\"notebook_comms_target\":\"1883\",\"roots\":{\"1820\":\"3d3d6923-a0bf-469a-97d2-64f7cc0df7a2\"}}];\n", + " root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n", + "\n", + " }\n", + " if (root.Bokeh !== undefined) {\n", + " embed_document(root);\n", + " } else {\n", + " var attempts = 0;\n", + " var timer = setInterval(function(root) {\n", + " if (root.Bokeh !== undefined) {\n", + " clearInterval(timer);\n", + " embed_document(root);\n", + " } else {\n", + " attempts++;\n", + " if (attempts > 100) {\n", + " clearInterval(timer);\n", + " console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n", + " }\n", + " }\n", + " }, 10, root)\n", + " }\n", + "})(window);" + ], + "application/vnd.bokehjs_exec.v0+json": "" + }, + "metadata": { + "application/vnd.bokehjs_exec.v0+json": { + "id": "1820" + } + }, + "output_type": "display_data" + }, { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "834bf19d4f9846ac9f7f356d4b75bbcd", + "model_id": "19659e1b293f4ca2a4e0535ec3f56f23", "version_major": 2, "version_minor": 0 }, @@ -179,25 +536,29 @@ }, "metadata": {}, "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "interactive(interactive_ab, k=(0.1, 100))" + "show(p, notebook_handle=True)\n", + "interact(interactive_ab, k=(0.1, 100))" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "nmrsim", "language": "python", - "name": "python3" + "name": "nmrsim" }, "language_info": { "codemirror_mode": { From 9b603db69163c744203ccbbb409ff95dc7dd7fd1 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 2 Feb 2020 16:49:56 -0500 Subject: [PATCH 06/80] Changed setup.py dev requirement from 'plotly' to 'bokeh'. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7651acc..c06d579 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ 'sphinxcontrib-napoleon', # below are for current "extra" jupyter notebook features, # which may change as other dataviz options tested. - 'plotly', + 'bokeh', ] } ) From d459e17488cce9b7b52dd8fb3afda233aec03d7b Mon Sep 17 00:00:00 2001 From: sametz Date: Mon, 3 Feb 2020 09:11:37 -0500 Subject: [PATCH 07/80] flake8. Added some github actions examples in dev_notes/github_actions to ponder. DEVELOPERS.rst now includes 'make html' as one of the tests to ensure the contributer's environment is set up properly. --- DEVELOPERS.rst | 11 +++++-- dev_notes/github_actions/python-package.yml | 33 +++++++++++++++++++++ dev_notes/github_actions/python-publish.yml | 26 ++++++++++++++++ nmrsim/bin/__init__.py | 2 +- nmrsim/qm.py | 1 + 5 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 dev_notes/github_actions/python-package.yml create mode 100644 dev_notes/github_actions/python-publish.yml diff --git a/DEVELOPERS.rst b/DEVELOPERS.rst index 68b0382..0e8fc4b 100644 --- a/DEVELOPERS.rst +++ b/DEVELOPERS.rst @@ -137,7 +137,8 @@ use the command line: conda activate nmrsim (Mac) activate nmrsim (Windows) -If you later want to exit this environment, you can activate another environment, +If you later want to exit this environment, +you can activate another environment, or enter: .. code-block:: bash @@ -156,17 +157,21 @@ This will install nmrsim, plus dependencies. It will also install the developer dependencies, which are not required by casual nmrsim users, but are required for developers to run tests, check formatting and so on. -From the command line, in the top nmrsim directory that contains setup.py, enter: +From the command line, in the top nmrsim directory that contains setup.py, +enter: .. code-block:: bash pip install -e ".[dev]" -To check your installation, run the tests using pytest: +To check your installation, run the tests using pytest, +then navigate to the `docs` directory and build the documentation: .. code-block:: bash pytest + cd docs + make html There will be several pop-up plots that are visual tests for correct behavior; close these windows as they pop up to proceed through the tests. diff --git a/dev_notes/github_actions/python-package.yml b/dev_notes/github_actions/python-package.yml new file mode 100644 index 0000000..30682f3 --- /dev/null +++ b/dev_notes/github_actions/python-package.yml @@ -0,0 +1,33 @@ +name: Python package + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.5, 3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Lint with flake8 + run: | + pip install flake8 + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pip install pytest + pytest \ No newline at end of file diff --git a/dev_notes/github_actions/python-publish.yml b/dev_notes/github_actions/python-publish.yml new file mode 100644 index 0000000..1c31277 --- /dev/null +++ b/dev_notes/github_actions/python-publish.yml @@ -0,0 +1,26 @@ +name: Upload Python Package + +on: + release: + types: [created] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* \ No newline at end of file diff --git a/nmrsim/bin/__init__.py b/nmrsim/bin/__init__.py index ea79640..28e0eb8 100644 --- a/nmrsim/bin/__init__.py +++ b/nmrsim/bin/__init__.py @@ -1,2 +1,2 @@ # bin may require an __init__ file for importlib.resources. See: -# https://importlib-resources.readthedocs.io/en/latest/using.html \ No newline at end of file +# https://importlib-resources.readthedocs.io/en/latest/using.html diff --git a/nmrsim/qm.py b/nmrsim/qm.py index fdfe318..ab14b11 100644 --- a/nmrsim/qm.py +++ b/nmrsim/qm.py @@ -342,6 +342,7 @@ def _tm_cache(nspins): sparse.save_npz(path, T_sparse) return T_sparse + def _intensity_and_energy(H, nspins): """ Calculate intensity matrix and energies (eigenvalues) from Hamiltonian. From ff4858b4a4b34961e6b160270f1b2ebee3d04070 Mon Sep 17 00:00:00 2001 From: sametz Date: Mon, 3 Feb 2020 09:15:07 -0500 Subject: [PATCH 08/80] Update CHANGELOG.rst: Issues #2, #3, #4 fixed. --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c9f93a9..6007a17 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -36,7 +36,7 @@ Changed Fixed ^^^^^ -* Documentation errors (Issues #2, #4) +* Documentation errors (Issues #2, #3, #4) 0.3.0 - 2019-11-08 (beta release) --------------------------------- From 3d111fa2df989338745b402c49f9bc921cb5aec8 Mon Sep 17 00:00:00 2001 From: sametz Date: Mon, 3 Feb 2020 20:55:56 -0500 Subject: [PATCH 09/80] Started to implement pyenv/tox/tox-pyenv. In conda env, not finding pyenv environments. Tried using a venv, but spaces in googledrive directory path name caused error. Try cloning to another local directory? --- .gitignore | 1 + setup.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3f9764e..a4b422c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ build dist .vscode/ docs/build +.tox \ No newline at end of file diff --git a/setup.py b/setup.py index c06d579..141681e 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,8 @@ # below are for current "extra" jupyter notebook features, # which may change as other dataviz options tested. 'bokeh', + 'tox', + 'tox-pyenv', ] } ) From 9818ead41b1e53ca28ed498ec89d02ea7b701585 Mon Sep 17 00:00:00 2001 From: sametz Date: Mon, 3 Feb 2020 21:18:35 -0500 Subject: [PATCH 10/80] Using conda nmrsim environment and a clone of nmrsim on a path with no spaces, tox successfully runs with a py37 environment, but not 36 and 38--seems that it still can't find pyenv python installations. --- tox.ini | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tox.ini diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..b024e46 --- /dev/null +++ b/tox.ini @@ -0,0 +1,14 @@ +# tox (https://tox.readthedocs.io/) is a tool for running tests +# in multiple virtualenvs. This configuration file will run the +# test suite on all supported python versions. To use it, "pip install tox" +# and then run "tox" from this directory. + +[tox] +envlist = py36, py37, py38 + +[testenv] +deps = + pytest + pyfakefs +commands = + pytest From 668e285035d6d06007245298db08b6c74a85058b Mon Sep 17 00:00:00 2001 From: sametz Date: Mon, 3 Feb 2020 21:40:48 -0500 Subject: [PATCH 11/80] Tox works as expected after putting project folder in path without space names (googledrive/My Folder/... caused error) and after using "pyenv global 3.6.10 3.7.6 3.8.1". As expected, tox found 3.6 failed because importlib.resources is new to python 3.7. TODO: add backport option for python 3.6. --- dev_notes/tox.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 dev_notes/tox.txt diff --git a/dev_notes/tox.txt b/dev_notes/tox.txt new file mode 100644 index 0000000..b267ab6 --- /dev/null +++ b/dev_notes/tox.txt @@ -0,0 +1,6 @@ +Notes on getting tox to work: +- use pyenv to install targeted versions (initially 3.6.10, 3.7.6, 3.8.1) +- install tox and tox-pyenv +- use a clone of the rep in a path that doesn't have spaces in the name + (googledrive/My Drive caused error) +- "pyenv global 3.6.10 3.7.6 3.8.1" required for tox to find the envs. \ No newline at end of file From da3cffc38e63e6aac923751043fb02c3292413c2 Mon Sep 17 00:00:00 2001 From: sametz Date: Mon, 3 Feb 2020 23:35:05 -0500 Subject: [PATCH 12/80] Added importlib_resources backport to make py36 compatible. Py37/38 pass but 36 fails. Created (temporary) test_tm_cache_creates_file_IRP (refactoring to use importlib.resources or importlib_resources). Again py37/38 pass but not 36; print statements inside qm._tm_cache show a different cryptically-named target in the venv folder. Wonder if the tests (old and new) are reliably testing true save location. Suggestion: create real installations under py36 and py37, delete the bin contents, call a qm function, and see if the matrices save in the correct locations. --- nmrsim/qm.py | 7 ++++++- setup.py | 3 ++- tests/test_qm.py | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/nmrsim/qm.py b/nmrsim/qm.py index ab14b11..9709618 100644 --- a/nmrsim/qm.py +++ b/nmrsim/qm.py @@ -40,7 +40,11 @@ calculating second-order spectra: one using pydata/sparse and caching, and the other using neither. """ -from importlib import resources +import sys +if sys.version_info >= (3, 7): + from importlib import resources +else: + import importlib_resources as resources import numpy as np import sparse @@ -339,6 +343,7 @@ def _tm_cache(nspins): print(f'creating {filename}') T_sparse = _transition_matrix_dense(nspins) T_sparse = sparse.COO(T_sparse) + print('_tm_cache will save on path: ', path) sparse.save_npz(path, T_sparse) return T_sparse diff --git a/setup.py b/setup.py index 141681e..5cd37ad 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,8 @@ python_requires='>=3.6', install_requires=['matplotlib', 'numpy', - 'sparse'], + 'sparse', + "importlib_resources ; python_version<'3.7'"], extras_require={ 'dev': [ 'flake8', diff --git a/tests/test_qm.py b/tests/test_qm.py index 36923a8..72988e5 100644 --- a/tests/test_qm.py +++ b/tests/test_qm.py @@ -34,9 +34,30 @@ def test_tm_cache_creates_file(fs): assert not expected_T.exists() T = _tm_cache(3) assert T + print('test is checking for existence of: ', expected_T) assert expected_T.exists() +def test_tm_cache_creates_file_IRP(fs): + import sys + if sys.version_info >= (3, 7): + from importlib import resources + else: + import importlib_resources as resources + import nmrsim.bin + test_bin_file_context = resources.path(nmrsim.bin, 'T3.npz') + with test_bin_file_context as f: + test_bin_file = f + test_bin = test_bin_file.parent + print(f'test creating dir {test_bin}') + fs.create_dir(test_bin) + assert not test_bin_file.exists() + T = _tm_cache(3) + assert T + print('test is checking for existence of: ', test_bin_file) + assert test_bin_file.exists() + + def test_hamiltonian_dense(): # GIVEN v and J inputs for the Rioux 3-spin system v, J = rioux() From 4d05d9a45c2d9e34a89185ce1b2c67eec0d11be6 Mon Sep 17 00:00:00 2001 From: sametz Date: Tue, 4 Feb 2020 12:39:13 -0500 Subject: [PATCH 13/80] qm._tm_cache now exhibits correct behavior on manual testing with py36 and py37 venvs: will replace a missing file. However, tox on py36 still fails for the so and tm creates_file tests. TODO next: apply similar fix to qm._so_sparse, and manually test vs. py36 and 37 for correct behavior. Then, ensure pytest tests work correctly. --- dev_notes/bin_fixing.txt | 36 +++++++++++++++++++++++++++++ dev_notes/infrequent_operations.txt | 2 ++ nmrsim/qm.py | 11 ++++++--- 3 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 dev_notes/bin_fixing.txt diff --git a/dev_notes/bin_fixing.txt b/dev_notes/bin_fixing.txt new file mode 100644 index 0000000..97968d9 --- /dev/null +++ b/dev_notes/bin_fixing.txt @@ -0,0 +1,36 @@ +tox reveals py36 fails the so and tm "creates file tests", 37 and 38 pass. +BUT testing actual installs in a venv shows that all fail to repair themselves. Therefore problem with how importlib.resources was implemented? +And problem with tests? +3.7: +Traceback (most recent call last): + File "", line 1, in + File "/Users/geoffreysametz/Documents/3.7/venv/lib/python3.7/site-packages/nmrsim/qm.py", line 337, in _tm_cache + with path_context as p: + File "/Users/geoffreysametz/.pyenv/versions/3.7.6/lib/python3.7/contextlib.py", line 112, in __enter__ + return next(self.gen) + File "/Users/geoffreysametz/.pyenv/versions/3.7.6/lib/python3.7/importlib/resources.py", line 201, in path + with open_binary(package, resource) as fp: + File "/Users/geoffreysametz/.pyenv/versions/3.7.6/lib/python3.7/importlib/resources.py", line 91, in open_binary + return reader.open_resource(resource) + File "", line 929, in open_resource +FileNotFoundError: [Errno 2] No such file or directory: '/Users/geoffreysametz/Documents/3.7/venv/lib/python3.7/site-packages/nmrsim/bin/T3.npz' + +3.6: +Traceback (most recent call last): + File "/Users/geoffreysametz/Documents/3.6/venv/lib/python3.6/site-packages/importlib_resources/_py3.py", line 85, in open_binary + return open(full_path, mode='rb') +FileNotFoundError: [Errno 2] No such file or directory: '/Users/geoffreysametz/Documents/3.6/venv/lib/python3.6/site-packages/nmrsim/bin/T3.npz' + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "", line 1, in + File "/Users/geoffreysametz/Documents/3.6/venv/lib/python3.6/site-packages/nmrsim/qm.py", line 337, in _tm_cache + with path_context as p: + File "/Users/geoffreysametz/.pyenv/versions/3.6.10/lib/python3.6/contextlib.py", line 81, in __enter__ + return next(self.gen) + File "/Users/geoffreysametz/Documents/3.6/venv/lib/python3.6/site-packages/importlib_resources/_py3.py", line 188, in path + with open_binary(package, resource) as fp: + File "/Users/geoffreysametz/Documents/3.6/venv/lib/python3.6/site-packages/importlib_resources/_py3.py", line 99, in open_binary + raise FileNotFoundError(message) +FileNotFoundError: 'T3.npz' resource not found in 'nmrsim.bin' diff --git a/dev_notes/infrequent_operations.txt b/dev_notes/infrequent_operations.txt index ced0c35..32b53e0 100644 --- a/dev_notes/infrequent_operations.txt +++ b/dev_notes/infrequent_operations.txt @@ -42,5 +42,7 @@ testpypi: UPDATE 2019-10-27: need to add reference to standard pypi to guarantee dependencies load: pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ nmrsim +pip install from gh branch: + pip install git+https://github.com/sametz/nmrsim.git@tox for tox branch Adding jupyter to dev requirements. Also to get it working: diff --git a/nmrsim/qm.py b/nmrsim/qm.py index 9709618..87daa10 100644 --- a/nmrsim/qm.py +++ b/nmrsim/qm.py @@ -333,9 +333,14 @@ def _tm_cache(nspins): # Speed tests indicated that using sparse-array transition matrices # provides a modest speed improvement on larger spin systems. filename = f'T{nspins}.npz' - path_context = resources.path(nmrsim.bin, filename) - with path_context as p: - path = p + init_path_context = resources.path(nmrsim.bin, '__init__.py') + with init_path_context as p: + init_path = p + print('path to init: ', init_path) + bin_path = init_path.parent + print('path to bin: ', bin_path) + path = bin_path.joinpath(filename) + print('searching for: ', path) try: T_sparse = sparse.load_npz(path) return T_sparse From ed59c81d873aaa24317055e11a14638d14738925 Mon Sep 17 00:00:00 2001 From: sametz Date: Tue, 4 Feb 2020 13:06:37 -0500 Subject: [PATCH 14/80] qm._so_sparse now has correct behavior as hand-tested in venvs for py36 and py37. Tox only passes for py37/38 still; py36 fails for the _so_dense and _tm_cache save tests. TODO: determine why tests aren't reliable predictors for accepted behavior. --- nmrsim/qm.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/nmrsim/qm.py b/nmrsim/qm.py index 87daa10..86d6b38 100644 --- a/nmrsim/qm.py +++ b/nmrsim/qm.py @@ -56,6 +56,15 @@ SPARSE = True # the sparse library is available +def _bin_path(): + """Return a Path to the nmrsim/bin directory.""" + init_path_context = resources.path(nmrsim.bin, '__init__.py') + with init_path_context as p: + init_path = p + bin_path = init_path.parent + return bin_path + + def _so_dense(nspins): """ Calculate spin operators required for constructing the spin hamiltonian, @@ -140,12 +149,13 @@ def _so_sparse(nspins): # for user? filename_Lz = f'Lz{nspins}.npz' filename_Lproduct = f'Lproduct{nspins}.npz' - path_context_Lz = resources.path(nmrsim.bin, filename_Lz) - path_context_Lproduct = resources.path(nmrsim.bin, filename_Lproduct) - with path_context_Lz as p: - path_Lz = p - with path_context_Lproduct as p: - path_Lproduct = p + bin_path = _bin_path() + path_Lz = bin_path.joinpath(filename_Lz) + path_Lproduct = bin_path.joinpath(filename_Lproduct) + # with path_context_Lz as p: + # path_Lz = p + # with path_context_Lproduct as p: + # path_Lproduct = p try: Lz = sparse.load_npz(path_Lz) Lproduct = sparse.load_npz(path_Lproduct) @@ -333,11 +343,12 @@ def _tm_cache(nspins): # Speed tests indicated that using sparse-array transition matrices # provides a modest speed improvement on larger spin systems. filename = f'T{nspins}.npz' - init_path_context = resources.path(nmrsim.bin, '__init__.py') - with init_path_context as p: - init_path = p - print('path to init: ', init_path) - bin_path = init_path.parent + # init_path_context = resources.path(nmrsim.bin, '__init__.py') + # with init_path_context as p: + # init_path = p + # print('path to init: ', init_path) + # bin_path = init_path.parent + bin_path = _bin_path() print('path to bin: ', bin_path) path = bin_path.joinpath(filename) print('searching for: ', path) From 22e63dccc0d75b05f12be2054a395447bf44df85 Mon Sep 17 00:00:00 2001 From: sametz Date: Tue, 4 Feb 2020 17:30:37 -0500 Subject: [PATCH 15/80] Added minimal test case for importlib_resources (importlib_r.py and test_importlib_resources.py). py36 name mangling occurs. Hypothesis is still that py36 use of importlib_resources backport has a problem with using its fakefilesystem with pytest/pyfakefs. --- dev_notes/tox.txt | 4 +++- nmrsim/importlib_r.py | 18 ++++++++++++++++++ setup.py | 3 ++- tests/test_importlib_resources.py | 17 +++++++++++++++++ tox.ini | 4 +++- 5 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 nmrsim/importlib_r.py create mode 100644 tests/test_importlib_resources.py diff --git a/dev_notes/tox.txt b/dev_notes/tox.txt index b267ab6..54aba87 100644 --- a/dev_notes/tox.txt +++ b/dev_notes/tox.txt @@ -3,4 +3,6 @@ Notes on getting tox to work: - install tox and tox-pyenv - use a clone of the rep in a path that doesn't have spaces in the name (googledrive/My Drive caused error) -- "pyenv global 3.6.10 3.7.6 3.8.1" required for tox to find the envs. \ No newline at end of file +- "pyenv global 3.6.10 3.7.6 3.8.1" required for tox to find the envs. + +--recreate will rebuild venv \ No newline at end of file diff --git a/nmrsim/importlib_r.py b/nmrsim/importlib_r.py new file mode 100644 index 0000000..a03c1be --- /dev/null +++ b/nmrsim/importlib_r.py @@ -0,0 +1,18 @@ +"""Hypothesis: py36 importlib_resources is incompatible with pytest/pyfakefs. +Creating minimal case for testing path-mangling. +""" +import sys +if sys.version_info >= (3, 7): + from importlib import resources +else: + import importlib_resources as resources + +import nmrsim.bin + + +def findbin(): + init_path_context = resources.path(nmrsim.bin, '__init__.py') + with init_path_context as p: + init_path = p + bin_path = init_path.parent + return bin_path diff --git a/setup.py b/setup.py index 5cd37ad..db6ffc1 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,8 @@ install_requires=['matplotlib', 'numpy', 'sparse', - "importlib_resources ; python_version<'3.7'"], + "importlib_resources ; python_version<'3.7'" + ], extras_require={ 'dev': [ 'flake8', diff --git a/tests/test_importlib_resources.py b/tests/test_importlib_resources.py new file mode 100644 index 0000000..d1bb930 --- /dev/null +++ b/tests/test_importlib_resources.py @@ -0,0 +1,17 @@ +import pathlib + +from nmrsim.importlib_r import findbin + + +def test_findbin(fs): + test_bin = (pathlib.Path(__file__) + .resolve() + .parent.parent + .joinpath('nmrsim', 'bin')) + print('test bin is ', test_bin) + expected_bin = findbin() + print('expected bin is ', expected_bin) + assert not expected_bin.exists() + print('test creating fakefs ', test_bin) + fs.create_dir(test_bin) + assert expected_bin.exists() diff --git a/tox.ini b/tox.ini index b024e46..23d3cc2 100644 --- a/tox.ini +++ b/tox.ini @@ -11,4 +11,6 @@ deps = pytest pyfakefs commands = - pytest + # pip install -e . + # pytest + pytest {posargs} From 733eef25379552b64999339a55bc7ffae61b73e2 Mon Sep 17 00:00:00 2001 From: sametz Date: Wed, 5 Feb 2020 20:59:18 -0500 Subject: [PATCH 16/80] Tests fixed! See https://stackoverflow.com/questions/60068183/importlib-resources-backport-not-compatible-with-pyfakefs/60082830#60082830 --- .gitignore | 3 ++- nmrsim/importlib_r.py | 18 ----------------- tests/test_importlib_resources.py | 17 ---------------- tests/test_qm.py | 32 +++++++++---------------------- 4 files changed, 11 insertions(+), 59 deletions(-) delete mode 100644 nmrsim/importlib_r.py delete mode 100644 tests/test_importlib_resources.py diff --git a/.gitignore b/.gitignore index a4b422c..6dd9d7c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ build dist .vscode/ docs/build -.tox \ No newline at end of file +.tox +.DS_Store \ No newline at end of file diff --git a/nmrsim/importlib_r.py b/nmrsim/importlib_r.py deleted file mode 100644 index a03c1be..0000000 --- a/nmrsim/importlib_r.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Hypothesis: py36 importlib_resources is incompatible with pytest/pyfakefs. -Creating minimal case for testing path-mangling. -""" -import sys -if sys.version_info >= (3, 7): - from importlib import resources -else: - import importlib_resources as resources - -import nmrsim.bin - - -def findbin(): - init_path_context = resources.path(nmrsim.bin, '__init__.py') - with init_path_context as p: - init_path = p - bin_path = init_path.parent - return bin_path diff --git a/tests/test_importlib_resources.py b/tests/test_importlib_resources.py deleted file mode 100644 index d1bb930..0000000 --- a/tests/test_importlib_resources.py +++ /dev/null @@ -1,17 +0,0 @@ -import pathlib - -from nmrsim.importlib_r import findbin - - -def test_findbin(fs): - test_bin = (pathlib.Path(__file__) - .resolve() - .parent.parent - .joinpath('nmrsim', 'bin')) - print('test bin is ', test_bin) - expected_bin = findbin() - print('expected bin is ', expected_bin) - assert not expected_bin.exists() - print('test creating fakefs ', test_bin) - fs.create_dir(test_bin) - assert expected_bin.exists() diff --git a/tests/test_qm.py b/tests/test_qm.py index 72988e5..3bfc5ab 100644 --- a/tests/test_qm.py +++ b/tests/test_qm.py @@ -14,12 +14,17 @@ def test_so_sparse_creates_files(fs): .resolve() .parent.parent .joinpath('nmrsim', 'bin')) - fs.create_dir(test_bin) + fs.add_real_directory(test_bin, read_only=False) expected_Lz = test_bin.joinpath('Lz3.npz') expected_Lproduct = test_bin.joinpath('Lproduct3.npz') + assert expected_Lz.exists() + assert expected_Lproduct.exists() + fs.remove_object(str(expected_Lz)) + fs.remove_object(str(expected_Lproduct)) assert not expected_Lz.exists() assert not expected_Lproduct.exists() Lz, Lproduct = _so_sparse(3) # noqa + assert Lz, Lproduct assert expected_Lz.exists() assert expected_Lproduct.exists() @@ -29,35 +34,16 @@ def test_tm_cache_creates_file(fs): .resolve() .parent.parent .joinpath('nmrsim', 'bin')) - fs.create_dir(test_bin) + fs.add_real_directory(test_bin, read_only=False) expected_T = test_bin.joinpath('T3.npz') + assert expected_T.exists() + fs.remove_object(str(expected_T)) assert not expected_T.exists() T = _tm_cache(3) assert T - print('test is checking for existence of: ', expected_T) assert expected_T.exists() -def test_tm_cache_creates_file_IRP(fs): - import sys - if sys.version_info >= (3, 7): - from importlib import resources - else: - import importlib_resources as resources - import nmrsim.bin - test_bin_file_context = resources.path(nmrsim.bin, 'T3.npz') - with test_bin_file_context as f: - test_bin_file = f - test_bin = test_bin_file.parent - print(f'test creating dir {test_bin}') - fs.create_dir(test_bin) - assert not test_bin_file.exists() - T = _tm_cache(3) - assert T - print('test is checking for existence of: ', test_bin_file) - assert test_bin_file.exists() - - def test_hamiltonian_dense(): # GIVEN v and J inputs for the Rioux 3-spin system v, J = rioux() From c4feacd9fcf8f77ae9026daef7153e00a24bee39 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 10:38:47 -0500 Subject: [PATCH 17/80] Added env and .tox to .flake8 exclude. --- .flake8 | 2 ++ nmrsim/qm.py | 8 ++++---- tox.ini | 2 ++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.flake8 b/.flake8 index a59cf59..f835096 100644 --- a/.flake8 +++ b/.flake8 @@ -3,7 +3,9 @@ exclude = .git, __pycache__, docs/source/conf.py, + env, venv, + .tox, jupyter, tests/dnmr_standards.py, build, diff --git a/nmrsim/qm.py b/nmrsim/qm.py index 86d6b38..bcc2199 100644 --- a/nmrsim/qm.py +++ b/nmrsim/qm.py @@ -46,11 +46,11 @@ else: import importlib_resources as resources -import numpy as np -import sparse +import numpy as np # noqa: E402 +import sparse # noqa: E402 -import nmrsim.bin -from nmrsim.math import normalize_peaklist +import nmrsim.bin # noqa: E402 +from nmrsim.math import normalize_peaklist # noqa: E402 CACHE = True # saving of partial solutions is allowed SPARSE = True # the sparse library is available diff --git a/tox.ini b/tox.ini index 23d3cc2..ae44d01 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,8 @@ envlist = py36, py37, py38 [testenv] +# usedevelop=True should = "pip install -e ." +usedevelop=True deps = pytest pyfakefs From d7cebbfe5b890149c8bca430a7cc417606ce4d2a Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 11:52:23 -0500 Subject: [PATCH 18/80] Added flake8 to tox.ini --- tox.ini | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tox.ini b/tox.ini index ae44d01..f9f079f 100644 --- a/tox.ini +++ b/tox.ini @@ -10,8 +10,25 @@ envlist = py36, py37, py38 # usedevelop=True should = "pip install -e ." usedevelop=True deps = + flake8 + pyfakefs + pytest + +commands = + # pip install -e . + flake8 pytest + +[testenv:flake8] +deps = + flake8 +commands = + flake8 {posargs} + +[testenv:pytest] +deps = pyfakefs + pytest commands = # pip install -e . # pytest From 752ea6ad75b9f507ab428bdd104e4ebff1018988 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 13:09:02 -0500 Subject: [PATCH 19/80] Separated flake8 in tox.ini so only runs once, in its own environment. --- tox.ini | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index f9f079f..cab1181 100644 --- a/tox.ini +++ b/tox.ini @@ -4,19 +4,17 @@ # and then run "tox" from this directory. [tox] -envlist = py36, py37, py38 +envlist = flake8, py36, py37, py38 [testenv] # usedevelop=True should = "pip install -e ." usedevelop=True deps = - flake8 pyfakefs pytest commands = # pip install -e . - flake8 pytest [testenv:flake8] From be10b9a267f9d580ce2fae77e2494a209f3cbb58 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 13:34:05 -0500 Subject: [PATCH 20/80] Added a linting GH workflow but not sure yet if it will work. --- .github/workflows/linting.yml | 22 ++++++++++++++++++++++ dev_notes/github_actions/README.txt | 5 +++-- 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/linting.yml diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 0000000..679ad5a --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,22 @@ +name: lint + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python }} + - name: Install Tox and Dependencies + run: pip install tox + - name: Run Tox Linting + run: tox -e flake8 diff --git a/dev_notes/github_actions/README.txt b/dev_notes/github_actions/README.txt index e21215a..27cb0c1 100644 --- a/dev_notes/github_actions/README.txt +++ b/dev_notes/github_actions/README.txt @@ -3,6 +3,7 @@ Considering using GitHub Actions. This folder contains examples to possibly modi pythonpackage.yml is GH's demo for setting up and testing a python package. pythonpublish.ym, is GH's demo for uploading a package to PyPI. -This link may be very useful: +Useful links: https://dan.yeaw.me/posts/github-actions-automate-your-python-development-workflow/ -https://medium.com/swlh/automate-python-testing-with-github-actions-7926b5d8a865 \ No newline at end of file +https://medium.com/swlh/automate-python-testing-with-github-actions-7926b5d8a865 +https://help.github.com/en/actions/automating-your-workflow-with-github-actions/using-python-with-github-actions \ No newline at end of file From 6fbbfffcfcb405579841f900a13a38c5855317b6 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 13:36:31 -0500 Subject: [PATCH 21/80] Added an extra line at end of setup.py to try and trigger a GH Action on linting. --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index db6ffc1..14504e5 100644 --- a/setup.py +++ b/setup.py @@ -55,3 +55,4 @@ ] } ) + From 2f3949dc05e44643ee2b74bb9ee549d5c519d188 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 13:49:56 -0500 Subject: [PATCH 22/80] modified setup.py with temporary hash comment to trigger a successful GH action linting. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 14504e5..c746b6a 100644 --- a/setup.py +++ b/setup.py @@ -55,4 +55,4 @@ ] } ) - +# Adding comment to test for passing GH lint From 0b08277b8dcca474f018441b4afc8b83712e9438 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 14:09:01 -0500 Subject: [PATCH 23/80] restored setup.py. Creating tox GH action for master, develop, and temporarily tox to see if it runs as expected. --- .github/workflows/linting.yml | 6 +++++- .github/workflows/tox.yml | 27 +++++++++++++++++++++++++++ setup.py | 1 - 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/tox.yml diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 679ad5a..c4487af 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -1,6 +1,10 @@ name: lint -on: [push] +on: + push: + branches: + - master + - develop jobs: build: diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml new file mode 100644 index 0000000..6b211d0 --- /dev/null +++ b/.github/workflows/tox.yml @@ -0,0 +1,27 @@ +name: lint + +on: + push: + branches: + - master + - develop + - tox + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python }} + - name: Install Tox and Dependencies + run: pip install tox + - name: Run Tox + run: tox -e py \ No newline at end of file diff --git a/setup.py b/setup.py index c746b6a..db6ffc1 100644 --- a/setup.py +++ b/setup.py @@ -55,4 +55,3 @@ ] } ) -# Adding comment to test for passing GH lint From 2b9aafb333fe1e915c73a65f78345ba8d644001e Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 14:25:38 -0500 Subject: [PATCH 24/80] added a tox-platforms GH action to test on mac, windows and linux. currently only set to tox branch. --- .github/workflows/tox-platforms.yml | 27 +++++++++++++++++++++++++++ .github/workflows/tox.yml | 5 ++--- 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/tox-platforms.yml diff --git a/.github/workflows/tox-platforms.yml b/.github/workflows/tox-platforms.yml new file mode 100644 index 0000000..6b211d0 --- /dev/null +++ b/.github/workflows/tox-platforms.yml @@ -0,0 +1,27 @@ +name: lint + +on: + push: + branches: + - master + - develop + - tox + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python }} + - name: Install Tox and Dependencies + run: pip install tox + - name: Run Tox + run: tox -e py \ No newline at end of file diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 6b211d0..3803883 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -3,16 +3,15 @@ name: lint on: push: branches: - - master - - develop - tox jobs: build: - runs-on: ubuntu-latest + runs-on: {{ matrix.os }} strategy: matrix: + os: [macos-latest, windows-latest, ubuntu-latest] python: [3.6, 3.7, 3.8] steps: From 177619afb729df21dd0090e276c8ecb49bb64595 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 14:31:55 -0500 Subject: [PATCH 25/80] fix to tox gh actions --- .github/workflows/tox-platforms.yml | 7 +++---- .github/workflows/tox.yml | 7 ++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tox-platforms.yml b/.github/workflows/tox-platforms.yml index 6b211d0..a4df443 100644 --- a/.github/workflows/tox-platforms.yml +++ b/.github/workflows/tox-platforms.yml @@ -1,18 +1,17 @@ -name: lint +name: Tox Cross-Platform on: push: branches: - - master - - develop - tox jobs: build: - runs-on: ubuntu-latest + runs-on: {{ matrix.os }} strategy: matrix: + os: [macos-latest, windows-latest, ubuntu-latest] python: [3.6, 3.7, 3.8] steps: diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 3803883..7c73258 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -1,17 +1,18 @@ -name: lint +name: tox on: push: branches: + - master + - develop - tox jobs: build: - runs-on: {{ matrix.os }} + runs-on: ubuntu-latest strategy: matrix: - os: [macos-latest, windows-latest, ubuntu-latest] python: [3.6, 3.7, 3.8] steps: From 5be39850743f788cd507fc549ae11712151795a5 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 14:40:58 -0500 Subject: [PATCH 26/80] fix to tox gh actions --- .github/workflows/tox-platforms.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tox-platforms.yml b/.github/workflows/tox-platforms.yml index a4df443..e322add 100644 --- a/.github/workflows/tox-platforms.yml +++ b/.github/workflows/tox-platforms.yml @@ -8,7 +8,7 @@ on: jobs: build: - runs-on: {{ matrix.os }} + runs-on: ${{ matrix.os }} strategy: matrix: os: [macos-latest, windows-latest, ubuntu-latest] From ce74d243d0687898fb4c7301ec92efc4bded4c0c Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 15:45:01 -0500 Subject: [PATCH 27/80] added master/develop to tox cross-platform gh action to trigger it --- .github/workflows/tox-platforms.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tox-platforms.yml b/.github/workflows/tox-platforms.yml index e322add..1d5b644 100644 --- a/.github/workflows/tox-platforms.yml +++ b/.github/workflows/tox-platforms.yml @@ -3,6 +3,8 @@ name: Tox Cross-Platform on: push: branches: + - master + - develop - tox jobs: From c3ea980cabdb7097e24b690f45ae5366d44bcb5d Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 8 Feb 2020 16:57:14 -0500 Subject: [PATCH 28/80] added sphinx doc building to tox --- tox.ini | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index cab1181..0fe9568 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = flake8, py36, py37, py38 +envlist = flake8, py36, py37, py38, docs [testenv] # usedevelop=True should = "pip install -e ." @@ -31,3 +31,9 @@ commands = # pip install -e . # pytest pytest {posargs} + +[testenv:docs] +basepython = python3.7 +extras = docs +commands = + sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html From a798b9f532fc43ea97778b5ab2aab434c326dbf6 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 09:28:01 -0500 Subject: [PATCH 29/80] Mac/Windows runners were hanging up. Created a disabled_workflows folder to move workflows to, to deactivate them selectively. Created a cross-platform smoketest (platform-smoke.yml) to help get these runners working. --- .../tox-platforms.yml | 0 .github/workflows/platform-smoke.yml | 28 +++++++++++++++++++ 2 files changed, 28 insertions(+) rename .github/{workflows => disabled_workflows}/tox-platforms.yml (100%) create mode 100644 .github/workflows/platform-smoke.yml diff --git a/.github/workflows/tox-platforms.yml b/.github/disabled_workflows/tox-platforms.yml similarity index 100% rename from .github/workflows/tox-platforms.yml rename to .github/disabled_workflows/tox-platforms.yml diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml new file mode 100644 index 0000000..853777e --- /dev/null +++ b/.github/workflows/platform-smoke.yml @@ -0,0 +1,28 @@ +name: Cross-Platform Smoke Test + +on: + push: + branches: + - tox + +jobs: + build: + + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-latest, windows-latest, ubuntu-latest] + python: [3.7] + + steps: + - name: Enter + run: echo 'Checkout at ${{ steps.hello.outputs.time }}' +# - uses: actions/checkout@v2 +# - name: Setup Python +# uses: actions/setup-python@v1 +# with: +# python-version: ${{ matrix.python }} +# - name: Install Tox and Dependencies +# run: pip install tox +# - name: Run Tox +# run: tox -e py \ No newline at end of file From 52796ae0cc6809eb0b9dfb2fb8d990027ef46532 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 09:31:27 -0500 Subject: [PATCH 30/80] added two echos at start of smoketest run, to trigger smoke test on push. --- .github/workflows/platform-smoke.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml index 853777e..06738a4 100644 --- a/.github/workflows/platform-smoke.yml +++ b/.github/workflows/platform-smoke.yml @@ -16,7 +16,9 @@ jobs: steps: - name: Enter - run: echo 'Checkout at ${{ steps.hello.outputs.time }}' + run: + - echo 'Initiated runner.' + - echo 'Checkout at ${{ steps.hello.outputs.time }}' # - uses: actions/checkout@v2 # - name: Setup Python # uses: actions/setup-python@v1 From 39193e440245b6fb66270fd3f978ec58a9415ec8 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 09:37:02 -0500 Subject: [PATCH 31/80] fixed smoke test runs? --- .github/{workflows => disabled_workflows}/linting.yml | 0 .github/{workflows => disabled_workflows}/tox.yml | 0 .github/workflows/platform-smoke.yml | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename .github/{workflows => disabled_workflows}/linting.yml (100%) rename .github/{workflows => disabled_workflows}/tox.yml (100%) diff --git a/.github/workflows/linting.yml b/.github/disabled_workflows/linting.yml similarity index 100% rename from .github/workflows/linting.yml rename to .github/disabled_workflows/linting.yml diff --git a/.github/workflows/tox.yml b/.github/disabled_workflows/tox.yml similarity index 100% rename from .github/workflows/tox.yml rename to .github/disabled_workflows/tox.yml diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml index 06738a4..aa4297c 100644 --- a/.github/workflows/platform-smoke.yml +++ b/.github/workflows/platform-smoke.yml @@ -17,8 +17,8 @@ jobs: steps: - name: Enter run: - - echo 'Initiated runner.' - - echo 'Checkout at ${{ steps.hello.outputs.time }}' + echo 'Initiated runner.' + echo 'Checkout at ${{ steps.hello.outputs.time }}' # - uses: actions/checkout@v2 # - name: Setup Python # uses: actions/setup-python@v1 From fe1b3171279543155b8bf986c1ed29271972b476 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 09:38:08 -0500 Subject: [PATCH 32/80] triggering smoketest --- .github/workflows/platform-smoke.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml index aa4297c..dc61f2c 100644 --- a/.github/workflows/platform-smoke.yml +++ b/.github/workflows/platform-smoke.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Enter run: - echo 'Initiated runner.' + echo 'Initiated runner' echo 'Checkout at ${{ steps.hello.outputs.time }}' # - uses: actions/checkout@v2 # - name: Setup Python From ef9991497e400dfdbbc8edd56ab4878a277ca6e0 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 09:42:21 -0500 Subject: [PATCH 33/80] added checkout to smoke test --- .github/workflows/platform-smoke.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml index dc61f2c..26afddd 100644 --- a/.github/workflows/platform-smoke.yml +++ b/.github/workflows/platform-smoke.yml @@ -19,7 +19,7 @@ jobs: run: echo 'Initiated runner' echo 'Checkout at ${{ steps.hello.outputs.time }}' -# - uses: actions/checkout@v2 + - uses: actions/checkout@v2 # - name: Setup Python # uses: actions/setup-python@v1 # with: From a2d2f000adc7c5199edbf6258431c2a7f0fa807f Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 09:47:46 -0500 Subject: [PATCH 34/80] Logs indicated problem was only in Run Tox step. Activating everything prior in smoke test. --- .github/workflows/platform-smoke.yml | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml index 26afddd..337f48a 100644 --- a/.github/workflows/platform-smoke.yml +++ b/.github/workflows/platform-smoke.yml @@ -15,16 +15,18 @@ jobs: python: [3.7] steps: - - name: Enter - run: - echo 'Initiated runner' - echo 'Checkout at ${{ steps.hello.outputs.time }}' +# - name: Enter +# run: +# echo 'Initiated runner.' +# echo 'Checkout at ${{ steps.hello.outputs.time }}' - uses: actions/checkout@v2 -# - name: Setup Python -# uses: actions/setup-python@v1 -# with: -# python-version: ${{ matrix.python }} -# - name: Install Tox and Dependencies -# run: pip install tox + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python }} + - name: Install Tox and Dependencies + run: + pip install tox + echo 'smoke test successful through tox intallation' # - name: Run Tox # run: tox -e py \ No newline at end of file From 50547c3dd61eef950ec781f8ba9665bf1c595967 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 09:48:36 -0500 Subject: [PATCH 35/80] triggering smoke test on push --- .github/workflows/platform-smoke.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml index 337f48a..401f26b 100644 --- a/.github/workflows/platform-smoke.yml +++ b/.github/workflows/platform-smoke.yml @@ -27,6 +27,6 @@ jobs: - name: Install Tox and Dependencies run: pip install tox - echo 'smoke test successful through tox intallation' + echo 'Smoke test successful through tox intallation.' # - name: Run Tox # run: tox -e py \ No newline at end of file From 0fa284513c9d8db28b25d08ca67acfbebe021910 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 09:53:20 -0500 Subject: [PATCH 36/80] fix multiline runs --- .github/workflows/platform-smoke.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml index 401f26b..32de259 100644 --- a/.github/workflows/platform-smoke.yml +++ b/.github/workflows/platform-smoke.yml @@ -16,7 +16,7 @@ jobs: steps: # - name: Enter -# run: +# run: | # echo 'Initiated runner.' # echo 'Checkout at ${{ steps.hello.outputs.time }}' - uses: actions/checkout@v2 @@ -25,7 +25,7 @@ jobs: with: python-version: ${{ matrix.python }} - name: Install Tox and Dependencies - run: + run: | pip install tox echo 'Smoke test successful through tox intallation.' # - name: Run Tox From f952a5e2fcf4cb78bf2738711bd14fa4718826a7 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 09:53:51 -0500 Subject: [PATCH 37/80] trigger smoke test --- .github/workflows/platform-smoke.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml index 32de259..ccc6d68 100644 --- a/.github/workflows/platform-smoke.yml +++ b/.github/workflows/platform-smoke.yml @@ -27,6 +27,6 @@ jobs: - name: Install Tox and Dependencies run: | pip install tox - echo 'Smoke test successful through tox intallation.' + echo 'Smoke test successful through tox intallation' # - name: Run Tox # run: tox -e py \ No newline at end of file From 14cb13c0673eb0427a615b2a9c9ccc635713ff38 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 09:59:23 -0500 Subject: [PATCH 38/80] unhashed rest of smoke test --- .github/workflows/platform-smoke.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml index ccc6d68..41e0660 100644 --- a/.github/workflows/platform-smoke.yml +++ b/.github/workflows/platform-smoke.yml @@ -28,5 +28,7 @@ jobs: run: | pip install tox echo 'Smoke test successful through tox intallation' -# - name: Run Tox -# run: tox -e py \ No newline at end of file + - name: Run Tox + run: | + echo 'About to run tox' + tox -e py \ No newline at end of file From 6b4644a15342194ec12b9f4498f6f12c702666f8 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 10:00:13 -0500 Subject: [PATCH 39/80] trigger smoke test --- .github/workflows/platform-smoke.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml index 41e0660..3a30382 100644 --- a/.github/workflows/platform-smoke.yml +++ b/.github/workflows/platform-smoke.yml @@ -27,7 +27,7 @@ jobs: - name: Install Tox and Dependencies run: | pip install tox - echo 'Smoke test successful through tox intallation' + echo 'Smoke test successful through tox intallation.' - name: Run Tox run: | echo 'About to run tox' From ccdcfdb75ca18d91a43e68190014cacb23760d4a Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 16:07:11 -0500 Subject: [PATCH 40/80] attempting fix for cross-platform tox. Shouldn't need multiple pythons in both tox.ini and GH action. Also, tox documentation test runs into problems with pandoc not available. TODO: once multi-platform works, spin doc test off to Ubuntu only and use a sudo install of pandoc there--shouldn't need to test doc build across platforms? --- tox.ini | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/tox.ini b/tox.ini index 0fe9568..335842a 100644 --- a/tox.ini +++ b/tox.ini @@ -4,18 +4,18 @@ # and then run "tox" from this directory. [tox] -envlist = flake8, py36, py37, py38, docs +envlist = flake8, pytest [testenv] # usedevelop=True should = "pip install -e ." usedevelop=True -deps = - pyfakefs - pytest - -commands = - # pip install -e . - pytest +;deps = +; pyfakefs +; pytest +; +;commands = +; # pip install -e . +; pytest [testenv:flake8] deps = @@ -33,7 +33,15 @@ commands = pytest {posargs} [testenv:docs] -basepython = python3.7 +;basepython = python3.7 extras = docs +;changedir = docs +deps = + sphinx + sphinx_rtd_theme + sphinxcontrib-napoleon + nbsphinx commands = +; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html +; make html \ No newline at end of file From 22b1314afc74db3495e69e1d530eaa1d174728c8 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 16:08:46 -0500 Subject: [PATCH 41/80] push to trigger cross-platform tox --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 335842a..73c4c4e 100644 --- a/tox.ini +++ b/tox.ini @@ -44,4 +44,5 @@ deps = commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html -; make html \ No newline at end of file +; make html +; \ No newline at end of file From 0c6e2fbc933fddbd034f509a4d134298b782fa0b Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 16:16:32 -0500 Subject: [PATCH 42/80] cross-platform worked with py37, so adding 3.6 and 3.8 to the GH action matrix. --- .github/workflows/platform-smoke.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/platform-smoke.yml b/.github/workflows/platform-smoke.yml index 3a30382..7bae409 100644 --- a/.github/workflows/platform-smoke.yml +++ b/.github/workflows/platform-smoke.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: os: [macos-latest, windows-latest, ubuntu-latest] - python: [3.7] + python: [3.6, 3.7, 3.8] steps: # - name: Enter From 9fa0cfcd9ea68d2fcc2080ea5bc09bc3f186771d Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 16:18:04 -0500 Subject: [PATCH 43/80] trigger cross-platform, cross-python tox test on GH --- tox.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/tox.ini b/tox.ini index 73c4c4e..8477495 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,3 @@ commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html ; make html -; \ No newline at end of file From 00f7b5cc9b171f353c35a08c2a792635774269bd Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 16:51:23 -0500 Subject: [PATCH 44/80] testing sphinx GitHub Action --- .../platform-smoke.yml | 0 .github/workflows/test-docs.yml | 29 +++++++++++++++++++ 2 files changed, 29 insertions(+) rename .github/{workflows => disabled_workflows}/platform-smoke.yml (100%) create mode 100644 .github/workflows/test-docs.yml diff --git a/.github/workflows/platform-smoke.yml b/.github/disabled_workflows/platform-smoke.yml similarity index 100% rename from .github/workflows/platform-smoke.yml rename to .github/disabled_workflows/platform-smoke.yml diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml new file mode 100644 index 0000000..9cff30a --- /dev/null +++ b/.github/workflows/test-docs.yml @@ -0,0 +1,29 @@ +name: Cross-Platform Smoke Test + +on: + push: + branches: + - tox + +jobs: + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v1 +# with: +# python-version: ${{ matrix.python }} + - name: Install Pandoc + uses: docker://pandoc/core:2.9 +# - name: Install Tox and Dependencies + ## run: | + ## pip install tox + ## echo 'Smoke test successful through tox intallation.' + ## - name: Run Tox + ## run: | + ## echo 'About to run tox' + ## tox -e docs + - name: Build HTML + run: | + sphinx-build -W -b html -d docs/doctrees docs/source docs/build/html \ No newline at end of file From d25f62fa74283fd70db03acac9f6c533da5fdd5c Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 16:52:33 -0500 Subject: [PATCH 45/80] trigger sphinx GH action --- .github/workflows/test-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index 9cff30a..0b2e1df 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -25,5 +25,5 @@ jobs: ## echo 'About to run tox' ## tox -e docs - name: Build HTML - run: | + run: sphinx-build -W -b html -d docs/doctrees docs/source docs/build/html \ No newline at end of file From c4c3eda0a59b22df1e65f06a09bb0ab3c519fa46 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:05:01 -0500 Subject: [PATCH 46/80] possible test-docs fix --- .github/workflows/test-docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index 0b2e1df..8739222 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -26,4 +26,5 @@ jobs: ## tox -e docs - name: Build HTML run: + pip install sphinx sphinx_rtd_theme sphinxcontrib-napoleon nbsphinx sphinx-build -W -b html -d docs/doctrees docs/source docs/build/html \ No newline at end of file From e5f6949ba294abd35d79797ce816e8fe7eaed9a7 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:05:51 -0500 Subject: [PATCH 47/80] possible test-docs fix --- .github/workflows/test-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index 8739222..e13f95a 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -25,6 +25,6 @@ jobs: ## echo 'About to run tox' ## tox -e docs - name: Build HTML - run: + run: | pip install sphinx sphinx_rtd_theme sphinxcontrib-napoleon nbsphinx sphinx-build -W -b html -d docs/doctrees docs/source docs/build/html \ No newline at end of file From 9181937a942469275da8d0fe0379814c6f36a3f2 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:06:45 -0500 Subject: [PATCH 48/80] trigger test-docs action --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 8477495..bb1bf3c 100644 --- a/tox.ini +++ b/tox.ini @@ -45,3 +45,4 @@ commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html ; make html +; From c4c1d151afa67584dfd95947b473e17d6961fe73 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:12:54 -0500 Subject: [PATCH 49/80] attempted fix of pandoc install for test-docs action --- .github/workflows/test-docs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index e13f95a..297f005 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -15,7 +15,8 @@ jobs: # with: # python-version: ${{ matrix.python }} - name: Install Pandoc - uses: docker://pandoc/core:2.9 + run: + sudo apt-get install pandoc # - name: Install Tox and Dependencies ## run: | ## pip install tox From e2c8cf1d310ed14a5f7c9788578094e9dd1a0797 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:13:45 -0500 Subject: [PATCH 50/80] trigger test-docs action --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index bb1bf3c..4681fc1 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,4 @@ commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html ; make html -; + From 4cd9d1a0679d71c5fd7b0c7ca0f5a698d9140bc5 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:24:56 -0500 Subject: [PATCH 51/80] possible test-docs fix --- .github/workflows/test-docs.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index 297f005..e374691 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -1,4 +1,4 @@ -name: Cross-Platform Smoke Test +name: Test Sphinx Build on: push: @@ -8,12 +8,15 @@ on: jobs: docs: runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.6, 3.7, 3.8] steps: - uses: actions/checkout@v2 - - name: Setup Python + - name: Setup Python ${{ matrix.python }} uses: actions/setup-python@v1 -# with: -# python-version: ${{ matrix.python }} + with: + python-version: ${{ matrix.python }} - name: Install Pandoc run: sudo apt-get install pandoc From f6bcd464edd80b6aaca24dda3065607f8b91d652 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:25:40 -0500 Subject: [PATCH 52/80] trigger test-docs --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 4681fc1..bb1bf3c 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,4 @@ commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html ; make html - +; From 7372e7483780e4fcff2edb7d7e3f0848e9a9a97d Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:34:11 -0500 Subject: [PATCH 53/80] trigger test-docs --- .github/workflows/test-docs.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index e374691..a6410e2 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -28,7 +28,11 @@ jobs: ## run: | ## echo 'About to run tox' ## tox -e docs - - name: Build HTML - run: | + - name: Install wheel + run: + pip install wheel + - name: Install sphinx dependencies + run: pip install sphinx sphinx_rtd_theme sphinxcontrib-napoleon nbsphinx + - name: Build HTML sphinx-build -W -b html -d docs/doctrees docs/source docs/build/html \ No newline at end of file From 92db9b00dd98ada4be620ea5de3c3937ff2fb591 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:35:24 -0500 Subject: [PATCH 54/80] trigger test-docs --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index bb1bf3c..4681fc1 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,4 @@ commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html ; make html -; + From 11b9bff9ef15df5512a3b5ab36cbbf902c828be2 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:37:40 -0500 Subject: [PATCH 55/80] fixed test-docs.yml --- .github/workflows/test-docs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index a6410e2..92b8ac2 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -35,4 +35,5 @@ jobs: run: pip install sphinx sphinx_rtd_theme sphinxcontrib-napoleon nbsphinx - name: Build HTML + run: sphinx-build -W -b html -d docs/doctrees docs/source docs/build/html \ No newline at end of file From 85d354454833cb756ff6ecb8a69a4664da27e969 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:38:13 -0500 Subject: [PATCH 56/80] trigger test-docs --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 4681fc1..bb1bf3c 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,4 @@ commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html ; make html - +; From 527e344fed21cd3ed3bebb354f8067da51e06fd5 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:48:46 -0500 Subject: [PATCH 57/80] fix to test-docs.yml --- .github/workflows/test-docs.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index 92b8ac2..aa76526 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -31,9 +31,12 @@ jobs: - name: Install wheel run: pip install wheel + - name: Install sphinx + run: + sudo apt-get install python-sphinx - name: Install sphinx dependencies run: - pip install sphinx sphinx_rtd_theme sphinxcontrib-napoleon nbsphinx + pip install sphinx_rtd_theme sphinxcontrib-napoleon nbsphinx - name: Build HTML run: sphinx-build -W -b html -d docs/doctrees docs/source docs/build/html \ No newline at end of file From 595ec321cf8482c0ab9cc119ef4f1882cf9bc0b6 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:49:21 -0500 Subject: [PATCH 58/80] trigger test-docs --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index bb1bf3c..4681fc1 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,4 @@ commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html ; make html -; + From d2c0bf2144dbb3c761f61b1e0cfaca1ca1ede055 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:58:43 -0500 Subject: [PATCH 59/80] fix test-docs.yml --- .github/workflows/test-docs.yml | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index aa76526..e84c30d 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -17,6 +17,10 @@ jobs: uses: actions/setup-python@v1 with: python-version: ${{ matrix.python }} + - name: Install dependencies + run: | + python -m pip install -upgrade pip setuptools wheel + python -m pip install -e ".[dev]" - name: Install Pandoc run: sudo apt-get install pandoc @@ -28,15 +32,15 @@ jobs: ## run: | ## echo 'About to run tox' ## tox -e docs - - name: Install wheel - run: - pip install wheel - - name: Install sphinx - run: - sudo apt-get install python-sphinx - - name: Install sphinx dependencies - run: - pip install sphinx_rtd_theme sphinxcontrib-napoleon nbsphinx +# - name: Install wheel +# run: +# pip install wheel +# - name: Install sphinx +# run: +# sudo apt-get install python-sphinx +# - name: Install sphinx dependencies +# run: +# pip install sphinx_rtd_theme sphinxcontrib-napoleon nbsphinx - name: Build HTML run: sphinx-build -W -b html -d docs/doctrees docs/source docs/build/html \ No newline at end of file From 7b8d4c66603424308bdb0a259d7e979acaee50b0 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 17:59:16 -0500 Subject: [PATCH 60/80] trigger test-docs --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 4681fc1..bb1bf3c 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,4 @@ commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html ; make html - +; From 3a69a825c186e9698993b157c85022c51285a7f9 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 18:01:47 -0500 Subject: [PATCH 61/80] fix test-docs.yml --- .github/workflows/test-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index e84c30d..2f4fa4f 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -19,7 +19,7 @@ jobs: python-version: ${{ matrix.python }} - name: Install dependencies run: | - python -m pip install -upgrade pip setuptools wheel + python -m pip install --upgrade pip setuptools wheel python -m pip install -e ".[dev]" - name: Install Pandoc run: From 3d163ab1406953868617ff2fc58164e811e72f0d Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 18:02:21 -0500 Subject: [PATCH 62/80] trigger test-docs --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index bb1bf3c..4681fc1 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,4 @@ commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html ; make html -; + From db2b0f3a6ebcfce22e0b2bdf57458122135ec545 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 18:13:45 -0500 Subject: [PATCH 63/80] debug test-doc -- error message saying tried to install nmrsim in a python 2 environment??? --- .github/workflows/test-docs.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index 2f4fa4f..ae0f97e 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -17,10 +17,20 @@ jobs: uses: actions/setup-python@v1 with: python-version: ${{ matrix.python }} +# received error: +# ERROR: Package 'nmrsim' requires a different Python: 2.7.17 not in '>=3.6' +# so adding paranoia checks for python version + - name: Check python version + run: + python --version - name: Install dependencies run: | python -m pip install --upgrade pip setuptools wheel + python --version + - name: Install package in dev mode + run: python -m pip install -e ".[dev]" + - name: Install Pandoc run: sudo apt-get install pandoc From 30b9d9f2252181f471995b76235ad4612cefe824 Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 18:14:20 -0500 Subject: [PATCH 64/80] trigger test-docs --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 4681fc1..bb1bf3c 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,4 @@ commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html ; make html - +; From 2acff135cc3aa9d8a61361b1fa8838f39535347e Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 18:28:11 -0500 Subject: [PATCH 65/80] fix test-docs.yml --- .github/workflows/test-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index ae0f97e..69b5257 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8] + python: [3.6, 3.7, 3.8] steps: - uses: actions/checkout@v2 - name: Setup Python ${{ matrix.python }} From 4f4b54608b8b350ec53a937b8de22454564ebb5b Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 18:29:11 -0500 Subject: [PATCH 66/80] trigger test-docs --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index bb1bf3c..4681fc1 100644 --- a/tox.ini +++ b/tox.ini @@ -45,4 +45,4 @@ commands = ; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html ; make html -; + From 97c865cf75443d3210e7cbf6ce3fcab6565ac23e Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 18:46:03 -0500 Subject: [PATCH 67/80] edited tox-cross-platform.yml (name change from tox-platforms; only develop and master branches now). Changed test-docs to branch "to*" to test wildcard. If successful, will add "release-*" to GH action branches as well. --- .github/workflows/test-docs.yml | 26 +------------------ .../tox-cross-platform.yml} | 1 - 2 files changed, 1 insertion(+), 26 deletions(-) rename .github/{disabled_workflows/tox-platforms.yml => workflows/tox-cross-platform.yml} (97%) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index 69b5257..707bbca 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -3,7 +3,7 @@ name: Test Sphinx Build on: push: branches: - - tox + - to* jobs: docs: @@ -17,12 +17,6 @@ jobs: uses: actions/setup-python@v1 with: python-version: ${{ matrix.python }} -# received error: -# ERROR: Package 'nmrsim' requires a different Python: 2.7.17 not in '>=3.6' -# so adding paranoia checks for python version - - name: Check python version - run: - python --version - name: Install dependencies run: | python -m pip install --upgrade pip setuptools wheel @@ -30,27 +24,9 @@ jobs: - name: Install package in dev mode run: python -m pip install -e ".[dev]" - - name: Install Pandoc run: sudo apt-get install pandoc -# - name: Install Tox and Dependencies - ## run: | - ## pip install tox - ## echo 'Smoke test successful through tox intallation.' - ## - name: Run Tox - ## run: | - ## echo 'About to run tox' - ## tox -e docs -# - name: Install wheel -# run: -# pip install wheel -# - name: Install sphinx -# run: -# sudo apt-get install python-sphinx -# - name: Install sphinx dependencies -# run: -# pip install sphinx_rtd_theme sphinxcontrib-napoleon nbsphinx - name: Build HTML run: sphinx-build -W -b html -d docs/doctrees docs/source docs/build/html \ No newline at end of file diff --git a/.github/disabled_workflows/tox-platforms.yml b/.github/workflows/tox-cross-platform.yml similarity index 97% rename from .github/disabled_workflows/tox-platforms.yml rename to .github/workflows/tox-cross-platform.yml index 1d5b644..14c1887 100644 --- a/.github/disabled_workflows/tox-platforms.yml +++ b/.github/workflows/tox-cross-platform.yml @@ -5,7 +5,6 @@ on: branches: - master - develop - - tox jobs: build: From b54574e19c92f425decf69d01a700091ac86de8a Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 18:48:47 -0500 Subject: [PATCH 68/80] cleanup of tox.ini; trying to trigger test-docs and test of wildcard branch name --- tox.ini | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/tox.ini b/tox.ini index 4681fc1..3ba22d5 100644 --- a/tox.ini +++ b/tox.ini @@ -9,13 +9,6 @@ envlist = flake8, pytest [testenv] # usedevelop=True should = "pip install -e ." usedevelop=True -;deps = -; pyfakefs -; pytest -; -;commands = -; # pip install -e . -; pytest [testenv:flake8] deps = @@ -28,21 +21,15 @@ deps = pyfakefs pytest commands = - # pip install -e . - # pytest pytest {posargs} [testenv:docs] -;basepython = python3.7 extras = docs -;changedir = docs deps = sphinx sphinx_rtd_theme sphinxcontrib-napoleon nbsphinx commands = -; sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html -; make html From 93893088e279140084c7dd2e862646498703ac7c Mon Sep 17 00:00:00 2001 From: sametz Date: Sun, 9 Feb 2020 19:00:34 -0500 Subject: [PATCH 69/80] test-docs and tox-cross-platform GH actions now set to master, develop, and release-x.y.z branches. Ready to merge into develop. --- .github/workflows/test-docs.yml | 4 +++- .github/workflows/tox-cross-platform.yml | 1 + tox.ini | 1 - 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-docs.yml b/.github/workflows/test-docs.yml index 707bbca..ba8449a 100644 --- a/.github/workflows/test-docs.yml +++ b/.github/workflows/test-docs.yml @@ -3,7 +3,9 @@ name: Test Sphinx Build on: push: branches: - - to* + - master + - develop + - release-* jobs: docs: diff --git a/.github/workflows/tox-cross-platform.yml b/.github/workflows/tox-cross-platform.yml index 14c1887..3abad73 100644 --- a/.github/workflows/tox-cross-platform.yml +++ b/.github/workflows/tox-cross-platform.yml @@ -5,6 +5,7 @@ on: branches: - master - develop + - release-* jobs: build: diff --git a/tox.ini b/tox.ini index 3ba22d5..9343832 100644 --- a/tox.ini +++ b/tox.ini @@ -32,4 +32,3 @@ deps = nbsphinx commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs/source docs/build/html - From 702251441fae081173394f33315ba5b063e21df9 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 15 Feb 2020 11:09:01 -0500 Subject: [PATCH 70/80] added to github_actions/README.txt; removed bin_fixing.txt. --- dev_notes/bin_fixing.txt | 36 ----------------------------- dev_notes/github_actions/README.txt | 3 ++- 2 files changed, 2 insertions(+), 37 deletions(-) delete mode 100644 dev_notes/bin_fixing.txt diff --git a/dev_notes/bin_fixing.txt b/dev_notes/bin_fixing.txt deleted file mode 100644 index 97968d9..0000000 --- a/dev_notes/bin_fixing.txt +++ /dev/null @@ -1,36 +0,0 @@ -tox reveals py36 fails the so and tm "creates file tests", 37 and 38 pass. -BUT testing actual installs in a venv shows that all fail to repair themselves. Therefore problem with how importlib.resources was implemented? -And problem with tests? -3.7: -Traceback (most recent call last): - File "", line 1, in - File "/Users/geoffreysametz/Documents/3.7/venv/lib/python3.7/site-packages/nmrsim/qm.py", line 337, in _tm_cache - with path_context as p: - File "/Users/geoffreysametz/.pyenv/versions/3.7.6/lib/python3.7/contextlib.py", line 112, in __enter__ - return next(self.gen) - File "/Users/geoffreysametz/.pyenv/versions/3.7.6/lib/python3.7/importlib/resources.py", line 201, in path - with open_binary(package, resource) as fp: - File "/Users/geoffreysametz/.pyenv/versions/3.7.6/lib/python3.7/importlib/resources.py", line 91, in open_binary - return reader.open_resource(resource) - File "", line 929, in open_resource -FileNotFoundError: [Errno 2] No such file or directory: '/Users/geoffreysametz/Documents/3.7/venv/lib/python3.7/site-packages/nmrsim/bin/T3.npz' - -3.6: -Traceback (most recent call last): - File "/Users/geoffreysametz/Documents/3.6/venv/lib/python3.6/site-packages/importlib_resources/_py3.py", line 85, in open_binary - return open(full_path, mode='rb') -FileNotFoundError: [Errno 2] No such file or directory: '/Users/geoffreysametz/Documents/3.6/venv/lib/python3.6/site-packages/nmrsim/bin/T3.npz' - -During handling of the above exception, another exception occurred: - -Traceback (most recent call last): - File "", line 1, in - File "/Users/geoffreysametz/Documents/3.6/venv/lib/python3.6/site-packages/nmrsim/qm.py", line 337, in _tm_cache - with path_context as p: - File "/Users/geoffreysametz/.pyenv/versions/3.6.10/lib/python3.6/contextlib.py", line 81, in __enter__ - return next(self.gen) - File "/Users/geoffreysametz/Documents/3.6/venv/lib/python3.6/site-packages/importlib_resources/_py3.py", line 188, in path - with open_binary(package, resource) as fp: - File "/Users/geoffreysametz/Documents/3.6/venv/lib/python3.6/site-packages/importlib_resources/_py3.py", line 99, in open_binary - raise FileNotFoundError(message) -FileNotFoundError: 'T3.npz' resource not found in 'nmrsim.bin' diff --git a/dev_notes/github_actions/README.txt b/dev_notes/github_actions/README.txt index 27cb0c1..5c3da92 100644 --- a/dev_notes/github_actions/README.txt +++ b/dev_notes/github_actions/README.txt @@ -6,4 +6,5 @@ pythonpublish.ym, is GH's demo for uploading a package to PyPI. Useful links: https://dan.yeaw.me/posts/github-actions-automate-your-python-development-workflow/ https://medium.com/swlh/automate-python-testing-with-github-actions-7926b5d8a865 -https://help.github.com/en/actions/automating-your-workflow-with-github-actions/using-python-with-github-actions \ No newline at end of file +https://help.github.com/en/actions/automating-your-workflow-with-github-actions/using-python-with-github-actions +https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ \ No newline at end of file From d43f9570b685c412c399cd0de9814176c4f17031 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 15 Feb 2020 11:44:24 -0500 Subject: [PATCH 71/80] Bump versions and year. Updated CHANGELOG.rst. --- CHANGELOG.rst | 44 ++++++++++++++++++++++++------------ LICENSE | 2 +- README.rst | 2 +- dev_notes/TODO.txt | 5 ++++ docs/source/conf.py | 6 ++--- docs/source/introduction.rst | 2 +- nmrsim/__init__.py | 2 +- setup.py | 2 +- 8 files changed, 42 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6007a17..8abc9d2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,31 +4,45 @@ Change Log All notable changes to this project will be documented in this file. -The format is inspired by `Keep a Changelog `_ and tries to adhere to `Semantic Versioning `_ as well as `PEP 440 `_. - -Working towards a Version 1.0.0 release, the author interprets the terms below as follows: - -* **pre-alpha status**: Working code, but every aspect of the project is in flux, including API and documentation. - -* **alpha status**: The "broad strokes" of an API are in place. Code testing and optimization, API adjustments, - addition of new features and tests, and creating packaging requirements will be ongoing. The package can be - installed via setup.py. - -* **beta status**: All anticipated Version 1.0.0 features are implemented and documented. The package can be +The format is inspired by +`Keep a Changelog `_ +and tries to adhere to `Semantic Versioning `_ +as well as `PEP 440 `_. + +Working towards a Version 1.0.0 release, +the author interprets the terms below as follows: + +* **pre-alpha status**: + Working code, + but every aspect of the project is in flux, + including API and documentation. + +* **alpha status**: + The "broad strokes" of an API are in place. + Code testing and optimization, API adjustments, + addition of new features and tests, + and creating packaging requirements will be ongoing. + The package can be installed via setup.py. + +* **beta status**: + All anticipated Version 1.0.0 features are implemented and documented. + The package can be 'pip install'ed via TestPyPI and possibly PyPI. * **release candidate status**: The package passes tests on multiple platforms and python/dependency versions. The package can be pip installed from PyPI. -* **Version 1.0.0 release**: API is stable. The package is available on PyPI (and perhaps conda). +* **Version 1.0.0 release**: + API is stable. + The package is available on PyPI (and perhaps conda). -Unreleased ----------- +0.4.0 - 2020-xx-xx (beta release) +--------------------------------- Changed ^^^^^^^ -* nmrsim.qp now uses importlib.resources.path to find the nmrsim/bin folder, +* nmrsim.qm now uses importlib.resources.path to find the nmrsim/bin folder, instead of relying on the use of __file__. For users, this means that if your application uses nmrsim as a dependency, and you want to freeze the application (e.g. with PyInstaller or PyOxidizer), diff --git a/LICENSE b/LICENSE index b1f0f8b..b7292d2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Geoffrey M. Sametz +Copyright (c) 2020 Geoffrey M. Sametz Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.rst b/README.rst index fe9a79d..2daf9cf 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ Click the "**Launch Binder**" link above to see how **nmrsim** can be used in Ju `Documentation on Read the Docs `_ -nmrsim (version 0.3.0 beta) +nmrsim (version 0.4.0 beta) ============================ **nmrsim** is a Python library for the simulation of solution-state nuclear magnetic resonance (NMR) spectra. diff --git a/dev_notes/TODO.txt b/dev_notes/TODO.txt index 8f2a956..e77a5da 100644 --- a/dev_notes/TODO.txt +++ b/dev_notes/TODO.txt @@ -3,3 +3,8 @@ A list of high-priority TODOs. Create a script that will rebuild all binaries for the nmrsim/bin folder of sparse matrices. Also, time it so you know how much time this build will take on your fastest computer. This should become part of the publish workflow. + +Better yet: have GH Actions do this prior to a PyPI release. + +Make sure your versioning scheme will agree with the Python standard and not +necessarily SemVer. diff --git a/docs/source/conf.py b/docs/source/conf.py index 91959de..161367d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,13 +21,13 @@ # -- Project information ----------------------------------------------------- project = 'nmrsim' -copyright = '2019, Geoffrey M. Sametz' +copyright = '2020, Geoffrey M. Sametz' author = 'Geoffrey M. Sametz' # The short X.Y version -version = '0.3' +version = '0.4' # The full version, including alpha/beta/rc tags -release = '0.3.0-beta' +release = '0.4.0-beta' # -- General configuration --------------------------------------------------- diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst index f56bc82..ad5e40f 100644 --- a/docs/source/introduction.rst +++ b/docs/source/introduction.rst @@ -1,4 +1,4 @@ -Introduction to nmrsim v0.3.0 (beta) +Introduction to nmrsim v0.4.0 (beta) ===================================== **nmrsim** is a library of tools for simulating NMR spectra, starting from diff --git a/nmrsim/__init__.py b/nmrsim/__init__.py index 238fb4e..b156732 100644 --- a/nmrsim/__init__.py +++ b/nmrsim/__init__.py @@ -62,4 +62,4 @@ from ._classes import Multiplet, SpinSystem, Spectrum # noqa: F401 -__version__ = '0.3.0' +__version__ = '0.4.0' diff --git a/setup.py b/setup.py index db6ffc1..d9aec64 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="nmrsim", - version="0.3.0.post1", + version="0.4.0", author="Geoffrey M. Sametz", author_email="sametz@udel.edu", description="A library for simulating nuclear magnetic resonance (NMR) spectra.", From 482dc59f88eda7e1eb0f30931396cce2c9225571 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 22 Feb 2020 10:47:18 -0500 Subject: [PATCH 72/80] Added actions for TestPyPI automation. Added 'rc1' to version number in setup.py, anticipating possible multiple test uploads for debugging --- .github/workflows/build_linux.yml | 33 ++++++++++++++++++++++++ .github/workflows/build_mac.yml | 43 +++++++++++++++++++++++++++++++ .github/workflows/build_win.yml | 43 +++++++++++++++++++++++++++++++ .github/workflows/make_sdist.yml | 34 ++++++++++++++++++++++++ setup.py | 2 +- 5 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/build_linux.yml create mode 100644 .github/workflows/build_mac.yml create mode 100644 .github/workflows/build_win.yml create mode 100644 .github/workflows/make_sdist.yml diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml new file mode 100644 index 0000000..3fad35d --- /dev/null +++ b/.github/workflows/build_linux.yml @@ -0,0 +1,33 @@ +name: build wheels for linux + +on: + create: + tags: + - '*' + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - uses: ./.github/workflows/actions/manylinux1_x86_64/ + - uses: ./.github/workflows/actions/manylinux1_i686/ + - name: copy manylinux wheels + run: | + mkdir dist + cp wheelhouse/nmrsim*-manylinux1_x86_64.whl dist/ + cp wheelhouse/nmrsim*-manylinux1_i686.whl dist/ + - name: upload wheels + uses: actions/upload-artifact@v1 + with: + name: dist + path: dist + - name: Publish to PyPI + env: + PYPI_USERNAME: ${{ secrets.test_pypi_user }} + PYPI_PASSWORD: ${{ secrets.test_pypi_password }} + run: | + pip install twine + python -m twine upload -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* diff --git a/.github/workflows/build_mac.yml b/.github/workflows/build_mac.yml new file mode 100644 index 0000000..c973d24 --- /dev/null +++ b/.github/workflows/build_mac.yml @@ -0,0 +1,43 @@ +name: build wheels for macos + +on: + create: + tags: + - '*' + +jobs: + build: + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [macos-latest] + architecture: [x64] + python-version: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + architecture: ${{ matrix.architecture }} + python-version: ${{ matrix.python-version }} + - name: build wheel + run: | + pip install -U wheel + python setup.py bdist_wheel + shell: bash + - name: upload wheel + uses: actions/upload-artifact@v1 + with: + name: dist_${{ matrix.os }}_${{ matrix.architecture }}_${{ matrix.python-version }} + path: dist + - name: Publish to PyPI + env: + PYPI_USERNAME: ${{ secrets.test_pypi_user }} + PYPI_PASSWORD: ${{ secrets.test_pypi_password }} + run: | + pip install twine + python -m twine upload -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + shell: bash diff --git a/.github/workflows/build_win.yml b/.github/workflows/build_win.yml new file mode 100644 index 0000000..5efb9c2 --- /dev/null +++ b/.github/workflows/build_win.yml @@ -0,0 +1,43 @@ +name: build wheels for windows + +on: + create: + tags: + - '*' + +jobs: + build: + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [windows-latest] + architecture: [x64, x86] + python-version: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + architecture: ${{ matrix.architecture }} + python-version: ${{ matrix.python-version }} + - name: build wheel + run: | + pip install -U wheel + python setup.py bdist_wheel + shell: bash + - name: upload wheel + uses: actions/upload-artifact@v1 + with: + name: dist_${{ matrix.os }}_${{ matrix.architecture }}_${{ matrix.python-version }} + path: dist + - name: Publish to PyPI + env: + PYPI_USERNAME: ${{ secrets.test_pypi_user }} + PYPI_PASSWORD: ${{ secrets.test_pypi_password }} + run: | + pip install twine + python -m twine upload -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + shell: bash diff --git a/.github/workflows/make_sdist.yml b/.github/workflows/make_sdist.yml new file mode 100644 index 0000000..d49b4e0 --- /dev/null +++ b/.github/workflows/make_sdist.yml @@ -0,0 +1,34 @@ +name: make source distribution + +on: + create: + tags: + - '*' + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: make sdist + run: + python setup.py sdist + - name: upload sdist + uses: actions/upload-artifact@v1 + with: + name: dist + path: dist + - name: Publish to TestPyPI + env: + PYPI_USERNAME: ${{ secrets.test_pypi_user }} + PYPI_PASSWORD: ${{ secrets.test_pypi_password }} + run: | + pip install twine + python -m twine check dist/* + python -m twine upload -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* diff --git a/setup.py b/setup.py index d9aec64..f0a1449 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="nmrsim", - version="0.4.0", + version="0.4.0rc1", author="Geoffrey M. Sametz", author_email="sametz@udel.edu", description="A library for simulating nuclear magnetic resonance (NMR) spectra.", From 96a8bc1f41407e31b6adf4e85293bfd5fd6c7d5b Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 22 Feb 2020 10:53:04 -0500 Subject: [PATCH 73/80] git tag 0.4.0rc1 --- .flake8 | 1 + 1 file changed, 1 insertion(+) diff --git a/.flake8 b/.flake8 index f835096..175ac34 100644 --- a/.flake8 +++ b/.flake8 @@ -14,3 +14,4 @@ ignore = # allowing I for Intensity/Integration *for now* E741 max-line-length = 119 + From 08378483fb5d7f794adc47fe68044a75841690d3 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 22 Feb 2020 12:16:15 -0500 Subject: [PATCH 74/80] Linux build needs to be completely redone, following mac/win. Everything but authentication seems to be working. Leaving only make_sdist active, and adding --verbose, to try and determine auth issue. --- .github/{workflows => disabled_workflows}/build_linux.yml | 0 .github/{workflows => disabled_workflows}/build_mac.yml | 0 .github/{workflows => disabled_workflows}/build_win.yml | 0 .github/workflows/make_sdist.yml | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) rename .github/{workflows => disabled_workflows}/build_linux.yml (100%) rename .github/{workflows => disabled_workflows}/build_mac.yml (100%) rename .github/{workflows => disabled_workflows}/build_win.yml (100%) diff --git a/.github/workflows/build_linux.yml b/.github/disabled_workflows/build_linux.yml similarity index 100% rename from .github/workflows/build_linux.yml rename to .github/disabled_workflows/build_linux.yml diff --git a/.github/workflows/build_mac.yml b/.github/disabled_workflows/build_mac.yml similarity index 100% rename from .github/workflows/build_mac.yml rename to .github/disabled_workflows/build_mac.yml diff --git a/.github/workflows/build_win.yml b/.github/disabled_workflows/build_win.yml similarity index 100% rename from .github/workflows/build_win.yml rename to .github/disabled_workflows/build_win.yml diff --git a/.github/workflows/make_sdist.yml b/.github/workflows/make_sdist.yml index d49b4e0..88fcc81 100644 --- a/.github/workflows/make_sdist.yml +++ b/.github/workflows/make_sdist.yml @@ -31,4 +31,4 @@ jobs: run: | pip install twine python -m twine check dist/* - python -m twine upload -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + python -m twine upload --verbose -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* From f8b33c570c359e2852be1dcbc1a027ee4eee9546 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 22 Feb 2020 12:26:42 -0500 Subject: [PATCH 75/80] Linux build needs to be completely redone, following mac/win. Everything but authentication seems to be working. Leaving only make_sdist active, and adding --verbose, to try and determine auth issue. --- .github/workflows/make_sdist.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/make_sdist.yml b/.github/workflows/make_sdist.yml index 88fcc81..9985837 100644 --- a/.github/workflows/make_sdist.yml +++ b/.github/workflows/make_sdist.yml @@ -31,4 +31,4 @@ jobs: run: | pip install twine python -m twine check dist/* - python -m twine upload --verbose -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + python -m twine upload --verbose -u sametz -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* From 67fc27193b9ccc8904739395af875cc1b539b4a2 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 22 Feb 2020 12:44:02 -0500 Subject: [PATCH 76/80] bump setup.py version to rc2 after manual rc1 testpypi success. Refreshed api token so will retry. --- .github/workflows/make_sdist.yml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/make_sdist.yml b/.github/workflows/make_sdist.yml index 9985837..88fcc81 100644 --- a/.github/workflows/make_sdist.yml +++ b/.github/workflows/make_sdist.yml @@ -31,4 +31,4 @@ jobs: run: | pip install twine python -m twine check dist/* - python -m twine upload --verbose -u sametz -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + python -m twine upload --verbose -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* diff --git a/setup.py b/setup.py index f0a1449..a1f2907 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="nmrsim", - version="0.4.0rc1", + version="0.4.0rc2", author="Geoffrey M. Sametz", author_email="sametz@udel.edu", description="A library for simulating nuclear magnetic resonance (NMR) spectra.", From f6434ee71da6a5f77234afadb497396e4f57adfe Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 22 Feb 2020 12:54:32 -0500 Subject: [PATCH 77/80] bump to rc5 and changed username to __token__ in make_sdist.yml --- .github/workflows/make_sdist.yml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/make_sdist.yml b/.github/workflows/make_sdist.yml index 88fcc81..fa6b9c1 100644 --- a/.github/workflows/make_sdist.yml +++ b/.github/workflows/make_sdist.yml @@ -31,4 +31,4 @@ jobs: run: | pip install twine python -m twine check dist/* - python -m twine upload --verbose -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + python -m twine upload --verbose -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* diff --git a/setup.py b/setup.py index a1f2907..cd3a60c 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="nmrsim", - version="0.4.0rc2", + version="0.4.0rc5", author="Geoffrey M. Sametz", author_email="sametz@udel.edu", description="A library for simulating nuclear magnetic resonance (NMR) spectra.", From cd9fcd06ed894504fa3e4eb01d9c2aea48fb2f50 Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 22 Feb 2020 13:07:55 -0500 Subject: [PATCH 78/80] Changed user to __token__ fr build_mac.yml and build_win.yml. Bump setup.py to rc6 --- .github/{disabled_workflows => workflows}/build_mac.yml | 3 +-- .github/{disabled_workflows => workflows}/build_win.yml | 3 +-- .github/workflows/make_sdist.yml | 1 - setup.py | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) rename .github/{disabled_workflows => workflows}/build_mac.yml (84%) rename .github/{disabled_workflows => workflows}/build_win.yml (84%) diff --git a/.github/disabled_workflows/build_mac.yml b/.github/workflows/build_mac.yml similarity index 84% rename from .github/disabled_workflows/build_mac.yml rename to .github/workflows/build_mac.yml index c973d24..49b32e8 100644 --- a/.github/disabled_workflows/build_mac.yml +++ b/.github/workflows/build_mac.yml @@ -35,9 +35,8 @@ jobs: path: dist - name: Publish to PyPI env: - PYPI_USERNAME: ${{ secrets.test_pypi_user }} PYPI_PASSWORD: ${{ secrets.test_pypi_password }} run: | pip install twine - python -m twine upload -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + python -m twine upload -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* shell: bash diff --git a/.github/disabled_workflows/build_win.yml b/.github/workflows/build_win.yml similarity index 84% rename from .github/disabled_workflows/build_win.yml rename to .github/workflows/build_win.yml index 5efb9c2..4c91cad 100644 --- a/.github/disabled_workflows/build_win.yml +++ b/.github/workflows/build_win.yml @@ -35,9 +35,8 @@ jobs: path: dist - name: Publish to PyPI env: - PYPI_USERNAME: ${{ secrets.test_pypi_user }} PYPI_PASSWORD: ${{ secrets.test_pypi_password }} run: | pip install twine - python -m twine upload -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + python -m twine upload -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* shell: bash diff --git a/.github/workflows/make_sdist.yml b/.github/workflows/make_sdist.yml index fa6b9c1..0e6abb0 100644 --- a/.github/workflows/make_sdist.yml +++ b/.github/workflows/make_sdist.yml @@ -26,7 +26,6 @@ jobs: path: dist - name: Publish to TestPyPI env: - PYPI_USERNAME: ${{ secrets.test_pypi_user }} PYPI_PASSWORD: ${{ secrets.test_pypi_password }} run: | pip install twine diff --git a/setup.py b/setup.py index cd3a60c..9d9c5e1 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="nmrsim", - version="0.4.0rc5", + version="0.4.0rc6", author="Geoffrey M. Sametz", author_email="sametz@udel.edu", description="A library for simulating nuclear magnetic resonance (NMR) spectra.", From c1278c8f98c7115226eb2ac0f03c8d5ef346fcbd Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 22 Feb 2020 14:09:38 -0500 Subject: [PATCH 79/80] mac/pc wheels wouldn't upload b/c "already exists" so trying the --skip-existing twine option. Bump to rc7 --- .github/workflows/build_mac.yml | 2 +- .github/workflows/build_win.yml | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_mac.yml b/.github/workflows/build_mac.yml index 49b32e8..ddc2961 100644 --- a/.github/workflows/build_mac.yml +++ b/.github/workflows/build_mac.yml @@ -38,5 +38,5 @@ jobs: PYPI_PASSWORD: ${{ secrets.test_pypi_password }} run: | pip install twine - python -m twine upload -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + python -m twine upload --skip-existing -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* shell: bash diff --git a/.github/workflows/build_win.yml b/.github/workflows/build_win.yml index 4c91cad..58ab18c 100644 --- a/.github/workflows/build_win.yml +++ b/.github/workflows/build_win.yml @@ -38,5 +38,5 @@ jobs: PYPI_PASSWORD: ${{ secrets.test_pypi_password }} run: | pip install twine - python -m twine upload -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + python -m twine upload --skip-existing -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* shell: bash diff --git a/setup.py b/setup.py index 9d9c5e1..85ad168 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="nmrsim", - version="0.4.0rc6", + version="0.4.0rc7", author="Geoffrey M. Sametz", author_email="sametz@udel.edu", description="A library for simulating nuclear magnetic resonance (NMR) spectra.", From abafaa95b870f5ae95bd7b314de7bb258b8936ec Mon Sep 17 00:00:00 2001 From: sametz Date: Sat, 22 Feb 2020 14:41:40 -0500 Subject: [PATCH 80/80] Not sure if mac/pc wheels will upload to PyPI (didn't to testPyPI) but at least sdist and universal wheel currently post. Saved copies of actions with test_ prefix in disabled_workflows. Removed --repository-url from the PyPI publishing actions so that they will go to live PyPI server when next invoked. Decremented version to rc1 in anticipation of push to master and publishing. --- .github/disabled_workflows/test_build_mac.yml | 42 +++++++++++++++++++ .github/disabled_workflows/test_build_win.yml | 42 +++++++++++++++++++ .../disabled_workflows/test_make_sdist.yml | 33 +++++++++++++++ .github/workflows/build_mac.yml | 2 +- .github/workflows/build_win.yml | 2 +- .github/workflows/make_sdist.yml | 2 +- setup.py | 2 +- 7 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 .github/disabled_workflows/test_build_mac.yml create mode 100644 .github/disabled_workflows/test_build_win.yml create mode 100644 .github/disabled_workflows/test_make_sdist.yml diff --git a/.github/disabled_workflows/test_build_mac.yml b/.github/disabled_workflows/test_build_mac.yml new file mode 100644 index 0000000..ddc2961 --- /dev/null +++ b/.github/disabled_workflows/test_build_mac.yml @@ -0,0 +1,42 @@ +name: build wheels for macos + +on: + create: + tags: + - '*' + +jobs: + build: + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [macos-latest] + architecture: [x64] + python-version: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + architecture: ${{ matrix.architecture }} + python-version: ${{ matrix.python-version }} + - name: build wheel + run: | + pip install -U wheel + python setup.py bdist_wheel + shell: bash + - name: upload wheel + uses: actions/upload-artifact@v1 + with: + name: dist_${{ matrix.os }}_${{ matrix.architecture }}_${{ matrix.python-version }} + path: dist + - name: Publish to PyPI + env: + PYPI_PASSWORD: ${{ secrets.test_pypi_password }} + run: | + pip install twine + python -m twine upload --skip-existing -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + shell: bash diff --git a/.github/disabled_workflows/test_build_win.yml b/.github/disabled_workflows/test_build_win.yml new file mode 100644 index 0000000..58ab18c --- /dev/null +++ b/.github/disabled_workflows/test_build_win.yml @@ -0,0 +1,42 @@ +name: build wheels for windows + +on: + create: + tags: + - '*' + +jobs: + build: + + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [windows-latest] + architecture: [x64, x86] + python-version: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + architecture: ${{ matrix.architecture }} + python-version: ${{ matrix.python-version }} + - name: build wheel + run: | + pip install -U wheel + python setup.py bdist_wheel + shell: bash + - name: upload wheel + uses: actions/upload-artifact@v1 + with: + name: dist_${{ matrix.os }}_${{ matrix.architecture }}_${{ matrix.python-version }} + path: dist + - name: Publish to PyPI + env: + PYPI_PASSWORD: ${{ secrets.test_pypi_password }} + run: | + pip install twine + python -m twine upload --skip-existing -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + shell: bash diff --git a/.github/disabled_workflows/test_make_sdist.yml b/.github/disabled_workflows/test_make_sdist.yml new file mode 100644 index 0000000..0e6abb0 --- /dev/null +++ b/.github/disabled_workflows/test_make_sdist.yml @@ -0,0 +1,33 @@ +name: make source distribution + +on: + create: + tags: + - '*' + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: 3.7 + - name: make sdist + run: + python setup.py sdist + - name: upload sdist + uses: actions/upload-artifact@v1 + with: + name: dist + path: dist + - name: Publish to TestPyPI + env: + PYPI_PASSWORD: ${{ secrets.test_pypi_password }} + run: | + pip install twine + python -m twine check dist/* + python -m twine upload --verbose -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* diff --git a/.github/workflows/build_mac.yml b/.github/workflows/build_mac.yml index ddc2961..71a633c 100644 --- a/.github/workflows/build_mac.yml +++ b/.github/workflows/build_mac.yml @@ -38,5 +38,5 @@ jobs: PYPI_PASSWORD: ${{ secrets.test_pypi_password }} run: | pip install twine - python -m twine upload --skip-existing -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + python -m twine upload --skip-existing -u __token__ -p ${PYPI_PASSWORD} dist/* shell: bash diff --git a/.github/workflows/build_win.yml b/.github/workflows/build_win.yml index 58ab18c..92fe92b 100644 --- a/.github/workflows/build_win.yml +++ b/.github/workflows/build_win.yml @@ -38,5 +38,5 @@ jobs: PYPI_PASSWORD: ${{ secrets.test_pypi_password }} run: | pip install twine - python -m twine upload --skip-existing -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + python -m twine upload --skip-existing -u __token__ -p ${PYPI_PASSWORD} dist/* shell: bash diff --git a/.github/workflows/make_sdist.yml b/.github/workflows/make_sdist.yml index 0e6abb0..cf71803 100644 --- a/.github/workflows/make_sdist.yml +++ b/.github/workflows/make_sdist.yml @@ -30,4 +30,4 @@ jobs: run: | pip install twine python -m twine check dist/* - python -m twine upload --verbose -u __token__ -p ${PYPI_PASSWORD} --repository-url https://test.pypi.org/legacy/ dist/* + python -m twine upload --verbose -u __token__ -p ${PYPI_PASSWORD} dist/* diff --git a/setup.py b/setup.py index 85ad168..f0a1449 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="nmrsim", - version="0.4.0rc7", + version="0.4.0rc1", author="Geoffrey M. Sametz", author_email="sametz@udel.edu", description="A library for simulating nuclear magnetic resonance (NMR) spectra.",