from __future__ import print_function

import json

from faker import Faker
from faker.config import DEFAULT_LOCALE


def load_file(file_path):
    with open(file_path) as f:
        return json.load(f)


class FakerSchema(object):
    
    def __init__(self, schema, faker=None, locale=None, providers=None, includes=None):
        self._faker = faker or Faker(locale=locale, providers=providers, includes=includes)
        self._schema = schema
        
    def generate_fake(self, iterations=1):
        result = [self._generate_one_fake() for _ in range(iterations)]
        return result[0] if len(result) == 1 else result

    def _generate_one_fake(self):
        fake_data = {}
        for key, key_type in self._schema.items():
            try:
                fake_data[key] = self._generate(key_type)
            except AttributeError:
                raise ValueError('No faker found for key "{}", which has type "{}".'
                                 .format(key, key_type))
        return fake_data

    def _generate(self, key_type):
        if isinstance(key_type, list):
            result = []
            for item in key_type:
                for k, v in list(item.items()):
                    result.append({k: getattr(self._faker, v)()})
            return result
        return getattr(self._faker, key_type)()

    @classmethod
    def from_json_file(cls, json_file, *args, **kwargs):
        try:
            json_data = load_file(json_file)
        except ValueError as e:
            raise ValueError('Given file {} is not a valid JSON file: {}'.format(json_file, e))
        else:    
            return cls(json_data, *args, **kwargs)

    @classmethod
    def from_json_string(cls, json_string, *args, **kwargs):
        try:
            json_data = json.loads(json_string)
        except ValueError as e:
            raise ValueError('Given string is not valid JSON: {}'.format(e))
        else:    
            return cls(json_data, *args, **kwargs)
@@ -534,16 +534,28 @@
 ne_fake(
+self._schema
 ) for _ 
@@ -665,16 +665,24 @@
 ake(self
+, schema
 ):%0A     
@@ -688,395 +688,545 @@
     
-fake_data = %7B%7D%0A        for key, key_type in self._schema.items():%0A            try:%0A                fake_data%5Bkey%5D = self._generate(key_type)%0A            except AttributeError:%0A                raise ValueError('No faker found for key %22%7B%7D%22, which has type %22%7B%7D%22.'%0A                                 .format(key, key_type))%0A        return fake_data%0A%0A    def _generate(self, key_type):%0A
+%22%22%22%0A        Recursively traverse schema dictionary and for each %22leaf node%22, evaluate the fake%0A        value%0A%0A        Implementation:%0A        For each key-value pair:%0A        1) If value is not an iterable (i.e. dict or list), evaluate the fake data (base case)%0A        2) If value is a dictionary, recurse%0A        3) If value is a list, iteratively recurse over each item%0A        %22%22%22%0A        data = %7B%7D%0A        for k, v in schema.items():%0A            if isinstance(v, dict):%0A                data%5Bk%5D = self._generate_one_fake(v)%0A 
         
 if i
@@ -1213,32 +1213,37 @@
 ake(v)%0A         
+   el
 if isinstance(ke
@@ -1240,24 +1240,17 @@
 nstance(
-key_type
+v
 , list):
@@ -1266,31 +1266,52 @@
     
-result = %5B%5D%0A           
+    data%5Bk%5D = %5Bself._generate_one_fake(item)
  for
@@ -1323,17 +1323,10 @@
  in 
-key_type:
+v%5D
 %0A   
@@ -1338,81 +1338,39 @@
     
-    for k, v in list(item.items()):%0A                    result.append(%7Bk:
+else:%0A                data%5Bk%5D =
  get
@@ -1395,22 +1395,17 @@
 v)()
-%7D)%0A   
+%0A
         
  ret
@@ -1404,70 +1404,19 @@
     
- 
 return 
-result%0A        return getattr(self._faker, key_type)()
+data
 %0A%0A  

Implement ability to generate fake data with a nested schema