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.in addition can be something like recursive-include fre/fre<tool> *.fileExtension which would recursively include every file matching that fileExtension within 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

  1. Verify that git submodules gfdl_msd_schemas and mkmf reflect 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 in fre-cli.

  2. Update the package release number (i.e. reported by fre --version) in your PR branch before merging to main

    1. edit version in setup.py

    2. edit two version mentions in fre/tests/test_fre_cli.py

    3. Update release in docs/conf.py

  3. Create tag in fre-cli (this repository) and associated github release

    1. locally this can be done with git tag -a <release> and git push --tags

    2. after 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.

    3. verify the tagged release is present here

  4. Update the package release number in the fre-workflows repository:

    1. edit FRE_VERSION in flow.cylc (line 4)

  5. Create corresponding tag in fre-workflows

  6. 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 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.org account for you, attached to your github account.

  • 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.

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.