Skip to content

Commit

Permalink
Set up beginning CI and add YAML check script (#1)
Browse files Browse the repository at this point in the history
Adds:
pycodestyle CI test
unit test for YAML checker
YAML checker
basic cmake structure
  • Loading branch information
CoryMartin-NOAA authored Mar 17, 2022
1 parent ff0a5f3 commit d561e30
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 0 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/pynorms.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Check Python Coding Norms
on: [push, pull_request]

jobs:
check_pynorms:
runs-on: ubuntu-latest
name: Check Python coding norms with pycodestyle

steps:

- name: Install dependencies
run: |
pip install --upgrade pip
pip install pycodestyle
- name: Checkout
uses: actions/checkout@v2
with:
path: GDASApp

- name: Run pycodestyle
run: |
cd $GITHUB_WORKSPACE/GDASApp
pycodestyle -v --config ./.pycodestyle .
31 changes: 31 additions & 0 deletions .github/workflows/unittests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Run Unit Tests
on: [push, pull_request]

jobs:
ctests:
runs-on: ubuntu-latest
name: Run Unit Tests with ctest

steps:

- name: Checkout
uses: actions/checkout@v2
with:
path: GDASApp

- name: Configure with cmake
run: |
cd $GITHUB_WORKSPACE/GDASApp
mkdir build
cd build
cmake ../
- name: Build GDASApp
run: |
cd $GITHUB_WORKSPACE/GDASApp/build
make
- name: Run ctest
run: |
cd $GITHUB_WORKSPACE/GDASApp/build
ctest
6 changes: 6 additions & 0 deletions .pycodestyle
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[pycodestyle]
count = False
ignore = E226,E401,E402,W504
max-line-length = 160
statistics = True
exclude = Experimental
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
cmake_minimum_required(VERSION 3.10)

# set the project name
project(GDASApp)

find_package(Python3 REQUIRED COMPONENTS Interpreter)

enable_testing()

add_subdirectory(test)
3 changes: 3 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
add_test(NAME test_check_yaml_keys
COMMAND ${Python3_EXECUTABLE} ${PROJECT_SOURCE_DIR}/ush/check_yaml_keys.py ${PROJECT_SOURCE_DIR}/test/testinput/check_yaml_keys_ref.yaml ${PROJECT_SOURCE_DIR}/test/testinput/check_yaml_keys_test.yaml
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/test/)
12 changes: 12 additions & 0 deletions test/testinput/check_yaml_keys_ref.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
app: GDAS
components:
- name: atmosphere
model: FV3
- name: ocean
model: MOM6
- name: ice
model: CICE6
- name: aerosols
model: GOCART
- name: land
model: noah
4 changes: 4 additions & 0 deletions test/testinput/check_yaml_keys_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
app: GDAS
components:
- name: atmosphere
model: FV3
69 changes: 69 additions & 0 deletions ush/check_yaml_keys.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#!/usr/bin/env python3
# compare keys between two YAML files
import argparse
import logging
import os
import yaml


def check_yaml(YAMLref, YAMLtest, checkValues=False):
assert os.path.exists(YAMLref), f"File {YAMLref} not found."
assert os.path.exists(YAMLtest), f"File {YAMLtest} not found."
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(message)s', level=logging.INFO, datefmt='%Y-%m-%d %H:%M:%S')
logging.info(f'Comparing {YAMLtest} against {YAMLref}...')
# load reference file
try:
with open(YAMLref, 'r') as YAMLref_opened:
ref_dict = yaml.safe_load(YAMLref_opened)
except Exception as e:
logging.error(f'Error occurred when attempting to load: {YAMLref}, error: {e}')
# load file to test
try:
with open(YAMLtest, 'r') as YAMLtest_opened:
test_dict = yaml.safe_load(YAMLtest_opened)
except Exception as e:
logging.error(f'Error occurred when attempting to load: {YAMLtest}, error: {e}')
# loop through top level of YAML
compare_dict('', ref_dict, test_dict, checkValues)


def compare_dict(rootkey, dict1, dict2, checkValues):
for key, value in dict1.items():
keypath = f"{rootkey}/{key}"
if key not in dict2:
logging.error(f"'{keypath}' not in test file.")
else:
if isinstance(value, dict):
compare_dict(keypath, value, dict2[key], checkValues)
elif isinstance(value, list):
compare_list(keypath, value, dict2[key], checkValues)
else:
if checkValues:
if value != dict2[key]:
logging.warning(f"{keypath}: {dict2[key]} != {value}")


def compare_list(rootkey, list1, list2, checkValues):
if len(list2) != len(list1):
logging.error(f"{rootkey} len={len(list2)} != {len(list1)}")
for i, item in enumerate(list1):
newkey = f"{rootkey}[{i}]"
if i+1 <= len(list2):
if isinstance(item, dict) and i+1 <= len(list2):
compare_dict(newkey, item, list2[i], checkValues)
elif isinstance(item, list):
compare_list(newkey, item, list2[i], checkValues)
else:
if checkValues:
if item != list2[i]:
logging.warning(f"{rootkey}/{i}: {list2[i]} != {item}")


if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('YAMLref', type=str, help='Reference YAML file')
parser.add_argument('YAMLtest', type=str, help='YAML file to compare to reference')
parser.add_argument("--checkvalues", help="Check values in addition to keys",
action="store_true")
args = parser.parse_args()
check_yaml(args.YAMLref, args.YAMLtest, args.checkvalues)

0 comments on commit d561e30

Please sign in to comment.