For developers
Developers should consult this section for detailed and specific information relevant to development/maintenance efforts, AFTER familiarizing themselves with the rest of the user-targeted documentation. Some material in this section is earmarked as specific to maintaining the code repository (see Repository Maintenance).
Contributing to fre-cli
Get a Copy of the Code
Get your own copy of the code with git clone --recursive git@github.com:NOAA-GFDL/fre-cli.git for the NOAA-GFDL fork,
or replace with your fork’s link (recommended).
Local/Editable Installation
Developers can test local changes by running a pip install [-e] . inside of the root directory after activating a
virtual environment with python>=3.11.* and all requirements. This installs the fre-cli package locally with
any local changes.
Development work on fre-cli should occur within a conda environment housing fre-cli’s requirements, and
a local copy of the repository to install with pip using the -e/--editable flag on. This specific approach is
described in Setup.
Testing Your Local Changes and Installation
There are a myriad of different ways of testing your efforts locally during your development cycle. A few examples and
fre-cli specific recommendations are described here, but contributors are welcome to be creative so-long as they
provide documentation of what they have contributed.
All contributed code should come with a corresponding unit-test. This section is not about writing unit-tests, but does contain some advice towards it. This section is mostly for streamlining a new developer’s approach to working with the code.
Running CLI-calls
Most development cycles will involve focused efforts isolated to a specific fre TOOL COMMAND *ARGV, where *ARGV
stands in for a shell-style argument vector (e.g. -e FOO -p BAR -t BAZ, a common pattern in fre-cli). Likely, the
code one is working on here is housed somewhere approximately like fre/TOOL/COMMAND.py (generally, this is not a
law), with the click CLI entry-point under fre/TOOL/freTOOL.py.
Here, the developer usually uses the fre TOOL COMMAND *ARGV call as a test, focused on seeing the changes they are
introducing, and develop the code until they see the result they are looking for. The specific fre TOOL COMMAND *ARGV
should/can often become a unit-test in one of the corresponding files in fre/tests. The sought-after changes the
developer wished to introduce should become assert conditions encoded within the unit-test. Both success and failure
conditions should ideally be tested.
Running the above, with no click
Every fre TOOL COMMAND *ARGV approximately maps to a single function call shown in fre/TOOL/freTOOL.py at this time.
Then, to accomplish the same thing as the previous section, but removing click and the CLI-aspect from it, and assuming
the code being executed is in fre/TOOL/COMMAND.py, in a function called named like FUNCTION,
python -i -c 'from fre.TOOL.COMMAND import FUNCTION; FUNCTION(**args);.
Writing a pytest unit-test for fre-cli
If the functionality one desires to test is that of CLI call, the tests should use the CliRunner approach shown in
fre/tests. click based CLI calls should NOT be tested with subprocess.run ever within pytest. See
click’s documentation for more
information.
If the functionality one desires to test is removed from that of a CLI call, the test should likely be housed in the directory
structure corresponding to the TOOL under-which the functionality lives. In that case, the usual pythonic-testing
approaches, guidelines, documentation etc. applies.
Adding a New Requirement to fre-cli
Currently, all required packages are conda packages listed in environment.yml, and also, equivalently in meta.yaml.
conda packages that have a corresponding pip package should list the pip package as a python requirement in
setup.py.
Pure pip packages cannot be listed currently as a requirement for fre-cli. This is because only environment.yml can
list pip packages as requirements. But, only meta.yaml can be used as a the conda build target. New dependencies
for fre-cli MUST have a conda package available through a non-proprietary conda channel, but preferable the
open-source conda-forge channel, which requires stronger quality control.
In general, the requirement being added is created by a third-party. As such, before adding a new requirement, the developer is
responsible for verifying that the desired package is safe, well-documented, and actively-maintained as necessary. The developer should
also consider the cost-benefit-problem of taking the extra time to introduce new functionality via standard-library approaches first,
and be prepared to defend the proposition of adding the new third-party package as a fre-cli requirement.
How fre-cli is updated
fre-cli is published and hosted as a Conda package on the NOAA-GFDL conda channel. On
pushes to the main branch, the package located at https://anaconda.org/NOAA-GFDL/fre-cli will automatically be updated using
the workflow defined in .github/workflows/publish_conda.yml, which is equivalent to .github/workflows/build_conda.yml
with an extra conda publish step.
Get desired logging verbosity
The logging module’s configuration initially occurs in fre/__init__.py, and gets inherited everywhere else logging
creates a logger object under the fre. namespace. If your development is being tested with a fre TOOL COMMAND *ARGV
style CLI call, it’s recommended you add verbosity flags, i.e. like fre -vv TOOL COMMAND *ARGV.
If your development does not fit nicely into that category, the next easiest thing to do is to adjust the base logger object
in fre/__init__.py to have the verbosity level you’d like. It’s important you adjust it back to the default verbosity level of
fre-cli before requesting a merge of your branch/fork to the repository’s trunk.
logging practice to avoid
The pitfall to avoid during development is calling logging.basicConfig to re-configure the logging behavior OUTSIDE of
fre/__init__.py. What this does is it creates another logging.handler to manage the output, but does not resolve
the ambiguity to previously defined loggers of which handler should be getting used. If this secondary logging.basicConfig
call is left in the PR or fork at merge-time, it can cause oddly silent logging behavior. This can be VERY tricky to debug!
avoid os.chdir if you can
Directory changing in python is not transient by-default, i.e., if when running fre the interpreter changes directories,
then the result of a os.cwd() later in the program may be changed to an unexpected value, leading to difficult bugs.
This being said, sometimes an os.chdir is hard to not want to use. If one has to use directory changing instead of managing
directory targets explicitly as pathlib.Path instances, then one can use the following logic to safely chdir where needed
and chdir back:
go_back_here = os.cwd()
try:
os.chdir(target_dir)
# DO STUFF AFTER CHDIR HERE
except:
raise Exception('some error explaining what went wrong')
finally:
os.chdir(go_back_here)
MANIFEST.in
In the case where non-python files like templates, examples, and outputs are to be included in the fre-cli package,
MANIFEST.in can provide the solution. Ensure that the file exists within the correct folder, and add a line to the
MANIFEST.in file saying something like include fre/fre<tool>/fileName.fileExtension
For more efficiency, if there are multiple files of the same type needed, the
MANIFEST.inaddition can be something likerecursive-include fre/fre<tool> *.fileExtensionwhich would recursively include every file matching thatfileExtensionwithin the specified directory and its respective subdirectories.
Repository Maintenance
Maintainers should consult this section for detailed and specific information relevant to maintaining github repositories, releasing, and deployments.
Release Versioning Procedure
Note
fre-cli and fre-workflows are versioned together. When fre-cli deploys a new release, a corresponding release is deployed in fre-workflows
Note
fre-cli has 3 submodules:
The release schedules of these submodules may vary from that of fre-cli
Verify that git submodules
gfdl_msd_schemasandmkmfreflect the latest state of the upstream repositories. If not, consult the manager of the upstream repository and determine whether the update should be included in this FRE release. If so, ask the sub-project maintainer to tag the upstream repository, and then commit the submodule update infre-cli.Update the package release number (i.e. reported by
fre --version) in your PR branch before merging tomainedit
versioninsetup.pyedit two version mentions in
fre/tests/test_fre_cli.pyUpdate release in
docs/conf.py
Create tag in fre-cli (this repository) and associated github release
locally this can be done with
git tag -a <release>andgit push --tagsafter the tag is pushed, CI will trigger the creation of a PR changing any reference to the previous tag with the new tag. Review the PR and merge.
verify the tagged release is present here
Update the package release number in the
fre-workflowsrepository:edit
FRE_VERSIONinflow.cylc(line 4)
Create corresponding tag in fre-workflows
Navigate to noaa-gfdl conda channel and verify that the last upload date corresponds to the date of this release and that the release number is correct.
Contributing to Documentation
fre-cli’s documentation is built with sphinx and written in restructured-text.
A decent cheat-sheet for restructured-text can be found
at this gist.
with a PR to NOAA-GFDL/fre-cli (recommended)
This approach is the easiest, most-automated we have to offer open-source contributors. It is completely appropriate for casual editing of the docs and previewing the changes, all while not eating up your personal github account’s free CI/CD minutes, and making PR reviews incredibly easy for documentation changes.
you DO NOT NEED a
readthedocs.orgaccount.Make a branch, either with
NOAA-GFDL/fre-clias the remote, or your own fork.Edit a file any non-zero amount, commit that change to your branch, and push. If the branch is identical to
main, you cannot open a PR!Once the PR is opened, a
readthedocsworkflow will be run, even if that PR is in draft mode. To confirm it is running, or did run, open your PR in a web browser, scroll to the bottom to find the latests workflow runs under “checks”, and click thereadthedocsworkflow.after clicking, you should see a URL like
https://noaa-gfdl--<PR_NUMBER>.org.readthedocs.build/projects/fre-cli/en/<PR_NUMBER>/, where<PR_NUMBER>is the PR number, for examples, these doc updates were added in PR 530 .If the doc build is successful, you should see the usual
fre-clidocumentation page. If unsuccessful, you should see a404error.To review documentation differences, play with the “Show diff” checkbox, which gives an explicit visual difference highlight right on the built webpage
with a fork and your own readthedocs account
This approach is good for playing with configuration of the workflow and not making a lot of noise on the main repository
with one’s development. If you want to experiment more freely and not send notifications to every maintainer of fre-cli,
this is for you. It also won’t use your own github account minutes.
Make sure you HAVE a fork underneath your github profile, if not, fork the repository under the NOAA-GFDL namespace
Navigate to readthedocs’ log-in page and sign in with your GitHub account. This effectively creates a
readthedocs.orgaccount for you, attached to yourgithubaccount.Click “Add project” and search for
fre-cli. If your fork doesn’t automatically come up, you do not have a fork! Go back to the first step in this list.If your changes do not live on a branch named
main(they should not, at least), configure the project to look for your branch’s name.If perms and everything lines up right, on your next push to the aforementioned branch, the docs should build and offer a preview relatively quickly. You should not have to re-configure anything to get it to work.
local sphinx build
This is good for deep debugging of the documentation build.
lightweight approach (recommended for docs-only)
If you only need to build documentation and don’t need the full fre-cli environment,
you can use a minimal setup. This approach uses autodoc_mock_imports in docs/conf.py
to mock heavy dependencies like netCDF4, cmor, xarray, etc.
From the root-directory of your local repository copy:
# Create a lightweight docs-only environment
conda create -n fre-cli-docs python=3.11 -y
conda activate fre-cli-docs
# Install minimal dependencies
pip install sphinx renku-sphinx-theme sphinx-rtd-theme click pyyaml jsonschema
# Set PYTHONPATH so Sphinx can find the fre modules
export PYTHONPATH="${PWD}:${PYTHONPATH}"
# Generate API docs and build
sphinx-apidoc --output-dir docs fre/ --separate
sphinx-build docs build
This will produce warnings about missing pytest and metomi imports from test modules,
but the build will succeed. To view the result, open build/index.html in your browser.
full environment approach
If you’re also developing/testing fre-cli functionality, get a local conda
env of
fre-cli going. This provides all dependencies and allows sphinx to use python’s
importlib functionality to auto-generate a clickable module-index from doc-strings.
From the root-directory of your local repository copy:
# Activate your fre-cli environment
conda activate fre-cli
# optional: load fre-nctools into your PATH to gain access to regridding and certain time-averaging routines
# module load fre-nctools
# Install documentation dependencies
pip install .[docs]
# Generate API docs and build
sphinx-apidoc --output-dir docs fre/ --separate
sphinx-build docs build
Then, to view the result, open up the resultant fre-cli/build/index.html with your favorite web browser.
You should be able to click around the locally built html and links should work as expected.
Note
sphinx-build is quite permissive, though loud. It makes accurate and numerous complaints, but often
is able to successfully finish anyways. After the first successful build, many warnings will not be displayed
a second time unless the file throwing the warning was changed. To get all the (useful AND useless) build
output like the first run, simply add -E or --fresh-env to the call to avoid using sphinx's
build-cache.
Troubleshooting
Both conda and python can interact oddly with c-shell. If you get the following error:
> pip install .[docs]
pip: No match.
You are likely in c-shell and can solve your issue by switching to a bash shell, re-loading your fre-cli environment, and running the local sphinx build commands again.