"""Object representing boolean API option."""## (C) Pywikibot team, 2015-2024## Distributed under the terms of the MIT license.#from__future__importannotationsfromcollections.abcimportMutableMappingimportpywikibotfrompywikibot.toolsimportdeprecate_arg__all__=['OptionSet']
[docs]classOptionSet(MutableMapping):"""A class to store a set of options which can be either enabled or not. If it is instantiated with the associated site, module and parameter it will only allow valid names as options. If instantiated 'lazy loaded' it won't checks if the names are valid until the site has been set (which isn't required, but recommended). The site can only be set once if it's not None and after setting it, any site (even None) will fail. """@deprecate_arg('dict','data')# since 9.0def__init__(self,site:pywikibot.site.APISite|None=None,module:str|None=None,param:str|None=None,data:dict|None=None)->None:"""Initializer. If a site is given, the module and param must be given too. .. versionchanged:: 9.0 *dict* parameter was renamed to *data*. :param site: The associated site :param module: The module name which is used by paraminfo. (Ignored when site is None) :param param: The parameter name inside the module. That parameter must have a 'type' entry. (Ignored when site is None) :param data: The initializing data dict which is used for :meth:`from_dict` """self._site_set=Falseself._enabled=set()self._disabled=set()self._set_site(site,module,param)ifdata:self.from_dict(data)def_set_site(self,site,module:str,param:str,clear_invalid:bool=False):"""Set the site and valid names. As soon as the site has been not None, any subsequent calls will fail, unless there had been invalid names and a KeyError was thrown. :param site: The associated site :type site: pywikibot.site.APISite :param module: The module name which is used by paraminfo. :param param: The parameter name inside the module. That parameter must have a 'type' entry. :param clear_invalid: Instead of throwing a KeyError, invalid names are silently removed from the options (disabled by default). """ifself._site_set:raiseTypeError('The site cannot be set multiple times.')# If the entries written to this are valid, it will never be# overwrittenself._valid_enable=set()self._valid_disable=set()ifsiteisNone:returnfortype_valueinsite._paraminfo.parameter(module,param)['type']:iftype_value[0]=='!':self._valid_disable.add(type_value[1:])else:self._valid_enable.add(type_value)ifclear_invalid:self._enabled&=self._valid_enableself._disabled&=self._valid_disableelse:invalid_names=((self._enabled-self._valid_enable)|(self._disabled-self._valid_disable))ifinvalid_names:raiseKeyError('OptionSet already contains invalid name(s) ''"{}"'.format('", "'.join(invalid_names)))self._site_set=True
[docs]deffrom_dict(self,dictionary):"""Load options from the dict. The options are not cleared before. If changes have been made previously, but only the dict values should be applied it needs to be cleared first. :param dictionary: a dictionary containing for each entry either the value False, True or None. The names must be valid depending on whether they enable or disable the option. All names with the value None can be in either of the list. :type dictionary: dict (keys are strings, values are bool/None) """enabled=set()disabled=set()removed=set()forname,valueindictionary.items():ifvalueisTrue:enabled.add(name)elifvalueisFalse:disabled.add(name)elifvalueisNone:removed.add(name)else:raiseValueError(f'Dict contains invalid value "{value}"')invalid_names=((enabled-self._valid_enable)|(disabled-self._valid_disable)|(removed-self._valid_enable-self._valid_disable))ifinvalid_namesandself._site_set:raiseValueError('Dict contains invalid name(s) "{}"'.format('", "'.join(invalid_names)))self._enabled=enabled|(self._enabled-disabled-removed)self._disabled=disabled|(self._disabled-enabled-removed)
def__setitem__(self,name,value):"""Set option to enabled, disabled or neither."""ifvalueisTrue:ifself._site_setandnamenotinself._valid_enable:raiseKeyError(f'Invalid name "{name}"')self._enabled.add(name)self._disabled.discard(name)elifvalueisFalse:ifself._site_setandnamenotinself._valid_disable:raiseKeyError(f'Invalid name "{name}"')self._disabled.add(name)self._enabled.discard(name)elifvalueisNone:ifself._site_setand(namenotinself._valid_enableornamenotinself._valid_disable):raiseKeyError(f'Invalid name "{name}"')self._enabled.discard(name)self._disabled.discard(name)else:raiseValueError(f'Invalid value "{value}"')def__getitem__(self,name)->bool|None:"""Return whether the option is enabled. :return: If the name has been set it returns whether it is enabled. Otherwise it returns None. If the site has been set it raises a KeyError if the name is invalid. Otherwise it might return a value even though the name might be invalid. """ifnameinself._enabled:returnTrueifnameinself._disabled:returnFalseif(self._site_setornameinself._valid_enableornameinself._valid_disable):returnNoneraiseKeyError(f'Invalid name "{name}"')def__delitem__(self,name)->None:"""Remove the item by setting it to None."""self[name]=Nonedef__iter__(self):"""Iterate over each enabled and disabled option."""yield fromself._enabledyield fromself._disabled
[docs]defapi_iter(self):"""Iterate over each option as they appear in the URL."""yield fromself._enabledfordisabledinself._disabled:yieldf'!{disabled}'
def__len__(self)->int:"""Return the number of enabled and disabled options."""returnlen(self._enabled)+len(self._disabled)