Skip to content

Instantly share code, notes, and snippets.

@psobot
Created August 8, 2012 20:28
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save psobot/3298353 to your computer and use it in GitHub Desktop.
Save psobot/3298353 to your computer and use it in GitHub Desktop.
Real-time YAML object access in Python
"""
liveyamlfile.py
Live Pythonic attribute access to properties in a yaml file.
by Peter Sobot (hi@psobot.com), August 8, 2012
"""
import os
import time
import yaml
import logging
class LiveYamlFile(object):
"""
Magical class that allows for real-time access of variables in a yaml
file via Pythonic attribute-like access.
Any functions, objects or properties on the class (or its subclasses)
will not be looked up in the yaml file. Every access may result in at
worst, a YAML load and at best, an os.stat call at most every __timeout
seconds.
Simple example usage:
> my_object = yaml_file("file.yml")
> my_object.some_changing_property
5
> # (now here, file.yml has changed outside of python)
> my_object.some_changing_property
6
> my_object.blah # blah is not defined in the yaml file
AttributeError: object my_object has no attribute 'blah'
"""
__last_updated = 0
__timeout = 5 # seconds
__exclude = []
def __init__(self, filename):
self.__file = filename
# Any subclass's properties will be ignored here
supers = LiveYamlFile.__dict__
subs = self.__class__.__dict__
self.__exclude = [k for k, _ in
dict(supers.items() + subs.items()).iteritems()
if not k.startswith("_")]
def update(self):
"""
Update the object's attributes from the YAML file.
"""
target_file = open(self.__file)
for attr in dir(self):
if not attr.startswith("_") and attr not in self.__exclude:
delattr(self, attr)
for key, val in yaml.load(target_file).iteritems():
if not key.startswith("_") and key not in self.__exclude:
setattr(self, key, val)
target_file.close()
if hasattr(self, 'log_config_file_changes')\
and self.log_config_file_changes:
logging.getLogger(__name__).info("Config file has updated.")
def __getattribute__(self, name):
"""
When trying to access an attribute, check if the underlying file
has changed first. Best case: reads from Python cache. Worst case:
performs an os.stat and a YAML load every __timeout seconds.
"""
if name.startswith("_") or name in self.__exclude:
return object.__getattribute__(self, name)
else:
last_updated = self.__last_updated
# Only check once every __timeout seconds
if (time.time() - last_updated) > self.__timeout:
fmod_time = os.stat(self.__file)[9]
if last_updated < fmod_time:
self.__last_updated = fmod_time
self.update()
return object.__getattribute__(self, name)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment