Repository: winter-telescope/mirar (language: python)

Issue:
[FEATURE] Optionally close images to save RAM
**Is your feature request related to a problem? Please describe.**
I'm always frustrated when I run out of ram. Sometimes this is the fault of the code, because it opens all the images and that can be a large cumulative size for N00 images in a night. 

**Describe the solution you'd like**
I think we should at least have the option of writing images to a temp file rather than keeping it in RAM. You could have a custom `get_data()` method in the newly-designed `Image` object class (#196 is a prerequisite to this) to manage this.

**Describe alternatives you've considered**
Always opening/closing images, but that would be slower for some N images. 



Discussion / hints:

Fix patch:
diff --git a/.github/CONTRIBUTING.rst b/.github/CONTRIBUTING.rst
index f4cc9d389..eea88cb08 100644
--- a/.github/CONTRIBUTING.rst
+++ b/.github/CONTRIBUTING.rst
@@ -21,13 +21,20 @@ We have a few general guidelines that are helpful for keeping things organised:
 Updating the documentation
 --------------------------
 
-The documentation (generated primarily from docstrings) can be modified with the following command, executed from the docs directory:
+The documentation (generated primarily from docstrings) can be modified with the following command, **executed from the docs directory**:
 
 .. code-block:: bash
 
     sphinx-apidoc -o source/ ../winterdrp --module-first --force
 
 
+You can then build the documentation locally:
+
+.. code-block:: bash
+
+    sphinx-build source build
+
+
 Checking the tests locally
 --------------------------
 
diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml
index 0fc3b7110..9b206fafb 100644
--- a/.github/workflows/pylint.yml
+++ b/.github/workflows/pylint.yml
@@ -23,4 +23,4 @@ jobs:
           poetry install
       - name: Analysing the code with pylint
         run: |
-           poetry run pylint winterdrp
+           poetry run pylint winterdrp --fail-under=0.84
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d67478acc..f49f97697 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -22,7 +22,7 @@ repos:
         types: [python]
         args:
           [
-            "--disable=C,fixme",
+            "--disable=missing-function-docstring,missing-module-docstring,missing-class-docstring",
             "-rn", # Only display messages
             "-sn", # Don't display the score
           ]
diff --git a/docs/source/about.rst b/docs/source/about.rst
index 547f9ebef..fd3799e74 100644
--- a/docs/source/about.rst
+++ b/docs/source/about.rst
@@ -14,3 +14,10 @@ Data Structure
 .. include:: ../../winterdrp/data/base_data.py
     :start-line: 1
     :end-line: 15
+
+How does the code actually work?
+--------------------------------
+
+.. include:: ../../winterdrp/data/image_data.py
+    :start-line: 5
+    :end-line: 54
diff --git a/env.example b/env.example
index bf6aa0a9a..251b815a7 100644
--- a/env.example
+++ b/env.example
@@ -25,4 +25,6 @@ WATCHDOG_EMAIL_PASSWORD = <password>
 WATCHDOG_EMAIL_RECIPIENTS = <target email address>
 
 # Set maximum number of CPUs to use, with a default of half the available total
-MAX_N_CPU = <integer number of CPUs>
\ No newline at end of file
+MAX_N_CPU = <integer number of CPUs>
+# Set whether to store images in cache, with a default of true
+USE_WINTER_CACHE = <boolean>
diff --git a/poetry.lock b/poetry.lock
index 7ea4c9914..fb4ec4622 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -110,7 +110,7 @@ test = ["pytest-astropy"]
 
 [[package]]
 name = "astropy"
-version = "5.1.1"
+version = "5.2"
 description = "Astronomy and astrophysics core library"
 category = "main"
 optional = false
@@ -118,18 +118,18 @@ python-versions = ">=3.8"
 
 [package.dependencies]
 matplotlib = {version = ">=3.1,<3.4.0 || >3.4.0,<3.5.2 || >3.5.2", optional = true, markers = "extra == \"recommended\""}
-numpy = ">=1.18"
+numpy = ">=1.20"
 packaging = ">=19.0"
 pyerfa = ">=2.0"
 PyYAML = ">=3.13"
-scipy = {version = ">=1.3", optional = true, markers = "extra == \"recommended\""}
+scipy = {version = ">=1.5", optional = true, markers = "extra == \"recommended\""}
 
 [package.extras]
-all = ["asdf (>=2.10.0)", "beautifulsoup4", "bleach", "bottleneck", "certifi", "dask[array]", "h5py", "html5lib", "ipython (>=4.2)", "jplephem", "matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "mpmath", "pandas", "pyarrow (>=5.0.0)", "pytest (>=7.0)", "pytz", "scipy (>=1.3)", "sortedcontainers", "typing-extensions (>=3.10.0.1)"]
-docs = ["Jinja2 (<3.1)", "matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "pytest (>=7.0)", "scipy (>=1.3)", "sphinx (<4)", "sphinx-astropy (>=1.6)", "sphinx-changelog (>=1.2.0)"]
-recommended = ["matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "scipy (>=1.3)"]
+all = ["asdf (>=2.10.0)", "beautifulsoup4", "bleach", "bottleneck", "certifi", "dask[array]", "fsspec[http] (>=2022.8.2)", "h5py", "html5lib", "ipython (>=4.2)", "jplephem", "matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "mpmath", "pandas", "pyarrow (>=5.0.0)", "pytest (>=7.0)", "pytz", "s3fs (>=2022.8.2)", "scipy (>=1.5)", "sortedcontainers", "typing-extensions (>=3.10.0.1)"]
+docs = ["Jinja2 (>=3.0,<3.1)", "matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "pytest (>=7.0)", "scipy (>=1.3)", "sphinx (<4)", "sphinx-astropy (>=1.6)", "sphinx-changelog (>=1.2.0)"]
+recommended = ["matplotlib (>=3.1,!=3.4.0,!=3.5.2)", "scipy (>=1.5)"]
 test = ["pytest (>=7.0)", "pytest-astropy (>=0.10)", "pytest-astropy-header (>=0.2.1)", "pytest-doctestplus (>=0.12)", "pytest-xdist"]
-test-all = ["coverage", "ipython (>=4.2)", "objgraph", "pooch", "pytest (>=7.0)", "pytest-astropy (>=0.10)", "pytest-astropy-header (>=0.2.1)", "pytest-doctestplus (>=0.12)", "pytest-xdist", "sgp4 (>=2.3)", "skyfield (>=1.20)"]
+test-all = ["coverage[toml]", "ipython (>=4.2)", "objgraph", "pooch", "pytest (>=7.0)", "pytest-astropy (>=0.10)", "pytest-astropy-header (>=0.2.1)", "pytest-doctestplus (>=0.12)", "pytest-xdist", "sgp4 (>=2.3)", "skyfield (>=1.20)"]
 
 [[package]]
 name = "astroquery"
@@ -229,7 +229,7 @@ lxml = ["lxml"]
 
 [[package]]
 name = "black"
-version = "22.10.0"
+version = "22.12.0"
 description = "The uncompromising code formatter."
 category = "main"
 optional = false
@@ -692,7 +692,7 @@ python-versions = "*"
 
 [[package]]
 name = "ipykernel"
-version = "6.19.1"
+version = "6.19.2"
 description = "IPython Kernel for Jupyter"
 category = "main"
 optional = false
@@ -793,11 +793,11 @@ arrow = ">=0.15.0"
 
 [[package]]
 name = "isort"
-version = "5.10.1"
+version = "5.11.2"
 description = "A Python utility / library to sort Python imports."
 category = "main"
 optional = false
-python-versions = ">=3.6.1,<4.0"
+python-versions = ">=3.7.0"
 
 [package.extras]
 colors = ["colorama (>=0.4.3,<0.5.0)"]
@@ -1379,7 +1379,7 @@ testing = ["docopt", "pytest (<6.0.0)"]
 
 [[package]]
 name = "pathspec"
-version = "0.10.2"
+version = "0.10.3"
 description = "Utility library for gitignore style pattern matching of file paths."
 category = "main"
 optional = false
@@ -1523,28 +1523,28 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
 
 [[package]]
 name = "psycopg"
-version = "3.1.4"
+version = "3.1.5"
 description = "PostgreSQL database adapter for Python"
 category = "main"
 optional = false
 python-versions = ">=3.7"
 
 [package.dependencies]
-psycopg-binary = {version = "3.1.4", optional = true, markers = "extra == \"binary\""}
-typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""}
+psycopg-binary = {version = "3.1.5", optional = true, markers = "extra == \"binary\""}
+typing-extensions = ">=4.1"
 tzdata = {version = "*", markers = "sys_platform == \"win32\""}
 
 [package.extras]
-binary = ["psycopg-binary (==3.1.4)"]
-c = ["psycopg-c (==3.1.4)"]
-dev = ["black (>=22.3.0)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=0.981)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
+binary = ["psycopg-binary (==3.1.5)"]
+c = ["psycopg-c (==3.1.5)"]
+dev = ["black (>=22.3.0)", "dnspython (>=2.1)", "flake8 (>=4.0)", "mypy (>=0.990)", "types-setuptools (>=57.4)", "wheel (>=0.37)"]
 docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"]
 pool = ["psycopg-pool"]
-test = ["mypy (>=0.981)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-asyncio (>=0.17)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.10)"]
+test = ["mypy (>=0.990)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-asyncio (>=0.17)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.10)"]
 
 [[package]]
 name = "psycopg-binary"
-version = "3.1.4"
+version = "3.1.5"
 description = "PostgreSQL database adapter for Python -- C optimisation distribution"
 category = "main"
 optional = false
@@ -2203,7 +2203,7 @@ telegram = ["requests"]
 
 [[package]]
 name = "traitlets"
-version = "5.6.0"
+version = "5.7.1"
 description = "Traitlets Python configuration system"
 category = "main"
 optional = false
@@ -2211,7 +2211,9 @@ python-versions = ">=3.7"
 
 [package.extras]
 docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
+lint = ["black (>=22.6.0)", "mdformat (>0.7)", "ruff (>=0.0.156)"]
 test = ["pre-commit", "pytest"]
+typing = ["mypy (>=0.990)"]
 
 [[package]]
 name = "typing-extensions"
@@ -2411,37 +2413,37 @@ astroplan = [
     {file = "astroplan-0.8.tar.gz", hash = "sha256:0cc463474e034f5f58f7399ef830ff60e91d2fac32e89cf2224e1de50946bdc7"},
 ]
 astropy = [
-    {file = "astropy-5.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1845b338605a1115d2a5854707e86a9c118a7cce5d2a1c0da6a39d67ebb1648c"},
-    {file = "astropy-5.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ca1fb44178d8357f17da585099cf13d99020a7e4321f90a2e823d6357f60fc8"},
-    {file = "astropy-5.1.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a48fdea31efcf7dae8c2f9e35c8d9c2d464ab72f2282ab9e93ef332eb2c6223"},
-    {file = "astropy-5.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f7ba415912751351aa4bf4e44f159215abbb50f94f470090a4df0e1a8343341"},
-    {file = "astropy-5.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:605ecbe244f8122f2611593208502adf4c065471bce88657cf2d67122cb51923"},
-    {file = "astropy-5.1.1-cp310-cp310-win32.whl", hash = "sha256:2ae78973bb97a2ed1eecf912d3e7c122e5beceea9325cd4d08f907c025769f60"},
-    {file = "astropy-5.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:725276a6d4bcd815ef1f4c7080d91d7e3b46aa449b3acffd495195d4a793d0d8"},
-    {file = "astropy-5.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17286c4d4cfdc26c8a9cb3e4b00d8c1b90a6fb06f0f86caf8c83b2a00907323a"},
-    {file = "astropy-5.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:80f178e0d25c0ad1d6370383bef72fd4c9b9764e66efa37a3b1b457b516199ce"},
-    {file = "astropy-5.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28c5b72355b59128c590dd77c7a10cf5557d4b8775a0ae4f561d00c7a7a7a890"},
-    {file = "astropy-5.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99a5e32b9df3ca904e7297fe4896b09db821c2ab13110225897d901aa389ec44"},
-    {file = "astropy-5.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:64a0944ac04a904029ffcbe7ff41276527cb10f2cd419f52ab4135a78de49e21"},
-    {file = "astropy-5.1.1-cp311-cp311-win32.whl", hash = "sha256:f00a5a108d5a723d8c98632e4f1692eeaec847de70675eb6fb680b6bee46073f"},
-    {file = "astropy-5.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:38c31260cfc8dea69062ee2488bcc2e65f8553c6ca0c8784a92fdf21512ca856"},
-    {file = "astropy-5.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b69337baee96d8c49ca76900b18bcb7e8b154d411018025214136ee1b8f2458d"},
-    {file = "astropy-5.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e049af9e672d7d53c9f58d128ade935f08829167f0e522f82ed59d44e118724c"},
-    {file = "astropy-5.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7342a2fa897cccac0227d9cbd39f3aef2a6a95dd3a57448c0e1ef4815dd39151"},
-    {file = "astropy-5.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:df49549891920b9d8e9ed8ea3f465ff1d5ae92788e534a581fb1be41dd39bb4b"},
-    {file = "astropy-5.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a492b5231e02745c70e159ff3eaf887be1d2dbf2ef1d557d882474e09ca01ae4"},
-    {file = "astropy-5.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1a1d3e57473d679ed0cbd864e61bfe866ed7ae2f43caa2a5d1d3c42b0ecfe15b"},
-    {file = "astropy-5.1.1-cp38-cp38-win32.whl", hash = "sha256:52d87dc4af21a96f30e13c0e8386752eceecaf00fe06266f5344ba7c02226015"},
-    {file = "astropy-5.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:176af7050c4cb248a11eb73b297fc0c028cef5097f35866c0066a55877a69f3a"},
-    {file = "astropy-5.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0a15cab2912f13fef370ac5de33c2f83e3f5090d1d6199f9e0bd44d6f7321386"},
-    {file = "astropy-5.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:350c55ddcf92802feb053cc5e780a173c91dd1c01df3bc9bff9096e682193a23"},
-    {file = "astropy-5.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aa571a52cd311d03389b0fcbb102722a3e05be19dbb8ad418124138559fc70a8"},
-    {file = "astropy-5.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:33ce18410c76bf6e1b7cd7e42ead215e845c53b7cebec2ed5d4559d0fb9ff99b"},
-    {file = "astropy-5.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fd08dbe2387107b4ec1f3748d99276515e0d61ff2fdee0304b7ab39a704a171"},
-    {file = "astropy-5.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4a61250c2550615227990322c224072808b0f8aaee89f92ca8c9d5779f3e72be"},
-    {file = "astropy-5.1.1-cp39-cp39-win32.whl", hash = "sha256:423ee2c6fad28cbf5a6576a028872764d3c27d02e389c540f16aeb28c5edc923"},
-    {file = "astropy-5.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:30008bba984bfca24db30a5c397ac781f984abfeb5f50e12706245ca32d7f973"},
-    {file = "astropy-5.1.1.tar.gz", hash = "sha256:ba4bd696af7090fd399b464c704bf27b5633121e461785edc70432606a94bd81"},
+    {file = "astropy-5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:252ba92bee687548581f6c6a9ab61c7eec4c7a77e04bcf72478e474a578db338"},
+    {file = "astropy-5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d73b379b4521f68b11a3bae7343b948568191f3be74d3f7b4958d627c705212a"},
+    {file = "astropy-5.2-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dff0ca9dc48fb15714c88416e85e9dd55936ba7858b9d98e3ad9d6fe98020fa3"},
+    {file = "astropy-5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:711d2d4a0cc01aa456796c4ca931be90da27f7d2e529ca427c9f40fca0c8ec5d"},
+    {file = "astropy-5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f0bb41d304a6b01b0cca0a4cfc91062754c33bee64ee0cb7ef448773cf2fc14"},
+    {file = "astropy-5.2-cp310-cp310-win32.whl", hash = "sha256:91359e2944816de216e0657e5c6a94fe5e7178781533d71e724b9f2612e80fdb"},
+    {file = "astropy-5.2-cp310-cp310-win_amd64.whl", hash = "sha256:0eabb6d7ff62ef8c0fd1901217dfb34ca3de14f6aa45ece0965970b7b71d7651"},
+    {file = "astropy-5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbd94b7d41b314382617b5240082af5c8b10172ec84c239dc6f7ed92844f0b70"},
+    {file = "astropy-5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6bf229b6579e6ce7ca08f979b008475839d89047217d5a3ce7f1f11082158817"},
+    {file = "astropy-5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abeb1486a35d906f8e7f58d6b848a3637b33f180104f7cd9b4094ec810ac6aa3"},
+    {file = "astropy-5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b906104f6a4c125d4a5794416b371d3bd4c176cf0db6cbcfd3107d7ce8a2103"},
+    {file = "astropy-5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4bb7f58ede5f47548e61ed4496cc61d341a1c000cc4b39fb84a03a71e2c7dfac"},
+    {file = "astropy-5.2-cp311-cp311-win32.whl", hash = "sha256:6d33f0559fb6152496480301c058a55ca49a0759d499a4be023efeae7a7d2412"},
+    {file = "astropy-5.2-cp311-cp311-win_amd64.whl", hash = "sha256:ef8162c121ec2fe30d3720f838d7fe1d2d70811a83b1087bdb31c72df88c857e"},
+    {file = "astropy-5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:15e1a663e415a7f4bc56aede6bbf9985e93264c69fc45e67265e22d7cabaf392"},
+    {file = "astropy-5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad196f3c16138155b28660e4fba7b4f39431459a1d3b453a168a5bef3163fb07"},
+    {file = "astropy-5.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0e4db330bce35a66e98d56aa9eb61472b42f0af79c5e8985d4af10ac435f310c"},
+    {file = "astropy-5.2-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bd87867dd7d7a1962d6e5f6c63e8e38958018879fd5b13bf3d0efe2a0b7c4ae1"},
+    {file = "astropy-5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:861a4cbe7be43a7dda4d5965650cde00185338c203fa16d6fed1268a0f6b57a0"},
+    {file = "astropy-5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7fe2303d21c9572a5a5a44775339d01f2fc1097db8a925b75ca4377cdd18cae2"},
+    {file = "astropy-5.2-cp38-cp38-win32.whl", hash = "sha256:ce0b7b079737dc2716aa78f5e2d13c1e1ec70ead13538440ce1a96bac210c5ae"},
+    {file = "astropy-5.2-cp38-cp38-win_amd64.whl", hash = "sha256:7ce65dfd539d6c5b7871e8f86475fd6e6bd28e3c31f92ee8feefe5c4316444d9"},
+    {file = "astropy-5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e56a88590e2f6e041ef4312f353e321de210ad82ae280965c76b8b6bd598e156"},
+    {file = "astropy-5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1a8f3ef46b411e04ef2cdf383049a63869ed7df307f25384411c799f58d66a1c"},
+    {file = "astropy-5.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c31de7d7e00e31dbc843a0201bcf9099dc9b08ae937ceee16235f37aabe330dc"},
+    {file = "astropy-5.2-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ab71607d954b3284256fb995969600dbdfaf461f6036e474b7bbf3b3c2e1a4a6"},
+    {file = "astropy-5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7444a441df33916cb3d18afe3b187c368c605b34196ea625f7fb560a9973cbda"},
+    {file = "astropy-5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:96dc0b9d6c17346e4877a97464ba1bad83be02b565259a094a891358872a10d3"},
+    {file = "astropy-5.2-cp39-cp39-win32.whl", hash = "sha256:3d48efa24dc7d1dfb301c8dbb8edb0b544af945512f0a802aca421eb675ed197"},
+    {file = "astropy-5.2-cp39-cp39-win_amd64.whl", hash = "sha256:d9bd65b8232f8b866fef70c2984a5b96cecd8c53e3231394cbcb9b8f2c84162e"},
+    {file = "astropy-5.2.tar.gz", hash = "sha256:d335604025f6e16f7c9bf82d5ba28e5db4745a82e5823a9d17bdd9b9bd46b2a2"},
 ]
 astroquery = [
     {file = "astroquery-0.4.6-py3-none-any.whl", hash = "sha256:e1bc4996af7500370837d31491bd4ee7f0c954c78d24cd54fb1cceb755469094"},
@@ -2471,27 +2473,18 @@ beautifulsoup4 = [
     {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"},
 ]
 black = [
-    {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"},
-    {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"},
-    {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"},
-    {file = "black-22.10.0-1fixedarch-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:2644b5d63633702bc2c5f3754b1b475378fbbfb481f62319388235d0cd104c2d"},
-    {file = "black-22.10.0-1fixedarch-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:e41a86c6c650bcecc6633ee3180d80a025db041a8e2398dcc059b3afa8382cd4"},
-    {file = "black-22.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2039230db3c6c639bd84efe3292ec7b06e9214a2992cd9beb293d639c6402edb"},
-    {file = "black-22.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14ff67aec0a47c424bc99b71005202045dc09270da44a27848d534600ac64fc7"},
-    {file = "black-22.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:819dc789f4498ecc91438a7de64427c73b45035e2e3680c92e18795a839ebb66"},
-    {file = "black-22.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5b9b29da4f564ba8787c119f37d174f2b69cdfdf9015b7d8c5c16121ddc054ae"},
-    {file = "black-22.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8b49776299fece66bffaafe357d929ca9451450f5466e997a7285ab0fe28e3b"},
-    {file = "black-22.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:21199526696b8f09c3997e2b4db8d0b108d801a348414264d2eb8eb2532e540d"},
-    {file = "black-22.10.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e464456d24e23d11fced2bc8c47ef66d471f845c7b7a42f3bd77bf3d1789650"},
-    {file = "black-22.10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9311e99228ae10023300ecac05be5a296f60d2fd10fff31cf5c1fa4ca4b1988d"},
-    {file = "black-22.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fba8a281e570adafb79f7755ac8721b6cf1bbf691186a287e990c7929c7692ff"},
-    {file = "black-22.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:915ace4ff03fdfff953962fa672d44be269deb2eaf88499a0f8805221bc68c87"},
-    {file = "black-22.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:444ebfb4e441254e87bad00c661fe32df9969b2bf224373a448d8aca2132b395"},
-    {file = "black-22.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:974308c58d057a651d182208a484ce80a26dac0caef2895836a92dd6ebd725e0"},
-    {file = "black-22.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ef3925f30e12a184889aac03d77d031056860ccae8a1e519f6cbb742736383"},
-    {file = "black-22.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:432247333090c8c5366e69627ccb363bc58514ae3e63f7fc75c54b1ea80fa7de"},
-    {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"},
-    {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
+    {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"},
+    {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"},
+    {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"},
+    {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"},
+    {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"},
+    {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"},
+    {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"},
+    {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"},
+    {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"},
+    {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"},
+    {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"},
+    {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"},
 ]
 bleach = [
     {file = "bleach-5.0.1-py3-none-any.whl", hash = "sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a"},
@@ -2957,8 +2950,8 @@ iniconfig = [
     {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
 ]
 ipykernel = [
-    {file = "ipykernel-6.19.1-py3-none-any.whl", hash = "sha256:8b358a5aaa77c7e4b6cec22b03a910ab6c2968a449dced12082b6a6c56404083"},
-    {file = "ipykernel-6.19.1.tar.gz", hash = "sha256:d472029d14408273265a7b0ec6d1923f1b1f51d8643f125c6a6881e5e6e56a39"},
+    {file = "ipykernel-6.19.2-py3-none-any.whl", hash = "sha256:1374a55c57ca7a7286c3d8b15799cd76e1a2381b6b1fea99c494b955988926b6"},
+    {file = "ipykernel-6.19.2.tar.gz", hash = "sha256:1ab68d3d3654196266baa93990055413e167263ffbe4cfe834f871bcd3d3506d"},
 ]
 ipython = [
     {file = "ipython-8.7.0-py3-none-any.whl", hash = "sha256:352042ddcb019f7c04e48171b4dd78e4c4bb67bf97030d170e154aac42b656d9"},
@@ -2977,8 +2970,8 @@ isoduration = [
     {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"},
 ]
 isort = [
-    {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
-    {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
+    {file = "isort-5.11.2-py3-none-any.whl", hash = "sha256:e486966fba83f25b8045f8dd7455b0a0d1e4de481e1d7ce4669902d9fb85e622"},
+    {file = "isort-5.11.2.tar.gz", hash = "sha256:dd8bbc5c0990f2a095d754e50360915f73b4c26fc82733eb5bfc6b48396af4d2"},
 ]
 jaraco-classes = [
     {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"},
@@ -3345,8 +3338,8 @@ parso = [
     {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
 ]
 pathspec = [
-    {file = "pathspec-0.10.2-py3-none-any.whl", hash = "sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5"},
-    {file = "pathspec-0.10.2.tar.gz", hash = "sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0"},
+    {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"},
+    {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"},
 ]
 penquins = [
     {file = "penquins-2.2.0-py3-none-any.whl", hash = "sha256:bbf22b4761c9d0ecd2a4dac519a841b0c0e45eb39c0af0085b4e7780e6edc856"},
@@ -3483,64 +3476,64 @@ psutil = [
     {file = "psutil-5.9.4.tar.gz", hash = "sha256:3d7f9739eb435d4b1338944abe23f49584bde5395f27487d2ee25ad9a8774a62"},
 ]
 psycopg = [
-    {file = "psycopg-3.1.4-py3-none-any.whl", hash = "sha256:aba61f12b11936cb25ec7bdf2d222d1ec36e4541ddc41f774ca763e0c50dd06f"},
-    {file = "psycopg-3.1.4.tar.gz", hash = "sha256:6874dbaba583cc6663437604bf45f7c244b5fd702f88af6211cd5a74e8ee3a8a"},
+    {file = "psycopg-3.1.5-py3-none-any.whl", hash = "sha256:4d149af3c2ec8eb9ef6bc2baaeb99f557a887e7171e9fd2c30b89a418e3adc2c"},
+    {file = "psycopg-3.1.5.tar.gz", hash = "sha256:e87074f289b7c9bb4cb6e2aaa069e8bd9f9a260221c7170ad90052042ff5577b"},
 ]
 psycopg-binary = [
-    {file = "psycopg_binary-3.1.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d59066c73aa23fb2962aa071d3ec2acf8f67a6f64557d0064d88337ef7c8f718"},
-    {file = "psycopg_binary-3.1.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:98a215c5f61456dd3d64f0e128231601ad8d9e5e3c375459cfb00a2436442ec6"},
-    {file = "psycopg_binary-3.1.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:135411321c7d4afb86186469ee5f94e015dea528e928d5facc3f56fd0708930a"},
-    {file = "psycopg_binary-3.1.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:556b51a3fbe23da73fdb5e395c10a8bab1ff0cff0c79c8d976e9bb251eae4099"},
-    {file = "psycopg_binary-3.1.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a8958e44615e104b2b613792b0dcbc82cdc26cd9e2544f97c12c3b6b59b509f"},
-    {file = "psycopg_binary-3.1.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:180b0a044133f58e4762804121e2230af048f627ef93def47536db4f7f2f3deb"},
-    {file = "psycopg_binary-3.1.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3f34a754febf4e4d1031b359cd715bc2e7bb9525537addf185f4487e66bc408e"},
-    {file = "psycopg_binary-3.1.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2aa0030420191291e892d20d0bd28bc70bc296a1a0b43ec588bb729d336df6cf"},
-    {file = "psycopg_binary-3.1.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b3a61a05196a2e9419c3c025e33e91fea0e94087850d8d130ae98a08257a1401"},
-    {file = "psycopg_binary-3.1.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3be3e13394f355369506070ee0d051aa384cf1ce95756dc566fda641eee35366"},
-    {file = "psycopg_binary-3.1.4-cp310-cp310-win_amd64.whl", hash = "sha256:47941112391b35ecf4647476d81da21b929a6b2884eb4935560ec3e952b963d8"},
-    {file = "psycopg_binary-3.1.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e91ef5ffaa5d279d4c8ac1c0ad435a5aa07db7fb159f9de69532abcfb8475d73"},
-    {file = "psycopg_binary-3.1.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4526e5e5ae816a4dd91c38d8806531fd25cc194cb0e3013cefc5cb5527d121d8"},
-    {file = "psycopg_binary-3.1.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d772bfcd90eb3ed0938f499b9f851b39da0a167067920852ccda4a94b3968c6"},
-    {file = "psycopg_binary-3.1.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:585ccdfae2e22d031bd77b5f481c23d7246dcb75bb9acd04ab1cfb0a4bfca4d4"},
-    {file = "psycopg_binary-3.1.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1dac09b3e12b56c0eb28b77a07c8d8f4b04d72b4055c543c47fd9f589261c40f"},
-    {file = "psycopg_binary-3.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e478ef5ea7b21b1383132001ff8a6a0776d2b43a12e8d4a7b77e2f510c994c65"},
-    {file = "psycopg_binary-3.1.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4a9ef8af564d5126433c2d3c0913d2084f49ace343bb9a8f8a34a1a97f77cdd0"},
-    {file = "psycopg_binary-3.1.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:90e1b3514b0ed8bf07f3a7072f1a85c61eaf483c7385a0669d51044c6cd500e5"},
-    {file = "psycopg_binary-3.1.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:33dca0656a17792d9335f079d743452c50f0b21173d2132c33ffd89a03be2912"},
-    {file = "psycopg_binary-3.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:84d85271cdfec03110bfdf6528fed3a3a0d487187127fd3d11721db575f92364"},
-    {file = "psycopg_binary-3.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:23adcfe0fb690dbefdb38a8c5acec446a43ba8185075c415d9d99a2922b454b0"},
-    {file = "psycopg_binary-3.1.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:40f1ed126d0c07cf8357bf1494eb0ad50c68e7ce056c1b336ce4e97b5ec6fd13"},
-    {file = "psycopg_binary-3.1.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18173b050fb0ef7753a9ebf9f8e3092af63baa294e1dc0b1210921117770f75f"},
-    {file = "psycopg_binary-3.1.4-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6540c339cf19cd4ce5d436b85fb7efd06712a53f2612f17b86789bb311f72fda"},
-    {file = "psycopg_binary-3.1.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:685495ceb7e04f0e8a4f0834810e9e8d8febc78101c70b468ba0c3292a05176b"},
-    {file = "psycopg_binary-3.1.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07b635712f7bd16161aa67c567340fb5f1cd03d8319b717627a2ce669710dd11"},
-    {file = "psycopg_binary-3.1.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aab0cce0034323bebdc120cd3ab1270cb6dc6cf27b1a6a52100e4ecf787ff0b6"},
-    {file = "psycopg_binary-3.1.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:640acbb2ab8272bc44a2f182dfd93fe5d7b23bb134d97bcb23167d8fc2c26310"},
-    {file = "psycopg_binary-3.1.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e63bbbd49943830cb3e06f8eb36e0b91613cd397eeb2a171b46a663900cc1f8f"},
-    {file = "psycopg_binary-3.1.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70afeb6157bf55d58f22b0b8ef517ca5c62c965a78c5555ec585fc0b8cf5077b"},
-    {file = "psycopg_binary-3.1.4-cp37-cp37m-win_amd64.whl", hash = "sha256:cd9b90d56000b62f16d202a755bed9ac36b3a2c7b853bad19c702250ccbbf2ea"},
-    {file = "psycopg_binary-3.1.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:206cdad5c0a3418a68a374eeb73103ecc84de845e8ad2ff05eb8e4d7229e215a"},
-    {file = "psycopg_binary-3.1.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0cd3af8b6cb8178c7220c2a7e1384d1065d613f564e84baebbd675531ee167d2"},
-    {file = "psycopg_binary-3.1.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3d980f582654d7e07fa67f747b855c84957ad2ab9d911d5e90a1a2a5e79a129"},
-    {file = "psycopg_binary-3.1.4-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ab1fab06d93a7e4393385fa26f9ab4fe00ad0f32cc817eb5f11e73f9b05b5c7"},
-    {file = "psycopg_binary-3.1.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9711efb01b23b0dc66178358c5ed3da479facdedecbfb55294505c6da99c942"},
-    {file = "psycopg_binary-3.1.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b11f52e306dad28c3d15f27b9064c98de09e2fecb7378164babd4a0ce9dc5505"},
-    {file = "psycopg_binary-3.1.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95e071377a2e976f4ab1dee83df4c6c38b38f2b49f7066075b2cddb061f6fb0c"},
-    {file = "psycopg_binary-3.1.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8c59042f1b40cfbb004a22cf66285f12207e83a3578285ebd145e341cb2b6d46"},
-    {file = "psycopg_binary-3.1.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:b4bd08b809825baa45870412a17dd947f51353bbf61ae5bfde652843d8b8bed8"},
-    {file = "psycopg_binary-3.1.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:44e562dcf76b9dfc3b4ad6db0fca8d1e118c80da0ebb1446e7e9cf449b02f8b5"},
-    {file = "psycopg_binary-3.1.4-cp38-cp38-win_amd64.whl", hash = "sha256:bab49d2e5bfd8903dbba33adc5fa50000103bbcf04c6b8186f993c6173935569"},
-    {file = "psycopg_binary-3.1.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a8db1d739456706765d93b0a0d6c6c7d355408c13db1d947918bdc821f1322ce"},
-    {file = "psycopg_binary-3.1.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d7049e48d75be4319193ca48db019d82c929cb8e59ba3cd09efc11992eac0a5e"},
-    {file = "psycopg_binary-3.1.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6359525bf22fa148ba56d34f16d9ddbfb932fbfe3ed02f734c32c4acf12397db"},
-    {file = "psycopg_binary-3.1.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4d2660cb4eb082216853a923118a27dfc968c7b0716854fd31a3bee9e0385be"},
-    {file = "psycopg_binary-3.1.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d361406c95b248f58820d16d4ad2ca2822e035f24b8f1c5c8383a92ea53e75e"},
-    {file = "psycopg_binary-3.1.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b96b98651fba3e2635799801ae388af81c3c4ac5111eca24eae09d19d48f33a3"},
-    {file = "psycopg_binary-3.1.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:27d0e92e76e3ce05fde831399b916274b2086f93cc1e417fd44889e73667083b"},
-    {file = "psycopg_binary-3.1.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:643680dff3bb3eba56bd82269a9d1af609287607375e921091dc790854ed4c53"},
-    {file = "psycopg_binary-3.1.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:9a716ee67f11922d3a6f6eee9c3f4e26853cf1fd2da0808c84f1eeb2b2783e69"},
-    {file = "psycopg_binary-3.1.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:61d51110153230c6c4cfd45facde82ffa5f3a83bf72564b06d761bd66a3e31d6"},
-    {file = "psycopg_binary-3.1.4-cp39-cp39-win_amd64.whl", hash = "sha256:86bf10ed317523a88f361a8d9543bf4459ed93d5af7acc7b534b5c37f1ea1929"},
+    {file = "psycopg_binary-3.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bab750b1e8c1572a11a46984e5afeb5412977342d3bd4734afd2768177c0ba79"},
+    {file = "psycopg_binary-3.1.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa8c98ee5f33d7a1467f2a318da5a73b0854c693b67418b96a9eac94ee6f08a7"},
+    {file = "psycopg_binary-3.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36f655c330554ec548939e9f99cf8102130cba94e548369c8525884b111f9f4b"},
+    {file = "psycopg_binary-3.1.5-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18a70a589db639d5057bca68eec3d96bb29b3ebe1411124c5f82518ccae0da40"},
+    {file = "psycopg_binary-3.1.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b086b52764b92894559dceb57b40ac27656032b71fb3fb99402c014ec787a36b"},
+    {file = "psycopg_binary-3.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c13ba6131267ea39e1d446ba44773a6b50469f75133e2ca5fd5504c77c46a099"},
+    {file = "psycopg_binary-3.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ce21e074158b56949f2ba1668fd142b7289d931700e0ce2d29a756ae76ac5f90"},
+    {file = "psycopg_binary-3.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:01e00794d2a59c4feee2591e31ebbd248c1c396390eabf6e15f5857f64a95ba2"},
+    {file = "psycopg_binary-3.1.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2a746d2f25fe5851b37a5186bdbc6af1478281ffada8d6f9298ad0ee699eb5f1"},
+    {file = "psycopg_binary-3.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fb38672dc06697b4350348b6728e8efc5cc3a3fb8ed0f157755941c9c0500940"},
+    {file = "psycopg_binary-3.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:dac412825b96e75bfad17af359a1c416a756bfd1ef413a51a56f6e3ed0d0c3d7"},
+    {file = "psycopg_binary-3.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4fc60f37c2dd051d2fc956695955cf83123a996cce43711243d2cf5c8b7813af"},
+    {file = "psycopg_binary-3.1.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:506ea6dc7e5cfee59af11a0a3f215f97a0899aa533bb6477915d0564b36eed16"},
+    {file = "psycopg_binary-3.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17a45bcdb5fbebc452196d3108f428a4b11f5dd6e7d221f65592b6cffa5a7d48"},
+    {file = "psycopg_binary-3.1.5-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:920596cfe0593b44909fe5816f87affd7ba6d49a8d8c844a00ecddd3d6a015c0"},
+    {file = "psycopg_binary-3.1.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:37b662ccba3b0093064a6dd9bbf66346f6109b002502e150cd725bfbb9ff0c87"},
+    {file = "psycopg_binary-3.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f915281d381b85fd1791e7637608a880ebc9395b7bdc1ae0baef72d6a68a6640"},
+    {file = "psycopg_binary-3.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:026c78ad5d84d28cb71178f955f70a7e951ff56e140bf65d4a8d5172c28dd36b"},
+    {file = "psycopg_binary-3.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6e919caa1970fb2894f53d18f67d2c92253b27257fa25feb8e9149f1b29aadf8"},
+    {file = "psycopg_binary-3.1.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:0c599a3bab66cea50a5dc6afe0eba7004ddffb0ea9e0f3ceca5175bdc1f41f90"},
+    {file = "psycopg_binary-3.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:708c708770d7b1de75abf4a0d9dd230b6db32dd3be45e981d4d0ab3d418902d2"},
+    {file = "psycopg_binary-3.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:094b85e69ab2309f7fa48f4d0ca3d8ddc3d5a39549faff21f1c8b892492fca2b"},
+    {file = "psycopg_binary-3.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:80dbc81c3b920067d39f38436287216b21e45f887b8851863c66dc904477283d"},
+    {file = "psycopg_binary-3.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92286ee28c1b8dea6eccb9c3232a2e520288b4fd31bc77b9ad2403b6e0df9056"},
+    {file = "psycopg_binary-3.1.5-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4742e81bdb84eeecfb457fcf3fb853159055cd26e0350b3ac0807315e4ad1f4"},
+    {file = "psycopg_binary-3.1.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7d5f6c7c7661a4dc878d2bb8361e6e7f47efdc0ebc05831050c76b193218973"},
+    {file = "psycopg_binary-3.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b8ec8851192d9f7cd01c562bd7663819b80d7caf661b86d15fe5c8b3359b18c"},
+    {file = "psycopg_binary-3.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:84650c8ae0fc3c10edb519d7bc259e93aaaf2605c6cd66d2f42503b23e97a02d"},
+    {file = "psycopg_binary-3.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f943e386546e86b72f1874185933205efff51ac51cf2b1afcdd8ccabbcac87d4"},
+    {file = "psycopg_binary-3.1.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:612540c1f109e5fcd77415f20737d1d2875ce1a71b98d485514008310f01788f"},
+    {file = "psycopg_binary-3.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:3ee9749c2f6441a4a6cf339d301d9f2fea0baa8d206f21991525dde6593e25da"},
+    {file = "psycopg_binary-3.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:975d68a4e48fadbc30b548a5088f606128ccd1d505aa7943c4304b4e45c311c3"},
+    {file = "psycopg_binary-3.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a00865c7e7d9c414bbabfeaa266c52c0d1344ed5b9e714e98024b84be1ee554d"},
+    {file = "psycopg_binary-3.1.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9bb2d2a4f5528911b4dffdba3a13413d8d052ccf77ca09dcdb9a9e59524ef73e"},
+    {file = "psycopg_binary-3.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34aade12f80c3a4670af40f06959a3b2335de2dcc0794fba0766d6543400dce1"},
+    {file = "psycopg_binary-3.1.5-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a85dfca07a86fdbdbfee1bc47223a558e4336f896c94dd180a6b47277cf3da2e"},
+    {file = "psycopg_binary-3.1.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2281bcfa463cd2e0eba9eabeb9c49d7901383c1d7c5d45930d17e31187bee6c"},
+    {file = "psycopg_binary-3.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb2fea876088ad1ef9b04b511296ce83db9c9182c15c43cb32db2a163337f2d5"},
+    {file = "psycopg_binary-3.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:419410af2d0622fa3795c4a0025ffcd869be07b76b3b049807bfd807ecf49835"},
+    {file = "psycopg_binary-3.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5ee9cc9051f626d7e0764c028ec69205dcc73a65550bc0f3ae18f32105296cc9"},
+    {file = "psycopg_binary-3.1.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:75093fdfe09443b8b1dea1a6481c4c205808a2d42f155f465f4b94e034d3d159"},
+    {file = "psycopg_binary-3.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:287256aa14d2748e86e50410cf307bcf4db8e6c0ba409d6a178c70cc6282b1b1"},
+    {file = "psycopg_binary-3.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:d59be9412ffe3ef6641bbef202344fd10058685f3e215a744289bbb8e73ae261"},
+    {file = "psycopg_binary-3.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:80c3fbe598e64a81a6867927adeb6d67f54250117c87b7e0bb2ede98456dda87"},
+    {file = "psycopg_binary-3.1.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:50c47cf75de5668632faf782a3027e1b72f8f14d10917999d5cb871d920b2b33"},
+    {file = "psycopg_binary-3.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767be15e78bf5eb95e64a39e2c3fd5d8784a303e8d0ad3acf2472988d0e993de"},
+    {file = "psycopg_binary-3.1.5-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91fe6133ad4f89ddd6aab339cb0217bf35f0052c4dd1335726ff3bd2aeb92f38"},
+    {file = "psycopg_binary-3.1.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dab38745da1d63279d3bb21d0d7739a4e66317705c947366af4c3b8627647d6c"},
+    {file = "psycopg_binary-3.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b7b4e4289adfead12915ea03525b4347535b3418cd1b3a90fb8d40c8e7c6e6b"},
+    {file = "psycopg_binary-3.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2dfe981293323160007af0c08516130b59de858f8cd385b06d0545090616d4c5"},
+    {file = "psycopg_binary-3.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:aca97c6b2eb21f631378aeac8f1cbf3559239583ec13664c5e59483cd3e55353"},
+    {file = "psycopg_binary-3.1.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:91ef9cdb26cf78c6c07a2c1b04e0d58fbcfec7616b0f36427c477b39d0942228"},
+    {file = "psycopg_binary-3.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6e47782463f19d93eac7e670ab11bca552cca8575dc7e89638fa58da4eecb625"},
+    {file = "psycopg_binary-3.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:51f338f4dbd6e18d176207f1d953db734e59028ae420dd46e3a91a8c82f948f9"},
 ]
 ptyprocess = [
     {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
@@ -4041,8 +4034,8 @@ tqdm = [
     {file = "tqdm-4.64.1.tar.gz", hash = "sha256:5f4f682a004951c1b450bc753c710e9280c5746ce6ffedee253ddbcbf54cf1e4"},
 ]
 traitlets = [
-    {file = "traitlets-5.6.0-py3-none-any.whl", hash = "sha256:1410755385d778aed847d68deb99b3ba30fbbf489e17a1e8cbb753060d5cce73"},
-    {file = "traitlets-5.6.0.tar.gz", hash = "sha256:10b6ed1c9cedee83e795db70a8b9c2db157bb3778ec4587a349ecb7ef3b1033b"},
+    {file = "traitlets-5.7.1-py3-none-any.whl", hash = "sha256:57ba2ba951632eeab9388fa45f342a5402060a5cc9f0bb942f760fafb6641581"},
+    {file = "traitlets-5.7.1.tar.gz", hash = "sha256:fde8f62c05204ead43c2c1b9389cfc85befa7f54acb5da28529d671175bb4108"},
 ]
 typing-extensions = [
     {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"},
diff --git a/pyproject.toml b/pyproject.toml
index 7134f59a7..9a2dd71b1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -74,7 +74,7 @@ profile = "black"
 
 [tool.pylint.format]
 max-line-length = "88"
-disable="W1203"
+disable="logging-fstring-interpolation"
 
 [build-system]
 requires = ["setuptools", "wheel", "poetry-core",]
diff --git a/winterdrp/__main__.py b/winterdrp/__main__.py
index 1a063adfc..c625d093b 100644
--- a/winterdrp/__main__.py
+++ b/winterdrp/__main__.py
@@ -11,7 +11,7 @@
 from astropy import units as u
 from astropy.time import Time
 
-from winterdrp.data import Dataset, ImageBatch
+from winterdrp.data import Dataset, ImageBatch, clean_cache
 from winterdrp.monitor.base_monitor import Monitor
 from winterdrp.paths import package_name, raw_img_sub_dir
 from winterdrp.pipelines import Pipeline, get_pipeline
@@ -85,93 +85,99 @@
 
     logger.info("Download complete")
 
-
 night = args.night
 if night is None:
     night = str(ln).split(" ", maxsplit=1)[0].replace("-", "")
 
-if args.monitor:
-
-    if args.emailrecipients is not None:
-        EMAIL_RECIPIENTS = args.emailrecipients.split(",")
-    else:
-        EMAIL_RECIPIENTS = None
-
-    CONFIG = args.config
-    if CONFIG is None:
-        CONFIG = "realtime"
-
-    monitor = Monitor(
-        pipeline=args.pipeline,
-        night=night,
-        realtime_configurations=CONFIG,
-        postprocess_configurations=args.postprocessconfig.split(",")
-        if args.postprocessconfig is not None
-        else None,
-        log_level=args.level,
-        final_postprocess_hours=args.finalpostprocesshours,
-        midway_postprocess_hours=args.midwaypostprocesshours,
-        email_sender=args.emailsender,
-        email_recipients=EMAIL_RECIPIENTS,
-        raw_dir=args.rawdir,
-    )
-    monitor.process_realtime()
-
-else:
-
-    # Set up logging
 
-    log = logging.getLogger("winterdrp")
+try:
+
+    if args.monitor:
+
+        if args.emailrecipients is not None:
+            EMAIL_RECIPIENTS = args.emailrecipients.split(",")
+        else:
+            EMAIL_RECIPIENTS = None
+
+        CONFIG = args.config
+        if CONFIG is None:
+            CONFIG = "realtime"
+
+        monitor = Monitor(
+            pipeline=args.pipeline,
+            night=night,
+            realtime_configurations=CONFIG,
+            postprocess_configurations=args.postprocessconfig.split(",")
+            if args.postprocessconfig is not None
+            else None,
+            log_level=args.level,
+            final_postprocess_hours=args.finalpostprocesshours,
+            midway_postprocess_hours=args.midwaypostprocesshours,
+            email_sender=args.emailsender,
+            email_recipients=EMAIL_RECIPIENTS,
+            raw_dir=args.rawdir,
+        )
+        monitor.process_realtime()
 
-    if args.logfile is None:
-        handler = logging.StreamHandler(sys.stdout)
     else:
-        handler = logging.FileHandler(args.logfile)
 
-    formatter = logging.Formatter(
-        "%(name)s [l %(lineno)d] - %(levelname)s - %(message)s"
-    )
-    handler.setFormatter(formatter)
-    log.addHandler(handler)
-    log.setLevel(args.level)
-
-    CONFIG = args.config
-    if CONFIG is None:
-        CONFIG = "default"
-
-    pipe = get_pipeline(
-        args.pipeline,
-        selected_configurations=CONFIG,
-        night=night,
-    )
+        # Set up logging
 
-    batches, errorstack = pipe.reduce_images(
-        Dataset([ImageBatch()]), catch_all_errors=True
-    )
-    if args.postprocessconfig is not None:
-        post_config = [
-            x for x in pipe.set_configuration(CONFIG) if isinstance(x, ImageLoader)
-        ][:1]
-        post_config += pipe.postprocess_configuration(
-            errorstack=errorstack,
-            selected_configurations=args.postprocessconfig.split(","),
-        )
+        log = logging.getLogger("winterdrp")
 
-        PROTECTED_KEY = "_new_postprocess"
+        if args.logfile is None:
+            handler = logging.StreamHandler(sys.stdout)
+        else:
+            handler = logging.FileHandler(args.logfile)
 
-        pipe.add_configuration(PROTECTED_KEY, post_config)
-        pipe.set_configuration(PROTECTED_KEY)
+        formatter = logging.Formatter(
+            "%(name)s [l %(lineno)d] - %(levelname)s - %(message)s"
+        )
+        handler.setFormatter(formatter)
+        log.addHandler(handler)
+        log.setLevel(args.level)
+
+        CONFIG = args.config
+        if CONFIG is None:
+            CONFIG = "default"
+
+        pipe = get_pipeline(
+            args.pipeline,
+            selected_configurations=CONFIG,
+            night=night,
+        )
 
-        _, new_errorstack = pipe.reduce_images(
-            Dataset([ImageBatch()]),
-            selected_configurations=PROTECTED_KEY,
-            catch_all_errors=True,
+        batches, errorstack = pipe.reduce_images(
+            Dataset([ImageBatch()]), catch_all_errors=True
+        )
+        if args.postprocessconfig is not None:
+            post_config = [
+                x for x in pipe.set_configuration(CONFIG) if isinstance(x, ImageLoader)
+            ][:1]
+            post_config += pipe.postprocess_configuration(
+                errorstack=errorstack,
+                selected_configurations=args.postprocessconfig.split(","),
+            )
+
+            PROTECTED_KEY = "_new_postprocess"
+
+            pipe.add_configuration(PROTECTED_KEY, post_config)
+            pipe.set_configuration(PROTECTED_KEY)
+
+            _, new_errorstack = pipe.reduce_images(
+                Dataset([ImageBatch()]),
+                selected_configurations=PROTECTED_KEY,
+                catch_all_errors=True,
+            )
+            errorstack += new_errorstack
+
+        print(errorstack.summarise_error_stack(verbose=False))
+        errorstack.summarise_error_stack(
+            output_path=pipe.get_error_output_path(), verbose=True
         )
-        errorstack += new_errorstack
 
-    print(errorstack.summarise_error_stack(verbose=False))
-    errorstack.summarise_error_stack(
-        output_path=pipe.get_error_output_path(), verbose=True
-    )
+        logger.info("End of winterdrp execution")
 
-    logger.info("End of winterdrp execution")
+finally:
+    # Delete everything added to the cache
+    clean_cache()
diff --git a/winterdrp/data/__init__.py b/winterdrp/data/__init__.py
index 608be7138..074cd7965 100644
--- a/winterdrp/data/__init__.py
+++ b/winterdrp/data/__init__.py
@@ -1,5 +1,5 @@
 """Module to specify the input data classes for :module:`wintedrp.processors`
 """
 from winterdrp.data.base_data import DataBatch, DataBlock, Dataset
-from winterdrp.data.image_data import Image, ImageBatch
+from winterdrp.data.image_data import Image, ImageBatch, clean_cache
 from winterdrp.data.source_data import SourceBatch, SourceTable
diff --git a/winterdrp/data/base_data.py b/winterdrp/data/base_data.py
index 09a0d9cd4..205880bae 100644
--- a/winterdrp/data/base_data.py
+++ b/winterdrp/data/base_data.py
@@ -166,8 +166,8 @@ def __iter__(self):
 class DataBatch(PseudoList):
     """
     Base class for a collection of individual
-    :class:`~winterdrp.data.image_data.DataBlock` objects.
-    Each :class:`~winterdrp.data.image_data.DataBatch` will be operated on
+    :class:`~winterdrp.data.base_data.DataBlock` objects.
+    Each :class:`~winterdrp.data.base_data.DataBatch` will be operated on
     by a :class:`~wintedrp.processors.BaseProcessor`
     """
 
@@ -179,10 +179,10 @@ def __init__(self, batch: list[DataBlock] | DataBlock = None):
         super().__init__(data_list=batch)
 
     def get_batch(self) -> list[DataBlock]:
-        """Returns the :class:`~winterdrp.data.image_data.DataBlock`
+        """Returns the :class:`~winterdrp.data.base_data.DataBlock`
         items within the batch
 
-        :return: list of :class:`~winterdrp.data.image_data.DataBlock` objects
+        :return: list of :class:`~winterdrp.data.base_data.DataBlock` objects
         """
         return self.get_data_list()
 
@@ -200,19 +200,28 @@ def get_raw_image_names(self) -> list[Path]:
 class Dataset(PseudoList):
     """
     Base class for a collection of individual
-    :class:`~winterdrp.data.image_data.DataBatch` objects.
+    :class:`~winterdrp.data.base_data.DataBatch` objects.
     A :class:`~wintedrp.processors.BaseProcessor` will iterate over these.
     """
 
     data_type = DataBatch
 
     def get_batches(self):
-        """Returns the :class:`~winterdrp.data.image_data.DataBatch`
+        """Returns the :class:`~winterdrp.data.base_data.DataBatch`
         items within the batch
 
-        :return: list of :class:`~winterdrp.data.image_data.DataBatch` objects
+        :return: list of :class:`~winterdrp.data.base_data.DataBatch` objects
         """
         return self.get_data_list()
 
     def __init__(self, batches: list[DataBatch] | DataBatch = None):
         super().__init__(data_list=batches)
+
+    def append(self, item: DataBatch):
+        """
+        Function to append, list-style, new objects.
+
+        :param item: Object to be added
+        :return: None
+        """
+        super()._append(item)
diff --git a/winterdrp/data/image_data.py b/winterdrp/data/image_data.py
index 46e68e811..db82a8765 100644
--- a/winterdrp/data/image_data.py
+++ b/winterdrp/data/image_data.py
@@ -1,45 +1,182 @@
 """
-
 Module to specify the input data classes for
 :class:`winterdrp.processors.base_processor.ImageHandler`
 
+The basic idea of the code is to pass
+:class:`~winterdrp.data.base_data.DataBlock` objects
+through a series of :class:`~wintedrp.processors.BaseProcessor` objects.
+Since a given image can easily be ~10-100Mb, and there may be several hundred raw images
+from a typical survey in a given night, the total data volume for these processors
+could be several 10s of Gb or more. Storing these all in RAM would be very
+inefficient/slow for a typical laptop or many larger processing machines.
+
+To mitigate this, the code can be operated in **cache mode**. In that case,
+after raw images are loaded, only the header data is stored in memory.
+The actual image data itself is stored temporarily in as a npy file
+in a dedicated cache directory, and only loaded into memory when needed.
+When the data is updated, the npy file is changed.
+The path of the file is a unique hash, and includes the read time of the file,
+so multiple copies of an image can be read and modified independently.
+
+In cache mode, all of the image data is temporarily stored in a cache,
+and this cache can therefore reach the size of 10s of Gb.
+The location of the cache is in the configurable
+**output data directory**. This would increase linearly with successive code executions.
+To mitigate that, and to avoid cleaning the cache by hand,
+the code tries to automatically delete cache files as needed.
+
+Python provides a default `__del__()` method for handling clean up when an object
+is deleted. Images automatically delete their cache in this method. However, has a
+somewhat-complicated method of 'garbage collection' (see
+`the official description <https://devguide.python.org/internals/garbage-collector>`_
+for more info), and it is not guaranteed that Image objects will
+clean themselves.
+
+As a fallback, we provide the helper function to delete all cache files created
+during a session. When you run the code from the command line (and therefore call
+__main__), we automatically run the cleanup before exiting,
+even if the code crashes/raises errors. This is also true for the unit tests,
+as provided by the  base test class. **If you try to interact with the code in
+any other way, please be mindful of this behaviour, and ensure that you clean your
+cache in a responsible way!**
+
+If you don't like this feature, you don't need to use it. Cache mode is entirely
+optional, and can be disabled by setting the environment variable to false.
+
+.. literalinclude:: ../../winterdrp/paths.py
+    :lines: 29
+
+You can change this via an environment variable.
+
+.. code-block:: bash
+
+    export USE_WINTER_CACHE = false
+
+See :doc:`usage` for more information about selecting cache mode,
+and setting the output data directory.
 """
+import hashlib
 import logging
+from pathlib import Path
 
 import numpy as np
 from astropy.io.fits import Header
+from astropy.time import Time
 
 from winterdrp.data.base_data import DataBatch, DataBlock
-from winterdrp.errors import ProcessorError
+from winterdrp.paths import CACHE_DIR, USE_CACHE
 
 logger = logging.getLogger(__name__)
 
 
-class MissingReferenceError(ProcessorError, KeyError):
-    pass
+class Image(DataBlock):
+    """
+    A subclass of :class:`~winterdrp.data.base_data.DataBlock`,
+    containing an image and header.
 
+    This class serves as input for
+    :class:`~winterdrp.processors.base_processor.BaseImageProcessor` and
+    :class:`~winterdrp.processors.base_processor.BaseCandidateGenerator` processors.
+    """
+
+    cache_files = []
 
-class Image(DataBlock):
     def __init__(self, data: np.ndarray, header: Header):
-        self.data = data
+        self._data = None
         self.header = header
         super().__init__()
+        self.cache_path = self.get_cache_path()
+        if USE_CACHE:
+            self.cache_files.append(self.cache_path)
+        self.set_data(data=data)
+
+    def get_cache_path(self) -> Path:
+        """
+        Get a unique cache path for the image (.npy file).
+        This is hash, using name and time, so should be unique even
+        when rerunning on the same image.
+
+        :return: unique cache file path
+        """
+        base = "".join([str(Time.now()), self.get_name()])
+        name = f"{hashlib.sha1(base.encode()).hexdigest()}.npy"
+        return CACHE_DIR.joinpath(name)
 
     def __str__(self):
-        return (
-            f"<An {self.__class__.__name__} object, " f"built from {self.get_name()}>"
-        )
+        return f"<An {self.__class__.__name__} object, built from {self.get_name()}>"
+
+    def set_data(self, data: np.ndarray):
+        """
+        Set the data with cache
+
+        :param data: Updated image data
+        :return: None
+        """
+        if USE_CACHE:
+            self.set_cache_data(data)
+        else:
+            self.set_ram_data(data)
+
+    def set_cache_data(self, data: np.ndarray):
+        """
+        Set the data with cache
+
+        :param data: Updated image data
+        :return: None
+        """
+        np.save(self.cache_path.as_posix(), data)
+
+    def set_ram_data(self, data: np.ndarray):
+        """
+        Set the data in RAM
+
+        :param data: Updated image data
+        :return: None
+        """
+        self._data = data
 
     def get_data(self) -> np.ndarray:
-        return self.data
+        """
+        Get the image data from cache
 
-    def set_data(self, data: np.ndarray):
-        self.data = data
+        :return: image data (numpy array)
+        """
+        if USE_CACHE:
+            return self.get_cache_data()
+
+        return self.get_ram_data()
+
+    def get_cache_data(self) -> np.ndarray:
+        """
+        Get the image data from cache
+
+        :return: image data (numpy array)
+        """
+        return np.load(self.cache_path.as_posix())
+
+    def get_ram_data(self) -> np.ndarray:
+        """
+        Get the image data from RAM
+
+        :return: image data (numpy array)
+        """
+        return self._data
 
     def get_header(self) -> Header:
+        """
+        Get the image header
+
+        :return: astropy Header
+        """
         return self.header
 
     def set_header(self, header: Header):
+        """
+        Update the header
+
+        :param header: updated header
+        :return: None
+        """
         self.header = header
 
     def __getitem__(self, item):
@@ -49,32 +186,23 @@ def __setitem__(self, key, value):
         self.header.__setitem__(key, value)
 
     def keys(self):
-        return self.header.keys()
+        """
+        Get the header keys
 
+        :return: Keys of header
+        """
+        return self.header.keys()
 
-# class DifferenceImage(Image):
-#
-#     def __init__(self, *args, **kwargs):
-#         super().__init__(*args, **kwargs)
-#
-#         if ref_img_key not in self.keys():
-#             err = f"No reference image data found in header. " \
-#                   f"Please add '{ref_img_key} = /path/to/ref' to image {self.get_name()}"
-#             logger.error(err)
-#             raise MissingReferenceError(err)
-#
-#         self.ref_img_path = Path(self[ref_img_key])
-#         self.ref_img_name = self.ref_img_path.name
-#
-#     def get_ref_name(self) -> str:
-#         return self.ref_img_name
-#
-#     def __str__(self):
-#         return f"<An {self.__class__.__name__} object, " \
-#                f"generated by subtracting {self.get_name()} from {self.get_ref_name()}>"
+    def __del__(self):
+        self.cache_path.unlink(missing_ok=True)
+        self.cache_files.remove(self.cache_path)
 
 
 class ImageBatch(DataBatch):
+    """
+    A subclass of :class:`~winterdrp.data.base_data.DataBatch`,
+    which contains :class:`~winterdrp.data.image_data.Image` objects
+    """
 
     data_type = Image
 
@@ -85,7 +213,25 @@ def append(self, item: Image):
         self._append(item)
 
     def __str__(self):
-        return f"<An {self.__class__.__name__} object, containing {[x.get_name() for x in self.get_batch()]}>"
+        return (
+            f"<An {self.__class__.__name__} object, "
+            f"containing {[x.get_name() for x in self.get_batch()]}>"
+        )
 
     def get_batch(self) -> list[Image]:
+        """Returns the :class:`~winterdrp.data.image_data.ImageBatch`
+        items within the batch
+
+        :return: list of :class:`~winterdrp.data.image_data.Image` objects
+        """
         return self.get_data_list()
+
+
+def clean_cache():
+    """Function to clear all created cache files
+
+    :return: None
+    """
+    for path in Image.cache_files:
+        path.unlink(missing_ok=True)
+    Image.cache_files = []
diff --git a/winterdrp/paths.py b/winterdrp/paths.py
index e922d2118..d9034c816 100644
--- a/winterdrp/paths.py
+++ b/winterdrp/paths.py
@@ -1,3 +1,6 @@
+"""
+Central module hosting all shared paths/directory conventions/keys/variables
+"""
 import logging
 import os
 import shutil
@@ -14,7 +17,7 @@
 
 toml_path = winter_code_dir.joinpath("pyproject.toml")
 
-with open(toml_path.as_posix(), "r") as f:
+with open(toml_path.as_posix(), "r", encoding="UTF-8") as f:
     toml_info = toml.loads(f.read())
 
 package_name = toml_info["tool"]["poetry"]["name"]
@@ -23,25 +26,28 @@
 doc_dir = winter_code_dir.joinpath("docs/")
 
 max_n_cpu = int(os.getenv("MAX_N_CPU", max(int(os.cpu_count() / 2), 1)))
+USE_CACHE = bool(os.getenv("USE_WINTER_CACHE", "true"))
 
 base_raw_dir = os.getenv("RAW_DATA_DIR")
 
 if base_raw_dir is None:
-    err = (
-        "No raw data directory specified. Run 'export RAW_DATA_DIR=/path/to/data' to set. "
+    warning = (
+        "No raw data directory specified. "
+        "Run 'export RAW_DATA_DIR=/path/to/data' to set. "
         "The raw data directory will need to be specified manually for path function."
     )
-    logger.warning(err)
+    logger.warning(warning)
 
 _base_output_dir = os.getenv("OUTPUT_DATA_DIR")
 
 if _base_output_dir is None:
     default = Path.home()
-    err = (
-        f"No output data directory specified. Run 'export OUTPUT_DATA_DIR=/path/to/data' to set this. "
+    warning = (
+        f"No output data directory specified. "
+        f"Run 'export OUTPUT_DATA_DIR=/path/to/data' to set this. "
         f"The output directory is being set to {default}."
     )
-    logger.warning(err)
+    logger.warning(warning)
     base_output_dir = default
 else:
     base_output_dir = Path(_base_output_dir)
@@ -52,6 +58,14 @@
 def raw_img_dir(
     sub_dir: str = "", raw_dir: str = base_raw_dir, img_sub_dir: str = raw_img_sub_dir
 ) -> str:
+    """
+    Get directory for raw images
+
+    :param sub_dir: sub-dir (night)
+    :param raw_dir: Root raw directory for data
+    :param img_sub_dir: Default 'raw'
+    :return: Full path of raw images
+    """
     return os.path.join(raw_dir, os.path.join(str(sub_dir), img_sub_dir))
 
 
@@ -76,6 +90,12 @@ def get_output_path(
     )
 
 
+CACHE_DIR = base_output_dir.joinpath(f"{package_name}_cache")
+
+if not CACHE_DIR.exists():
+    CACHE_DIR.mkdir(parents=True)
+
+
 def cal_output_dir(sub_dir: str | int = "", output_dir: str = base_output_dir) -> str:
     return get_output_dir("calibration", sub_dir=str(sub_dir), output_dir=output_dir)
 
@@ -113,7 +133,7 @@ def get_temp_path(output_dir: str, file_path: str) -> str:
 
 def get_untemp_path(temp_path: str) -> str:
     path = os.path.join(
-        os.path.dirname(temp_path), os.path.basename(temp_path).split("temp_")[1]
+        os.path.dirname(temp_path), os.path.basename(temp_path).replace("temp_", "")
     )
     return path
 
diff --git a/winterdrp/processors/zogy/zogy.py b/winterdrp/processors/zogy/zogy.py
index e1e7ccafc..9f847e0b5 100644
--- a/winterdrp/processors/zogy/zogy.py
+++ b/winterdrp/processors/zogy/zogy.py
@@ -1,3 +1,13 @@
+"""
+Script to run ZOGY.
+
+This is performed by the :class:`winterdrp.processors.zogy.zogy.ZOGY`
+processor. This processor requires several header keys to be present.
+
+In most cases, a processor chain will first require
+:class:`winterdrp.processors.zogy.zogy.ZOGYPrepare`
+in order to set these relevant header paths.
+"""
 import logging
 import os
 from collections.abc import Callable
@@ -9,6 +19,7 @@
 from astropy.coordinates import SkyCoord
 from astropy.io import fits
 from astropy.stats import sigma_clipped_stats
+from astropy.table import Table
 
 from winterdrp.data import Image, ImageBatch
 from winterdrp.errors import ProcessorError
@@ -30,10 +41,10 @@
 
 
 class ZOGYError(ProcessorError):
-    pass
+    """Error derived from running ZOGY"""
 
 
-def default_wirc_catalog_purifier(sci_catalog, ref_catalog):
+def default_wirc_catalog_purifier(sci_catalog: Table, ref_catalog: Table):
     good_sci_sources = (
         (sci_catalog["FLAGS"] == 0)
         & (sci_catalog["SNR_WIN"] > 5)
@@ -52,8 +63,9 @@ def default_wirc_catalog_purifier(sci_catalog, ref_catalog):
     return good_sci_sources, good_ref_sources
 
 
-def default_summer_catalog_purifier(sci_catalog, ref_catalog):
-    # Need to do this because the summer data is typically much shallower than the PS1 data, and only the brightest
+def default_summer_catalog_purifier(sci_catalog: Table, ref_catalog: Table):
+    # Need to do this because the summer data is typically much
+    # shallower than the PS1 data, and only the brightest
     # sources in PS1 xmatch to it.
     good_sci_sources = (
         (sci_catalog["FLAGS"] == 0)
@@ -73,10 +85,21 @@ def default_summer_catalog_purifier(sci_catalog, ref_catalog):
 
 
 class ZOGYPrepare(BaseImageProcessor):
+    """
+    Class to create a cross-matched source catalogue for ZOGY,
+    save the catalogue and then add relevant info to the header.
+
+    After processing by this class, an
+     :class:`~winterdrp.data.image_data.Image`
+     can then by processed by an
+    :class:`winterdrp.processors.zogy.zogy.ZOGY` object.
+    """
+
     base_key = "ZOGYPREP"
 
     def __init__(
         self,
+        *args,
         output_sub_dir: str = "sub",
         sci_zp_header_key: str = "ZP",
         catalog_purifier: Callable[
@@ -85,10 +108,9 @@ def __init__(
         ] = default_wirc_catalog_purifier,
         write_region_bool: bool = False,
         crossmatch_radius_arcsec: float = 1.0,
-        *args,
         **kwargs,
     ):
-        super(ZOGYPrepare, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.output_sub_dir = output_sub_dir
         self.sci_zp_header_key = sci_zp_header_key
         self.catalog_purifier = catalog_purifier
@@ -99,13 +121,20 @@ def get_sub_output_dir(self) -> Path:
         return Path(get_output_dir(self.output_sub_dir, self.night_sub_dir))
 
     def get_path(self, name: str | Path) -> Path:
+        """
+        Get output path for a file of name
+
+        :param name: Name of file
+        :return: Full output path
+        """
         return self.get_sub_output_dir().joinpath(name)
 
     def get_ast_fluxscale(
         self, ref_catalog_name: str | Path, sci_catalog_name: str | Path
     ) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
         """
-        Cross match science and reference data catalogs to get flux scaling factor and astometric uncertainties
+        Cross match science and reference data catalogs to get flux scaling factor and
+        astometric uncertainties
 
         :param ref_catalog_name: path of ref catalog
         :param sci_catalog_name: path of sci catalog
@@ -122,7 +151,8 @@ def get_ast_fluxscale(
             sci_catalog, ref_catalog
         )
         logger.info(
-            f"Number of good sources SCI: {np.sum(good_sci_sources)} REF: {np.sum(good_ref_sources)}"
+            f"Number of good sources SCI: {np.sum(good_sci_sources)} "
+            f"REF: {np.sum(good_ref_sources)}"
         )
 
         # Filter catalog for quality
@@ -158,14 +188,14 @@ def get_ast_fluxscale(
         )
 
         # Cross match the catalogs
-        idx_ref, idx_sci, d2d, d3d = sci_coords.search_around_sky(
+        idx_ref, idx_sci, d2d, _ = sci_coords.search_around_sky(
             ref_coords, self.crossmatch_radius_arcsec * u.arcsec
         )
 
         if len(d2d) == 0:
             err = (
-                "No stars matched between science and reference data catalogs. Likely there is a huge mismatch in "
-                "the sensitivites of the two."
+                "No stars matched between science and reference data catalogs. "
+                "Likely there is a huge mismatch in the sensitivites of the two."
             )
             logger.error(err)
             raise ZOGYError(err)
@@ -177,7 +207,7 @@ def get_ast_fluxscale(
         sci_flux_auto = sci_catalog["FLUX_AUTO"]
         ref_flux_auto = ref_catalog["FLUX_AUTO"]
 
-        logger.info("Number of cross-matched sources is %d" % (len(d2d)))
+        logger.info(f"Number of cross-matched sources is {len(d2d)}")
 
         ast_unc_x = np.std(xpos_sci[idx_sci] - xpos_ref[idx_ref])
         ast_unc_y = np.std(ypos_sci[idx_sci] - ypos_ref[idx_ref])
@@ -189,12 +219,18 @@ def get_ast_fluxscale(
         )
         flux_scale = flux_scale_median
         logger.info(
-            "Flux scaling for reference is %.5f +/- %.5f" % (flux_scale, flux_scale_std)
+            f"Flux scaling for reference is {flux_scale:.5f} +/- {flux_scale_std:.5f}"
         )
         return ast_unc_x, ast_unc_y, flux_scale
 
     @staticmethod
     def get_rms_image(image: Image, rms: float) -> Image:
+        """Get an RMS image from a regular image
+
+        :param image: An :class:`~winterdrp.data.image_data.Image`
+        :param rms: rms of the image
+        :return: An RMS :class:`~winterdrp.data.image_data.Image`
+        """
         gain = image["GAIN"]
         poisson_noise = np.copy(image.get_data()) / gain
         poisson_noise[poisson_noise < 0] = 0
@@ -204,16 +240,15 @@ def get_rms_image(image: Image, rms: float) -> Image:
         return rms_image
 
     def _apply_to_images(self, batch: ImageBatch) -> ImageBatch:
-        for ind, image in enumerate(batch):
+        for image in batch:
             ref_img_path = image[ref_img_key]
             sci_img_path = image[base_name_key]
 
             ref_img = self.open_fits(self.get_path(ref_img_path))
 
             ref_catalog_path = ref_img["SRCCAT"]  # convert to key
-            ref_mask_path = ref_img[
-                "MASKPATH"
-            ]  # Change to 'weightpath' everywhere, change in Swarp too, also fix (get_mask/write_mask)?
+            ref_mask_path = ref_img["MASKPATH"]  # Change to 'weightpath' everywhere,
+            # change in Swarp too, also fix (get_mask/write_mask)?
 
             sci_catalog_path = image["SRCCAT"]  # convert to key
             sci_mask_path = image["MASKPATH"]
@@ -224,11 +259,16 @@ def _apply_to_images(self, batch: ImageBatch) -> ImageBatch:
             image_mask = (sci_weight_data.get_data() == 0.0) | (
                 ref_weight_data.get_data() == 0.0
             )
-            image.data[image_mask] = 0.0
-            ref_img.data[image_mask] = 0.0
+
+            image_data = image.get_data()
+            image_data[image_mask] = 0.0
+            image.set_data(image_data)
+            ref_data = ref_img.get_data()
+            ref_data[image_mask] = 0.0
+            ref_img.set_data(ref_data)
 
             # Create S correlation ('scorr') weight image
-            scorr_weight_data = sci_weight_data.data * ref_weight_data.data
+            scorr_weight_data = sci_weight_data.get_data() * ref_weight_data.get_data()
             scorr_header = sci_weight_data.get_header()
             scorr_weight_img = Image(data=scorr_weight_data, header=scorr_header)
             scorr_weight_path = sci_img_path.replace(".fits", ".scorr.weight.fits")
@@ -239,7 +279,9 @@ def _apply_to_images(self, batch: ImageBatch) -> ImageBatch:
             )
 
             # Scale reference image
-            ref_img.data *= flux_scale
+            ref_data *= flux_scale
+            ref_img.set_data(ref_data)
+
             ref_unscaled_zp = ref_img["ZP"]
             ref_img["ZP"] = float(ref_img["ZP"]) + 2.5 * np.log10(flux_scale)
 
@@ -257,26 +299,15 @@ def _apply_to_images(self, batch: ImageBatch) -> ImageBatch:
             self.save_fits(image, path=sci_scaled_path)
 
             sci_rms = 0.5 * (
-                np.percentile(image.data[image.data != 0.0], 84.13)
-                - np.percentile(image.data[image.data != 0.0], 15.86)
+                np.percentile(image_data[image_data != 0.0], 84.13)
+                - np.percentile(image_data[image_data != 0.0], 15.86)
             )
             ref_rms = 0.5 * (
-                np.percentile(ref_img.data[ref_img.data != 0.0], 84.13)
-                - np.percentile(ref_img.data[ref_img.data != 0.0], 15.86)
+                np.percentile(ref_data[ref_data != 0.0], 84.13)
+                - np.percentile(ref_data[ref_data != 0.0], 15.86)
             )
             logger.info(f"Science RMS is {sci_rms:.2f}. Reference RMS is {ref_rms:.2f}")
 
-            # Why are these duplicated? Maybe rename and have one weight image
-            # sci_weight_path = self.get_path(image[latest_mask_save_key])
-            # ref_weight_path = self.get_path(image[latest_mask_save_key])
-            # sci_weight_data, _ = open_fits(sci_weight_path)
-            # ref_weight_data, _ = open_fits(ref_weight_path)
-            #
-            # # Redundant?
-            # image_mask = (sci_weight_data == 0) | (ref_weight_data == 0)
-            # image.data[image_mask] = 0
-            # ref_img.data[image_mask] = 0
-
             # Calculate uncertainty images
             sci_rms_image = self.get_rms_image(image, sci_rms)
             ref_rms_image = self.get_rms_image(ref_img, ref_rms)
@@ -307,16 +338,22 @@ def _apply_to_images(self, batch: ImageBatch) -> ImageBatch:
 
 
 class ZOGY(ZOGYPrepare):
+    """
+    :class:`winterdrp.processors.base_processor.BaseProcessor` class to run
+    the ZOGY algorithm using the
+    :func:winterdrp.processors.zogy.pyzogy.pyzogy` function.
+    """
+
     base_key = "ZOGY"
 
     def __init__(
         self,
+        *args,
         output_sub_dir: str = "sub",
         sci_zp_header_key: str = "ZP",
-        *args,
         **kwargs,
     ):
-        super(ZOGY, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.output_sub_dir = output_sub_dir
         self.sci_zp_header_key = sci_zp_header_key
 
@@ -325,7 +362,7 @@ def _apply_to_images(
         batch: ImageBatch,
     ) -> ImageBatch:
         diff_batch = ImageBatch()
-        for ind, image in enumerate(batch):
+        for image in batch:
             sci_image_path = self.get_path(image["SCISCL"])
             ref_image_path = self.get_path(image["REFSCL"])
             sci_rms = image["SCIRMS"]
@@ -337,7 +374,7 @@ def _apply_to_images(
             ast_unc_x = image["ASTUNCX"]
             ast_unc_y = image["ASTUNCY"]
 
-            temp_files = [sci_image_path, ref_image_path, sci_rms_path, ref_rms_path]
+            # temp_files = [sci_image_path, ref_image_path, sci_rms_path, ref_rms_path]
 
             diff_data, diff_psf_data, scorr_data = pyzogy(
                 new_image_path=sci_image_path,
@@ -372,9 +409,7 @@ def _apply_to_images(
             diff_rms_data = np.sqrt(
                 sci_rms_image.get_data() ** 2 + ref_rms_image.get_data() ** 2
             )
-            diff_rms_mean, diff_rms_median, diff_rms_std = sigma_clipped_stats(
-                diff_rms_data
-            )
+            _, diff_rms_median, _ = sigma_clipped_stats(diff_rms_data)
             diff_rms_path = diff_image_path.with_suffix(".unc")
 
             image["DIFFIMG"] = diff_image_path.as_posix()


Test patch:
diff --git a/tests/test_errors.py b/tests/test_errors.py
index dfe7bbac9..94f81c624 100644
--- a/tests/test_errors.py
+++ b/tests/test_errors.py
@@ -1,11 +1,12 @@
-"""Script for testing the error handling in ..module::winterdrp.errors
+"""
+Script for testing the error handling in ..module::winterdrp.errors
 """
 import logging
-import unittest
 
 from winterdrp.data import Dataset, ImageBatch
 from winterdrp.errors import ImageNotFoundError
 from winterdrp.pipelines import get_pipeline
+from winterdrp.testing import BaseTestCase
 
 logger = logging.getLogger(__name__)
 
@@ -21,7 +22,7 @@
 }
 
 
-class TestErrors(unittest.TestCase):
+class TestErrors(BaseTestCase):
 
     """Class for testing errors in ..module::winterdrp.errors"""
 
diff --git a/tests/test_monitor.py b/tests/test_monitor.py
index 24c38140d..93da9f19f 100644
--- a/tests/test_monitor.py
+++ b/tests/test_monitor.py
@@ -5,6 +5,7 @@
 from winterdrp.downloader.get_test_data import get_test_data_dir
 from winterdrp.monitor.base_monitor import Monitor
 from winterdrp.processors.utils.cal_hunter import CalRequirement
+from winterdrp.testing import BaseTestCase
 
 logger = logging.getLogger(__name__)
 
@@ -18,7 +19,7 @@
 ]
 
 
-class TestMonitor(unittest.TestCase):
+class TestMonitor(BaseTestCase):
     """Class for testing ..module::winterdrp.monitor"""
 
     def setUp(self):
diff --git a/tests/test_pipeline_visualisation.py b/tests/test_pipeline_visualisation.py
index c91a374e0..37134489e 100644
--- a/tests/test_pipeline_visualisation.py
+++ b/tests/test_pipeline_visualisation.py
@@ -1,12 +1,13 @@
 import logging
 import unittest
 
+from winterdrp.testing import BaseTestCase
 from winterdrp.utils.pipeline_visualisation import iterate_flowify
 
 logger = logging.getLogger(__name__)
 
 
-class TestErrors(unittest.TestCase):
+class TestErrors(BaseTestCase):
     def setUp(self):
         self.logger = logging.getLogger(__name__)
         self.logger.setLevel(logging.INFO)
diff --git a/tests/test_summer_imsub_pipeline.py b/tests/test_summer_imsub_pipeline.py
index 48828447d..6a8b65242 100644
--- a/tests/test_summer_imsub_pipeline.py
+++ b/tests/test_summer_imsub_pipeline.py
@@ -3,6 +3,7 @@
 
 from winterdrp.data import Dataset, ImageBatch
 from winterdrp.pipelines.summer.summer_pipeline import SummerPipeline
+from winterdrp.testing import BaseTestCase
 
 logger = logging.getLogger(__name__)
 
@@ -15,7 +16,7 @@
 pipeline = SummerPipeline(night="20220815", selected_configurations=["test_imsub"])
 
 
-class TestSummerPipeline(unittest.TestCase):
+class TestSummerImsubPipeline(BaseTestCase):
     def setUp(self):
         self.logger = logging.getLogger(__name__)
         self.logger.setLevel(logging.INFO)
diff --git a/tests/test_summer_pipeline.py b/tests/test_summer_pipeline.py
index e3f32598c..0ceeabf4b 100644
--- a/tests/test_summer_pipeline.py
+++ b/tests/test_summer_pipeline.py
@@ -3,6 +3,9 @@
 
 from winterdrp.data import Dataset, ImageBatch
 from winterdrp.pipelines import get_pipeline
+from winterdrp.testing import BaseTestCase
+
+# from winterdrp.data.image_data import clean_cache
 
 logger = logging.getLogger(__name__)
 
@@ -37,7 +40,7 @@
 )
 
 
-class TestSummerPipeline(unittest.TestCase):
+class TestSummerPipeline(BaseTestCase):
     def setUp(self):
         self.logger = logging.getLogger(__name__)
         self.logger.setLevel(logging.INFO)
diff --git a/tests/test_wirc_imsub_pipeline.py b/tests/test_wirc_imsub_pipeline.py
index 55576ee50..7840aeb3e 100644
--- a/tests/test_wirc_imsub_pipeline.py
+++ b/tests/test_wirc_imsub_pipeline.py
@@ -17,6 +17,7 @@
 from winterdrp.processors.reference import Reference
 from winterdrp.processors.utils.image_loader import ImageLoader
 from winterdrp.references import WIRCRef
+from winterdrp.testing import BaseTestCase
 
 logger = logging.getLogger(__name__)
 
@@ -64,7 +65,7 @@ def test_reference_image_generator(
 )
 
 
-class TestWircImsubPipeline(unittest.TestCase):
+class TestWircImsubPipeline(BaseTestCase):
     def setUp(self):
         self.logger = logging.getLogger(__name__)
         self.logger.setLevel(logging.INFO)
diff --git a/tests/test_wirc_pipeline.py b/tests/test_wirc_pipeline.py
index cb57be40c..1cd601855 100644
--- a/tests/test_wirc_pipeline.py
+++ b/tests/test_wirc_pipeline.py
@@ -31,6 +31,7 @@
     ImageDebatcher,
     ImageSelector,
 )
+from winterdrp.testing import BaseTestCase
 
 logger = logging.getLogger(__name__)
 
@@ -102,7 +103,7 @@ def get_cal_path(name: str) -> str:
 pipeline.add_configuration(configuration_name="test", configuration=test_configuration)
 
 
-class TestWircPipeline(unittest.TestCase):
+class TestWircPipeline(BaseTestCase):
     def setUp(self):
         self.logger = logging.getLogger(__name__)
         self.logger.setLevel(logging.INFO)
diff --git a/winterdrp/testing.py b/winterdrp/testing.py
new file mode 100644
index 000000000..e11626e7c
--- /dev/null
+++ b/winterdrp/testing.py
@@ -0,0 +1,14 @@
+"""
+Base class for unit testing, with common cleanup method
+"""
+import unittest
+
+from winterdrp.data.image_data import clean_cache
+
+
+class BaseTestCase(unittest.TestCase):
+    """Base TestCase object with additional cleanup"""
+
+    def __init__(self, *arg, **kwargs):
+        super().__init__(*arg, **kwargs)
+        self.addCleanup(clean_cache)