# Development ## Setting up the development environment Make sure that all the Python versions supported by the plugin (3.8 to 3.10 at the time of writing) are installed on your computer and available to tox. [asdf](https://asdf-vm.com) and [python-launcher](https://github.com/brettcannon/python-launcher) can be of help in this regard. Clone the project's repository ```shell git clone git@github.com:saltastroops/imephu.git ``` Create a virtual repository named `.venv` in the cloned project: ```shell cd imephu python -m venv .venv ``` While in principle another name could be chosen for the virtual environment, python-launcher will automatically pick up and use the virtual environment if it is called `.venv`. Activate the virtual environment (just in case it isn't picked up): ```shell source .venv/bin/activate ``` Now you can install the development requirements: ```shell python -m pip install --upgrade pip python -m pip install -r requirements-dev.txt ``` Finally, install the project package itself: ```shell python -m pip install -e . ``` ## Development tools After setting up the development environment, you have a variety of tools at your disposal. They are listed here along with the commands for using them. Tool | Command | Use --- | --- | --- black | `black src tests` | Format the code. flake8 | `flake8 src tests` | Check the code for linting issues. isort | `isort src tests` | Sort the imports. mypy | `mypy src tests` | Check the types. pytest | `pytest` | Run the unit tests. sphinx-build | `sphinx-build docs docs/build` | Build the documentation. ## Testing with tox The following table lists the various tests which can be run with tox. The third column states whether you can replace the default positional arguments with your own ones. In most cases there should be no need for this, though. Command | Use | Own positional arguments? --- | --- | --- `tox` | Run unit tests for all Python versions. | Yes `tox -e docs` | Check whether the documentation builds without warnings. | No `tox -e format` | Check the code for formatting issues. | Yes `tox -e imports` | Check the order of the import statements | Yes `tox -e lint` | Check the code for linting issues. | Yes `tox -e typecheck` | Check the types in the code (apart from tests). | Yes ## Unit testing ```{note} The project setup includes coverage for unit tests run with `tox`. This is not the case when `pytest` is run straight from the command line, as test coverage interferes with debugger breakpoints, causing code execution not to be paused at them. However, you may still include coverage by using the `--cov` flag. ``` Most of the unit tests create a finder chart, save it and compare it to a previously saved version, asserting nothing has changed. The pytest plugin [pytest-regressions](https://pytest-regressions.readthedocs.io/en/latest/overview.html) is used for simplifying the task. The files created by this plugin should be put under version control. The first time you save a particular finder chart, you will get an error like `File not found in data directory, created:`. In theory, you would expect that the next test run is passing. In practice, there is a problem. The {py:obj}`~imephu.finder_chart.FinderChart` class internally uses AstroPy's {py:obj}`~astropy.visualization.mpl_normalize` function, which is non-deterministic. This implies that the files generated by saving finder charts are not deterministic either; different runs of the code will create files which look the same, but have a different byte content (and even a different file size). The solution is to set a random seed for the unit test: ```python import numpy as np np.random.seed(0) ``` Bear in mind that [this is setting NumPy's global random seed](https://towardsdatascience.com/stop-using-numpy-random-seed-581a9972805f), which may affect other code (but shouldn't in the context of this project). Rather than trying to remember to always set the random seed (and to remember how to use pytest-regressions), you can just use the `check_finder` fixture: ```python from imephu.finder_chart import FinderChart def test_create_finder_chart(fits_file, check_finder) -> None: fc = FinderChart(fits_file) check_finder(fc) ``` If you need to update the files generated by pytest-regressions, you may run `pytest` with the `--force-regen` flag: ```shell pytest --force-regen ``` In case you are using parametrized tests and want to have more meaningful names for the generated files, you may use the `ids` keyword of the `parametrized` marker. ### Fixtures The following fixtures are defined in `conftest.py`. Fixture | Description --- | --- check_finder | Function comparing a finder chart against a previously saved version, asserting nothing has changed. (Technically, the saved png file is compared.) fits_file | {py:obj}`~pathlib.Path` of a FITS file that can be used for generating a finder chart. ## Publishing the package ### Publishing manually In order to publish imephu from your machine, first install twine, if you haven't done so already: ```shell pipx install twine ``` Then remove the `dist` folder if need be: ```shell rm -r dist ``` Build the package: ```shell pyproject-build --sdist ``` If you have your own PyPI server, say `https://pypi.example.com`, create a file `.pypirc` in your home directory with the following content: ``` [distutils] index-servers = pypi testpypi saao [pypi] repository = https://upload.pypi.org/legacy/ [testpypi] repository = https://test.pypi.org/legacy/ [example] repository = https://pypi.example.com/ ``` You can then use twine to upload the package: ```shell twine upload -r https://pypi.example.com dist/* ``` Afterwards you can install imephu from your PyPI server with ```shell python -m pip install --index-url https://pypi.example.com/ ``` Upload the package to [Test PyPI](https://packaging.python.org/guides/using-testpypi/): ```shell twine upload -r testpypi dist/* ``` You can then install imephu from Test PyPI with ```shell python -m pip install --index-url https://test.pypi.org/simple/ imephu ``` If all looks good, you can publish your package: ```shell twine upload dist/* ```