diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..ea3d7d8f --- /dev/null +++ b/.gitignore @@ -0,0 +1,109 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# dotenv +.env + +# virtualenv +.venv +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.idea/ +*.cfg + +# pytest +.pytest_cache/ + +# MS VS Code +.vscode/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..01c47a0c --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 KangWon LEE + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/environment.3.5.yml b/environment.3.5.yml index 99395399..48005951 100644 --- a/environment.3.5.yml +++ b/environment.3.5.yml @@ -1,14 +1,18 @@ # ref : Francesco Mosconi, Travis + Anaconda + Jupyter, https://github.com/ghego/travis_anaconda_jupyter name: travis_anaconda_jupyter + channels: - defaults dependencies: -- python<3.6 +# https://docs.anaconda.com/anaconda/packages/old-pkg-lists/4.2.0/py35/ +- python=3.5 +- jupyter=1.0.0 +- matplotlib=2.0.2 - numpy=1.11.1 +- pandas=0.18.1 +- pytest=2.9.2 - scipy=0.18.1 +- statsmodels=0.6.1 - sympy=1.0=py35_0 -- jupyter=1.0.0 -- pytest=2.9.2 -- matplotlib=2.0.2 diff --git a/environment.3.6.yml b/environment.3.6.yml index 371f8def..939d934f 100644 --- a/environment.3.6.yml +++ b/environment.3.6.yml @@ -5,11 +5,13 @@ channels: - defaults dependencies: -- python<3.7 +- python=3.6 # Anaconda 5.0.1 python 3.6 +- jupyter=1.0.0 +- matplotlib=2.1.0 - numpy=1.13.3 +- pytest=3.2.1 +- pandas=0.23.4 - scipy=0.19.1 +- statsmodels=0.9.0 - sympy=1.1.1 -- jupyter=1.0.0 -- pytest=3.2.1 -- matplotlib=2.1.0 diff --git a/environment.3.7.1.yml b/environment.3.7.1.yml new file mode 100644 index 00000000..7716d03c --- /dev/null +++ b/environment.3.7.1.yml @@ -0,0 +1,18 @@ +# ref : Francesco Mosconi, Travis + Anaconda + Jupyter, https://github.com/ghego/travis_anaconda_jupyter + +name: travis_anaconda_jupyter +channels: +- defaults + +dependencies: +- python=3.7.1 +# Anaconda 2018.12 python 3.7.1 +# $ conda env export --file FILE +- jupyter=1.0.0 +- matplotlib=3.0.2 +- numpy=1.15.4 +- pandas=0.23.4 +- pytest=4.0.2 +- scipy=1.1.0 +- statsmodels=0.9.0 +- sympy=1.3 diff --git a/environment.3.7.yml b/environment.3.7.yml new file mode 100644 index 00000000..0907db2a --- /dev/null +++ b/environment.3.7.yml @@ -0,0 +1,18 @@ +# ref : Francesco Mosconi, Travis + Anaconda + Jupyter, https://github.com/ghego/travis_anaconda_jupyter + +name: travis_anaconda_jupyter +channels: +- defaults + +dependencies: +- python=3.7 +# Anaconda 5.3 python 3.7 +# https://docs.anaconda.com/anaconda/packages/py3.7_win-64/ +- jupyter=1.0.0 +- matplotlib=2.2.3 +- numpy=1.15.1 +- pandas=0.23.4 +- pytest=3.8.0 +- scipy=1.1.0 +- statsmodels=0.9.0 +- sympy=1.1.1 diff --git a/environment.nightly.yml b/environment.nightly.yml index 39eaea10..9ee9678f 100644 --- a/environment.nightly.yml +++ b/environment.nightly.yml @@ -5,10 +5,12 @@ channels: - defaults dependencies: -- python>=3.6 -- numpy>=1.12.1 -- scipy>=0.19 -- sympy>=1.1.1 +- python>=3.7 - jupyter>=1.0.0 -- pytest>=3.2.1 -- matplotlib>=2.0.2 +- matplotlib>=2.2.3 +- numpy>=1.15.1 +- pandas>=0.23.4 +- pytest>=3.8.0 +- scipy>=1.1.0 +- statsmodels>=0.9.0 +- sympy>=1.1.1 diff --git a/test_nb.py b/test_nb.py index 9ded6f75..565ed6d7 100644 --- a/test_nb.py +++ b/test_nb.py @@ -1,157 +1,128 @@ # ref : Francesco Mosconi, Travis + Anaconda + Jupyter, https://github.com/ghego/travis_anaconda_jupyter +import itertools import os import subprocess import tempfile +import pytest + def check_kernel_spec(): # https://jupyter-client.readthedocs.io/en/latest/api/kernelspec.html import jupyter_client.kernelspec as jk + kernel_spec_manager = jk.KernelSpecManager() print(kernel_spec_manager.get_all_specs()) -def _exec_notebook(path): +def _exec_notebook_nix(path): + """ + Run the ipynb file of path + Raise exception if any error + """ # http://nbconvert.readthedocs.io/en/latest/execute_api.html # ijstokes et al, Command line execution of a jupyter notebook fails in default Anaconda 4.1, https://github.com/Anaconda-Platform/nb_conda_kernels/issues/34 + # obtain a temporary filename + # https://docs.python.org/3/library/tempfile.html with tempfile.NamedTemporaryFile(suffix=".ipynb") as fout: - args = ["jupyter", "nbconvert", "--to", "notebook", "--execute", - "--ExecutePreprocessor.timeout=1000", - "--ExecutePreprocessor.kernel_name=python", - "--output", fout.name, path] + # prepare a command running .ipynb file while converting + args = [ + "jupyter", # name of program + "nbconvert", # option + "--to", "notebook", # conver to another ipynb file + "--execute", # run while convering + "--ExecutePreprocessor.timeout=1000", + "--ExecutePreprocessor.kernel_name=python", + "--output", fout.name, # output file name + path # input file name + ] + # run the command above + # and raise an exception if error subprocess.check_call(args) -folder_list = ( - 'Ch02_Strain', 'Ch03_Torsion', 'Ch04_SFD.BMD', 'Ch05_Stress.in.Beams', 'Ch06_Deflection', 'Ch07_Stat.Indet', - 'Ch08_Stress_Due.To_Combined.Loads', 'Ch10_Column', 'Ch12_SpecialTopic', -) - - -def test02(): - path = os.path.join(os.pardir, folder_list[0]) - ext = 'ipynb' - - # recursive loop - for root, dirnames, filenames in os.walk(path): - if 'ipynb_checkpoints' not in root: - # files loop - for filename in filenames: - if os.path.splitext(filename)[-1].endswith(ext): - print('test() : %s %s' % (root, filename)) - _exec_notebook(os.path.join(root, filename)) - - -def test03(): - path = os.path.join(os.pardir, folder_list[1]) - ext = 'ipynb' - - # recursive loop - for root, dirnames, filenames in os.walk(path): - if 'ipynb_checkpoints' not in root: - # files loop - for filename in filenames: - if os.path.splitext(filename)[-1].endswith(ext): - print('test() : %s %s' % (root, filename)) - _exec_notebook(os.path.join(root, filename)) - - -def test04(): - path = os.path.join(os.pardir, folder_list[2]) - ext = 'ipynb' - - # recursive loop - for root, dirnames, filenames in os.walk(path): - if 'ipynb_checkpoints' not in root: - # files loop - for filename in filenames: - if os.path.splitext(filename)[-1].endswith(ext): - print('test() : %s %s' % (root, filename)) - _exec_notebook(os.path.join(root, filename)) - - -def test05(): - path = os.path.join(os.pardir, folder_list[3]) - ext = 'ipynb' - - # recursive loop - for root, dirnames, filenames in os.walk(path): - if 'ipynb_checkpoints' not in root: - # files loop - for filename in filenames: - if os.path.splitext(filename)[-1].endswith(ext): - print('test() : %s %s' % (root, filename)) - _exec_notebook(os.path.join(root, filename)) +def _exec_notebook_win(path): + """ + Run the ipynb file of path + Raise exception if any error + """ + # http://nbconvert.readthedocs.io/en/latest/execute_api.html + # ijstokes et al, Command line execution of a jupyter notebook fails in default Anaconda 4.1, https://github.com/Anaconda-Platform/nb_conda_kernels/issues/34 + # obtain a temporary filename + # https://docs.python.org/3/library/tempfile.html + ftemp = tempfile.NamedTemporaryFile(suffix=".ipynb") + filename = os.path.join(os.getcwd(), os.path.split(ftemp.name)[-1]) + ftemp.close() + + # prepare a command running .ipynb file while converting + args = [ + "jupyter", # name of program + "nbconvert", # option + "--to", "notebook", # conver to another ipynb file + "--execute", # run while convering + "--ExecutePreprocessor.timeout=3600", + "--ExecutePreprocessor.kernel_name=python", + "--output", filename, # output file name + path # input file name + ] + + try: + # run the command above + # and raise an exception if error + subprocess.check_call(args) + except BaseException as e: + print(e) + if os.path.exists(filename): + os.remove(filename) + raise e + print('success') + if os.path.exists(filename): + os.remove(filename) -def test06(): - path = os.path.join(os.pardir, folder_list[4]) - ext = 'ipynb' - # recursive loop - for root, dirnames, filenames in os.walk(path): - if 'ipynb_checkpoints' not in root: - # files loop - for filename in filenames: - if os.path.splitext(filename)[-1].endswith(ext): - print('test() : %s %s' % (root, filename)) - _exec_notebook(os.path.join(root, filename)) +# https://docs.pytest.org/en/latest/fixture.html#scope-sharing-a-fixture-instance-across-tests-in-a-class-module-or-session +def get_exec_notebook(): + # https://docs.python.org/3/library/platform.html#cross-platform + os_to_function_table = { + 'posix': _exec_notebook_nix, + 'nt': _exec_notebook_win, + } + return os_to_function_table.get(os.name, _exec_notebook_nix) -def test07(): - path = os.path.join(os.pardir, folder_list[5]) - ext = 'ipynb' - # recursive loop - for root, dirnames, filenames in os.walk(path): - if 'ipynb_checkpoints' not in root: - # files loop - for filename in filenames: - if os.path.splitext(filename)[-1].endswith(ext): - print('test() : %s %s' % (root, filename)) - _exec_notebook(os.path.join(root, filename)) +def make_file_list(path=os.path.abspath(os.path.join(os.path.split(__file__)[0], os.pardir)), ext='ipynb'): - -def test08(): - path = os.path.join(os.pardir, folder_list[6]) - ext = 'ipynb' + file_list = [] # recursive loop - for root, dirnames, filenames in os.walk(path): - if 'ipynb_checkpoints' not in root: + for root, _, filenames in os.walk(path): + if not ( + ('.ipynb_checkpoints' in root) + or ('.git' in root) + or ('__pycache__'in root) + or ('.pytest_cache' in root) + ): # files loop for filename in filenames: if os.path.splitext(filename)[-1].endswith(ext): - print('test() : %s %s' % (root, filename)) - _exec_notebook(os.path.join(root, filename)) - + file_list.append(os.path.join(root, filename)) -def test10(): - path = os.path.join(os.pardir, folder_list[7]) - ext = 'ipynb' - - # recursive loop - for root, dirnames, filenames in os.walk(path): - if 'ipynb_checkpoints' not in root: - # files loop - for filename in filenames: - if os.path.splitext(filename)[-1].endswith(ext): - print('test() : %s %s' % (root, filename)) - _exec_notebook(os.path.join(root, filename)) + return file_list -def test12(): - path = os.path.join(os.pardir, folder_list[8]) - ext = 'ipynb' - - # recursive loop - for root, dirnames, filenames in os.walk(path): - if 'ipynb_checkpoints' not in root: - # files loop - for filename in filenames: - if os.path.splitext(filename)[-1].endswith(ext): - print('test() : %s %s' % (root, filename)) - _exec_notebook(os.path.join(root, filename)) +# https://docs.pytest.org/en/latest/example/parametrize.html +@pytest.mark.parametrize( + "filename, _exec_notebook", + itertools.zip_longest( + make_file_list(), + [get_exec_notebook()], fillvalue=get_exec_notebook() + ) +) +def test_ipynb_file(filename, _exec_notebook): + print('test() : %s' % filename) + _exec_notebook(filename)