diff --git a/.gitignore b/.gitignore index 684adee5cac3c318d61d660a906faa48e7185d1a..f2d219a41c5b6082a40a7949b51cb8e4b48fa70e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ #for docs and setup.py outputs build/ +tresults/ # test cache gwinc/test/cache diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..546d97072babe34cfebfa48731308e43f13f5c65 --- /dev/null +++ b/conftest.py @@ -0,0 +1,272 @@ +""" +Requires pytest to import +""" +import os +from os import path +import os +from shutil import rmtree +import contextlib + +import pytest + +_options_added = False + +def pytest_addoption(parser): + global _options_added + #this check fixes issues if this gets run multiple times from sub conftest.py's + if _options_added: + return + else: + _options_added = True + + parser.addoption( + "--plot", + action="store_true", + dest = 'plot', + help = "Have tests update plots (it is slow)", + ) + + parser.addoption( + "--do-stresstest", + action = "store_true", + help = "Run slow repeated stress tests" + ) + + parser.addoption( + "--no-preclear", + action="store_true", + default=False, + dest='no_preclear', + help="Do not preclear tpaths", + ) + + +@pytest.fixture +def plot(request): + return request.config.getvalue('--plot') + return request.config.option.plot + + +@pytest.fixture +def tpath_preclear(request): + """ + Fixture that indicates that the test path should be cleared automatically + before running each test. This cleans up the test data. + """ + tpath_raw = tpath_raw_make(request) + no_preclear = request.config.getvalue('--no-preclear') + if not no_preclear: + rmtree(tpath_raw, ignore_errors = True) + + +@pytest.fixture +def tpath(request): + """ + Fixture that takes the value of the special test-specific folder for test + run data and plots. Usually the <folder of the test>/tresults/test_name/ + """ + tpath_raw = tpath_raw_make(request) + + os.makedirs(tpath_raw, exist_ok = True) + os.utime(tpath_raw, None) + return tpath_raw + + +@pytest.fixture +def tpath_join(request): + """ + Fixture that joins subpaths to the value of the special test-specific folder for test + run data and plots. Usually the <folder of the test>/tresults/test_name/. + + This function should be use like test_thing.save(tpath_join('output_file.png')) + """ + tpath_raw = tpath_raw_make(request) + first_call = True + + def tpath_joiner(*subpath): + nonlocal first_call + if first_call: + os.makedirs(tpath_raw, exist_ok = True) + os.utime(tpath_raw, None) + first_call = False + return path.join(tpath_raw, *subpath) + + return tpath_joiner + + +@pytest.fixture +def fpath(request): + """ + py.test fixture that returns the folder path of the test being run. Useful + for accessing data files. + """ + return fpath_raw_make(request) + + +@pytest.fixture +def fpath_join(request): + """ + py.test fixture that runs :code:`os.path.join(path, *arguments)` to merge subpaths + with the folder path of the current test being run. Useful for referring to + data files. + """ + def join_func(*path): + return os.path.join(fpath_raw_make(request), *path) + return join_func + +@pytest.fixture +def closefigs(): + import matplotlib.pyplot as plt + yield + plt.close('all') + + +@pytest.fixture +def test_trigger(): + """ + This fixture provides a contextmanager that causes a function to call + if an AssertionError is raised. It will also call if any of its argument, + or keyword arguments is true. This allows you to conveniently force + calling using other flags or fixtures. + + The primary usage of this is to plot outputs only on test failures, while also + allowing plotting to happen using the plot fixture and pytest cmdline argument + """ + run_store = [] + + @contextlib.contextmanager + def fail(call, **kwargs): + run_store.append(call) + + def call(did_fail): + do_call = did_fail + for k, v in kwargs.items(): + if v: + do_call = True + break + + if do_call: + for call in run_store: + call(fail = did_fail, **kwargs) + run_store.clear() + try: + yield + except AssertionError: + call(True) + raise + else: + call(False) + + return + return fail + + +@pytest.fixture() +def ic(): + """ + Fixture to provide icecream imports without requiring that the package exist + """ + try: + from icecream import ic + return ic + except ImportError: + pass + try: + from IPython.lib.pretty import pprint + return pprint + except ImportError: + from pprint import pprint + return pprint + + +#these are used with the pprint fixture +try: + import icecream +except ImportError: + icecream = None + pass +try: + from IPython.lib.pretty import pprint, pretty + pformat = pretty +except ImportError: + from pprint import pprint, pformat + + +@pytest.fixture +def pprint(request, tpath_join): + """ + This is a fixture providing a wrapper function for pretty printing. It uses + the icecream module for pretty printing, falling back to ipythons pretty + printer if needed, then to the python build in pretty printing module. + + Along with printing to stdout, this function prints into the tpath_folder to + save all output into output.txt. + """ + fname = tpath_join('output.txt') + + #pushes past the dot + print('---------------:{}:--------------'.format(request.node.name)) + with open(fname, 'w') as F: + def pprint(*args, F = F, pretty = True, **kwargs): + outs = [] + if pretty: + for arg in args: + outs.append( + pformat(arg) + ) + else: + outs = args + if F is not None: + print(*outs, file = F) + if icecream is not None: + icecream.DEFAULT_OUTPUT_FUNCTION(' '.join(outs), **kwargs) + else: + print(*outs, **kwargs) + + yield pprint + + +def tpath_raw_make(request): + if isinstance(request.node, pytest.Function): + return relfile_test(request.node.function.__code__.co_filename, request, 'tresults') + raise RuntimeError("TPath currently only works for functions") + + +def fpath_raw_make(request): + if isinstance(request.node, pytest.Function): + return os.path.split(request.node.function.__code__.co_filename)[0] + raise RuntimeError("TPath currently only works for functions") + + +def relfile(_file_, *args, fname = None): + fpath = path.split(_file_)[0] + post = path.join(*args) + fpath = path.join(fpath, post) + #os.makedirs(fpath, exist_ok = True) + #os.utime(fpath, None) + + if fname is None: + return fpath + else: + return path.join(fpath, fname) + + +def relfile_test(_file_, request, pre = None, post = None, fname = None): + """ + Generates a folder specific to py.test function + (provided by using the "request" fixture in the test's arguments) + """ + if isinstance(pre, (list, tuple)): + pre = path.join(pre) + + testname = request.node.name + if pre is not None: + testname = path.join(pre, testname) + + if isinstance(post, (list, tuple)): + post = path.join(post) + if post is not None: + return relfile(_file_, testname, post, fname = fname) + else: + return relfile(_file_, testname, fname = fname) + diff --git a/docs/Makefile b/docs/Makefile index 61a3b8a0ebbadd5b285a983bfe3ce3f2afe50823..0912b469bba38d01f51c44239b6e7d6c31887726 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -41,7 +41,7 @@ clean: -rm -rf $(BUILDDIR)/* livehtml: - sphinx-autobuild -i .#* -i *.pyc -i .*.swp -i .*.swo -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + sphinx-autobuild -z ../gwinc -i '*.#*' -i '*.pyc' -i '*.swp' -i '*.swo' -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html diff --git a/docs/_static/my_theme.css b/docs/_static/my_theme.css new file mode 100644 index 0000000000000000000000000000000000000000..c6043e121f1c8203bd5edf2dec24c4576b252e71 --- /dev/null +++ b/docs/_static/my_theme.css @@ -0,0 +1,15 @@ + +.wy-nav-content { + max-width: 1200px !important; +} + +div.document { + width: auto; + margin: 30px auto 0 auto; +} + +div.body{ + max-width: 95%; + margin: 50px auto 0 auto; +} + diff --git a/docs/api.rst b/docs/api.rst new file mode 100644 index 0000000000000000000000000000000000000000..5bc793ebc271ed45399ba2ec64b78aebff6cf280 --- /dev/null +++ b/docs/api.rst @@ -0,0 +1,6 @@ +API +################################################################################ +.. _API: + +.. py:module:: gwinc + diff --git a/docs/conf.py b/docs/conf.py index ccbaff5399e7b6d6a057eb4eafa8810ea65949dc..5a0fa8b9fe4cf94e5d6c4fc7fb81e5a866ae02b9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,9 +17,15 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) +import os +import sys +#add the previous folder to the top of path, so that conftest.py can be found +#dirty but does the trick +sys.path.insert(0, os.path.abspath('..')) + +#gwinc must be importable to build the docs properly anyway, using apidoc, so +#import it now for the __version__ parameter +import gwinc # -- General configuration ------------------------------------------------ @@ -33,11 +39,12 @@ # ones. extensions = [ 'sphinx.ext.autodoc', - 'sphinx.ext.todo', - 'sphinx.ext.coverage', + #'sphinx.ext.todo', + #'sphinx.ext.coverage', 'sphinx.ext.mathjax', - 'sphinx.ext.githubpages', + #'sphinx.ext.githubpages', 'sphinx.ext.napoleon', + 'sphinx.ext.viewcode', ] # Add any paths that contain templates here, relative to this directory. @@ -54,7 +61,7 @@ master_doc = 'index' # General information about the project. project = 'GWINC' -copyright = '2018, LIGO Laboratory' +copyright = '2021, LIGO Laboratory' author = 'LIGO Laboratory' # The version info for the project you're documenting, acts as replacement for @@ -62,7 +69,7 @@ author = 'LIGO Laboratory' # built documents. # # The short X.Y version. -version = '0.0.0' +version = gwinc.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -74,7 +81,7 @@ language = None # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', '**.ipynb_checkpoints'] +exclude_patterns = ['_build', ] # The name of the Pygments (syntax highlighting) style to use. #pygments_style = 'sphinx' @@ -84,9 +91,10 @@ pygments_style = 'default' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True +# Autodoc settings +autodoc_default_flags = ["members", "undoc-members"] # -- Options for sourcelinks - srclink_project = 'https://git.ligo.org/gwinc/pygwinc' srclink_src_path = 'gwinc' srclink_branch = 'master' @@ -96,24 +104,41 @@ srclink_branch = 'master' #useful for downloading the ipynb files html_sourcelink_suffix = '' + +html_title = 'GWINC docs' +html_short_title = 'GWINC docs' + # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -import sphinx_rtd_theme -html_theme = "sphinx_rtd_theme" -html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +#import sphinx_rtd_theme +#html_theme = "sphinx_rtd_theme" +#html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] #import jupyter_sphinx_theme #html_theme = "jupyter_sphinx_theme" #html_theme_path = [jupyter_sphinx_theme.get_html_theme_path()] -#html_theme = "alabaster" +html_theme = "alabaster" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # -# html_theme_options = {} +html_theme_options = dict( + description = "Gravitational Wave Interferometer Noise Calculator, to determine the fundamental limiting noises in interferometer designs", + extra_nav_links = { + 'repository' : 'https://git.ligo.org/gwinc/pygwinc' + }, + show_powered_by = False, + show_related = True, + #page_width = 'auto', +) + +napoleon_type_aliases = { + #"CustomType": "mypackage.CustomType", + #"dict-like": ":term:`dict-like <mapping>`", +} # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -125,118 +150,24 @@ html_static_path = ['_static'] # # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars -#html_sidebars = { -# '**': [ -# 'about.html', -# 'navigation.html', -# 'relations.html', # needs 'show_related': True theme option to display -# 'searchbox.html', -# '' -# #'donate.html', -# ] -#} -# Custom sidebar templates, maps document names to template names. - html_sidebars = { '**': [ - 'localtoc.html', + 'about.html', + #'globaltoc.html', 'navigation.html', 'relations.html', 'searchbox.html', - 'srclinks.html', - ], - 'index': [ - 'globaltoc.html', - 'navigation.html', - 'relations.html', - 'searchbox.html', - 'srclinks.html', - ], + ] } -# -- Options for HTMLHelp output ------------------------------------------ - # Output file base name for HTML help builder. htmlhelp_basename = 'GWINC' +html_logo = 'logo/LIGO_F0900035-v1.jpg' -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ( - master_doc, - 'GWINC.tex', - 'GWINC Documentation', - author, - 'manual' - ), -] - -#http://www.sphinx-doc.org/en/master/usage/configuration.html#latex-options -#it appears that the LIGO docclass doesn't play well with all of the sphinx commands. Giving up for now -#latex_docclass = { -# 'manual' : 'ligodoc_v3', -#} -#latex_additional_files = [ -# 'ligodoc_v3.tex' -#] - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ( - master_doc, - 'GWINC', - 'GWINC Documentation', - [author], - 1 - ) -] - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ( - master_doc, - 'GWINC', - 'GWINC Documentation', - author, - 'GWINC', - 'One line description of project.', - 'Miscellaneous' - ), -] - -#def setup(app): -# app.add_stylesheet('my_theme.css') -# app.add_stylesheet('pygments_adjust.css') +def setup(app): + app.add_stylesheet('my_theme.css') + #updates the coloring + #app.add_stylesheet('pygments_adjust.css') diff --git a/docs/dev.rst b/docs/dev.rst new file mode 100644 index 0000000000000000000000000000000000000000..f09ae4b87032a7e8e24041eaf96b7cbfed5c7147 --- /dev/null +++ b/docs/dev.rst @@ -0,0 +1,76 @@ +Development +################################################################################ +.. _development: + +Testing with py.test +================================================================================ + +.. py:module:: conftest + + +py.test fixtures and setup +-------------------------------------------------------------------------------- + + +.. autofunction:: plot + + +.. autofunction:: tpath_preclear + + +.. autofunction:: tpath + + +.. autofunction:: tpath_join + + +.. autofunction:: fpath + + +.. autofunction:: fpath_join + + +.. autofunction:: closefigs + + +.. autofunction:: test_trigger + + +.. autofunction:: ic + + +.. autofunction:: pprint + +py.test setup hooks +-------------------------------------------------------------------------------- + +.. autofunction:: pytest_addoption + + +py.test helper functions +-------------------------------------------------------------------------------- + + +.. autofunction:: tpath_raw_make + + +.. autofunction:: fpath_raw_make + + +.. autofunction:: relfile + + +.. autofunction:: relfile_test + + + +Git setup for development +================================================================================ + + +PyPI Releases +================================================================================ + +versioning +-------------------------------------------------------------------------------- + diff --git a/docs/index.rst b/docs/index.rst index 9e9ea7f449c2b2d8b31083d3879dfac7c4fdeabd..67c4f34f691e6a12f414d7e4bd2cd4512c1ff39c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -30,6 +30,7 @@ Contents :maxdepth: 2 install - quickstart + api + dev diff --git a/docs/install.rst b/docs/install.rst new file mode 100644 index 0000000000000000000000000000000000000000..c5b5c0400f8639be5a69da386e3aadd6cfbf59a5 --- /dev/null +++ b/docs/install.rst @@ -0,0 +1,4 @@ +Install +################################################################################ +.. _install: + diff --git a/docs/logo/LIGO_F0900035-v1.jpg b/docs/logo/LIGO_F0900035-v1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f4d3245ede21fd1af4af4d31e53b5bdba8e047e2 Binary files /dev/null and b/docs/logo/LIGO_F0900035-v1.jpg differ diff --git a/docs/notebooks/.gitignore b/docs/notebooks/.gitignore deleted file mode 100644 index c05a8f846eae941788db09633ca05938e1bcc759..0000000000000000000000000000000000000000 --- a/docs/notebooks/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.mat -.ipynb_checkpoints/ diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000000000000000000000000000000000000..fec9652012f23441a2da101231609b67d26c1217 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,8 @@ +[pytest] +norecursedirs=_old fixme fix* out-* +python_files = test_*.py T*_*.py +python_functions = test_* T* +usefixtures = closefigs + +[pytest-watch] +ignore = .* diff --git a/test/budgets/test_budgets.py b/test/budgets/test_budgets.py new file mode 100644 index 0000000000000000000000000000000000000000..3c01773724db92b8db92baaed9af3b69d2681baa --- /dev/null +++ b/test/budgets/test_budgets.py @@ -0,0 +1,14 @@ +""" +""" +import gwinc +from gwinc import load_budget + + +def test_load(pprint, tpath_join, fpath_join): + pprint(gwinc.IFOS) + for ifo in gwinc.IFOS: + B = load_budget(ifo) + trace = B.run() + fig = trace.plot() + fig.savefig(tpath_join('budget_{}.pdf'.format(ifo))) +