Repository: horejsek/python-fastjsonschema

Issue:
errors for missing required properties
currently, if a required property is missing, the error message lists _all the required properties. could we consider listing only the missing ones? 

```
>>> fastjsonschema.validate({'required': ['foo', 'bar']}, {'foo': 1})
JsonSchemaValueException: data must contain ['foo', 'bar'] properties
```

this is particularly useful with a large number of required properties where eg only one may be missing

Discussion:

Patch:
diff --git a/fastjsonschema/draft04.py b/fastjsonschema/draft04.py
index cfc3b7b..25cb374 100644
--- a/fastjsonschema/draft04.py
+++ b/fastjsonschema/draft04.py
@@ -457,9 +457,10 @@ class CodeGeneratorDraft04(CodeGenerator):
         with self.l('if {variable}_is_dict:'):
             if not isinstance(self._definition['required'], (list, tuple)):
                 raise JsonSchemaDefinitionException('required must be an array')
-            self.create_variable_with_length()
-            with self.l('if not all(prop in {variable} for prop in {required}):'):
-                self.exc('{name} must contain {} properties', self.e(self._definition['required']), rule='required')
+            self.l('{variable}__missing_keys = set({required}) - {variable}.keys()')
+            with self.l('if {variable}__missing_keys:'):
+                dynamic = 'str(sorted({variable}__missing_keys)) + " properties"'
+                self.exc('{name} must contain ', self.e(self._definition['required']), rule='required', append_to_msg=dynamic)
 
     def generate_properties(self):
         """


Test patch:
diff --git a/tests/test_integration.py b/tests/test_integration.py
index d778b63..4ff8ee6 100644
--- a/tests/test_integration.py
+++ b/tests/test_integration.py
@@ -84,9 +84,13 @@ definition = {
         [9, 'hello', [1, 2, 3], {'a': 'a', 'b': 'b', 'c': 'xy'}, 'str', 5],
         JsonSchemaValueException('data[2][1] must be string', value=2, name='data[2][1]', definition={'type': 'string'}, rule='type'),
     ),
+    (
+        [9, 'hello', [1], {'q': 'q', 'x': 'x', 'y': 'y'}, 'str', 5],
+        JsonSchemaValueException('data[3] must contain [\'a\', \'b\'] properties', value={'q': 'q', 'x': 'x', 'y': 'y'}, name='data[3]', definition=definition['items'][3], rule='required'),
+    ),
     (
         [9, 'hello', [1], {'a': 'a', 'x': 'x', 'y': 'y'}, 'str', 5],
-        JsonSchemaValueException('data[3] must contain [\'a\', \'b\'] properties', value={'a': 'a', 'x': 'x', 'y': 'y'}, name='data[3]', definition=definition['items'][3], rule='required'),
+        JsonSchemaValueException('data[3] must contain [\'b\'] properties', value={'a': 'a', 'x': 'x', 'y': 'y'}, name='data[3]', definition=definition['items'][3], rule='required'),
     ),
     (
         [9, 'hello', [1], {}, 'str', 5],
diff --git a/tests/test_object.py b/tests/test_object.py
index 19967a3..228aa9d 100644
--- a/tests/test_object.py
+++ b/tests/test_object.py
@@ -43,10 +43,11 @@ def test_min_properties(asserter, value, expected):
     }, value, expected)
 
 
-exc = JsonSchemaValueException('data must contain [\'a\', \'b\'] properties', value='{data}', name='data', definition='{definition}', rule='required')
+def make_exc(missing):
+    return JsonSchemaValueException('data must contain {} properties'.format(missing), value='{data}', name='data', definition='{definition}', rule='required')
 @pytest.mark.parametrize('value, expected', [
-    ({}, exc),
-    ({'a': 1}, exc),
+    ({}, make_exc(['a', 'b'])),
+    ({'a': 1}, make_exc(['b'])),
     ({'a': 1, 'b': 2}, {'a': 1, 'b': 2}),
 ])
 def test_required(asserter, value, expected):