Repository: alecthomas/voluptuous

Issue:
[] should evaluate to an empty list, not any list
```python
>>> from voluptuous import Schema
>>> Schema({})({})
{}
>>> Schema({})({'foo': 1})
Traceback (most recent call last):
...
voluptuous.error.MultipleInvalid: extra keys not allowed @ data['foo']
>>> Schema(int)(5)
5
>>> Schema(2)(5)
Traceback (most recent call last):
...
voluptuous.error.MultipleInvalid: not a valid value
>>> Schema([])([])
[]
>>> Schema([])([1])  # shouldn't this throw error?
[1]
```

Wouldn't it be logical to evaluate `list` for any list and `[]` for an exact value (an empty list)?

Discussion:
tusharmakkar08: Hey @tuukkamustonen , 

Travis build is failing. Please update the PR. 

Thanks
coveralls: 
[![Coverage Status](https://coveralls.io/builds/8039012/badge)](https://coveralls.io/builds/8039012)

Coverage decreased (-3.1%) to 91.593% when pulling **5928c7c122b43764eac4874f63805ab955f225b4 on tuukkamustonen:empty-dict-handling** into **987fd98f88b7bfd5c34f4923056b62097db75deb on alecthomas:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/8039043/badge)](https://coveralls.io/builds/8039043)

Coverage increased (+0.1%) to 94.801% when pulling **dcb757f523211d01f6898e7d43602fa52c9dadbe on tuukkamustonen:empty-dict-handling** into **987fd98f88b7bfd5c34f4923056b62097db75deb on alecthomas:master**.

tuukkamustonen: @tusharmakkar08 Fixed, but I set `+IGNORE_EXCEPTION_DETAIL` doctest flag in `setup.cfg`.

Note that this is backwards-incompatible change: `Schema({}, extra=ALLOW_EXTRA)` does not work anymore, one should use ´Schema(dict)` instead. 

I see there's no `CHANGELOG` in this project, have you thought about adding one?
tusharmakkar08: Hey @tuukkamustonen 

It is preferred not to set `+IGNORE_EXCEPTION_DETAIL` since the content of exception is also important for `voluptuous`. We even have specialised modules like `humanize.py`  to make errors more reader-friendly. I would suggest to use `try-catch` instead of setting the flag.

Currently, there's no `CHANGELOG` in this project. It would be great if you can add it via this PR itself. 

Thanks. 
tuukkamustonen: @tusharmakkar08 Alright, sounds reasonable. I've been a bit busy but I'll update PR tomorrow!

Patch:
diff --git a/.coveragerc b/.coveragerc
deleted file mode 100644
index d978679..0000000
--- a/.coveragerc
+++ /dev/null
@@ -1,2 +0,0 @@
-[run]
-omit = *tests*
diff --git a/.travis.yml b/.travis.yml
index 725c58d..14ed583 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,11 +9,6 @@ python:
   - "3.5"
   - "pypy"
 # command to install dependencies
-install:
-  - pip install coveralls
-  # Need to do this since coverage is broken in travis https://github.com/travis-ci/travis-ci/issues/4866
-  - pip install 'coverage<4'
+#install: "pip install -r requirements.txt --use-mirrors"
 # command to run tests
-script: nosetests --with-coverage --cover-package=voluptuous
-after_success:
-  - coveralls
\ No newline at end of file
+script: nosetests
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..5112703
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,18 @@
+# Changelog
+
+## [Unreleased]
+
+**Changes**:
+
+- [#198](https://github.com/alecthomas/voluptuous/issues/198):
+  `{}` and `[]` now always evaluate as is, instead of as any dict or any list.
+  To specify a free-form list, use `list` instead of `[]`. To specify a
+  free-form dict, use `dict` instead of `Schema({}, extra=ALLOW_EXTRA)`.
+
+**New**:
+
+**Fixes**:
+
+## 0.9.3 (2016-08-03)
+
+Changelog not kept for 0.9.3 and earlier releases.
diff --git a/README.md b/README.md
index fc26f40..f85f838 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
 # Voluptuous is a Python data validation library
 
 [![Build Status](https://travis-ci.org/alecthomas/voluptuous.png)](https://travis-ci.org/alecthomas/voluptuous) [![Stories in Ready](https://badge.waffle.io/alecthomas/voluptuous.png?label=ready&title=Ready)](https://waffle.io/alecthomas/voluptuous)
-[![Coverage Status](https://coveralls.io/repos/github/alecthomas/voluptuous/badge.svg?branch=master)](https://coveralls.io/github/alecthomas/voluptuous?branch=master)
 
 Voluptuous, *despite* the name, is a Python data validation library. It
 is primarily intended for validating data coming into Python as JSON,
@@ -28,6 +27,10 @@ To file a bug, create a [new issue](https://github.com/alecthomas/voluptuous/iss
 
 The documentation is provided [here] (http://alecthomas.github.io/voluptuous/). 
 
+## Changelog
+
+See [CHANGELOG.md](CHANGELOG.md).
+
 ## Show me an example
 
 Twitter's [user search API](https://dev.twitter.com/docs/api/1/get/users/search) accepts
@@ -225,10 +228,13 @@ contain anything, specify it as `list`:
 
 ```pycon
 >>> schema = Schema([])
->>> schema([1])  # doctest: +IGNORE_EXCEPTION_DETAIL
-Traceback (most recent call last):
-    ...
-MultipleInvalid: not a valid value
+>>> try:
+...   schema([1])
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "not a valid value"
+True
 >>> schema([])
 []
 >>> schema = Schema(list)
@@ -361,6 +367,28 @@ token `extra` as a key:
 
 ```
 
+However, an empty dict (`{}`) is treated as is. If you want to specify a list that can
+contain anything, specify it as `dict`:
+
+```pycon
+>>> schema = Schema({}, extra=ALLOW_EXTRA)  # don't do this
+>>> try:
+...   schema({'extra': 1})
+...   raise AssertionError('MultipleInvalid not raised')
+... except MultipleInvalid as e:
+...   exc = e
+>>> str(exc) == "not a valid value"
+True
+>>> schema({})
+{}
+>>> schema = Schema(dict)  # do this instead
+>>> schema({})
+{}
+>>> schema({'extra': 1})
+{'extra': 1}
+
+```
+
 #### Required dictionary keys
 
 By default, keys in the schema are not required to be in the data:
diff --git a/voluptuous/schema_builder.py b/voluptuous/schema_builder.py
index 042a713..04fc86b 100644
--- a/voluptuous/schema_builder.py
+++ b/voluptuous/schema_builder.py
@@ -153,17 +153,6 @@ class Schema(object):
     Nodes can be values, in which case a direct comparison is used, types,
     in which case an isinstance() check is performed, or callables, which will
     validate and optionally convert the value.
-
-    We can equate schemas also.
-
-    For Example:
-
-            >>> v = Schema({Required('a'): unicode})
-            >>> v1 = Schema({Required('a'): unicode})
-            >>> v2 = Schema({Required('b'): unicode})
-            >>> assert v == v1
-            >>> assert v != v2
-
     """
 
     _extra_to_name = {
@@ -192,15 +181,6 @@ class Schema(object):
         self.extra = int(extra)  # ensure the value is an integer
         self._compiled = self._compile(schema)
 
-    def __eq__(self, other):
-        if str(other) == str(self.schema):
-            # Because repr is combination mixture of object and schema
-            return True
-        return False
-
-    def __str__(self):
-        return str(self.schema)
-
     def __repr__(self):
         return "<Schema(%s, extra=%s, required=%s) object at 0x%x>" % (
             self.schema, self._extra_to_name.get(self.extra, '??'),
@@ -221,7 +201,7 @@ class Schema(object):
             return lambda _, v: v
         if isinstance(schema, Object):
             return self._compile_object(schema)
-        if isinstance(schema, collections.Mapping):
+        if isinstance(schema, collections.Mapping) and len(schema):
             return self._compile_dict(schema)
         elif isinstance(schema, list) and len(schema):
             return self._compile_list(schema)
@@ -386,7 +366,7 @@ class Schema(object):
 
         A dictionary schema will only validate a dictionary:
 
-            >>> validate = Schema({})
+            >>> validate = Schema({'prop': str})
             >>> with raises(er.MultipleInvalid, 'expected a dictionary'):
             ...   validate([])
 
@@ -401,7 +381,6 @@ class Schema(object):
             >>> with raises(er.MultipleInvalid, "extra keys not allowed @ data['two']"):
             ...   validate({'two': 'three'})
 
-
         Validation function, in this case the "int" type:
 
             >>> validate = Schema({'one': 'two', 'three': 'four', int: str})
@@ -411,10 +390,17 @@ class Schema(object):
             >>> validate({10: 'twenty'})
             {10: 'twenty'}
 
+        An empty dictionary is matched as value:
+
+            >>> validate = Schema({})
+            >>> with raises(er.MultipleInvalid, 'not a valid value'):
+            ...   validate([])
+
         By default, a "type" in the schema (in this case "int") will be used
         purely to validate that the corresponding value is of that type. It
         will not Coerce the value:
 
+            >>> validate = Schema({'one': 'two', 'three': 'four', int: str})
             >>> with raises(er.MultipleInvalid, "extra keys not allowed @ data['10']"):
             ...   validate({'10': 'twenty'})
 
@@ -1073,7 +1059,7 @@ def validate(*a, **kw):
     Set restriction for returned value:
 
         >>> @validate(arg=int, __return__=int)
-        ... def bar(arg1):
+        ... def foo(arg1):
         ...   return arg1 * 2
 
     """


Test patch:
diff --git a/voluptuous/tests/tests.py b/voluptuous/tests/tests.py
index 922d62b..debd09a 100644
--- a/voluptuous/tests/tests.py
+++ b/voluptuous/tests/tests.py
@@ -423,6 +423,7 @@ def test_fix_157():
     assert_raises(MultipleInvalid, s, ['four'])
 
 
+
 def test_range_exlcudes_nan():
     s = Schema(Range(min=0, max=10))
     assert_raises(MultipleInvalid, s, float('nan'))
@@ -478,6 +479,40 @@ def test_empty_list_as_exact():
     s([])
 
 
+def test_empty_dict_as_exact():
+    # {} always evaluates as {}
+    s = Schema({})
+    assert_raises(Invalid, s, {'extra': 1})
+    s = Schema({}, extra=ALLOW_EXTRA)  # this should not be used
+    assert_raises(Invalid, s, {'extra': 1})
+
+    # {...} evaluates as Schema({...})
+    s = Schema({'foo': int})
+    assert_raises(Invalid, s, {'foo': 1, 'extra': 1})
+    s = Schema({'foo': int}, extra=ALLOW_EXTRA)
+    s({'foo': 1, 'extra': 1})
+
+    # dict matches {} or {...}
+    s = Schema(dict)
+    s({'extra': 1})
+    s({})
+    s = Schema(dict, extra=PREVENT_EXTRA)
+    s({'extra': 1})
+    s({})
+
+    # nested {} evaluate as {}
+    s = Schema({
+        'inner': {}
+    }, extra=ALLOW_EXTRA)
+    assert_raises(Invalid, s, {'inner': {'extra': 1}})
+    s({})
+    s = Schema({
+        'inner': Schema({}, extra=ALLOW_EXTRA)
+    })
+    assert_raises(Invalid, s, {'inner': {'extra': 1}})
+    s({})
+
+
 def test_schema_decorator_match_with_args():
     @validate(int)
     def fn(arg):