Repository: equinor/tagreader-python (language: python)

Issue:
Use diskcache as caching backend
In order to avoid dependencies and [NIH](https://en.wikipedia.org/wiki/Not_invented_here), we want to use diskcache as the caching backend instead of HDF5. This is a zero-dependency solution that uses SQLight as an ACID backend. This is much safer and easier, plus it has a lot of nice features such as expiry, caching strategies, statistics, etc.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 19d26286..0f78a42d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,6 +44,8 @@ htmlcov/
 .coverage
 .coverage.*
 .cache
+.cache*
+*cache*
 nosetests.xml
 coverage.xml
 *.cover
@@ -156,3 +158,6 @@ tests/test_adhoc.py
 
 # SNYK
 .dccache
+
+# Apple
+.DS_Store
diff --git a/README.md b/README.md
index 070e4d95..88996398 100644
--- a/README.md
+++ b/README.md
@@ -25,16 +25,12 @@ to cache results.
 Python >=3.8 with the following packages:
 
   + pandas >= 1.0.0
-  + tables (*)
   + requests
   + requests-kerberos
   + certifi >= 2023.5.7
+  + diskcache
   + pyodbc (**)
 
-*) If tables is not installed, caching of fetched data will be disabled.
-Tables will be installed alongside Tagreader unless the installation is
-known to fail, which is the case for Macs with ARM CPU.
-
 **) If using ODBC connections, you must also install proprietary drivers for
 PI ODBC and/or Aspen IP.21 SQLPlus. These drivers are only available for
 Microsoft Windows. Pyodbc will therefore not be installed for non-Windows
diff --git a/docs/manual.md b/docs/manual.md
index 062d4c5a..ff98f559 100644
--- a/docs/manual.md
+++ b/docs/manual.md
@@ -37,7 +37,7 @@ Tagreader is intended to be easy to use, and present the same interface to the u
 Python >= 3.8 with the following packages:
 
   + pandas >= 1.0.0
-  + pytables
+  + diskcache
   + requests
   + requests_kerberos
   + pyodbc (if using ODBC connections, Windows only)
diff --git a/poetry.lock b/poetry.lock
index c0e0018b..e4b0573c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -194,40 +194,6 @@ webencodings = "*"
 [package.extras]
 css = ["tinycss2 (>=1.1.0,<1.2)"]
 
-[[package]]
-name = "blosc2"
-version = "2.0.0"
-description = "Python wrapper for the C-Blosc2 library."
-category = "main"
-optional = false
-python-versions = ">=3.8, <4"
-files = [
-    {file = "blosc2-2.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4085e5c1df186e1747d8a8578b0cc1c8b7668391d635e9f89e17156912fba85a"},
-    {file = "blosc2-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cec4c0570543921ce6b8c9ffdbf9f2170de37ecaf8e2b213e867e3130b81f205"},
-    {file = "blosc2-2.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fbecd7ef4876811719aa58d84e4b414f430f329162c18578b870f5e77b59864"},
-    {file = "blosc2-2.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96dd63eb7641594208e6a865fd60a0bdde24568a180180beb8af4d6608796f3a"},
-    {file = "blosc2-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:659e18d5e606a0ca4d80766f86d87e640818051911d01679eed11022243a7e4f"},
-    {file = "blosc2-2.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c58990ab2bcd0f412496acf1d05c65d955d963197bbaa57b10b2ace31c29181a"},
-    {file = "blosc2-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f206e6a01a8167b441bf886ff022eb20e0f085b09300f49f3018f566c14d918a"},
-    {file = "blosc2-2.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ec63269428aa3fb45f7d4881b2d11b428c4cb62e854caf54a767a64da4df83e"},
-    {file = "blosc2-2.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98ab1cd57f9d7422f1636a6b290f2940113ee8be26bfe3823e8c011826972b9c"},
-    {file = "blosc2-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:54c5614b18f9f01473758fa64e3bc699adbe31b307a45eca0e07fa2204e4d4a1"},
-    {file = "blosc2-2.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f465b8ab54ecde6b8654672a50a4c3699aafd8e2de0dfcd84ed53f8a34c1734a"},
-    {file = "blosc2-2.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0497793f55db0b75de08eb4c047a0bc5b96dbe5e405b53803dd3368e36336188"},
-    {file = "blosc2-2.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5c52649837d514669107c77e8f172e9e5ecfa030eef0d378bb47ce1689921c9"},
-    {file = "blosc2-2.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8eb02f67d4ed8ac8f0ce5f3c8cafc0059255bb6899fd35127e4076925640f239"},
-    {file = "blosc2-2.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:32c365bf744353103ed91dc1f03889de03b986588181601594aa7ee773818cb4"},
-    {file = "blosc2-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d98e850f0de55e15c402c6e27105ba850f8954e784e30a7f8bde89eb70a08574"},
-    {file = "blosc2-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6545d6d7e7365a2a3533d4bdf7095856443aed7d5ddc577ecd0e78083790bff1"},
-    {file = "blosc2-2.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c193959a984e7814833c8be6b9026d7744d2cff4d450476561583a87152e13e"},
-    {file = "blosc2-2.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea3396de7757092d502fb297a44a8d019d92e750e5aebcd9d39a157fde8785b3"},
-    {file = "blosc2-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:ef018926b55799cf23345127dde8f29356b4451b3e067e1b07f0d186213bd821"},
-    {file = "blosc2-2.0.0.tar.gz", hash = "sha256:f19b0b3674f6c825b490f00d8264b0c540c2cdc11ec7e81178d38b83c57790a1"},
-]
-
-[package.dependencies]
-msgpack = "*"
-
 [[package]]
 name = "certifi"
 version = "2023.5.7"
@@ -575,57 +541,6 @@ files = [
     {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"},
 ]
 
-[[package]]
-name = "cython"
-version = "0.29.35"
-description = "The Cython compiler for writing C extensions for the Python language."
-category = "main"
-optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-files = [
-    {file = "Cython-0.29.35-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fb8c11cd3e2d5ab7c2da78c5698e527ecbe469437326811562a3fbf4c5780ae4"},
-    {file = "Cython-0.29.35-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9e54b4bee55fec952333126147b89c195ebe1d60e8e492ec778916ca5ca03151"},
-    {file = "Cython-0.29.35-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ba534e07543b44fb5ae37e56e61072ed1021b2d6ed643dbb92afa8239a04aa83"},
-    {file = "Cython-0.29.35-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c1d7a9ff809fa9b4a9fe04df86c9f7f574ca31c2ad896462a97ea89523db286a"},
-    {file = "Cython-0.29.35-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:247d585d8e49f002e522f3420751a4b3da0cf8532ef64d382e0bc9b4c840642c"},
-    {file = "Cython-0.29.35-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ef2fc6f81aa8fb512535b01199fbe0d0ecafb8a29f261055e4b3f103c7bd6c75"},
-    {file = "Cython-0.29.35-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:be7e1f98a359408186025f84d28d243e4527acb976f06b8ae8441dc5db204280"},
-    {file = "Cython-0.29.35-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e1e5d62f15ea4fa4a8bc76e4fcc2ea313a8afe70488b7b870716bcfb12b8246"},
-    {file = "Cython-0.29.35-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:75541567a2de1f893d247a7f9aa300dff5662fb33822a5fb75bc9621369b8ef0"},
-    {file = "Cython-0.29.35-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:99477c1d4a105a562c05d43cc01905b6711f0a6a558d90f20c7aee0fb23d59d5"},
-    {file = "Cython-0.29.35-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:c44bb47b314abc743705c7d038d351ffc3a34b95ab59b04b8cb27cf781b44ae8"},
-    {file = "Cython-0.29.35-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:94859c3fd90767995b33d803edecad21e73749823db468d34f21e80451a11a99"},
-    {file = "Cython-0.29.35-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5a47974f3ebccf25702ffdd569904f7807ea1ef0830987c133877fabefdc4bab"},
-    {file = "Cython-0.29.35-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:520c50d1875627c111900d7184fd658e32967a3ef807dc2fbc252e384839cbcf"},
-    {file = "Cython-0.29.35-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:516abc754f15b84d6a8e71c8abd90e10346ea86001563480f0be1b349d09c6b8"},
-    {file = "Cython-0.29.35-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:c38e2c1e94b596132454b29757536d5afa810011d8bcb86918cc6693d2302940"},
-    {file = "Cython-0.29.35-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:511f3adfb2db4db2eb882f892525db18a3a21803830474d2fa8b7a1a0f406985"},
-    {file = "Cython-0.29.35-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:445e092708c26b357c97b3c68ea3eab31846fc9c1360bb150225f340c20322ec"},
-    {file = "Cython-0.29.35-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3da42ef5b71674e4864b6afbe1bcacba75807684e22b6337f753cf297ae4e2d2"},
-    {file = "Cython-0.29.35-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:db695a19968a54b9ac53048c723234b4f0db7409def0a5c5517237202e7a9b92"},
-    {file = "Cython-0.29.35-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:156ae92bedcd8261b5259724e2dc4d8eb12ac29159359e34c8358b65d24430ac"},
-    {file = "Cython-0.29.35-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:ea1c166336188630cd3e48aea4bbe06ea1bab444624e31c78973fffcae1cf708"},
-    {file = "Cython-0.29.35-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e7b1901b03c37a082ba405e2cf73a57091e835c7af35f664f9dd1d855a992ad5"},
-    {file = "Cython-0.29.35-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:27f58d0dd53a8ffb614814c725d3ee3f136e53178611f7f769ff358f69e50502"},
-    {file = "Cython-0.29.35-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c19e2ba027d2e9e2d88a08aa6007344be781ed99bc0924deb237ec52ca14c09"},
-    {file = "Cython-0.29.35-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b63ea04db03190dc8b25d167598989be5c1fe9fc3121d7802c0aafc8a4ec383f"},
-    {file = "Cython-0.29.35-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:5cdd65f7d85e15f1662c75d85d837c20d5c68acdd1029bfd08fb44c4422d7d9b"},
-    {file = "Cython-0.29.35-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:c17c876db737e1183d18d23db9cc31a9f565c113a32523c672af72f6497e382f"},
-    {file = "Cython-0.29.35-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:2a2f2fb9b1c0a4a3890713127fba55a38d2cf1619b2570c43c92a93fee80111a"},
-    {file = "Cython-0.29.35-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:a1ad51612ff6cfe05cd58f584f01373d64906bb0c860a067c6441359ff10464f"},
-    {file = "Cython-0.29.35-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3cd717eee52072be8244bb07f0e4126f893214d2dfd1ba8b38b533e1ffec4f8a"},
-    {file = "Cython-0.29.35-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:acab11c834cbe8fb7b71f9f7b4c4655afd82ffadb1be93d5354a67702fcee69d"},
-    {file = "Cython-0.29.35-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8841158f274896702afe732571d37be22868a301275f952f6280547b25280538"},
-    {file = "Cython-0.29.35-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:0a9334d137bd42fca34b6b413063e19c194ba760846f34804ea1fb477cbe9a88"},
-    {file = "Cython-0.29.35-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c4cd7de707938b8385cd1f88e1446228fbfe09af7822fa13877a4374c4881198"},
-    {file = "Cython-0.29.35-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:05b7ede0b0eb1c6b9bd748fa67c5ebf3c3560d04d7c8a1486183ddd099de5a00"},
-    {file = "Cython-0.29.35-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:537bc1e0ed9bf7289c80f39a9a9359f5649068647631996313f77ba57afde40b"},
-    {file = "Cython-0.29.35-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:402307ad6fd209816cf539680035ef79cce171288cb98f81f3f11ea8ef3afd99"},
-    {file = "Cython-0.29.35-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:563a02ea675ed6321d6257df067c89f17b89a63487ef8b9ce0d598e88e7ff0bd"},
-    {file = "Cython-0.29.35-py2.py3-none-any.whl", hash = "sha256:417703dc67c447089258ab4b3d217f9c03894574e4a0d6c50648a208bc8352bb"},
-    {file = "Cython-0.29.35.tar.gz", hash = "sha256:6e381fa0bf08b3c26ec2f616b19ae852c06f5750f4290118bf986b6f85c8c527"},
-]
-
 [[package]]
 name = "debugpy"
 version = "1.6.7"
@@ -678,6 +593,18 @@ files = [
     {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
 ]
 
+[[package]]
+name = "diskcache"
+version = "5.6.1"
+description = "Disk Cache -- Disk and file backed persistent cache."
+category = "main"
+optional = false
+python-versions = ">=3"
+files = [
+    {file = "diskcache-5.6.1-py3-none-any.whl", hash = "sha256:558c6a2d5d7c721bb00e40711803d6804850c9f76c426ed81ecc627fe9d2ce2d"},
+    {file = "diskcache-5.6.1.tar.gz", hash = "sha256:e4c978532feff5814c4cc00fe1e11e40501985946643d73220d41ee7737c72c3"},
+]
+
 [[package]]
 name = "distlib"
 version = "0.3.6"
@@ -737,30 +664,62 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc
 
 [[package]]
 name = "filelock"
-version = "3.12.0"
+version = "3.12.2"
 description = "A platform independent file lock."
 category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"},
-    {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"},
+    {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"},
+    {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"},
 ]
 
 [package.extras]
-docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
-testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"]
+docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
+testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"]
 
 [[package]]
 name = "fonttools"
-version = "4.39.4"
+version = "4.40.0"
 description = "Tools to manipulate font files"
 category = "main"
 optional = true
 python-versions = ">=3.8"
 files = [
-    {file = "fonttools-4.39.4-py3-none-any.whl", hash = "sha256:106caf6167c4597556b31a8d9175a3fdc0356fdcd70ab19973c3b0d4c893c461"},
-    {file = "fonttools-4.39.4.zip", hash = "sha256:dba8d7cdb8e2bac1b3da28c5ed5960de09e59a2fe7e63bb73f5a59e57b0430d2"},
+    {file = "fonttools-4.40.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b802dcbf9bcff74672f292b2466f6589ab8736ce4dcf36f48eb994c2847c4b30"},
+    {file = "fonttools-4.40.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7f6e3fa3da923063c286320e728ba2270e49c73386e3a711aa680f4b0747d692"},
+    {file = "fonttools-4.40.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdf60f8a5c6bcce7d024a33f7e4bc7921f5b74e8ea13bccd204f2c8b86f3470"},
+    {file = "fonttools-4.40.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91784e21a1a085fac07c6a407564f4a77feb471b5954c9ee55a4f9165151f6c1"},
+    {file = "fonttools-4.40.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:05171f3c546f64d78569f10adc0de72561882352cac39ec7439af12304d8d8c0"},
+    {file = "fonttools-4.40.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7449e5e306f3a930a8944c85d0cbc8429cba13503372a1a40f23124d6fb09b58"},
+    {file = "fonttools-4.40.0-cp310-cp310-win32.whl", hash = "sha256:bae8c13abbc2511e9a855d2142c0ab01178dd66b1a665798f357da0d06253e0d"},
+    {file = "fonttools-4.40.0-cp310-cp310-win_amd64.whl", hash = "sha256:425b74a608427499b0e45e433c34ddc350820b6f25b7c8761963a08145157a66"},
+    {file = "fonttools-4.40.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:00ab569b2a3e591e00425023ade87e8fef90380c1dde61be7691cb524ca5f743"},
+    {file = "fonttools-4.40.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:18ea64ac43e94c9e0c23d7a9475f1026be0e25b10dda8f236fc956188761df97"},
+    {file = "fonttools-4.40.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:022c4a16b412293e7f1ce21b8bab7a6f9d12c4ffdf171fdc67122baddb973069"},
+    {file = "fonttools-4.40.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530c5d35109f3e0cea2535742d6a3bc99c0786cf0cbd7bb2dc9212387f0d908c"},
+    {file = "fonttools-4.40.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5e00334c66f4e83535384cb5339526d01d02d77f142c23b2f97bd6a4f585497a"},
+    {file = "fonttools-4.40.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb52c10fda31159c22c7ed85074e05f8b97da8773ea461706c273e31bcbea836"},
+    {file = "fonttools-4.40.0-cp311-cp311-win32.whl", hash = "sha256:6a8d71b9a5c884c72741868e845c0e563c5d83dcaf10bb0ceeec3b4b2eb14c67"},
+    {file = "fonttools-4.40.0-cp311-cp311-win_amd64.whl", hash = "sha256:15abb3d055c1b2dff9ce376b6c3db10777cb74b37b52b78f61657634fd348a0d"},
+    {file = "fonttools-4.40.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:14037c31138fbd21847ad5e5441dfdde003e0a8f3feb5812a1a21fd1c255ffbd"},
+    {file = "fonttools-4.40.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:94c915f6716589f78bc00fbc14c5b8de65cfd11ee335d32504f1ef234524cb24"},
+    {file = "fonttools-4.40.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37467cee0f32cada2ec08bc16c9c31f9b53ea54b2f5604bf25a1246b5f50593a"},
+    {file = "fonttools-4.40.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56d4d85f5374b45b08d2f928517d1e313ea71b4847240398decd0ab3ebbca885"},
+    {file = "fonttools-4.40.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8c4305b171b61040b1ee75d18f9baafe58bd3b798d1670078efe2c92436bfb63"},
+    {file = "fonttools-4.40.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a954b90d1473c85a22ecf305761d9fd89da93bbd31dae86e7dea436ad2cb5dc9"},
+    {file = "fonttools-4.40.0-cp38-cp38-win32.whl", hash = "sha256:1bc4c5b147be8dbc5df9cc8ac5e93ee914ad030fe2a201cc8f02f499db71011d"},
+    {file = "fonttools-4.40.0-cp38-cp38-win_amd64.whl", hash = "sha256:8a917828dbfdb1cbe50cf40eeae6fbf9c41aef9e535649ed8f4982b2ef65c091"},
+    {file = "fonttools-4.40.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:882983279bf39afe4e945109772c2ffad2be2c90983d6559af8b75c19845a80a"},
+    {file = "fonttools-4.40.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c55f1b4109dbc3aeb496677b3e636d55ef46dc078c2a5e3f3db4e90f1c6d2907"},
+    {file = "fonttools-4.40.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec468c022d09f1817c691cf884feb1030ef6f1e93e3ea6831b0d8144c06480d1"},
+    {file = "fonttools-4.40.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d5adf4ba114f028fc3f5317a221fd8b0f4ef7a2e5524a2b1e0fd891b093791a"},
+    {file = "fonttools-4.40.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aa83b3f151bc63970f39b2b42a06097c5a22fd7ed9f7ba008e618de4503d3895"},
+    {file = "fonttools-4.40.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:97d95b8301b62bdece1af943b88bcb3680fd385f88346a4a899ee145913b414a"},
+    {file = "fonttools-4.40.0-cp39-cp39-win32.whl", hash = "sha256:1a003608400dd1cca3e089e8c94973c6b51a4fb1ef00ff6d7641617b9242e637"},
+    {file = "fonttools-4.40.0-cp39-cp39-win_amd64.whl", hash = "sha256:7961575221e3da0841c75da53833272c520000d76f7f71274dbf43370f8a1065"},
+    {file = "fonttools-4.40.0-py3-none-any.whl", hash = "sha256:200729d12461e2038700d31f0d49ad5a7b55855dec7525074979a06b46f88505"},
+    {file = "fonttools-4.40.0.tar.gz", hash = "sha256:337b6e83d7ee73c40ea62407f2ce03b07c3459e213b6f332b94a69923b9e1cb9"},
 ]
 
 [package.extras]
@@ -902,14 +861,14 @@ files = [
 
 [[package]]
 name = "ipykernel"
-version = "6.23.1"
+version = "6.23.2"
 description = "IPython Kernel for Jupyter"
 category = "main"
 optional = true
 python-versions = ">=3.8"
 files = [
-    {file = "ipykernel-6.23.1-py3-none-any.whl", hash = "sha256:77aeffab056c21d16f1edccdc9e5ccbf7d96eb401bd6703610a21be8b068aadc"},
-    {file = "ipykernel-6.23.1.tar.gz", hash = "sha256:1aba0ae8453e15e9bc6b24e497ef6840114afcdb832ae597f32137fa19d42a6f"},
+    {file = "ipykernel-6.23.2-py3-none-any.whl", hash = "sha256:7ccb6e2d32fd958c21453db494c914f3474908a2fdefd99ab548a5375b548d1f"},
+    {file = "ipykernel-6.23.2.tar.gz", hash = "sha256:fcfb67c5b504aa1bfcda1c5b3716636239e0f7b9290958f1c558c79b4c0e7ed5"},
 ]
 
 [package.dependencies]
@@ -1548,79 +1507,6 @@ files = [
     {file = "mistune-2.0.5.tar.gz", hash = "sha256:0246113cb2492db875c6be56974a7c893333bf26cd92891c85f63151cee09d34"},
 ]
 
-[[package]]
-name = "msgpack"
-version = "1.0.5"
-description = "MessagePack serializer"
-category = "main"
-optional = false
-python-versions = "*"
-files = [
-    {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:525228efd79bb831cf6830a732e2e80bc1b05436b086d4264814b4b2955b2fa9"},
-    {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f8d8b3bf1ff2672567d6b5c725a1b347fe838b912772aa8ae2bf70338d5a198"},
-    {file = "msgpack-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdc793c50be3f01106245a61b739328f7dccc2c648b501e237f0699fe1395b81"},
-    {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cb47c21a8a65b165ce29f2bec852790cbc04936f502966768e4aae9fa763cb7"},
-    {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42b9594cc3bf4d838d67d6ed62b9e59e201862a25e9a157019e171fbe672dd3"},
-    {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55b56a24893105dc52c1253649b60f475f36b3aa0fc66115bffafb624d7cb30b"},
-    {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1967f6129fc50a43bfe0951c35acbb729be89a55d849fab7686004da85103f1c"},
-    {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20a97bf595a232c3ee6d57ddaadd5453d174a52594bf9c21d10407e2a2d9b3bd"},
-    {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d25dd59bbbbb996eacf7be6b4ad082ed7eacc4e8f3d2df1ba43822da9bfa122a"},
-    {file = "msgpack-1.0.5-cp310-cp310-win32.whl", hash = "sha256:382b2c77589331f2cb80b67cc058c00f225e19827dbc818d700f61513ab47bea"},
-    {file = "msgpack-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:4867aa2df9e2a5fa5f76d7d5565d25ec76e84c106b55509e78c1ede0f152659a"},
-    {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f5ae84c5c8a857ec44dc180a8b0cc08238e021f57abdf51a8182e915e6299f0"},
-    {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e6ca5d5699bcd89ae605c150aee83b5321f2115695e741b99618f4856c50898"},
-    {file = "msgpack-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5494ea30d517a3576749cad32fa27f7585c65f5f38309c88c6d137877fa28a5a"},
-    {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab2f3331cb1b54165976a9d976cb251a83183631c88076613c6c780f0d6e45a"},
-    {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28592e20bbb1620848256ebc105fc420436af59515793ed27d5c77a217477705"},
-    {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe5c63197c55bce6385d9aee16c4d0641684628f63ace85f73571e65ad1c1e8d"},
-    {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed40e926fa2f297e8a653c954b732f125ef97bdd4c889f243182299de27e2aa9"},
-    {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b2de4c1c0538dcb7010902a2b97f4e00fc4ddf2c8cda9749af0e594d3b7fa3d7"},
-    {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf22a83f973b50f9d38e55c6aade04c41ddda19b00c4ebc558930d78eecc64ed"},
-    {file = "msgpack-1.0.5-cp311-cp311-win32.whl", hash = "sha256:c396e2cc213d12ce017b686e0f53497f94f8ba2b24799c25d913d46c08ec422c"},
-    {file = "msgpack-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c4c68d87497f66f96d50142a2b73b97972130d93677ce930718f68828b382e2"},
-    {file = "msgpack-1.0.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a2b031c2e9b9af485d5e3c4520f4220d74f4d222a5b8dc8c1a3ab9448ca79c57"},
-    {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f837b93669ce4336e24d08286c38761132bc7ab29782727f8557e1eb21b2080"},
-    {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1d46dfe3832660f53b13b925d4e0fa1432b00f5f7210eb3ad3bb9a13c6204a6"},
-    {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:366c9a7b9057e1547f4ad51d8facad8b406bab69c7d72c0eb6f529cf76d4b85f"},
-    {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4c075728a1095efd0634a7dccb06204919a2f67d1893b6aa8e00497258bf926c"},
-    {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:f933bbda5a3ee63b8834179096923b094b76f0c7a73c1cfe8f07ad608c58844b"},
-    {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:36961b0568c36027c76e2ae3ca1132e35123dcec0706c4b7992683cc26c1320c"},
-    {file = "msgpack-1.0.5-cp36-cp36m-win32.whl", hash = "sha256:b5ef2f015b95f912c2fcab19c36814963b5463f1fb9049846994b007962743e9"},
-    {file = "msgpack-1.0.5-cp36-cp36m-win_amd64.whl", hash = "sha256:288e32b47e67f7b171f86b030e527e302c91bd3f40fd9033483f2cacc37f327a"},
-    {file = "msgpack-1.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:137850656634abddfb88236008339fdaba3178f4751b28f270d2ebe77a563b6c"},
-    {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c05a4a96585525916b109bb85f8cb6511db1c6f5b9d9cbcbc940dc6b4be944b"},
-    {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a62ec00b636583e5cb6ad313bbed36bb7ead5fa3a3e38938503142c72cba4f"},
-    {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef8108f8dedf204bb7b42994abf93882da1159728a2d4c5e82012edd92c9da9f"},
-    {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1835c84d65f46900920b3708f5ba829fb19b1096c1800ad60bae8418652a951d"},
-    {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e57916ef1bd0fee4f21c4600e9d1da352d8816b52a599c46460e93a6e9f17086"},
-    {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:17358523b85973e5f242ad74aa4712b7ee560715562554aa2134d96e7aa4cbbf"},
-    {file = "msgpack-1.0.5-cp37-cp37m-win32.whl", hash = "sha256:cb5aaa8c17760909ec6cb15e744c3ebc2ca8918e727216e79607b7bbce9c8f77"},
-    {file = "msgpack-1.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:ab31e908d8424d55601ad7075e471b7d0140d4d3dd3272daf39c5c19d936bd82"},
-    {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b72d0698f86e8d9ddf9442bdedec15b71df3598199ba33322d9711a19f08145c"},
-    {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:379026812e49258016dd84ad79ac8446922234d498058ae1d415f04b522d5b2d"},
-    {file = "msgpack-1.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:332360ff25469c346a1c5e47cbe2a725517919892eda5cfaffe6046656f0b7bb"},
-    {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:476a8fe8fae289fdf273d6d2a6cb6e35b5a58541693e8f9f019bfe990a51e4ba"},
-    {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9985b214f33311df47e274eb788a5893a761d025e2b92c723ba4c63936b69b1"},
-    {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48296af57cdb1d885843afd73c4656be5c76c0c6328db3440c9601a98f303d87"},
-    {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:addab7e2e1fcc04bd08e4eb631c2a90960c340e40dfc4a5e24d2ff0d5a3b3edb"},
-    {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:916723458c25dfb77ff07f4c66aed34e47503b2eb3188b3adbec8d8aa6e00f48"},
-    {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:821c7e677cc6acf0fd3f7ac664c98803827ae6de594a9f99563e48c5a2f27eb0"},
-    {file = "msgpack-1.0.5-cp38-cp38-win32.whl", hash = "sha256:1c0f7c47f0087ffda62961d425e4407961a7ffd2aa004c81b9c07d9269512f6e"},
-    {file = "msgpack-1.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:bae7de2026cbfe3782c8b78b0db9cbfc5455e079f1937cb0ab8d133496ac55e1"},
-    {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:20c784e66b613c7f16f632e7b5e8a1651aa5702463d61394671ba07b2fc9e025"},
-    {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:266fa4202c0eb94d26822d9bfd7af25d1e2c088927fe8de9033d929dd5ba24c5"},
-    {file = "msgpack-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18334484eafc2b1aa47a6d42427da7fa8f2ab3d60b674120bce7a895a0a85bdd"},
-    {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57e1f3528bd95cc44684beda696f74d3aaa8a5e58c816214b9046512240ef437"},
-    {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586d0d636f9a628ddc6a17bfd45aa5b5efaf1606d2b60fa5d87b8986326e933f"},
-    {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a740fa0e4087a734455f0fc3abf5e746004c9da72fbd541e9b113013c8dc3282"},
-    {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3055b0455e45810820db1f29d900bf39466df96ddca11dfa6d074fa47054376d"},
-    {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a61215eac016f391129a013c9e46f3ab308db5f5ec9f25811e811f96962599a8"},
-    {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:362d9655cd369b08fda06b6657a303eb7172d5279997abe094512e919cf74b11"},
-    {file = "msgpack-1.0.5-cp39-cp39-win32.whl", hash = "sha256:ac9dd47af78cae935901a9a500104e2dea2e253207c924cc95de149606dc43cc"},
-    {file = "msgpack-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:06f5174b5f8ed0ed919da0e62cbd4ffde676a374aba4020034da05fab67b9164"},
-    {file = "msgpack-1.0.5.tar.gz", hash = "sha256:c075544284eadc5cddc70f4757331d99dcbc16b2bbd4849d15f8aae4cf36d31c"},
-]
-
 [[package]]
 name = "nbclassic"
 version = "1.0.0"
@@ -1821,49 +1707,6 @@ jupyter-server = ">=1.8,<3"
 [package.extras]
 test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync"]
 
-[[package]]
-name = "numexpr"
-version = "2.8.4"
-description = "Fast numerical expression evaluator for NumPy"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-files = [
-    {file = "numexpr-2.8.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a75967d46b6bd56455dd32da6285e5ffabe155d0ee61eef685bbfb8dafb2e484"},
-    {file = "numexpr-2.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db93cf1842f068247de631bfc8af20118bf1f9447cd929b531595a5e0efc9346"},
-    {file = "numexpr-2.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bca95f4473b444428061d4cda8e59ac564dc7dc6a1dea3015af9805c6bc2946"},
-    {file = "numexpr-2.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e34931089a6bafc77aaae21f37ad6594b98aa1085bb8b45d5b3cd038c3c17d9"},
-    {file = "numexpr-2.8.4-cp310-cp310-win32.whl", hash = "sha256:f3a920bfac2645017110b87ddbe364c9c7a742870a4d2f6120b8786c25dc6db3"},
-    {file = "numexpr-2.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:6931b1e9d4f629f43c14b21d44f3f77997298bea43790cfcdb4dd98804f90783"},
-    {file = "numexpr-2.8.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9400781553541f414f82eac056f2b4c965373650df9694286b9bd7e8d413f8d8"},
-    {file = "numexpr-2.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6ee9db7598dd4001138b482342b96d78110dd77cefc051ec75af3295604dde6a"},
-    {file = "numexpr-2.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff5835e8af9a212e8480003d731aad1727aaea909926fd009e8ae6a1cba7f141"},
-    {file = "numexpr-2.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:655d84eb09adfee3c09ecf4a89a512225da153fdb7de13c447404b7d0523a9a7"},
-    {file = "numexpr-2.8.4-cp311-cp311-win32.whl", hash = "sha256:5538b30199bfc68886d2be18fcef3abd11d9271767a7a69ff3688defe782800a"},
-    {file = "numexpr-2.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:3f039321d1c17962c33079987b675fb251b273dbec0f51aac0934e932446ccc3"},
-    {file = "numexpr-2.8.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c867cc36cf815a3ec9122029874e00d8fbcef65035c4a5901e9b120dd5d626a2"},
-    {file = "numexpr-2.8.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:059546e8f6283ccdb47c683101a890844f667fa6d56258d48ae2ecf1b3875957"},
-    {file = "numexpr-2.8.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:845a6aa0ed3e2a53239b89c1ebfa8cf052d3cc6e053c72805e8153300078c0b1"},
-    {file = "numexpr-2.8.4-cp37-cp37m-win32.whl", hash = "sha256:a38664e699526cb1687aefd9069e2b5b9387da7feac4545de446141f1ef86f46"},
-    {file = "numexpr-2.8.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eaec59e9bf70ff05615c34a8b8d6c7bd042bd9f55465d7b495ea5436f45319d0"},
-    {file = "numexpr-2.8.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b318541bf3d8326682ebada087ba0050549a16d8b3fa260dd2585d73a83d20a7"},
-    {file = "numexpr-2.8.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b076db98ca65eeaf9bd224576e3ac84c05e451c0bd85b13664b7e5f7b62e2c70"},
-    {file = "numexpr-2.8.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90f12cc851240f7911a47c91aaf223dba753e98e46dff3017282e633602e76a7"},
-    {file = "numexpr-2.8.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c368aa35ae9b18840e78b05f929d3a7b3abccdba9630a878c7db74ca2368339"},
-    {file = "numexpr-2.8.4-cp38-cp38-win32.whl", hash = "sha256:b96334fc1748e9ec4f93d5fadb1044089d73fb08208fdb8382ed77c893f0be01"},
-    {file = "numexpr-2.8.4-cp38-cp38-win_amd64.whl", hash = "sha256:a6d2d7740ae83ba5f3531e83afc4b626daa71df1ef903970947903345c37bd03"},
-    {file = "numexpr-2.8.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:77898fdf3da6bb96aa8a4759a8231d763a75d848b2f2e5c5279dad0b243c8dfe"},
-    {file = "numexpr-2.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df35324666b693f13a016bc7957de7cc4d8801b746b81060b671bf78a52b9037"},
-    {file = "numexpr-2.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17ac9cfe6d0078c5fc06ba1c1bbd20b8783f28c6f475bbabd3cad53683075cab"},
-    {file = "numexpr-2.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df3a1f6b24214a1ab826e9c1c99edf1686c8e307547a9aef33910d586f626d01"},
-    {file = "numexpr-2.8.4-cp39-cp39-win32.whl", hash = "sha256:7d71add384adc9119568d7e9ffa8a35b195decae81e0abf54a2b7779852f0637"},
-    {file = "numexpr-2.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:9f096d707290a6a00b6ffdaf581ee37331109fb7b6c8744e9ded7c779a48e517"},
-    {file = "numexpr-2.8.4.tar.gz", hash = "sha256:d5432537418d18691b9115d615d6daa17ee8275baef3edf1afbbf8bc69806147"},
-]
-
-[package.dependencies]
-numpy = ">=1.13.3"
-
 [[package]]
 name = "numpy"
 version = "1.24.3"
@@ -2143,19 +1986,19 @@ files = [
 
 [[package]]
 name = "platformdirs"
-version = "3.5.1"
+version = "3.5.3"
 description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
 category = "main"
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"},
-    {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"},
+    {file = "platformdirs-3.5.3-py3-none-any.whl", hash = "sha256:0ade98a4895e87dc51d47151f7d2ec290365a585151d97b4d8d6312ed6132fed"},
+    {file = "platformdirs-3.5.3.tar.gz", hash = "sha256:e48fabd87db8f3a7df7150a4a5ea22c546ee8bc39bc2473244730d4b56d2cc4e"},
 ]
 
 [package.extras]
-docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
+docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
+test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"]
 
 [[package]]
 name = "pluggy"
@@ -2276,18 +2119,6 @@ files = [
 [package.extras]
 tests = ["pytest"]
 
-[[package]]
-name = "py-cpuinfo"
-version = "9.0.0"
-description = "Get CPU info with pure Python"
-category = "main"
-optional = false
-python-versions = "*"
-files = [
-    {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"},
-    {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"},
-]
-
 [[package]]
 name = "pycparser"
 version = "2.21"
@@ -2445,14 +2276,14 @@ yaml = ["ruamel.yaml"]
 
 [[package]]
 name = "pytest"
-version = "7.3.1"
+version = "7.3.2"
 description = "pytest: simple powerful testing with Python"
 category = "dev"
 optional = false
 python-versions = ">=3.7"
 files = [
-    {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"},
-    {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"},
+    {file = "pytest-7.3.2-py3-none-any.whl", hash = "sha256:cdcbd012c9312258922f8cd3f1b62a6580fdced17db6014896053d47cddf9295"},
+    {file = "pytest-7.3.2.tar.gz", hash = "sha256:ee990a3cc55ba808b80795a79944756f315c67c12b56abd3ac993a7b8c17030b"},
 ]
 
 [package.dependencies]
@@ -2464,7 +2295,7 @@ pluggy = ">=0.12,<2.0"
 tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
 
 [package.extras]
-testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
+testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
 
 [[package]]
 name = "python-dateutil"
@@ -2886,44 +2717,6 @@ pure-eval = "*"
 [package.extras]
 tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
 
-[[package]]
-name = "tables"
-version = "3.8.0"
-description = "Hierarchical datasets for Python"
-category = "main"
-optional = false
-python-versions = ">=3.8"
-files = [
-    {file = "tables-3.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:01e82e40f9845f71de137b4472210909e35c440bbcd0858bdd2871715daef4c7"},
-    {file = "tables-3.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db185d855afd45a7259ddd0b53e5f2f8993bb134b370002c6c19532f27ce92ac"},
-    {file = "tables-3.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70a3585a268beee6d0e71bfc9abec98da84d168182f350a2ffa1ae5e42798c18"},
-    {file = "tables-3.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:117cf0f73ee2a5cba5c2b04e4aca375779aec66045aa63128e043dc608f2023b"},
-    {file = "tables-3.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2861cd3ef9eb95eead7530e4de49fd130954871e7e6d2e288012797cb9d7c2e8"},
-    {file = "tables-3.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e9bdbfbe025b6c751976382123c5f5cbd8fab6956aed776b0e8c889669e90d3"},
-    {file = "tables-3.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0821007048f2af8c1a21eb3d832072046c5df366e39587a7c7e4afad14e73fc"},
-    {file = "tables-3.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:b9370c2a4dc0051aad6b71de4f1f9b0b8b60d30b662df5c742434f2b5c6a005e"},
-    {file = "tables-3.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e19686fad4e8f5a91c3dc1eb4b7ea928838e86fefa474c63c5787a125ea79fc7"},
-    {file = "tables-3.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:239f15fa9881c257b5c0d9fb4cb8832778af1c5c8c1db6f6722466f8f26541e2"},
-    {file = "tables-3.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c83a74cac3c0629a0e83570d465f88843ef3609ef56a8ef9a49ee85ab3b8f02f"},
-    {file = "tables-3.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:a5ccb80651c5fad6ac744e2a756b28cfac78eab3b8503f4a2320ee6653b3bee9"},
-    {file = "tables-3.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3375bfafc6cf305d13617a572ab3fffc51fae2fbe0f6efce9407a41f79970b62"},
-    {file = "tables-3.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:254a4d5c2009c7ebe4293b02b8d91ea60837bff85a3c0a40cd075b8f12b1e6c3"},
-    {file = "tables-3.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da3c96456c473fb977cf6dbca9e889710ac020df1fa5b9ebb7f676e83996337d"},
-    {file = "tables-3.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:72da9404094ef8277bf62fce8873e8dc141cee9a8763ec8e7080b2d0de206094"},
-    {file = "tables-3.8.0.tar.gz", hash = "sha256:34f3fa2366ce20b18f1df573a77c1d27306ce1f2a41d9f9eff621b5192ea8788"},
-]
-
-[package.dependencies]
-blosc2 = ">=2.0.0,<2.1.0"
-cython = ">=0.29.21"
-numexpr = ">=2.6.2"
-numpy = ">=1.19.0"
-packaging = "*"
-py-cpuinfo = "*"
-
-[package.extras]
-doc = ["ipython", "numpydoc", "sphinx (>=1.1)", "sphinx-rtd-theme"]
-
 [[package]]
 name = "terminado"
 version = "0.17.1"
@@ -3133,14 +2926,14 @@ files = [
 
 [[package]]
 name = "websocket-client"
-version = "1.5.2"
+version = "1.5.3"
 description = "WebSocket client for Python with low level API options"
 category = "main"
 optional = true
 python-versions = ">=3.7"
 files = [
-    {file = "websocket-client-1.5.2.tar.gz", hash = "sha256:c7d67c13b928645f259d9b847ab5b57fd2d127213ca41ebd880de1f553b7c23b"},
-    {file = "websocket_client-1.5.2-py3-none-any.whl", hash = "sha256:f8c64e28cd700e7ba1f04350d66422b6833b82a796b525a51e740b8cc8dab4b1"},
+    {file = "websocket-client-1.5.3.tar.gz", hash = "sha256:b96f3bce3e54e3486ebe6504bc22bd4c140392bd2eb71764db29be8f2639aa65"},
+    {file = "websocket_client-1.5.3-py3-none-any.whl", hash = "sha256:3566f8467cd350874c4913816355642a4942f6c1ed1e9406e3d42fae6d6c072a"},
 ]
 
 [package.extras]
@@ -3182,4 +2975,4 @@ notebooks = ["jupyter", "matplotlib"]
 [metadata]
 lock-version = "2.0"
 python-versions = "^3.8"
-content-hash = "4f5c64765fbd8017d3208135390916b2367e26f1dc12cf9c9706754a9ac1173d"
+content-hash = "f09a2c9f249bc436fc534d79fadd182b93f5f5c4130770f892c3b10c6908d880"
diff --git a/pyproject.toml b/pyproject.toml
index f64c8228..87e04f8b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -32,9 +32,9 @@ pyodbc = "^4"
 certifi = "^2023"
 requests = "^2"
 requests-kerberos = "^0"
-tables = { version = "^3", markers = "platform_machine != 'arm64'" }
 jupyter = { version = "^1", optional = true }
 matplotlib = { version = "^3", optional = true }
+diskcache = "^5.6.1"
 
 
 [tool.poetry.group.dev.dependencies]
diff --git a/tagreader/cache.py b/tagreader/cache.py
index 6867a8d8..6a21fbfb 100644
--- a/tagreader/cache.py
+++ b/tagreader/cache.py
@@ -1,10 +1,11 @@
-import os
-import warnings
-from importlib.util import find_spec
-from typing import Dict, List, Literal, Optional, Tuple, Union, cast
+from abc import ABC
+from pathlib import Path
+from typing import Dict, List, Optional, Tuple, Union, cast
 
 import pandas as pd
+from diskcache import Cache
 
+from tagreader.logger import logger
 from tagreader.utils import ReaderType
 
 
@@ -23,14 +24,80 @@ def timestamp_to_epoch(timestamp: pd.Timestamp) -> int:
     return (timestamp - origin) // pd.Timedelta("1s")
 
 
-class BucketCache:
-    def __init__(self, filename: str, path: str = ".") -> None:
-        if not find_spec("tables"):
-            warnings.warn("Package 'tables' not installed. Disabling cache.")
-            return None
-        self.filename = os.path.splitext(filename)[0] + ".h5"
-        self.filename = os.path.join(path, self.filename)
+def _infer_pandas_index_freq(df: pd.DataFrame) -> pd.DataFrame:
+    try:
+        if pd.infer_freq(df.index):  # type: ignore[arg-type]
+            df = df.asfreq(pd.infer_freq(df.index))  # type: ignore[arg-type]
+    except (TypeError, ValueError) as e:
+        logger.warning(f"Could not infer frequency of timeseries in Cache. {e}")
+    return df
 
+
+def _drop_duplicates_and_sort_index(df: pd.DataFrame) -> pd.DataFrame:
+    return df[~df.index.duplicated(keep="first")].sort_index()
+
+
+def clean_dataframe(df: pd.DataFrame) -> pd.DataFrame:
+    return _infer_pandas_index_freq(_drop_duplicates_and_sort_index(df))
+
+
+class BaseCache(ABC):
+    def __init__(
+        self,
+        directory: Path = Path("."),
+        expire_time_in_seconds: Optional[int] = None,
+        enable_stats: bool = False,
+    ) -> None:
+        self.cache = Cache(directory=directory.as_posix())
+        self._expire_time = expire_time_in_seconds
+
+        if enable_stats:
+            self.enable_cache_statistics()
+
+    def enable_cache_statistics(self) -> None:
+        self.cache.stats(enable=True)
+
+    def clear_cache(self) -> None:
+        self.cache.clear()
+
+    def put(self, key: str, value: pd.DataFrame) -> None:
+        self.cache.add(key, value, expire=self._expire_time)
+
+    def get(self, key: str) -> pd.DataFrame:
+        return cast(pd.DataFrame, self.cache.get(key))
+
+    def delete(self, key: str) -> None:
+        self.cache.delete(key)
+
+    def get_metadata(
+        self, key: str, properties: Union[str, List[str]]
+    ) -> Dict[str, Union[str, int, float]]:
+        if isinstance(properties, str):
+            properties = [properties]
+        _key = f"$metadata${key}"
+        metadata: Dict[str, Union[str, int, float]] = self.cache.get(_key)
+        return {k: v for (k, v) in metadata.items() if k in properties}
+
+    def put_metadata(
+        self, key: str, value: Dict[str, Union[str, int, float]]
+    ) -> Dict[str, Union[str, int, float]]:
+        _key = f"$metadata${key}"
+        combined_value = value
+        if _key in self.cache:
+            existing = self.cache.get(_key)
+            existing.update(value)
+            combined_value = existing
+            self.cache.delete(_key)
+
+        self.cache.add(_key, combined_value, expire=self._expire_time)
+        return combined_value
+
+    def delete_metadata(self, key: str) -> None:
+        _key = f"$metadata${key}"
+        self.cache.delete(_key)
+
+
+class BucketCache(BaseCache):
     def _key_path(
         self,
         tagname: str,
@@ -42,7 +109,7 @@ def _key_path(
         endtime: Optional[pd.Timestamp] = None,
     ) -> str:
         """Return a string on the form
-        /tagname/readtype[/sample_time][/stepped][/status]/_starttime_endtime
+        $tagname$readtype[$sample_time][$stepped][$status]$_starttime_endtime
         tagname: safe tagname
         sample_time: integer value. Empty for RAW.
         stepped: "stepped" if value was read as stepped. Empty if not.
@@ -60,57 +127,19 @@ def _key_path(
         timespan = ""
         if starttime is not None:
             starttime_epoch = timestamp_to_epoch(starttime)
-            endtime_epoch = timestamp_to_epoch(endtime)  # type: ignore[arg-type]
-            timespan = f"/_{starttime_epoch}_{endtime_epoch}"
+            endtime_epoch = timestamp_to_epoch(endtime) if endtime else endtime
+            timespan = f"$_{starttime_epoch}_{endtime_epoch}"
 
         keyval = (
-            f"/{tagname}"
-            f"/{readtype.name}"
-            f"{(ts is not None and readtype != ReaderType.RAW) * f'/s{ts}'}"
-            f"{stepped * '/stepped'}"
-            f"{status * '/status'}"
+            f"${tagname}"
+            f"${readtype.name}"
+            f"{(ts is not None and readtype != ReaderType.RAW) * f'$s{ts}'}"
+            f"{stepped * '$stepped'}"
+            f"{status * '$status'}"
             f"{timespan}"
         )
         return keyval
 
-    def store_tag_metadata(
-        self, tagname: str, metadata: Dict[str, Union[str, int]]
-    ) -> None:
-        tagname = safe_tagname(tagname)
-        key = f"/{tagname}"
-        with pd.HDFStore(self.filename, mode="a") as f:
-            if key not in f:
-                f.put(key, pd.DataFrame())
-            origmetadata = {}
-            if "metadata" in f.get_storer(key).attrs:  # type: ignore[operator]
-                origmetadata = f.get_storer(key).attrs.metadata  # type: ignore[operator]
-            f.get_storer(key).attrs.metadata = {**origmetadata, **metadata}  # type: ignore[operator]
-
-    def fetch_tag_metadata(
-        self, tagname: str, properties: Union[str, List[str]]
-    ) -> Dict[str, Union[str, int]]:
-        res: Dict[str, Union[str, int]] = {}
-        if not os.path.isfile(self.filename):
-            return res
-        tagname = safe_tagname(tagname)
-        key = f"/{tagname}"
-        if isinstance(properties, str):
-            properties = [properties]
-        with pd.HDFStore(self.filename, mode="r") as f:
-            if key not in f or "metadata" not in f.get_storer(key).attrs:  # type: ignore[operator]
-                return {}
-            metadata = f.get_storer(key).attrs.metadata  # type: ignore[operator]
-        for p in properties:
-            if p in metadata.keys():
-                res[p] = metadata.get(p)
-        return res
-
-    def remove(self, filename: Optional[str] = None) -> None:
-        if not filename:
-            filename = self.filename
-        if os.path.isfile(filename):
-            os.unlink(filename)
-
     def store(
         self,
         df: pd.DataFrame,
@@ -135,16 +164,12 @@ def store(
             endtime=endtime,
         )
         if len(intersecting) > 0:
-            with pd.HDFStore(self.filename, mode="a") as f:
-                for dataset in intersecting:
-                    this_start, this_end = self._get_intervals_from_dataset_name(
-                        dataset
-                    )
-                    starttime = min(starttime, this_start)
-                    endtime = max(endtime, this_end)
-                    df = pd.concat([df, f.get(dataset)])  # type: ignore[list-item]
-                    del f[dataset]
-            df = df[~df.index.duplicated(keep="first")].sort_index()
+            for dataset in intersecting:
+                this_start, this_end = self._get_intervals_from_dataset_name(dataset)
+                starttime = min(starttime, this_start if this_start else starttime)
+                endtime = max(endtime, this_end if this_end else endtime)
+                df = clean_dataframe(pd.concat([df, self.get(dataset)], axis=0))
+                self.delete(dataset)
         key = self._key_path(
             tagname=tagname,
             readtype=readtype,
@@ -154,14 +179,13 @@ def store(
             starttime=starttime,
             endtime=endtime,
         )
-        with pd.HDFStore(self.filename, mode="a") as f:
-            f.put(key, df, format="table")
+        self.put(key=key, value=df)
 
     @staticmethod
     def _get_intervals_from_dataset_name(
         name: str,
     ) -> Tuple[pd.Timestamp, pd.Timestamp]:
-        name_with_times = name.split("/")[-1]
+        name_with_times = name.split("$")[-1]
         if not name_with_times.count("_") == 2:
             return (None, None)  # type: ignore[return-value]
         _, starttime_epoch, endtime_epoch = name_with_times.split("_")
@@ -179,28 +203,25 @@ def get_intersecting_datasets(
         starttime: pd.Timestamp,
         endtime: pd.Timestamp,
     ) -> List[str]:
-        if not os.path.isfile(self.filename):
+        if not len(self.cache) > 0:
             return []
         intersecting_datasets = []
-        with pd.HDFStore(self.filename, mode="r") as f:
-            for bucket in f.walk(
-                where=self._key_path(
-                    tagname=tagname,
-                    readtype=readtype,
-                    starttime=None,
-                    endtime=None,
-                    ts=ts,
-                    stepped=stepped,
-                    status=status,
+        for dataset in self.cache.iterkeys():
+            target_key = self._key_path(
+                tagname=tagname,
+                readtype=readtype,
+                starttime=None,
+                endtime=None,
+                ts=ts,
+                stepped=stepped,
+                status=status,
+            )
+            if target_key in dataset:
+                starttime_ds, endtime_ds = self._get_intervals_from_dataset_name(
+                    dataset
                 )
-            ):
-                for leaf in bucket[2]:
-                    dataset = bucket[0] + "/" + leaf
-                    starttime_ds, endtime_ds = self._get_intervals_from_dataset_name(
-                        dataset
-                    )
-                    if endtime_ds >= starttime and endtime >= starttime_ds:
-                        intersecting_datasets.append(dataset)
+                if endtime_ds >= starttime and endtime >= starttime_ds:
+                    intersecting_datasets.append(dataset)
         return intersecting_datasets
 
     def get_missing_intervals(
@@ -249,16 +270,19 @@ def fetch(
         self,
         tagname: str,
         readtype: ReaderType,
-        ts: int,
+        ts: Union[int, pd.Timedelta],
         stepped: bool,
         status: bool,
         starttime: pd.Timestamp,
         endtime: pd.Timestamp,
     ) -> pd.DataFrame:
         df = pd.DataFrame()
-        if not os.path.isfile(self.filename):
+        if not len(self.cache) > 0:
             return df
 
+        if isinstance(ts, pd.Timedelta):
+            ts = int(ts.total_seconds())
+
         datasets = self.get_intersecting_datasets(
             tagname=tagname,
             readtype=readtype,
@@ -269,40 +293,15 @@ def fetch(
             endtime=endtime,
         )
 
-        with pd.HDFStore(self.filename, mode="r") as f:
-            for dataset in datasets:
-                # df = df.append(
-                #     f.select(dataset, where="index >= starttime and index <= endtime")
-                # )
-                df = pd.concat(
-                    [
-                        df,
-                        f.select(  # type: ignore[list-item]
-                            dataset, where="index >= starttime and index <= endtime"
-                        ),
-                    ]
-                )
-
-        return df.sort_index()
-
+        for dataset in datasets:
+            df = clean_dataframe(
+                pd.concat([df, self.get(dataset).loc[starttime:endtime]], axis=0)  # type: ignore[call-overload, misc]
+            )
 
-class SmartCache:
-    def __init__(self, filename: str, path: str = ".") -> None:
-        if not find_spec("tables"):
-            warnings.warn("Package 'tables' not installed. Disabling cache.")
-            return None
-        self.filename = os.path.splitext(filename)[0] + ".h5"
-        self.filename = os.path.join(path, self.filename)
-        # self.open(self.filename)
+        return df
 
-    # def open(self, filename=None):
-    #     if filename is None:
-    #         filename = self.filename
-    #     self.hdfstore = pd.HDFStore(filename)
-    #
-    # def close(self):
-    #     self.hdfstore.close()
 
+class SmartCache(BaseCache):
     @staticmethod
     def key_path(
         df: Union[str, pd.DataFrame],
@@ -310,11 +309,11 @@ def key_path(
         ts: Optional[Union[int, pd.Timedelta]] = None,
     ) -> str:
         """Return a string on the form
-        XXX/sYY/ZZZ where XXX is the ReadType, YY is the interval between samples
+        XXX$sYY$ZZZ where XXX is the ReadType, YY is the interval between samples
         (in seconds) and ZZZ is a safe tagname.
         """
-        name = list(df)[0] if isinstance(df, pd.DataFrame) else df
-        name = safe_tagname(name)  # type: ignore[arg-type]
+        name = str(list(df)[0]) if isinstance(df, pd.DataFrame) else df
+        name = safe_tagname(name)
         ts = int(ts.total_seconds()) if isinstance(ts, pd.Timedelta) else ts
         if readtype != ReaderType.RAW:
             if ts is None:
@@ -326,9 +325,9 @@ def key_path(
                     raise TypeError
             else:
                 interval = int(ts)
-            return f"{readtype.name}/s{interval}/{name}"
+            return f"{readtype.name}$s{interval}${name}"
         else:
-            return f"{readtype.name}/{name}"
+            return f"{readtype.name}${name}"
 
     def store(
         self,
@@ -339,135 +338,28 @@ def store(
         key = self.key_path(df=df, readtype=readtype, ts=ts)
         if df.empty:
             return  # Weirdness ensues when using empty df in select statement below
-        with pd.HDFStore(self.filename, mode="a") as f:
-            if key in f:
-                idx = f.select(  # noqa: F841
-                    key, where="index in df.index", columns=["index"]
-                ).index
-                f.append(key, df.query("index not in @idx"))
-            else:
-                f.append(key, df)
+        if key in self.cache:
+            data = clean_dataframe(pd.concat([df, self.get(key)], axis=0))
+            self.delete(key=key)
+            self.put(
+                key=key,
+                value=data,
+            )
+        else:
+            self.put(key, df)
 
     def fetch(
         self,
         tagname: str,
         readtype: ReaderType,
-        ts: Optional[Union[int, pd.Timestamp]] = None,
+        ts: Optional[Union[int, pd.Timedelta]] = None,
         start_time: Optional[pd.Timestamp] = None,
         stop_time: Optional[pd.Timestamp] = None,
     ) -> pd.DataFrame:
-        df = pd.DataFrame()
-        if not os.path.isfile(self.filename):
-            return df
-        key = self.key_path(df=tagname, readtype=readtype, ts=ts)  # type: ignore[arg-type]
-        where = []
-        if start_time is not None:
-            where.append("index >= start_time")
-        if stop_time is not None:
-            where.append("index <= stop_time")
-        wheretxt = " and ".join(where)
-        with pd.HDFStore(self.filename, mode="r") as f:
-            if key in f:
-                if wheretxt:
-                    df = f.select(key, where=wheretxt)  # type: ignore[assignment]
-                else:
-                    df = f.select(key)  # type: ignore[assignment]
-        return df.sort_index()
-
-    def store_tag_metadata(
-        self, tagname: str, metadata: Dict[str, Union[str, int]]
-    ) -> None:
-        tagname = safe_tagname(tagname)
-        key = f"/metadata/{tagname}"
-        with pd.HDFStore(self.filename, mode="a") as f:
-            if key not in f:
-                f.put(key, pd.DataFrame())
-            origmetadata = {}
-            if "metadata" in f.get_storer(key).attrs:  # type: ignore[operator]
-                origmetadata = f.get_storer(key).attrs.metadata  # type: ignore[operator]
-            f.get_storer(key).attrs.metadata = {**origmetadata, **metadata}  # type: ignore[operator]
-
-    def fetch_tag_metadata(
-        self, tagname: str, properties: Union[str, List[str]]
-    ) -> Dict[str, Union[str, int]]:
-        res: Dict[str, Union[str, int]] = {}
-        if not os.path.isfile(self.filename):
-            return res
-        tagname = safe_tagname(tagname)
-        key = f"/metadata/{tagname}"
-        if isinstance(properties, str):
-            properties = [properties]
-        with pd.HDFStore(self.filename, mode="r") as f:
-            if key not in f or "metadata" not in f.get_storer(key).attrs:  # type: ignore[operator]
-                return {}
-            metadata = f.get_storer(key).attrs.metadata  # type: ignore[operator]
-        for p in properties:
-            if p in metadata.keys():
-                res[p] = metadata.get(p)
-        return res
-
-    def remove(self, filename: Optional[str] = None) -> None:
-        if not filename:
-            filename = self.filename
-            # self.close()
-        if os.path.isfile(filename):
-            os.unlink(filename)
-
-    def _match_tag(
-        self,
-        key: str,
-        readtype: Optional[Union[List[ReaderType], ReaderType]] = None,
-        ts: Optional[List[Optional[Union[int, pd.Timestamp]]]] = None,
-        tagname: Optional[Union[List[str], str]] = None,
-    ) -> bool:
-        def readtype_to_str(rt):  # type: ignore
-            return getattr(
-                rt, "name", rt
-            )  # if isinstance(rt, ReaderType) always returns False...?
-
-        def timedelta_to_str(t):  # type: ignore[no-untyped-def]
-            if isinstance(t, pd.Timedelta):
-                return str(int(t.total_seconds()))
-            return t
-
-        key = "/" + key.lstrip("/")  # Ensure absolute path:
-        readtype = readtype if isinstance(readtype, list) else [readtype]  # type: ignore[list-item]
-        ts = ts if isinstance(ts, list) else [ts]
-        tagname = tagname if isinstance(tagname, list) else [tagname]  # type: ignore[list-item]
-        readtype = list(map(readtype_to_str, readtype))
-        ts = list(map(timedelta_to_str, ts))
-        if tagname[0] is not None:
-            tagname = list(map(safe_tagname, tagname))
-        # print(f"Readtype: {readtype}, ts: {ts}, tagname: {tagname}")
-        elements = key.split("/")[1:]
-        if len(elements) == 2:
-            elements.insert(1, None)  # type: ignore[arg-type]
-        else:
-            elements[1] = int(  # type: ignore[call-overload]
-                elements[1][1:]
-            )  # Discard the initial s
-        if elements[0] not in readtype and readtype[0] is not None:  # type: ignore[comparison-overlap]
-            # print(f"{elements[0]} not in {readtype}")
-            return False
-        if elements[1] not in ts and ts[0] is not None:
-            # print(f"{elements[1]} not in {ts}")
-            return False
-        if elements[2] not in tagname and tagname[0] is not None:
-            # print(f"{elements[2]} not in {tagname}")
-            return False
-        return True
-
-    def delete_key(
-        self,
-        tagname: Optional[str] = None,
-        readtype: Optional[ReaderType] = None,
-        ts: Optional[Union[int, pd.Timestamp]] = None,
-    ) -> None:
-        with pd.HDFStore(self.filename) as f:
-            for key in f:
-                if self._match_tag(key=key, tagname=tagname, readtype=readtype, ts=ts):  # type: ignore[arg-type]
-                    f.remove(key)  # type: ignore[operator]
-
-    def _get_hdfstore(self, mode: Literal["a", "w", "r", "r+"] = "r") -> pd.HDFStore:
-        f = pd.HDFStore(self.filename, mode=mode)
-        return f
+        key = self.key_path(df=tagname, readtype=readtype, ts=ts)
+        df: pd.DataFrame = self.cache.get(key=key)
+        if start_time:
+            df = df.loc[df.index >= start_time]
+        if stop_time:
+            df = df.loc[df.index <= stop_time]
+        return df
diff --git a/tagreader/clients.py b/tagreader/clients.py
index cb8f8903..4f5069d6 100644
--- a/tagreader/clients.py
+++ b/tagreader/clients.py
@@ -3,6 +3,7 @@
 from datetime import datetime, timedelta
 from itertools import groupby
 from operator import itemgetter
+from pathlib import Path
 from typing import Any, Dict, List, Optional, Tuple, Union
 
 import pandas as pd
@@ -313,7 +314,7 @@ def __init__(
             verifySSL=verifySSL,
             auth=auth,
         )
-        self.cache = SmartCache(filename=datasource)
+        self.cache = SmartCache(directory=Path(".") / ".cache" / datasource)
 
     def connect(self):
         self.handler.connect()
@@ -465,13 +466,13 @@ def get_units(self, tags: Union[str, List[str]]):
         units = {}
         for tag in tags:
             if self.cache is not None:
-                r = self.cache.fetch_tag_metadata(tagname=tag, properties="unit")
+                r = self.cache.get_metadata(key=tag, properties="unit")
                 if "unit" in r:
                     units[tag] = r["unit"]
             if tag not in units:
                 unit = self.handler._get_tag_unit(tag)
                 if self.cache is not None and unit is not None:
-                    self.cache.store_tag_metadata(tagname=tag, metadata={"unit": unit})
+                    self.cache.put_metadata(key=tag, value={"unit": unit})
                 units[tag] = unit
         return units
 
@@ -481,15 +482,13 @@ def get_descriptions(self, tags: Union[str, List[str]]) -> Dict[str, str]:
         descriptions = {}
         for tag in tags:
             if self.cache is not None:
-                r = self.cache.fetch_tag_metadata(tagname=tag, properties="description")
+                r = self.cache.get_metadata(key=tag, properties="description")
                 if "description" in r:
                     descriptions[tag] = r["description"]
             if tag not in descriptions:
                 desc = self.handler._get_tag_description(tag)
                 if self.cache is not None and desc is not None:
-                    self.cache.store_tag_metadata(
-                        tagname=tag, metadata={"description": desc}
-                    )
+                    self.cache.put_metadata(key=tag, value={"description": desc})
                 descriptions[tag] = desc
         return descriptions
 


Test patch:
diff --git a/pytest.ini b/pytest.ini
index 702b9e78..4eff4469 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -2,8 +2,7 @@
 filterwarnings = 
     error
     ignore::ResourceWarning
-    ignore:Package 'tables' not installed. Disabling cache.
-    
+
 
 junit_family = xunit1
 python_files = test_*.py
diff --git a/tests/test_PIHandlerREST_connect.py b/tests/test_PIHandlerREST_connect.py
index 570eda56..a9754ea7 100644
--- a/tests/test_PIHandlerREST_connect.py
+++ b/tests/test_PIHandlerREST_connect.py
@@ -260,9 +260,3 @@ def test_tags_raw_with_no_data_included_in_results(Client):
         read_type=ReaderType.RAW,
     )
     assert df.empty
-
-
-# def test_connect_no_pytables():
-#     with pytest.warns(UserWarning):
-#         c = IMSClient(datasource="whatever", host="host", imstype="piwebapi")
-#         c.connect()
diff --git a/tests/test_bucketcache.py b/tests/test_bucketcache.py
index 9c0fa171..f1611839 100644
--- a/tests/test_bucketcache.py
+++ b/tests/test_bucketcache.py
@@ -1,4 +1,3 @@
-import sys
 from importlib.util import find_spec
 
 import pandas as pd
@@ -7,15 +6,12 @@
 from tagreader.cache import BucketCache, safe_tagname, timestamp_to_epoch
 from tagreader.utils import ReaderType
 
-if not find_spec("tables"):
-    pytest.skip("Bucketcache requires package 'tables'", allow_module_level=True)
-
 TAGNAME = "tag1"
 READERTYPE = ReaderType.INT
 
 TZ = "UTC"
-TS = 300
-FREQ = f"{TS}s"
+TS = pd.Timedelta(seconds=300)
+FREQ = f"{int(TS.total_seconds())}s"
 
 STARTTIME_1 = pd.to_datetime("2020-01-01 12:00:00", utc=True)
 ENDTIME_1 = pd.to_datetime("2020-01-01 13:00:00", utc=True)
@@ -48,10 +44,9 @@
 
 
 @pytest.fixture(autouse=True)
-def cache():
-    cache = BucketCache("testbucketcache.h5")
+def cache(tmp_path):
+    cache = BucketCache(tmp_path)
     yield cache
-    cache.remove()
 
 
 def test_timestamp_to_epoch():
@@ -69,7 +64,7 @@ def test_safe_tagname():
     assert safe_tagname("ASGB.tt-___56_ _%_/_") == "ASGB_tt___56____"
 
 
-def test_get_intervals_from_dataset_name(cache):
+def test_get_intervals_from_dataset_name(cache: BucketCache):
     badtag = f"/tag1/INT/{STARTTIME_1_EPOCH}_{ENDTIME_1_EPOCH}"
     goodtag = f"/tag1/INT/_{STARTTIME_1_EPOCH}_{ENDTIME_1_EPOCH}"
     starttime, endtime = cache._get_intervals_from_dataset_name(badtag)
@@ -80,7 +75,7 @@ def test_get_intervals_from_dataset_name(cache):
     assert endtime == ENDTIME_1
 
 
-def test_key_path_with_time(cache):
+def test_key_path_with_time(cache: BucketCache):
     assert (
         cache._key_path(
             tagname=TAGNAME,
@@ -91,11 +86,11 @@ def test_key_path_with_time(cache):
             starttime=STARTTIME_1,
             endtime=ENDTIME_1,
         )
-        == f"/tag1/INT/s60/_{STARTTIME_1_EPOCH}_{ENDTIME_1_EPOCH}"
+        == f"$tag1$INT$s60$_{STARTTIME_1_EPOCH}_{ENDTIME_1_EPOCH}"
     )
 
 
-def test_key_path_stepped(cache):
+def test_key_path_stepped(cache: BucketCache):
     assert (
         cache._key_path(
             tagname=TAGNAME,
@@ -106,11 +101,11 @@ def test_key_path_stepped(cache):
             starttime=STARTTIME_1,
             endtime=ENDTIME_1,
         )
-        == f"/tag1/INT/s60/stepped/_{STARTTIME_1_EPOCH}_{ENDTIME_1_EPOCH}"
+        == f"$tag1$INT$s60$stepped$_{STARTTIME_1_EPOCH}_{ENDTIME_1_EPOCH}"
     )
 
 
-def test_key_path_with_status(cache):
+def test_key_path_with_status(cache: BucketCache):
     assert (
         cache._key_path(
             tagname=TAGNAME,
@@ -119,11 +114,11 @@ def test_key_path_with_status(cache):
             stepped=False,
             status=True,
         )
-        == "/tag1/INT/s60/status"
+        == "$tag1$INT$s60$status"
     )
 
 
-def test_key_path_RAW(cache):
+def test_key_path_RAW(cache: BucketCache):
     assert (
         cache._key_path(
             tagname=TAGNAME,
@@ -132,11 +127,11 @@ def test_key_path_RAW(cache):
             stepped=False,
             status=False,
         )
-        == "/tag1/RAW"
+        == "$tag1$RAW"
     )
 
 
-def test_get_missing_intervals(cache):
+def test_get_missing_intervals(cache: BucketCache):
     cache.store(
         DF1,
         TAGNAME,
@@ -218,7 +213,7 @@ def test_get_missing_intervals(cache):
     assert missing_intervals[2] == (ENDTIME_2, ENDTIME_2 + pd.Timedelta("15m"))
 
 
-def test_get_intersecting_datasets(cache):
+def test_get_intersecting_datasets(cache: BucketCache):
     cache.store(
         DF1,
         TAGNAME,
@@ -333,18 +328,18 @@ def test_get_intersecting_datasets(cache):
     assert len(intersecting_datasets) == 2
 
 
-def test_store_metadata(cache):
-    cache.store_tag_metadata(TAGNAME, {"unit": "%", "desc": "Some description"})
-    cache.store_tag_metadata(TAGNAME, {"max": 60})
-    r = cache.fetch_tag_metadata(TAGNAME, "unit")
+def test_store_metadata(cache: BucketCache):
+    cache.put_metadata(TAGNAME, {"unit": "%", "desc": "Some description"})
+    cache.put_metadata(TAGNAME, {"max": 60})
+    r = cache.get_metadata(TAGNAME, "unit")
     assert "%" == r["unit"]
-    r = cache.fetch_tag_metadata(TAGNAME, ["unit", "max", "noworky"])
+    r = cache.get_metadata(TAGNAME, ["unit", "max", "noworky"])
     assert "%" == r["unit"]
     assert 60 == r["max"]
     assert "noworky" not in r
 
 
-def test_store_empty_df(cache):
+def test_store_empty_df(cache: BucketCache):
     # Empty dataframes should not be stored (note: df full of NaN is not empty!)
     df = pd.DataFrame({TAGNAME: []})
     cache.store(
@@ -364,7 +359,7 @@ def test_store_empty_df(cache):
     pd.testing.assert_frame_equal(DF1, df_read, check_freq=False)
 
 
-def test_store_single_df(cache):
+def test_store_single_df(cache: BucketCache):
     cache.store(
         DF1,
         TAGNAME,
@@ -379,36 +374,36 @@ def test_store_single_df(cache):
     pd.testing.assert_frame_equal(DF1, df_read, check_freq=False)
 
 
-def test_fetch(cache):
+def test_fetch(cache: BucketCache):
     cache.store(
-        DF1,
-        TAGNAME,
-        READERTYPE,
-        TS,
-        False,
-        False,
-        STARTTIME_1,
-        ENDTIME_1,
+        df=DF1,
+        tagname=TAGNAME,
+        readtype=READERTYPE,
+        ts=TS,
+        stepped=False,
+        status=False,
+        starttime=STARTTIME_1,
+        endtime=ENDTIME_1,
     )
     cache.store(
-        DF2,
-        TAGNAME,
-        READERTYPE,
-        TS,
-        False,
-        False,
-        STARTTIME_2,
-        ENDTIME_2,
+        df=DF2,
+        tagname=TAGNAME,
+        readtype=READERTYPE,
+        ts=TS,
+        stepped=False,
+        status=False,
+        starttime=STARTTIME_2,
+        endtime=ENDTIME_2,
     )
 
     df_read = cache.fetch(
-        TAGNAME,
-        READERTYPE,
-        TS,
-        False,
-        False,
-        STARTTIME_1,
-        ENDTIME_1 - pd.Timedelta("15m"),
+        tagname=TAGNAME,
+        readtype=READERTYPE,
+        ts=TS,
+        stepped=False,
+        status=False,
+        starttime=STARTTIME_1,
+        endtime=ENDTIME_1 - pd.Timedelta("15m"),
     )
     pd.testing.assert_frame_equal(
         DF1.loc[STARTTIME_1 : ENDTIME_1 - pd.Timedelta("15m")],
@@ -417,29 +412,29 @@ def test_fetch(cache):
     )
 
     df_read = cache.fetch(
-        TAGNAME,
-        READERTYPE,
-        TS,
-        False,
-        False,
-        STARTTIME_1 - pd.Timedelta("15m"),
-        ENDTIME_1 + pd.Timedelta("15m"),
+        tagname=TAGNAME,
+        readtype=READERTYPE,
+        ts=TS,
+        stepped=False,
+        status=False,
+        starttime=STARTTIME_1 - pd.Timedelta("15m"),
+        endtime=ENDTIME_1 + pd.Timedelta("15m"),
     )
     pd.testing.assert_frame_equal(DF1, df_read, check_freq=False)
 
     df_read = cache.fetch(
-        TAGNAME,
-        READERTYPE,
-        TS,
-        False,
-        False,
-        STARTTIME_1 - pd.Timedelta("15m"),
-        ENDTIME_2 + pd.Timedelta("15m"),
+        tagname=TAGNAME,
+        readtype=READERTYPE,
+        ts=TS,
+        stepped=False,
+        status=False,
+        starttime=STARTTIME_1 - pd.Timedelta("15m"),
+        endtime=ENDTIME_2 + pd.Timedelta("15m"),
     )
     pd.testing.assert_frame_equal(pd.concat([DF1, DF2]), df_read, check_freq=False)
 
 
-def test_store_overlapping_df(cache):
+def test_store_overlapping_df(cache: BucketCache):
     cache.store(
         DF1,
         TAGNAME,
@@ -461,14 +456,11 @@ def test_store_overlapping_df(cache):
         ENDTIME_2,
     )
     cache.store(DF3, TAGNAME, READERTYPE, TS, False, False, STARTTIME_3, ENDTIME_3)
-    with pd.HDFStore(cache.filename, mode="r") as f:
-        keys = list(f.walk(where="/"))
     leaves = None
-    for key in keys:
-        if len(key[2]) > 0:
-            leaves = key[2]
-    assert len(leaves) == 1
-    _, starttime, endtime = leaves[0].split("_")
+    for key in cache.cache.iterkeys():
+        if len(key) > 0:
+            leaves = key
+    _, starttime, endtime = leaves.split("_")
     assert int(starttime) == STARTTIME_1_EPOCH
     assert int(endtime) == ENDTIME_2_EPOCH
     df_read = cache.fetch(TAGNAME, READERTYPE, TS, False, False, STARTTIME_1, ENDTIME_2)
diff --git a/tests/test_cache.py b/tests/test_cache.py
index 59d2d5a9..888cc270 100644
--- a/tests/test_cache.py
+++ b/tests/test_cache.py
@@ -1,5 +1,4 @@
 import os
-import sys
 from importlib.util import find_spec
 
 import pandas as pd
@@ -8,9 +7,6 @@
 from tagreader.cache import SmartCache, safe_tagname
 from tagreader.utils import ReaderType
 
-if not find_spec("tables"):
-    pytest.skip("Cache requires package 'tables'", allow_module_level=True)
-
 os.environ["NUMEXPR_MAX_THREADS"] = "8"
 
 
@@ -27,27 +23,26 @@ def data():
 
 
 @pytest.fixture()
-def cache(request):
-    cache = SmartCache("testcache.h5")
+def cache(tmp_path):
+    cache = SmartCache(tmp_path)
     yield cache
-    cache.remove()
 
 
 def test_safe_tagname():
     assert safe_tagname("ASGB.tt-___56_ _%_/_") == "ASGB_tt___56____"
 
 
-def test_key_path(cache):
+def test_key_path(cache: SmartCache):
     pass
 
 
-def test_cache_single_store_and_fetch(cache, data):
+def test_cache_single_store_and_fetch(cache: SmartCache, data):
     cache.store(data, readtype=ReaderType.INT)
     df_read = cache.fetch("tag1", ReaderType.INT, 60)
     pd.testing.assert_frame_equal(data, df_read)
 
 
-def test_cache_multiple_store_single_fetch(cache, data):
+def test_cache_multiple_store_single_fetch(cache: SmartCache, data):
     df1 = data[0:3]
     df2 = data[2:10]
     cache.store(df1, readtype=ReaderType.INT)
@@ -56,7 +51,7 @@ def test_cache_multiple_store_single_fetch(cache, data):
     pd.testing.assert_frame_equal(df_read, data)
 
 
-def test_interval_reads(cache, data):
+def test_interval_reads(cache: SmartCache, data):
     cache.store(data, readtype=ReaderType.INT)
     start_time_oob = pd.to_datetime("2018-01-18 04:55:00")
     start_time = pd.to_datetime("2018-01-18 05:05:00")
@@ -77,89 +72,7 @@ def test_interval_reads(cache, data):
     pd.testing.assert_frame_equal(data[start_time:stop_time], df_read)
 
 
-def test_match_tag(cache):
-    assert (
-        cache._match_tag("INT/s60/tag1", readtype=ReaderType.INT, ts=60, tagname="tag1")
-        is True
-    )
-    assert (
-        cache._match_tag(
-            "INT/s86401/tag1", readtype=ReaderType.INT, ts=86401, tagname="tag1"
-        )
-        is True
-    )
-    assert (
-        cache._match_tag("INT/s60/tag1", readtype=ReaderType.RAW, ts=60, tagname="tag1")
-        is False
-    )
-    assert (
-        cache._match_tag("INT/s60/tag1", readtype=ReaderType.INT, ts=10, tagname="tag1")
-        is False
-    )
-    assert (
-        cache._match_tag("INT/s60/tag1", readtype=ReaderType.INT, ts=60, tagname="tag2")
-        is False
-    )
-    assert cache._match_tag("INT/s60/tag1", ts=60, tagname="tag1") is True
-    assert (
-        cache._match_tag(
-            "INT/s60/tag1", readtype=ReaderType.INTERPOLATE, tagname="tag1"
-        )
-        is True
-    )
-    assert cache._match_tag("INT/s60/tag1", readtype=ReaderType.INT, ts=60) is True
-    assert (
-        cache._match_tag(
-            "INT/s60/tag1",
-            readtype=[ReaderType.INT, ReaderType.RAW],
-            ts=[60, 10],
-            tagname=["tag1", "tag2"],
-        )
-        is True
-    )
-    assert (
-        cache._match_tag(
-            "INT/s60/tag1",
-            readtype=[ReaderType.AVERAGE, ReaderType.RAW],
-            ts=[60, 10],
-            tagname=["tag1", "tag2"],
-        )
-        is False
-    )
-    assert (
-        cache._match_tag(
-            "INT/s60/tag1",
-            readtype=[ReaderType.INT, ReaderType.RAW],
-            ts=[120, 10],
-            tagname=["tag1", "tag2"],
-        )
-        is False
-    )
-    assert (
-        cache._match_tag(
-            "INT/s60/tag1",
-            readtype=[ReaderType.INT, ReaderType.RAW],
-            ts=[60, 10],
-            tagname=["tag3", "tag2"],
-        )
-        is False
-    )
-
-
-def test_delete_tag(cache, data):
-    cache.store(data, readtype=ReaderType.INT)
-    cache.store(data, readtype=ReaderType.RAW)
-    with cache._get_hdfstore() as f:
-        assert "INT/s60/tag1" in f
-        assert "RAW/tag1" in f
-    cache.delete_key("tag1", ReaderType.INT, 60)
-    cache.delete_key("tag1")
-    with cache._get_hdfstore() as f:
-        assert "INT/s60/tag1" not in f
-        assert "RAW/tag1" not in f
-
-
-def test_store_empty_df(cache, data):
+def test_store_empty_df(cache: SmartCache, data):
     # Empty dataframes should not be stored (note: df full of NaN is not empty!)
     cache.store(data, readtype=ReaderType.INT)
     df = pd.DataFrame({"tag1": []})
@@ -170,18 +83,18 @@ def test_store_empty_df(cache, data):
     pd.testing.assert_frame_equal(data, df_read)
 
 
-def test_store_metadata(cache):
-    cache.store_tag_metadata("tag1", {"unit": "%", "desc": "Some description"})
-    cache.store_tag_metadata("tag1", {"max": 60})
-    r = cache.fetch_tag_metadata("tag1", "unit")
+def test_store_metadata(cache: SmartCache):
+    cache.put_metadata("tag1", {"unit": "%", "desc": "Some description"})
+    cache.put_metadata("tag1", {"max": 60})
+    r = cache.get_metadata("tag1", "unit")
     assert "%" == r["unit"]
-    r = cache.fetch_tag_metadata("tag1", ["unit", "max", "noworky"])
+    r = cache.get_metadata("tag1", ["unit", "max", "noworky"])
     assert "%" == r["unit"]
     assert 60 == r["max"]
     assert "noworky" not in r
 
 
-def test_to_DST_skips_time(cache):
+def test_to_DST_skips_time(cache: SmartCache):
     index = pd.date_range(
         start="2018-03-25 01:50:00",
         end="2018-03-25 03:30:00",
@@ -199,7 +112,7 @@ def test_to_DST_skips_time(cache):
     pd.testing.assert_frame_equal(df_read, df)
 
 
-def test_from_DST_folds_time(cache):
+def test_from_DST_folds_time(cache: SmartCache):
     index = pd.date_range(
         start="2017-10-29 00:30:00",
         end="2017-10-29 04:30:00",
diff --git a/tests/test_data_integrity.py b/tests/test_data_integrity.py
index 48cf2387..2abdf736 100644
--- a/tests/test_data_integrity.py
+++ b/tests/test_data_integrity.py
@@ -168,10 +168,8 @@ def test_concat_proper_fill_up(PIClientWeb):
     PIClientWeb.handler._max_rows = max_rows_backup
 
 
-def test_cache_proper_fill_up(PIClientWeb):
-    PIClientWeb.cache = SmartCache(PI_DS)
-    if os.path.exists(PI_DS + ".h5"):
-        os.remove(PI_DS + ".h5")
+def test_cache_proper_fill_up(PIClientWeb, tmp_path):
+    PIClientWeb.cache = SmartCache(tmp_path)
     df_int_1 = PIClientWeb.read(
         PI_TAG, PI_START_TIME, PI_END_TIME, TS, read_type=ReaderType.INT
     )