Repository: RosettaCommons/binder (language: python)

Issue:
ImportError when importing two different packages generated with binder that share an overlap
When loading two different packages (say A and B) for which the interface has been generated with binder and if packages A and B share at least the usage of one class ( `std::fpos<__mbstate_t>` in my case ), I have an error message as follows:

```
ImportError: generic_type: type "fpos___mbstate_t_t" is already registered!
```

The type is defined in a binder generated line:
```
pybind11::class_<std::fpos<__mbstate_t>> cl(M("std"), "fpos___mbstate_t_t", "");
```
both in A and B.

I don't want/can't introduce a dependency of B on A or A on B in this case and I need both A and B to work as a stand-alone interface.
To solve the multiple registration of a type, would it make sense to add an option to prepend a string in the function `string python_class_name(CXXRecordDecl const *C)` that would be library dependent such that we would have:

For A:
```
pybind11::class_<std::fpos<__mbstate_t>> cl(M("std"), "A_fpos___mbstate_t_t", "");
```

For B:
```
pybind11::class_<std::fpos<__mbstate_t>> cl(M("std"), "B_fpos___mbstate_t_t", "");
```


Discussion / hints:
This is a known pybind11 issue https://github.com/pybind/pybind11/issues/439
This would probably require a modification of binder though.

Fix patch:
diff --git a/source/class.cpp b/source/class.cpp
index 1d294828..0b7d6fa4 100644
--- a/source/class.cpp
+++ b/source/class.cpp
@@ -1194,15 +1194,22 @@ void ClassBinder::bind(Context &context)
 	}
 	string maybe_trampoline = callback_structure_constructible ? ", " + binding_qualified_name : "";
 
+	// Add module local if requested for the namespace
+	std::string module_local_annotation = "";
+	if (Config::get().is_module_local_requested(namespace_from_named_decl(C)))
+		module_local_annotation = ", pybind11::module_local()";
+
 	// Add buffer protocol if requested
 	std::string buffer_protocol_annotation = "";
 	if (Config::get().is_buffer_protocol_requested(qualified_name_without_template))
 		buffer_protocol_annotation = ", pybind11::buffer_protocol()";
 
+	std::string extra_annotation = module_local_annotation + buffer_protocol_annotation;
+
 	if( named_class )
 		c += '\t' +
 			 R"(pybind11::class_<{}{}{}{}> cl({}, "{}", "{}"{});)"_format(qualified_name, maybe_holder_type, maybe_trampoline, maybe_base_classes(context), module_variable_name, python_class_name(C),
-																		generate_documentation_string_for_declaration(C), buffer_protocol_annotation) +
+																		generate_documentation_string_for_declaration(C), extra_annotation) +
 			 '\n';
 	// c += "\tpybind11::handle cl_type = cl;\n\n";
 
diff --git a/source/config.cpp b/source/config.cpp
index 987a6f02..a372c966 100644
--- a/source/config.cpp
+++ b/source/config.cpp
@@ -65,6 +65,7 @@ void Config::read(string const &file_name)
 	string const _include_for_namespace_{"include_for_namespace"};
 
 	string const _buffer_protocol_{"buffer_protocol"};
+	string const _module_local_namespace_{"module_local_namespace"};
 
 	string const _binder_{"binder"};
 	string const _add_on_binder_{"add_on_binder"};
@@ -161,6 +162,14 @@ void Config::read(string const &file_name)
 				buffer_protocols.push_back(name_without_spaces);
 			}
 		}
+		else if( token == _module_local_namespace_) {
+			if(bind) {
+				module_local_namespaces_to_add.push_back(name_without_spaces);
+			}
+			else {
+				module_local_namespaces_to_skip.push_back(name_without_spaces);
+			}
+		}
 		else if( token == _binder_ ) {
 
 			if( bind ) {
@@ -350,6 +359,30 @@ bool Config::is_buffer_protocol_requested(string const &class__) const
 	return false;
 }
 
+bool Config::is_module_local_requested(string const &namespace_) const
+{
+	const string namespace_all = "@all_namespaces";
+	auto module_local_all = std::find(module_local_namespaces_to_add.begin(), module_local_namespaces_to_add.end(), namespace_all);
+	if( module_local_all != module_local_namespaces_to_add.end() ) {
+		auto module_local_to_skip = std::find(module_local_namespaces_to_skip.begin(), module_local_namespaces_to_skip.end(), namespace_);
+		if( module_local_to_skip != module_local_namespaces_to_skip.end()) {
+			return false;
+		}
+		return true;
+	}
+
+	auto module_local_to_add = std::find(module_local_namespaces_to_add.begin(), module_local_namespaces_to_add.end(), namespace_);
+	if( module_local_to_add != module_local_namespaces_to_add.end()) {
+		auto module_local_to_skip = std::find(module_local_namespaces_to_skip.begin(), module_local_namespaces_to_skip.end(), namespace_);
+		if( module_local_to_skip != module_local_namespaces_to_skip.end()) {
+			throw std::runtime_error("Could not determent if namespace '" + namespace_ + "' should use module_local or not... please resolve the conlficting options +module_local_namespace and -module_local_namespace!!!");
+		}
+		return true;
+	}
+
+	return false;
+}
+
 bool Config::is_include_skipping_requested(string const &include) const
 {
 	for( auto &i : includes_to_skip )
diff --git a/source/config.hpp b/source/config.hpp
index df89330c..7a3bc260 100644
--- a/source/config.hpp
+++ b/source/config.hpp
@@ -62,7 +62,7 @@ class Config
 	string root_module;
 
 	std::vector<string> namespaces_to_bind, classes_to_bind, functions_to_bind, namespaces_to_skip, classes_to_skip, functions_to_skip, includes_to_add, includes_to_skip;
-	std::vector<string> buffer_protocols;
+	std::vector<string> buffer_protocols, module_local_namespaces_to_add, module_local_namespaces_to_skip;
 
 	std::map<string, string> const &binders() const { return binders_; }
 	std::map<string, string> const &add_on_binders() const { return add_on_binders_; }
@@ -93,6 +93,7 @@ class Config
 	/// check if user requested binding for given declaration
 	bool is_namespace_binding_requested(string const &namespace_) const;
 	bool is_namespace_skipping_requested(string const &namespace_) const;
+	bool is_module_local_requested(string const &namespace_) const;
 
 	bool is_function_binding_requested(string const &function) const;
 	bool is_function_skipping_requested(string const &function) const;
diff --git a/source/enum.cpp b/source/enum.cpp
index 9b509169..e8d4de8e 100644
--- a/source/enum.cpp
+++ b/source/enum.cpp
@@ -94,7 +94,12 @@ std::string bind_enum(std::string const &module, EnumDecl const *E)
 
 	string maybe_arithmetic = E->isScoped() ? "" : ", pybind11::arithmetic()";
 
-	string r = "\tpybind11::enum_<{}>({}, \"{}\"{}, \"{}\")\n"_format(qualified_name, module, name, maybe_arithmetic, generate_documentation_string_for_declaration(E));
+	// Add module local if requested for the namespace
+	std::string module_local_annotation = "";
+	if (Config::get().is_module_local_requested(namespace_from_named_decl(E)))
+		module_local_annotation = ", pybind11::module_local()";
+
+	string r = "\tpybind11::enum_<{}>({}, \"{}\"{}, \"{}\"{})\n"_format(qualified_name, module, name, maybe_arithmetic, generate_documentation_string_for_declaration(E), module_local_annotation);
 
 	// r += "\t // is_bindable " + E->getNameAsString() + " -> " + std::to_string(is_bindable(E)) + "\n";
 


Test patch:
diff --git a/test/T70.module_local.config b/test/T70.module_local.config
new file mode 100644
index 00000000..e885e16d
--- /dev/null
+++ b/test/T70.module_local.config
@@ -0,0 +1,2 @@
++module_local_namespace @all_namespaces
+-module_local_namespace bbb
\ No newline at end of file
diff --git a/test/T70.module_local.hpp b/test/T70.module_local.hpp
new file mode 100644
index 00000000..c5483eda
--- /dev/null
+++ b/test/T70.module_local.hpp
@@ -0,0 +1,31 @@
+// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
+// vi: set ts=2 noet:
+//
+// Copyright (c) 2016 Sergey Lyskov <sergey.lyskov@jhu.edu>
+//
+// All rights reserved. Use of this source code is governed by a
+// MIT license that can be found in the LICENSE file.
+
+/// @file   binder/test/T70.module_local.hpp
+/// @author Sergey Lyskov
+
+#ifndef _INCLUDED_T70_module_local_hpp_
+#define _INCLUDED_T70_module_local_hpp_
+
+namespace aaa {
+	class A 
+	{
+		public:
+			void foo(){};
+	};
+}
+
+namespace bbb {
+	class B 
+	{
+		public:
+			void foo(){};
+	};
+}
+
+#endif // _INCLUDED_T70_module_local_hpp_
diff --git a/test/T70.module_local.ref b/test/T70.module_local.ref
new file mode 100644
index 00000000..4b59f796
--- /dev/null
+++ b/test/T70.module_local.ref
@@ -0,0 +1,106 @@
+// File: T70_module_local.cpp
+#include <T70.module_local.hpp> // aaa::A
+#include <sstream> // __str__
+
+#include <functional>
+#include <pybind11/pybind11.h>
+#include <string>
+
+#ifndef BINDER_PYBIND11_TYPE_CASTER
+	#define BINDER_PYBIND11_TYPE_CASTER
+	PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
+	PYBIND11_DECLARE_HOLDER_TYPE(T, T*)
+	PYBIND11_MAKE_OPAQUE(std::shared_ptr<void>)
+#endif
+
+void bind_T70_module_local(std::function< pybind11::module &(std::string const &namespace_) > &M)
+{
+	{ // aaa::A file:T70.module_local.hpp line:16
+		pybind11::class_<aaa::A, std::shared_ptr<aaa::A>> cl(M("aaa"), "A", "", pybind11::module_local());
+		cl.def( pybind11::init( [](){ return new aaa::A(); } ) );
+		cl.def("foo", (void (aaa::A::*)()) &aaa::A::foo, "C++: aaa::A::foo() --> void");
+	}
+}
+
+
+// File: T70_module_local_1.cpp
+#include <T70.module_local.hpp> // bbb::B
+#include <sstream> // __str__
+
+#include <functional>
+#include <pybind11/pybind11.h>
+#include <string>
+
+#ifndef BINDER_PYBIND11_TYPE_CASTER
+	#define BINDER_PYBIND11_TYPE_CASTER
+	PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
+	PYBIND11_DECLARE_HOLDER_TYPE(T, T*)
+	PYBIND11_MAKE_OPAQUE(std::shared_ptr<void>)
+#endif
+
+void bind_T70_module_local_1(std::function< pybind11::module &(std::string const &namespace_) > &M)
+{
+	{ // bbb::B file:T70.module_local.hpp line:24
+		pybind11::class_<bbb::B, std::shared_ptr<bbb::B>> cl(M("bbb"), "B", "");
+		cl.def( pybind11::init( [](){ return new bbb::B(); } ) );
+		cl.def("foo", (void (bbb::B::*)()) &bbb::B::foo, "C++: bbb::B::foo() --> void");
+	}
+}
+
+
+#include <map>
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+#include <pybind11/pybind11.h>
+
+typedef std::function< pybind11::module & (std::string const &) > ModuleGetter;
+
+void bind_T70_module_local(std::function< pybind11::module &(std::string const &namespace_) > &M);
+void bind_T70_module_local_1(std::function< pybind11::module &(std::string const &namespace_) > &M);
+
+
+PYBIND11_MODULE(T70_module_local, root_module) {
+	root_module.doc() = "T70_module_local module";
+
+	std::map <std::string, pybind11::module> modules;
+	ModuleGetter M = [&](std::string const &namespace_) -> pybind11::module & {
+		auto it = modules.find(namespace_);
+		if( it == modules.end() ) throw std::runtime_error("Attempt to access pybind11::module for namespace " + namespace_ + " before it was created!!!");
+		return it->second;
+	};
+
+	modules[""] = root_module;
+
+	static std::vector<std::string> const reserved_python_words {"nonlocal", "global", };
+
+	auto mangle_namespace_name(
+		[](std::string const &ns) -> std::string {
+			if ( std::find(reserved_python_words.begin(), reserved_python_words.end(), ns) == reserved_python_words.end() ) return ns;
+			else return ns+'_';
+		}
+	);
+
+	std::vector< std::pair<std::string, std::string> > sub_modules {
+		{"", "aaa"},
+		{"", "bbb"},
+	};
+	for(auto &p : sub_modules ) modules[p.first.size() ? p.first+"::"+p.second : p.second] = modules[p.first].def_submodule( mangle_namespace_name(p.second).c_str(), ("Bindings for " + p.first + "::" + p.second + " namespace").c_str() );
+
+	//pybind11::class_<std::shared_ptr<void>>(M(""), "_encapsulated_data_");
+
+	bind_T70_module_local(M);
+	bind_T70_module_local_1(M);
+
+}
+
+// Source list file: TEST/T70_module_local.sources
+// T70_module_local.cpp
+// T70_module_local.cpp
+// T70_module_local_1.cpp
+
+// Modules list file: TEST/T70_module_local.modules
+// aaa bbb 
diff --git a/test/T71.module_local.config b/test/T71.module_local.config
new file mode 100644
index 00000000..b01f59d1
--- /dev/null
+++ b/test/T71.module_local.config
@@ -0,0 +1,1 @@
++module_local_namespace aaa
\ No newline at end of file
diff --git a/test/T71.module_local.hpp b/test/T71.module_local.hpp
new file mode 100644
index 00000000..27f88862
--- /dev/null
+++ b/test/T71.module_local.hpp
@@ -0,0 +1,31 @@
+// -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
+// vi: set ts=2 noet:
+//
+// Copyright (c) 2016 Sergey Lyskov <sergey.lyskov@jhu.edu>
+//
+// All rights reserved. Use of this source code is governed by a
+// MIT license that can be found in the LICENSE file.
+
+/// @file   binder/test/T71.module_local.hpp
+/// @author Sergey Lyskov
+
+#ifndef _INCLUDED_T71_module_local_hpp_
+#define _INCLUDED_T71_module_local_hpp_
+
+namespace aaa {
+	class A 
+	{
+		public:
+			void foo(){};
+	};
+}
+
+namespace bbb {
+	class B 
+	{
+		public:
+			void foo(){};
+	};
+}
+
+#endif // _INCLUDED_T71_module_local_hpp_
diff --git a/test/T71.module_local.ref b/test/T71.module_local.ref
new file mode 100644
index 00000000..5beccc1b
--- /dev/null
+++ b/test/T71.module_local.ref
@@ -0,0 +1,106 @@
+// File: T71_module_local.cpp
+#include <T71.module_local.hpp> // aaa::A
+#include <sstream> // __str__
+
+#include <functional>
+#include <pybind11/pybind11.h>
+#include <string>
+
+#ifndef BINDER_PYBIND11_TYPE_CASTER
+	#define BINDER_PYBIND11_TYPE_CASTER
+	PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
+	PYBIND11_DECLARE_HOLDER_TYPE(T, T*)
+	PYBIND11_MAKE_OPAQUE(std::shared_ptr<void>)
+#endif
+
+void bind_T71_module_local(std::function< pybind11::module &(std::string const &namespace_) > &M)
+{
+	{ // aaa::A file:T71.module_local.hpp line:16
+		pybind11::class_<aaa::A, std::shared_ptr<aaa::A>> cl(M("aaa"), "A", "", pybind11::module_local());
+		cl.def( pybind11::init( [](){ return new aaa::A(); } ) );
+		cl.def("foo", (void (aaa::A::*)()) &aaa::A::foo, "C++: aaa::A::foo() --> void");
+	}
+}
+
+
+// File: T71_module_local_1.cpp
+#include <T71.module_local.hpp> // bbb::B
+#include <sstream> // __str__
+
+#include <functional>
+#include <pybind11/pybind11.h>
+#include <string>
+
+#ifndef BINDER_PYBIND11_TYPE_CASTER
+	#define BINDER_PYBIND11_TYPE_CASTER
+	PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr<T>)
+	PYBIND11_DECLARE_HOLDER_TYPE(T, T*)
+	PYBIND11_MAKE_OPAQUE(std::shared_ptr<void>)
+#endif
+
+void bind_T71_module_local_1(std::function< pybind11::module &(std::string const &namespace_) > &M)
+{
+	{ // bbb::B file:T71.module_local.hpp line:24
+		pybind11::class_<bbb::B, std::shared_ptr<bbb::B>> cl(M("bbb"), "B", "");
+		cl.def( pybind11::init( [](){ return new bbb::B(); } ) );
+		cl.def("foo", (void (bbb::B::*)()) &bbb::B::foo, "C++: bbb::B::foo() --> void");
+	}
+}
+
+
+#include <map>
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
+
+#include <pybind11/pybind11.h>
+
+typedef std::function< pybind11::module & (std::string const &) > ModuleGetter;
+
+void bind_T71_module_local(std::function< pybind11::module &(std::string const &namespace_) > &M);
+void bind_T71_module_local_1(std::function< pybind11::module &(std::string const &namespace_) > &M);
+
+
+PYBIND11_MODULE(T71_module_local, root_module) {
+	root_module.doc() = "T71_module_local module";
+
+	std::map <std::string, pybind11::module> modules;
+	ModuleGetter M = [&](std::string const &namespace_) -> pybind11::module & {
+		auto it = modules.find(namespace_);
+		if( it == modules.end() ) throw std::runtime_error("Attempt to access pybind11::module for namespace " + namespace_ + " before it was created!!!");
+		return it->second;
+	};
+
+	modules[""] = root_module;
+
+	static std::vector<std::string> const reserved_python_words {"nonlocal", "global", };
+
+	auto mangle_namespace_name(
+		[](std::string const &ns) -> std::string {
+			if ( std::find(reserved_python_words.begin(), reserved_python_words.end(), ns) == reserved_python_words.end() ) return ns;
+			else return ns+'_';
+		}
+	);
+
+	std::vector< std::pair<std::string, std::string> > sub_modules {
+		{"", "aaa"},
+		{"", "bbb"},
+	};
+	for(auto &p : sub_modules ) modules[p.first.size() ? p.first+"::"+p.second : p.second] = modules[p.first].def_submodule( mangle_namespace_name(p.second).c_str(), ("Bindings for " + p.first + "::" + p.second + " namespace").c_str() );
+
+	//pybind11::class_<std::shared_ptr<void>>(M(""), "_encapsulated_data_");
+
+	bind_T71_module_local(M);
+	bind_T71_module_local_1(M);
+
+}
+
+// Source list file: TEST/T71_module_local.sources
+// T71_module_local.cpp
+// T71_module_local.cpp
+// T71_module_local_1.cpp
+
+// Modules list file: TEST/T71_module_local.modules
+// aaa bbb