# ----------------------------------------------------------------------
# Numenta Platform for Intelligent Computing (NuPIC)
# Copyright (C) 2013, Numenta, Inc.  Unless you have purchased from
# Numenta, Inc. a separate commercial license for this software code, the
# following terms and conditions apply:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see http://www.gnu.org/licenses.
#
# http://numenta.org/licenses/
# ----------------------------------------------------------------------

# This script is a wrapper for JSON primitives, such as validation.
# Using routines of this module permits us to replace the underlying
# implementaiton with a better one without distrupting client code.
#
# In particular, at the time of this writing, there weren't really great
# json validation packages available for python.  We initially settled
# on validictory, but it has a number of shortcomings, such as:
#   * format error diagnostic message isn't always helpful for diagnosis
#   * doesn't support references
#   * doesn't support application of defaults
#   * doesn't support dependencies
#
# TODO: offer a combined json parsing/validation function that applies
#       defaults from the schema

import math
import os
import json

import validictory
from validictory import ValidationError as ValidictoryValidationError

ValidationError = ValidictoryValidationError


class NaNInvalidator(validictory.SchemaValidator):
  """ validictory.SchemaValidator subclass to not accept NaN values as numbers.

  Usage:

      validate(value, schemaDict, validator_cls=NaNInvalidator)

  """
  def validate_type_number(self, val):
    return not math.isnan(val) \
      and super(NaNInvalidator, self).validate_type_number(val)



###############################################################################
def validate(value, **kwds):
  """ Validate a python value against json schema:
  validate(value, schemaPath)
  validate(value, schemaDict)

  value:          python object to validate against the schema

  The json schema may be specified either as a path of the file containing
  the json schema or as a python dictionary using one of the
  following keywords as arguments:
    schemaPath:     Path of file containing the json schema object.
    schemaDict:     Python dictionary containing the json schema object

  Returns: nothing

  Raises:
          ValidationError when value fails json validation
  """

  assert len(kwds.keys()) >= 1
  assert 'schemaPath' in kwds or 'schemaDict' in kwds

  schemaDict = None
  if 'schemaPath' in kwds:
    schemaPath = kwds.pop('schemaPath')
    schemaDict = loadJsonValueFromFile(schemaPath)
  elif 'schemaDict' in kwds:
    schemaDict = kwds.pop('schemaDict')

  validictory.validate(value, schemaDict, **kwds)



###############################################################################
def loadJsonValueFromFile(inputFilePath):
  """ Loads a json value from a file and converts it to the corresponding python
  object.

  inputFilePath:
                  Path of the json file;

  Returns:
                  python value that represents the loaded json value

  """
  with open(inputFilePath) as fileObj:
    value = json.load(fileObj)

  return value



###############################################################################
def test():
  """
  """
  import sys

  schemaDict = {
    "description":"JSON schema for jsonhelpers.py test code",
    "type":"object",
    "additionalProperties":False,
    "properties":{
      "myBool":{
        "description":"Some boolean property",
        "required":True,
        "type":"boolean"
      }
    }
  }

  d = {
    'myBool': False
  }

  print "Validating schemaDict method in positive test..."
  validate(d, schemaDict=schemaDict)
  print "ok\n"

  print "Validating schemaDict method in negative test..."
  try:
    validate({}, schemaDict=schemaDict)
  except ValidationError:
    print "ok\n"
  else:
    print "FAILED\n"
    sys.exit(1)


  schemaPath = os.path.join(os.path.dirname(__file__), "testSchema.json")
  print "Validating schemaPath method in positive test using %s..." % \
            (os.path.abspath(schemaPath),)
  validate(d, schemaPath=schemaPath)
  print "ok\n"

  print "Validating schemaPath method in negative test using %s..." % \
            (os.path.abspath(schemaPath),)
  try:
    validate({}, schemaPath=schemaPath)
  except ValidationError:
    print "ok\n"
  else:
    print "FAILED\n"
    sys.exit(1)



  return



###############################################################################
if __name__ == "__main__":
  test()
@@ -1696,20 +1696,20 @@
 ort 
-math
+json
 %0Aimport 
 os%0Ai
@@ -1708,18 +1708,20 @@
 ort 
-os
+math
 %0Aimport 
 json
@@ -1716,20 +1716,19 @@
 %0Aimport 
-json
+os%0A
 %0A%0Aimport
@@ -1744,31 +1744,14 @@
 ory%0A
-from validictory import
+%0Aclass
  Val
@@ -1766,13 +1766,10 @@
 rror
- as V
+(v
 alid
@@ -1766,32 +1766,33 @@
 rror(validictory
+.
 ValidationError%0A
@@ -1794,54 +1794,17 @@
 rror
-%0A%0AValidationError = ValidictoryValidationError
+):%0A  pass
 %0A%0A%0Ac
@@ -3141,16 +3141,25 @@
 Dict')%0A%0A
+  try:%0A  
   validi
@@ -3200,16 +3200,88 @@
 **kwds)%0A
+  except validictory.ValidationError as e:%0A    raise ValidationError(e)%0A
 %0A%0A%0A#####

Raise own ValidationError exception implementation, which happens to be a validictory.ValidationError subclass (which may change at some point in the future), honoring the original intent of this module.