Added write only property functions for issue #1142 (#1144)

py::class_<T>'s `def_property` and `def_property_static` can now take a
`nullptr` as the getter to allow a write-only property to be established
(mirroring Python's `property()` built-in when `None` is given for the
getter).

This also updates properties to use the new nullptr constructor internally.
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 8b7047d..b0d958d 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -15,6 +15,9 @@
   for non-MSVC compilers).
   `#934 <https://github.com/pybind/pybind11/pull/934>`_.
 
+* Added support for write only properties.
+  `#1144 <https://github.com/pybind/pybind11/pull/1144>`_.
+
 v2.2.1 (September 14, 2017)
 -----------------------------------------------------
 
diff --git a/docs/classes.rst b/docs/classes.rst
index ca2477e..890257d 100644
--- a/docs/classes.rst
+++ b/docs/classes.rst
@@ -155,6 +155,9 @@
             .def_property("name", &Pet::getName, &Pet::setName)
             // ... remainder ...
 
+Write only properties can be defined by passing ``nullptr`` as the
+input for the read function.
+
 .. seealso::
 
     Similar functions :func:`class_::def_readwrite_static`,
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index 30c6917..b489bb2 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -51,6 +51,7 @@
 class cpp_function : public function {
 public:
     cpp_function() { }
+    cpp_function(std::nullptr_t) { }
 
     /// Construct a cpp_function from a vanilla function pointer
     template <typename Return, typename... Args, typename... Extra>
@@ -951,18 +952,18 @@
         tinfo->get_buffer_data = get_buffer_data;
     }
 
+    // rec_func must be set for either fget or fset.
     void def_property_static_impl(const char *name,
                                   handle fget, handle fset,
-                                  detail::function_record *rec_fget) {
-        const auto is_static = !(rec_fget->is_method && rec_fget->scope);
-        const auto has_doc = rec_fget->doc && pybind11::options::show_user_defined_docstrings();
-
+                                  detail::function_record *rec_func) {
+        const auto is_static = rec_func && !(rec_func->is_method && rec_func->scope);
+        const auto has_doc = rec_func && rec_func->doc && pybind11::options::show_user_defined_docstrings();
         auto property = handle((PyObject *) (is_static ? get_internals().static_property_type
                                                        : &PyProperty_Type));
         attr(name) = property(fget.ptr() ? fget : none(),
                               fset.ptr() ? fset : none(),
                               /*deleter*/none(),
-                              pybind11::str(has_doc ? rec_fget->doc : ""));
+                              pybind11::str(has_doc ? rec_func->doc : ""));
     }
 };
 
@@ -1196,7 +1197,7 @@
     /// Uses cpp_function's return_value_policy by default
     template <typename... Extra>
     class_ &def_property_readonly(const char *name, const cpp_function &fget, const Extra& ...extra) {
-        return def_property(name, fget, cpp_function(), extra...);
+        return def_property(name, fget, nullptr, extra...);
     }
 
     /// Uses return_value_policy::reference by default
@@ -1208,7 +1209,7 @@
     /// Uses cpp_function's return_value_policy by default
     template <typename... Extra>
     class_ &def_property_readonly_static(const char *name, const cpp_function &fget, const Extra& ...extra) {
-        return def_property_static(name, fget, cpp_function(), extra...);
+        return def_property_static(name, fget, nullptr, extra...);
     }
 
     /// Uses return_value_policy::reference_internal by default
@@ -1238,21 +1239,25 @@
     template <typename... Extra>
     class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const Extra& ...extra) {
         auto rec_fget = get_function_record(fget), rec_fset = get_function_record(fset);
-        char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */
-        detail::process_attributes<Extra...>::init(extra..., rec_fget);
-        if (rec_fget->doc && rec_fget->doc != doc_prev) {
-            free(doc_prev);
-            rec_fget->doc = strdup(rec_fget->doc);
+        auto *rec_active = rec_fget;
+        if (rec_fget) {
+           char *doc_prev = rec_fget->doc; /* 'extra' field may include a property-specific documentation string */
+           detail::process_attributes<Extra...>::init(extra..., rec_fget);
+           if (rec_fget->doc && rec_fget->doc != doc_prev) {
+              free(doc_prev);
+              rec_fget->doc = strdup(rec_fget->doc);
+           }
         }
         if (rec_fset) {
-            doc_prev = rec_fset->doc;
+            char *doc_prev = rec_fset->doc;
             detail::process_attributes<Extra...>::init(extra..., rec_fset);
             if (rec_fset->doc && rec_fset->doc != doc_prev) {
                 free(doc_prev);
                 rec_fset->doc = strdup(rec_fset->doc);
             }
+            if (! rec_active) rec_active = rec_fset;
         }
-        def_property_static_impl(name, fget, fset, rec_fget);
+        def_property_static_impl(name, fget, fset, rec_active);
         return *this;
     }
 
diff --git a/tests/test_methods_and_attributes.cpp b/tests/test_methods_and_attributes.cpp
index cd15869..fde152b 100644
--- a/tests/test_methods_and_attributes.cpp
+++ b/tests/test_methods_and_attributes.cpp
@@ -279,12 +279,20 @@
         .def(py::init<>())
         .def_readonly("def_readonly", &TestProperties::value)
         .def_readwrite("def_readwrite", &TestProperties::value)
+        .def_property("def_writeonly", nullptr,
+                      [](TestProperties& s,int v) { s.value = v; } )
+        .def_property("def_property_writeonly", nullptr, &TestProperties::set)
         .def_property_readonly("def_property_readonly", &TestProperties::get)
         .def_property("def_property", &TestProperties::get, &TestProperties::set)
+        .def_property("def_property_impossible", nullptr, nullptr)
         .def_readonly_static("def_readonly_static", &TestProperties::static_value)
         .def_readwrite_static("def_readwrite_static", &TestProperties::static_value)
+        .def_property_static("def_writeonly_static", nullptr,
+                             [](py::object, int v) { TestProperties::static_value = v; })
         .def_property_readonly_static("def_property_readonly_static",
                                       [](py::object) { return TestProperties::static_get(); })
+        .def_property_static("def_property_writeonly_static", nullptr,
+                             [](py::object, int v) { return TestProperties::static_set(v); })
         .def_property_static("def_property_static",
                              [](py::object) { return TestProperties::static_get(); },
                              [](py::object, int v) { TestProperties::static_set(v); })
diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py
index 9fd9cb7..86b2c3b 100644
--- a/tests/test_methods_and_attributes.py
+++ b/tests/test_methods_and_attributes.py
@@ -98,6 +98,21 @@
     instance.def_property = 3
     assert instance.def_property == 3
 
+    with pytest.raises(AttributeError) as excinfo:
+        dummy = instance.def_property_writeonly  # noqa: F841 unused var
+    assert "unreadable attribute" in str(excinfo)
+
+    instance.def_property_writeonly = 4
+    assert instance.def_property_readonly == 4
+
+    with pytest.raises(AttributeError) as excinfo:
+        dummy = instance.def_property_impossible  # noqa: F841 unused var
+    assert "unreadable attribute" in str(excinfo)
+
+    with pytest.raises(AttributeError) as excinfo:
+        instance.def_property_impossible = 5
+    assert "can't set attribute" in str(excinfo)
+
 
 def test_static_properties():
     assert m.TestProperties.def_readonly_static == 1
@@ -108,13 +123,27 @@
     m.TestProperties.def_readwrite_static = 2
     assert m.TestProperties.def_readwrite_static == 2
 
-    assert m.TestProperties.def_property_readonly_static == 2
     with pytest.raises(AttributeError) as excinfo:
-        m.TestProperties.def_property_readonly_static = 3
+        dummy = m.TestProperties.def_writeonly_static  # noqa: F841 unused var
+    assert "unreadable attribute" in str(excinfo)
+
+    m.TestProperties.def_writeonly_static = 3
+    assert m.TestProperties.def_readonly_static == 3
+
+    assert m.TestProperties.def_property_readonly_static == 3
+    with pytest.raises(AttributeError) as excinfo:
+        m.TestProperties.def_property_readonly_static = 99
     assert "can't set attribute" in str(excinfo)
 
-    m.TestProperties.def_property_static = 3
-    assert m.TestProperties.def_property_static == 3
+    m.TestProperties.def_property_static = 4
+    assert m.TestProperties.def_property_static == 4
+
+    with pytest.raises(AttributeError) as excinfo:
+        dummy = m.TestProperties.def_property_writeonly_static
+    assert "unreadable attribute" in str(excinfo)
+
+    m.TestProperties.def_property_writeonly_static = 5
+    assert m.TestProperties.def_property_static == 5
 
     # Static property read and write via instance
     instance = m.TestProperties()
@@ -127,6 +156,13 @@
     assert m.TestProperties.def_readwrite_static == 2
     assert instance.def_readwrite_static == 2
 
+    with pytest.raises(AttributeError) as excinfo:
+        dummy = instance.def_property_writeonly_static  # noqa: F841 unused var
+    assert "unreadable attribute" in str(excinfo)
+
+    instance.def_property_writeonly_static = 4
+    assert instance.def_property_static == 4
+
     # It should be possible to override properties in derived classes
     assert m.TestPropertiesOverride().def_readonly == 99
     assert m.TestPropertiesOverride.def_readonly_static == 99