Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QCSchema coordination, testing, and export #237

Merged
merged 10 commits into from
Jan 30, 2021
22 changes: 19 additions & 3 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
name: CI

on: [push, pull_request]
on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
build:

runs-on: ubuntu-18.04
runs-on: ubuntu-latest
strategy:
matrix:
cfg:
- conda-env: minimal
python-version: 3.6
label: mindep
- conda-env: base
python-version: 3.6
label: minpy
- conda-env: base
python-version: 3.8
python-version: 3.9
label: full
env:
PYVER: ${{ matrix.cfg.python-version }}
CONDA_ENV: ${{ matrix.cfg.conda-env }}
Expand Down Expand Up @@ -54,5 +63,12 @@ jobs:
eval "$(conda shell.bash hook)" && conda activate test
pytest -rws -v --cov=qcelemental --color=yes --cov-report=xml qcelemental/

- name: PyTest Validate
shell: bash
if: matrix.cfg.label == 'full'
run: |
eval "$(conda shell.bash hook)" && conda activate test
pytest -rws -v --color=yes --validate qcelemental/

- name: CodeCov
uses: codecov/codecov-action@v1
90 changes: 90 additions & 0 deletions .github/workflows/QCSchema.yml_wip
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: QCSchema

on: [pull_request]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
conda-env: [base]
python-version: [3.7]
env:
PYVER: ${{ matrix.python-version }}
CONDA_ENV: ${{ matrix.conda-env }}

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

- name: Checkout schema repo
uses: actions/checkout@v2
with:
repository: MolSSI/QCSchema
path: qcsk
ref: qcsk_export_2
#ref: master
persist-credentials: true
fetch-depth: 0
token: ${{ secrets.qcschema_from_qcelemental }}

- name: Python Setup
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}

- name: Create Environment
shell: bash
working-directory: ./qcel
run: |
eval "$(conda shell.bash hook)" && conda activate
python devtools/scripts/create_conda_env.py -n=test -p=$PYVER devtools/conda-envs/$CONDA_ENV.yaml

- name: Install
shell: bash
working-directory: ./qcel
run: |
eval "$(conda shell.bash hook)" && conda activate test
python -m pip install . --no-deps

- name: Environment Information
shell: bash
run: |
eval "$(conda shell.bash hook)" && conda activate test
conda list --show-channel-urls

- name: QCSchema from QCElemental
shell: bash
working-directory: ./qcel
run: |
eval "$(conda shell.bash hook)" && conda activate test
make qcschema
ls -l qcschema
cp -p qcschema/* ../qcsk/qcschema/data/vdev/
mv ../qcsk/qcschema/data/vdev/QCSchema.schema ../qcsk/qcschema/dev/

- name: Compare Schemas (generated vs. community)
shell: bash
working-directory: ./qcsk
run: |
git diff --color-words
pull_number=$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH")
branch=qcel-${pull_number}
git checkout -b ${branch}
git remote -v
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add -A
git commit -m "auto-generated from QCElemental"
echo "::set-env name=prbranch::${branch}"

- name: Propose changes
uses: ad-m/github-push-action@master
with:
directory: ./qcsk
repository: MolSSI/QCSchema
branch: ${{ env.prbranch }}
github_token: ${{ secrets.qcschema_from_qcelemental }}
force: true
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,7 @@ runinfo/*
# VSCode
.vscode/
raw_data/**/*_blob.py

# autogen
qcschema/*.schema
qcelemental/tests/qcschema_instances/*/*.json
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ data: cpu_data
cpu_data:
(cd raw_data/cpu_data; python build_cpu_data.py; mv cpu_data_blob.py ../../qcelemental/info/data/)

.PHONY: qcschema
qcschema:
mkdir -p qcschema
python -c "exec(\"import pathlib, qcelemental\nfor md in qcelemental.models.qcschema_models():\n\tmfile = (pathlib.Path('qcschema') / md.__name__).with_suffix('.schema')\n\twith open(mfile, 'w') as fp:\n\t\tfp.write(md.schema_json(indent=None))\")"
python -c "exec(\"import json, pathlib, pydantic, qcelemental\nwith open((pathlib.Path('qcschema') / 'QCSchema').with_suffix('.schema'), 'w') as fp:\n\tjson.dump(pydantic.schema.schema(qcelemental.models.qcschema_models(), title='QCSchema'), fp, indent=4)\")"

.PHONY: clean
clean:
rm -rf `find . -name __pycache__`
Expand Down
3 changes: 2 additions & 1 deletion devtools/conda-envs/base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dependencies:
- nomkl
- python
- pint>=0.10.0
- pydantic>=1.0.0
- pydantic>=1.5.0,!=1.6.0

# Optional depends
- msgpack-python
Expand All @@ -20,3 +20,4 @@ dependencies:
- pytest-cov
- codecov
- scipy # tests an aspect of a helper fn not used by qcel functionality
- jsonschema
3 changes: 2 additions & 1 deletion devtools/conda-envs/minimal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ dependencies:
- nomkl
- python
- pint=0.10.0 # technically, qcel has no lower bound for pint version for py36,37 but needs 0.10 for 38
- pydantic=1.2.0 # technically, qcel works with 1.0.0 but c-f doesn't have py38 builds for it
- pydantic=1.5.0

# Testing
- pytest=4.6.4 # technically, qcel works with 4.0.0 but c-f doesn't have py38 builds for it
- pytest-cov
- codecov
- jsonschema
9 changes: 8 additions & 1 deletion docs/source/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,22 @@ Changelog

New Features
++++++++++++
- (:pr:`247`) Exports models to JSON Schema with ``make schema``.
- (:pr:`247`) Build bank of JSON examples from Pydantic models defined in tests. Test that bank against exported schema with ``pytest --validate qcelemental/``.
- (:pr:`247`) Many model descriptions edited, dimensions added to array properties, ``AtomicInput.model.basis`` now takes
``BasisSet`` object not just string, several properties added to match QCSchema, several limitations on number and
uniqueness imposed.

Enhancements
++++++++++++
- (:pr:`247`) Improve mypy conformance including dynamic provenance. Necessitates Pydantic >=1.5.
- (:pr:`247`) ``a0`` without underscore added as computable pint unit.
- (:pr:`246`) Removes types deprecated in NumPy v1.20.0.

Bug Fixes
+++++++++
- (:pr:`244`) Fixes where in code validation throws if ``center_data`` missing from ``BasisSet`` model.
- (:pr:`1000`) Fixes web tests that weren't marked.
- (:pr:`249`) Fixes web tests that weren't marked.


0.17.0 / 2020-10-01
Expand Down
40 changes: 40 additions & 0 deletions qcelemental/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from pathlib import Path

import pytest


def pytest_addoption(parser):
parser.addoption(
"--validate", action="store_true", help="validate JSON from previous test run against exported schema"
)


@pytest.fixture(scope="session", autouse=True)
def set_up_overall(request):
# in all pytest runs except --validate (which uses the files), clear away the JSON examples and generate fresh
if not request.config.getoption("--validate", default=False):
_data_path = Path(__file__).parent.resolve() / "tests" / "qcschema_instances"
for fl in _data_path.rglob("*.json"):
fl.unlink()


def pytest_runtest_setup(item):
# there's a bug where can only set options if specify path in call, so needs to be ``pytest qcelemental/ --validate``

# skip the validate-generated-instances-against-exported-schema tests on most ``pytest`` runs.
# run only the validate-generated-instances-against-exported-schema tests on ``pytest --validate`` runs.
if not item.config.getoption("--validate", default=False) and item.name.startswith("test_qcschema"):
pytest.skip("can't run with --validate option")
elif item.config.getoption("--validate", default=False) and not item.name.startswith("test_qcschema"):
pytest.skip("need --validate option to run")


# Uncomment below to probe for tests needing `@using_web`

# import socket
#
# class block_network(socket.socket):
# def __init__(self, *args, **kwargs):
# raise Exception("Network call blocked")
#
# socket.socket = block_network
17 changes: 15 additions & 2 deletions qcelemental/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,18 @@
from .basis import BasisSet
from .common_models import ComputeError, DriverEnum, FailedOperation, Provenance
from .molecule import Molecule
from .procedures import Optimization, OptimizationInput, OptimizationResult
from .results import AtomicInput, AtomicResult, AtomicResultProperties, Result, ResultInput, ResultProperties
from .procedures import OptimizationInput, OptimizationResult
from .procedures import Optimization # scheduled for removal
from .results import AtomicInput, AtomicResult, AtomicResultProperties
from .results import Result, ResultInput, ResultProperties # scheduled for removal


def qcschema_models():
return [
AtomicInput,
AtomicResult,
AtomicResultProperties,
BasisSet,
Molecule,
Provenance,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimization?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimization was never added over at qcsk, so I was aiming for a 1:1 changeset. Agree that it'd be good to get OptimizationInput/Result formalized over there soon.

]
13 changes: 13 additions & 0 deletions qcelemental/models/basemodels.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ def serialize(
include: Optional[Set[str]] = None,
exclude: Optional[Set[str]] = None,
exclude_unset: Optional[bool] = None,
exclude_defaults: Optional[bool] = None,
exclude_none: Optional[bool] = None,
) -> Union[bytes, str]:
"""Generates a serialized representation of the model

Expand All @@ -138,6 +140,10 @@ def serialize(
Fields to be excluded in the serialization.
exclude_unset : Optional[bool], optional
If True, skips fields that have default values provided.
exclude_defaults: Optional[bool], optional
If True, skips fields that have set or defaulted values equal to the default.
exclude_none: Optional[bool], optional
If True, skips fields that have value ``None``.

Returns
-------
Expand All @@ -152,6 +158,10 @@ def serialize(
kwargs["exclude"] = exclude
if exclude_unset:
kwargs["exclude_unset"] = exclude_unset
if exclude_defaults:
kwargs["exclude_defaults"] = exclude_defaults
if exclude_none:
kwargs["exclude_none"] = exclude_none

data = self.dict(**kwargs)

Expand Down Expand Up @@ -182,3 +192,6 @@ def compare(self, other: Union["ProtoModel", BaseModel], **kwargs) -> bool:
class AutodocBaseSettings(BaseSettings):
def __init_subclass__(cls) -> None:
cls.__doc__ = AutoPydanticDocGenerator(cls, always_apply=True)


qcschema_draft = "http://json-schema.org/draft-04/schema#"
Loading