@@ -700,25 +700,33 @@ def get_validated_options(options, warn=True):
700700 Returns a copy of options with invalid entries removed.
701701
702702 :Parameters:
703- - `opts`: A dict of MongoDB URI options.
703+ - `opts`: A dict containing MongoDB URI options.
704704 - `warn` (optional): If ``True`` then warnings will be logged and
705705 invalid options will be ignored. Otherwise, invalid options will
706706 cause errors.
707707 """
708- validated_options = {}
708+ if isinstance (options , _CaseInsensitiveDictionary ):
709+ validated_options = _CaseInsensitiveDictionary ()
710+ get_normed_key = lambda x : x
711+ get_setter_key = lambda x : options .cased_key (x )
712+ else :
713+ validated_options = {}
714+ get_normed_key = lambda x : x .lower ()
715+ get_setter_key = lambda x : x
716+
709717 for opt , value in iteritems (options ):
710- lower = opt . lower ( )
718+ normed_key = get_normed_key ( opt )
711719 try :
712720 validator = URI_OPTIONS_VALIDATOR_MAP .get (
713- lower , raise_config_error )
721+ normed_key , raise_config_error )
714722 value = validator (opt , value )
715723 except (ValueError , TypeError , ConfigurationError ) as exc :
716724 if warn :
717725 warnings .warn (str (exc ))
718726 else :
719727 raise
720728 else :
721- validated_options [lower ] = value
729+ validated_options [get_setter_key ( normed_key ) ] = value
722730 return validated_options
723731
724732
@@ -814,3 +822,83 @@ def read_concern(self):
814822 .. versionadded:: 3.2
815823 """
816824 return self .__read_concern
825+
826+
827+ class _CaseInsensitiveDictionary (abc .MutableMapping ):
828+ def __init__ (self , * args , ** kwargs ):
829+ self .__casedkeys = {}
830+ self .__data = {}
831+ self .update (dict (* args , ** kwargs ))
832+
833+ def __contains__ (self , key ):
834+ return key .lower () in self .__data
835+
836+ def __len__ (self ):
837+ return len (self .__data )
838+
839+ def __iter__ (self ):
840+ return (key for key in self .__casedkeys )
841+
842+ def __repr__ (self ):
843+ return str ({self .__casedkeys [k ]: self .__data [k ] for k in self })
844+
845+ def __setitem__ (self , key , value ):
846+ lc_key = key .lower ()
847+ self .__casedkeys [lc_key ] = key
848+ self .__data [lc_key ] = value
849+
850+ def __getitem__ (self , key ):
851+ return self .__data [key .lower ()]
852+
853+ def __delitem__ (self , key ):
854+ lc_key = key .lower ()
855+ del self .__casedkeys [lc_key ]
856+ del self .__data [lc_key ]
857+
858+ def __eq__ (self , other ):
859+ if not isinstance (other , abc .Mapping ):
860+ return NotImplemented
861+ if len (self ) != len (other ):
862+ return False
863+ for key in other :
864+ if self [key ] != other [key ]:
865+ return False
866+
867+ return True
868+
869+ def get (self , key , default = None ):
870+ return self .__data .get (key .lower (), default )
871+
872+ def pop (self , key , * args , ** kwargs ):
873+ lc_key = key .lower ()
874+ self .__casedkeys .pop (lc_key , None )
875+ return self .__data .pop (lc_key , * args , ** kwargs )
876+
877+ def popitem (self ):
878+ lc_key , cased_key = self .__casedkeys .popitem ()
879+ value = self .__data .pop (lc_key )
880+ return cased_key , value
881+
882+ def clear (self ):
883+ self .__casedkeys .clear ()
884+ self .__data .clear ()
885+
886+ def setdefault (self , key , default = None ):
887+ lc_key = key .lower ()
888+ if key in self :
889+ return self .__data [lc_key ]
890+ else :
891+ self .__casedkeys [lc_key ] = key
892+ self .__data [lc_key ] = default
893+ return default
894+
895+ def update (self , other ):
896+ if isinstance (other , _CaseInsensitiveDictionary ):
897+ for key in other :
898+ self [other .cased_key (key )] = other [key ]
899+ else :
900+ for key in other :
901+ self [key ] = other [key ]
902+
903+ def cased_key (self , key ):
904+ return self .__casedkeys [key .lower ()]
0 commit comments