#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Proxy to abstract access to JSON configuration and gracefully handle missing
keys."""
from __future__ import print_function, unicode_literals, absolute_import
import sys, os, json

if sys.version_info[0] == 3:  # Alternate import names
    enc_dict = {}
else:
    enc_dict = {'encoding': 'utf-8'}

class JSONConfiguration(object):
    """Proxy for JSON-based configuration files."""

    def __init__(self, filename, default=None):
        """
        Constructor for JSONConfiguration.

        Params:
            filename
                JSON filename to load data from.
            default
                Default value to use in case loading fails.
        """
        self.filename = filename
        self.data = default if default else {}
        if not os.path.isfile(filename):
            print(
                "File " + filename + " does not exist",
                file=sys.stderr)
            return
        try:
            #pylint: disable=bare-except
            self.data = json.load(open(filename), **enc_dict)
        except:
            print(
                "Note: Failed to read JSON from " + filename +
                ", ignoring data - error details follow", file=sys.stderr)
            sys.excepthook(*sys.exc_info())

    def save_data(self):
        """Saves the data to the JSON file."""
        json.dump(
            self.data, open(self.filename, 'w'), indent=2, **enc_dict)

    def get_value(self, path, default=None):
        """
        Retrieves a value from the configuration.
        Returns default if the path does not exist.

        Params:
            path
                /-delimited path to the string.
            default
                Value returned if path does not exist.
        """
        try:
            path = path.split('/')
            result = self.data
            for p in path:
                result = result[p]
            return result
        except KeyError:
            return default

    def get_string(self, path):
        """
        Retrieves a value from the configuration.
        Returns an empty string if the path does not exist.

        Params:
            path
                /-delimited path to the string.
        """
        return self.get_value(path, "")

    def get_number(self, path):
        """
        Retrieves a value from the configuration.
        Returns 0 if the path does not exist.

        Params:
            path
                /-delimited path to the string.
        """
        return self.get_value(path, 0)

    def get_bool(self, path):
        """
        Retrieves a value from the configuration.
        Returns False if the path does not exist.

        Params:
            path
                /-delimited path to the string.
        """
        return self.get_value(path, False)

    def get_list(self, path):
        """
        Retrieves a value from the configuration.
        Returns an empty list if the path does not exist.

        Params:
            path
                /-delimited path to the string.
        """
        return self.get_value(path, [])

    def get_dict(self, path):
        """
        Retrieves a value from the configuration.
        Returns an empty dictionary if the path does not exist.

        Params:
            path
                /-delimited path to the string.
        """
        return self.get_value(path, {})

    def set_value(self, key, value):
        """
        Writes a value to a key.
        Note: Arbitrary paths not supported - you must refresh entire key.

        Params:
            key
                The key to save the value under.
            value
                The value to save.
        """
        self.__setitem__(key, value)

    def __getitem__(self, key):
        """Accessor for indexing directly into the configuration."""
        return self.get_value(key)

    def __setitem__(self, key, value):
        """Accessor for writing into the configuration with indexing."""
        self.data[key] = value

@@ -615,16 +615,74 @@
 ta from.
+ Use None to only use object%0A                from default.
 %0A       
@@ -846,16 +846,64 @@
 else %7B%7D%0A
+        if filename is None:%0A            return%0A
         
@@ -1396,16 +1396,180 @@
 nfo())%0A%0A
+    @staticmethod%0A    def from_text(text):%0A        %22%22%22Create a JSONConfiguration object from a string.%22%22%22%0A        return JSONConfiguration(None, json.loads(text))%0A%0A
     def 
@@ -1618,16 +1618,25 @@
  to the 
+original 
 JSON fil
@@ -1637,20 +1637,118 @@
 ON file.
-%22%22%22%0A
+ Has no effect if no%0A        filename was given during construction.%22%22%22%0A        if self.filename:%0A    
         
@@ -1758,16 +1758,20 @@
 n.dump(%0A
+    
         
@@ -1834,16 +1834,17 @@
 _dict)%0A%0A
+%0A
     def 
@@ -1846,22 +1846,16 @@
  def get
-_value
 (self, p
@@ -2377,16 +2377,385 @@
 efault%0A%0A
+    def get_value(self, path, default=None):%0A        %22%22%22%0A        Retrieves a value from the configuration.%0A        Returns default if the path does not exist.%0A%0A        Params:%0A            path%0A                /-delimited path to the string.%0A            default%0A                Value returned if path does not exist.%0A        %22%22%22%0A        return self.get(path, default)%0A%0A%0A
     def 

Allow construction of JSONConfiguration from string Add get as alias for get_value in JSONConfiguration