mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-05-18 10:40:33 +02:00
Optimized safe logic
This commit is contained in:
parent
cc399e3899
commit
2506065142
55
filter_plugins/safe.py
Normal file
55
filter_plugins/safe.py
Normal file
@ -0,0 +1,55 @@
|
||||
from jinja2 import Undefined
|
||||
|
||||
|
||||
def safe_placeholders(template: str, mapping: dict = None) -> str:
|
||||
"""
|
||||
Format a template like "{url}/logo.png".
|
||||
If mapping is provided (not None) and ANY placeholder is missing or maps to None/empty string, the function will raise KeyError.
|
||||
If mapping is None, missing placeholders or invalid templates return empty string.
|
||||
Numerical zero or False are considered valid values.
|
||||
Any other formatting errors return an empty string.
|
||||
"""
|
||||
# Non-string templates yield empty
|
||||
if not isinstance(template, str):
|
||||
return ''
|
||||
|
||||
class SafeDict(dict):
|
||||
def __getitem__(self, key):
|
||||
val = super().get(key, None)
|
||||
# Treat None or empty string as missing
|
||||
if val is None or (isinstance(val, str) and val == ''):
|
||||
raise KeyError(key)
|
||||
return val
|
||||
def __missing__(self, key):
|
||||
raise KeyError(key)
|
||||
|
||||
silent = mapping is None
|
||||
data = mapping or {}
|
||||
try:
|
||||
return template.format_map(SafeDict(data))
|
||||
except KeyError:
|
||||
if silent:
|
||||
return ''
|
||||
raise
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
def safe_var(value):
|
||||
"""
|
||||
Ansible filter: returns the value unchanged unless it's Undefined or None,
|
||||
in which case returns an empty string.
|
||||
Catches all exceptions and yields ''.
|
||||
"""
|
||||
try:
|
||||
if isinstance(value, Undefined) or value is None:
|
||||
return ''
|
||||
return value
|
||||
except Exception:
|
||||
return ''
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
'safe_var': safe_var,
|
||||
'safe_placeholders': safe_placeholders,
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
# file: filter_plugins/safe_var.py
|
||||
|
||||
from jinja2 import Undefined
|
||||
|
||||
def safe_var(value):
|
||||
"""
|
||||
Returns the original value unless it is None or Jinja2‐Undefined.
|
||||
Catches all exceptions and returns an empty string on error.
|
||||
"""
|
||||
try:
|
||||
# If the value is an Undefined from Jinja2, treat it as missing
|
||||
if isinstance(value, Undefined):
|
||||
return ''
|
||||
# Treat None as missing as well
|
||||
if value is None:
|
||||
return ''
|
||||
# Otherwise return the actual value
|
||||
return value
|
||||
except Exception:
|
||||
# Catch any other errors and return empty string
|
||||
return ''
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
'safe_var': safe_var
|
||||
}
|
59
tests/unit/test_safe_placeholders_filter.py
Normal file
59
tests/unit/test_safe_placeholders_filter.py
Normal file
@ -0,0 +1,59 @@
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Ensure filter_plugins directory is on the path
|
||||
sys.path.insert(
|
||||
0,
|
||||
os.path.abspath(os.path.join(os.path.dirname(__file__), '../../filter_plugins'))
|
||||
)
|
||||
|
||||
from safe import safe_placeholders
|
||||
|
||||
class TestSafePlaceholdersFilter(unittest.TestCase):
|
||||
def test_simple_replacement(self):
|
||||
template = "Hello, {user}!"
|
||||
mapping = {'user': 'Alice'}
|
||||
self.assertEqual(safe_placeholders(template, mapping), "Hello, Alice!")
|
||||
|
||||
def test_missing_placeholder(self):
|
||||
template = "Hello, {user}!"
|
||||
# Missing placeholder should raise KeyError
|
||||
with self.assertRaises(KeyError):
|
||||
safe_placeholders(template, {})
|
||||
|
||||
def test_none_template(self):
|
||||
self.assertEqual(safe_placeholders(None, {'user': 'Alice'}), "")
|
||||
|
||||
def test_no_placeholders(self):
|
||||
template = "Just a plain string"
|
||||
mapping = {'any': 'value'}
|
||||
self.assertEqual(safe_placeholders(template, mapping), "Just a plain string")
|
||||
|
||||
def test_multiple_placeholders(self):
|
||||
template = "{greet}, {user}!"
|
||||
mapping = {'greet': 'Hi', 'user': 'Bob'}
|
||||
self.assertEqual(safe_placeholders(template, mapping), "Hi, Bob!")
|
||||
|
||||
def test_numeric_values(self):
|
||||
template = "Count: {n}"
|
||||
mapping = {'n': 0}
|
||||
self.assertEqual(safe_placeholders(template, mapping), "Count: 0")
|
||||
|
||||
def test_extra_mapping_keys(self):
|
||||
template = "Value: {a}"
|
||||
mapping = {'a': '1', 'b': '2'}
|
||||
self.assertEqual(safe_placeholders(template, mapping), "Value: 1")
|
||||
|
||||
def test_malformed_template(self):
|
||||
# Unclosed placeholder should be caught and return empty string
|
||||
template = "Unclosed {key"
|
||||
mapping = {'key': 'value'}
|
||||
self.assertEqual(safe_placeholders(template, mapping), "")
|
||||
|
||||
def test_mapping_none(self):
|
||||
template = "Test {x}"
|
||||
self.assertEqual(safe_placeholders(template, None), "")
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -9,7 +9,7 @@ sys.path.insert(
|
||||
os.path.abspath(os.path.join(os.path.dirname(__file__), '../../filter_plugins'))
|
||||
)
|
||||
|
||||
from safe_var import FilterModule
|
||||
from safe import FilterModule
|
||||
|
||||
class TestSafeVarFilter(unittest.TestCase):
|
||||
def setUp(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user