Skip to content

Commit

Permalink
Initial doctest action and associated fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
mwaxmonsky committed Jul 26, 2023
1 parent 861af3e commit c4df7a2
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 109 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/python.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,26 @@ jobs:
run: |
export PYTHONPATH=$(pwd)/scripts:$(pwd)/scripts/parse_tools
pytest
doctest:
if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' || github.repository == 'NCAR/ccpp-framework' || github.event_name == 'push'
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.7, 3.9, 3.11]

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Doctest
run :|
export PYTHONPATH=$(pwd)/scripts:$(pwd)/scripts/parse_tools
pytest scripts -m --doctest-modules
2 changes: 1 addition & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[pytest]
addopts = -ra -q --ignore=tests/test_capgen.py
addopts = -ra --ignore=scripts/metadata2html.py
testpaths =
tests
11 changes: 9 additions & 2 deletions scripts/code_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
class CodeBlock(object):
"""Class to store a block of code and a method to write it to a file
>>> CodeBlock([]) #doctest: +ELLIPSIS
<__main__.CodeBlock object at 0x...>
<code_block.CodeBlock object at 0x...>
>>> CodeBlock(['hi mom']) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ParseInternalError: Each element of <code_list> must contain exactly two items, a code string and a relative indent
Expand All @@ -24,14 +24,21 @@ class CodeBlock(object):
Traceback (most recent call last):
ParseInternalError: Each element of <code_list> must contain exactly two items, a code string and a relative indent
>>> CodeBlock([('hi mom', 1)]) #doctest: +ELLIPSIS
<__main__.CodeBlock object at 0x...>
<code_block.CodeBlock object at 0x...>
>>> from fortran_tools import FortranWriter
>>> outfile_name = "__code_block_temp.F90"
>>> outfile = FortranWriter(outfile_name, 'w', 'test file', 'test_mod')
>>> CodeBlock([('hi mom', 1)]).write(outfile, 1, {})
>>> CodeBlock([('hi {greet} mom', 1)]).write(outfile, 1, {}) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ParseInternalError: 'greet' missing from <var_dict>
>>> CodeBlock([('hi {{greet}} mom', 1)]).write(outfile, 1, {})
>>> CodeBlock([('{greet} there mom', 1)]).write(outfile, 1, {'greet':'hi'})
>>> outfile.__exit__()
False
>>> import os
>>> os.remove(outfile_name)
"""

__var_re = re.compile(r"[{][ ]*([A-Za-z][A-Za-z0-9_]*)[ ]*[}]")
Expand Down
4 changes: 4 additions & 0 deletions scripts/fortran_tools/parse_fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,10 @@ def parse_fortran_var_decl(line, source, run_env):
'(8)'
>>> _VAR_ID_RE.match("foo(::,a:b,a:,:b)").group(2)
'(::,a:b,a:,:b)'
>>> from framework_env import CCPPFrameworkEnv
>>> _DUMMY_RUN_ENV = CCPPFrameworkEnv(None, ndict={'host_files':'', \
'scheme_files':'', \
'suites':''})
>>> parse_fortran_var_decl("integer :: foo", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('local_name')
'foo'
>>> parse_fortran_var_decl("integer :: foo = 0", ParseSource('foo.F90', 'module', ParseContext()), _DUMMY_RUN_ENV)[0].get_prop_value('local_name')
Expand Down
18 changes: 5 additions & 13 deletions scripts/metadata_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,14 +504,18 @@ def table_start(cls, line):

class MetadataSection(ParseSource):
"""Class to hold all information from a metadata header
>>> from framework_env import CCPPFrameworkEnv
>>> _DUMMY_RUN_ENV = CCPPFrameworkEnv(None, {'host_files':'', \
'scheme_files':'', \
'suites':''})
>>> MetadataSection("footable", "scheme", _DUMMY_RUN_ENV, \
parse_object=ParseObject("foobar.txt", \
["name = footable", "type = scheme", "module = foo", \
"[ im ]", "standard_name = horizontal_loop_extent", \
"long_name = horizontal loop extent, start at 1", \
"units = index | type = integer", \
"dimensions = () | intent = in"])) #doctest: +ELLIPSIS
<__main__.MetadataSection foo / footable at 0x...>
<metadata_table.MetadataSection foo / footable at 0x...>
>>> MetadataSection("footable", "scheme", _DUMMY_RUN_ENV, \
parse_object=ParseObject("foobar.txt", \
["name = footable", "type = scheme", "module = foobar", \
Expand Down Expand Up @@ -1267,15 +1271,3 @@ def is_scalar_reference(test_val):
return check_fortran_ref(test_val, None, False) is not None

########################################################################

if __name__ == "__main__":
# pylint: enable=ungrouped-imports
import doctest
import sys
# pylint: disable=ungrouped-imports
from framework_env import CCPPFrameworkEnv
_DUMMY_RUN_ENV = CCPPFrameworkEnv(None, {'host_files':'',
'scheme_files':'',
'suites':''})
fail, _ = doctest.testmod()
sys.exit(fail)
12 changes: 2 additions & 10 deletions scripts/metavar.py
Original file line number Diff line number Diff line change
Expand Up @@ -1418,11 +1418,11 @@ class VarDictionary(OrderedDict):
>>> VarDictionary('bar', _MVAR_DUMMY_RUN_ENV, variables={})
VarDictionary(bar)
>>> VarDictionary('baz', _MVAR_DUMMY_RUN_ENV, variables=Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm s-1', 'dimensions' : '()', 'type' : 'real', 'intent' : 'in'}, ParseSource('vname', 'scheme', ParseContext()), _MVAR_DUMMY_RUN_ENV)) #doctest: +ELLIPSIS
VarDictionary(baz, [('hi_mom', <__main__.Var hi_mom: foo at 0x...>)])
VarDictionary(baz, [('hi_mom', <metavar.Var hi_mom: foo at 0x...>)])
>>> print("{}".format(VarDictionary('baz', _MVAR_DUMMY_RUN_ENV, variables=Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm s-1', 'dimensions' : '()', 'type' : 'real', 'intent' : 'in'}, ParseSource('vname', 'scheme', ParseContext()), _MVAR_DUMMY_RUN_ENV))))
VarDictionary(baz, ['hi_mom'])
>>> VarDictionary('qux', _MVAR_DUMMY_RUN_ENV, variables=[Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm s-1', 'dimensions' : '()', 'type' : 'real', 'intent' : 'in'}, ParseSource('vname', 'scheme', ParseContext()), _MVAR_DUMMY_RUN_ENV)]) #doctest: +ELLIPSIS
VarDictionary(qux, [('hi_mom', <__main__.Var hi_mom: foo at 0x...>)])
VarDictionary(qux, [('hi_mom', <metavar.Var hi_mom: foo at 0x...>)])
>>> VarDictionary('boo', _MVAR_DUMMY_RUN_ENV).add_variable(Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm s-1', 'dimensions' : '()', 'type' : 'real', 'intent' : 'in'}, ParseSource('vname', 'scheme', ParseContext()), _MVAR_DUMMY_RUN_ENV), _MVAR_DUMMY_RUN_ENV)
>>> VarDictionary('who', _MVAR_DUMMY_RUN_ENV, variables=[Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm s-1', 'dimensions' : '()', 'type' : 'real', 'intent' : 'in'}, ParseSource('vname', 'scheme', ParseContext()), _MVAR_DUMMY_RUN_ENV)]).prop_list('local_name')
Expand Down Expand Up @@ -2023,11 +2023,3 @@ def new_internal_variable_name(self, prefix=None, max_len=63):
_MVAR_DUMMY_RUN_ENV)])

###############################################################################
if __name__ == "__main__":
# pylint: disable=ungrouped-imports
import doctest
import sys
# pylint: enable=ungrouped-imports
fail, _ = doctest.testmod()
sys.exit(fail)
# end if
11 changes: 1 addition & 10 deletions scripts/parse_tools/parse_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class ParseObject(ParseContext):
"""ParseObject is a simple class that keeps track of an object's
place in a file and safely produces lines from an array of lines
>>> ParseObject('foobar.F90', []) #doctest: +ELLIPSIS
<__main__.ParseObject object at 0x...>
<parse_tools.parse_object.ParseObject object at 0x...>
>>> ParseObject('foobar.F90', []).filename
'foobar.F90'
>>> ParseObject('foobar.F90', ["##hi mom",], line_start=1).curr_line()
Expand Down Expand Up @@ -164,12 +164,3 @@ def __del__(self):
# end try

########################################################################

if __name__ == "__main__":
# pylint: disable=ungrouped-imports
import doctest
import sys
# pylint: enable=ungrouped-imports
fail, _ = doctest.testmod()
sys.exit(fail)
# end if
14 changes: 3 additions & 11 deletions scripts/parse_tools/parse_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,10 @@ def __getitem__(self, index):
class ParseContext:
"""A class for keeping track of a parsing position
>>> ParseContext(32, "source.F90") #doctest: +ELLIPSIS
<__main__.ParseContext object at 0x...>
<parse_tools.parse_source.ParseContext object at 0x...>
>>> ParseContext("source.F90", 32)
Traceback (most recent call last):
CCPPError: ParseContext linenum must be an int
parse_tools.parse_source.CCPPError: ParseContext linenum must be an int
>>> ParseContext(32, 90) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
CCPPError: ParseContext filenum must be a string
Expand Down Expand Up @@ -381,7 +381,7 @@ class ParseSource:
"""
A simple object for providing source information
>>> ParseSource("myname", "mytype", ParseContext(13, "foo.F90")) #doctest: +ELLIPSIS
<__main__.ParseSource object at 0x...>
<parse_tools.parse_source.ParseSource object at 0x...>
>>> ParseSource("myname", "mytype", ParseContext(13, "foo.F90")).ptype
'mytype'
>>> ParseSource("myname", "mytype", ParseContext(13, "foo.F90")).name
Expand Down Expand Up @@ -412,11 +412,3 @@ def context(self):
return self.__context

########################################################################

if __name__ == "__main__":
# pylint: disable=ungrouped-imports
import doctest
# pylint: enable=ungrouped-imports
fail, _ = doctest.testmod()
sys.exit(fail)
# end if
18 changes: 2 additions & 16 deletions scripts/parse_tools/xml_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ def call_command(commands, logger, silent=False):
###############################################################################
"""
Try a command line and return the output on success (None on failure)
>>> _LOGGER = init_log('xml_tools')
>>> set_log_to_null(_LOGGER)
>>> call_command(['ls', 'really__improbable_fffilename.foo'], _LOGGER) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
CCPPError: Execution of 'ls really__improbable_fffilename.foo' failed:
Expand Down Expand Up @@ -358,19 +360,3 @@ def write(self, file, encoding="us-ascii", xml_declaration=None,
# end with

##############################################################################

if __name__ == "__main__":
_LOGGER = init_log('xml_tools')
set_log_to_null(_LOGGER)
try:
# First, run doctest
# pylint: disable=ungrouped-imports
import doctest
# pylint: enable=ungrouped-imports
fail, _ = doctest.testmod()
sys.exit(fail)
except CCPPError as cerr:
print("{}".format(cerr))
sys.exit(fail)
# end try
# end if
Loading

0 comments on commit c4df7a2

Please sign in to comment.