Skip to content

Commit

Permalink
Add cache file versioning, distrust cache with different version
Browse files Browse the repository at this point in the history
  • Loading branch information
TojikCZ committed Oct 23, 2023
1 parent 803cc90 commit 2e37f6c
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 8 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ ChangeLog
0.2.0dev
* Improve automatic thumbnail and preview selection
* `sl1s` added as sla extension gcode type
* Add cache file versioning, distrust cache files with a different version

0.1.0
* Initial release
38 changes: 36 additions & 2 deletions gcode_metadata/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import zipfile
from typing import Dict, Any, Type, Callable, List, Optional
from logging import getLogger
from importlib.metadata import version

GCODE_EXTENSIONS = (".gcode", ".gc", ".g", ".gco")
SLA_EXTENSIONS = ("sl1", "sl1s")
Expand Down Expand Up @@ -238,14 +239,46 @@ def cache_name(self):
return new_path

def is_cache_fresh(self):
"""If cache is fresher than file, returns True"""
"""Checks if we can use the current cache file"""
return self.is_cache_recent() and self.is_cache_correct_version()

def is_cache_recent(self):
"""Checks if the cache file is newer than the source file"""
try:
file_time_created = os.path.getctime(self.path)
cache_time_created = os.path.getctime(self.cache_name)
return file_time_created < cache_time_created
except FileNotFoundError:
return False

return file_time_created > cache_time_created

def is_cache_correct_version(self):
"""Checks if the cache file was created with the same version
of gcode-metadata"""

def isallowed(char):
"""Filters out dissalowed characters, used with str.filter"""
return char not in "\",\n} "

# This expects the first item in the json file to be the
# gcode-metadata version, with which the cache was created.
# If it's not there, or the version is different, the cache is deleted
with open(self.cache_name, "r", encoding="utf-8") as file:
for line in file:
if text := "".join(filter(str.isalpha, line)):
if text.startswith("version"):
break
return False
else: # didn't reach break
return False
first_pair = line.split(",", 1)[0]
_, version_part = first_pair.split(":", 1)
file_version = "".join(filter(isallowed, version_part))
if file_version == version('gcode-metadata'):
return True

return False

def save_cache(self):
"""Take metadata from source file and save them as JSON to
<file_name>.cache file.
Expand All @@ -264,6 +297,7 @@ def get_cache_data(info):
try:
if self.data:
cache = {
"version": version('gcode-metadata'),
"metadata": self.data,
}

Expand Down
53 changes: 47 additions & 6 deletions tests/test_metadata.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""Tests for gcode-metadata tool for g-code files."""
import json
import os
import tempfile
import shutil
from importlib.metadata import version

import time
import pytest

Expand All @@ -23,6 +26,18 @@ def tmp_dir():
del temp


def give_cache_version(path, version_to_give):
"""Modifies the cache file and adds a valid version number"""
with open(path, "r", encoding='utf-8') as cache_file:
cache = json.load(cache_file)
my_cache = {
"version": version_to_give
}
my_cache.update(cache)
with open(path, "w", encoding='utf-8') as cache_file:
json.dump(my_cache, cache_file)


def test_get_metadata_file_does_not_exist():
"""Test get_metadata() with a non-existing file"""
fname = '/somehwere/in/the/rainbow/my.gcode'
Expand Down Expand Up @@ -56,26 +71,52 @@ def test_load_cache_key_error():
MetaData(fname).load_cache()


def test_is_cache_fresh_fresher(tmp_dir):
"""is_cache_fresh, when cache file is fresher, than original file"""
def test_is_cache_recent_fresher(tmp_dir):
"""is_cache_recent, when cache file is fresher, than original file"""
fn_gcode = os.path.join(gcodes_dir, "fdn_filename.gcode")
temp_gcode = shutil.copy(fn_gcode, tmp_dir)
# Create the time difference
time.sleep(0.01)
fn_cache = os.path.join(gcodes_dir, ".fdn_filename.gcode.cache")
shutil.copy(fn_cache, tmp_dir)
assert MetaData(temp_gcode).is_cache_fresh()
assert MetaData(temp_gcode).is_cache_recent()


def test_is_cache_fresh_older(tmp_dir):
"""is_cache_fresh, when cache file is older, than original file"""
def test_is_cache_recent_older(tmp_dir):
"""is_cache_recent, when cache file is older, than original file"""
fn_cache = os.path.join(gcodes_dir, ".fdn_filename.gcode.cache")
shutil.copy(fn_cache, tmp_dir)
# Create the time difference
time.sleep(0.01)
fn_gcode = os.path.join(gcodes_dir, "fdn_filename.gcode")
temp_gcode = shutil.copy(fn_gcode, tmp_dir)
assert MetaData(temp_gcode).is_cache_fresh() is False
assert MetaData(temp_gcode).is_cache_recent() is False


def test_is_cache_correct_version_none():
"""is_cache_correct_version, when cache file has no version"""
fn_gcode = os.path.join(gcodes_dir, "fdn_filename.gcode")
assert not MetaData(fn_gcode).is_cache_correct_version()


def test_is_cache_correct_version_mismatch(tmp_dir):
"""is_cache_correct_version, when cache file has different version"""
fn_cache = os.path.join(gcodes_dir, ".fdn_filename.gcode.cache")
fn_gcode = os.path.join(gcodes_dir, "fdn_filename.gcode")
temp_gcode = shutil.copy(fn_gcode, tmp_dir)
cache_path = shutil.copy(fn_cache, tmp_dir)
give_cache_version(cache_path, "0.0.0")
assert MetaData(temp_gcode).is_cache_correct_version() is False


def test_is_cache_correct_version_match(tmp_dir):
"""is_cache_correct_version, when cache file has the same version"""
fn_cache = os.path.join(gcodes_dir, ".fdn_filename.gcode.cache")
fn_gcode = os.path.join(gcodes_dir, "fdn_filename.gcode")
temp_gcode = shutil.copy(fn_gcode, tmp_dir)
cache_path = shutil.copy(fn_cache, tmp_dir)
give_cache_version(cache_path, version('gcode-metadata'))
assert MetaData(temp_gcode).is_cache_correct_version() is True


def test_get_metadata_invalid_file():
Expand Down

0 comments on commit 2e37f6c

Please sign in to comment.