jenkins-bot has submitted this change and it was merged.
Change subject: [FEAT] Deprecrate module classes and variables ......................................................................
[FEAT] Deprecrate module classes and variables
This adds a way to deprecrate classes or variables in modules. The difference to '@deprecated' is that the access alone is important and not calling the function. So this warns also if someone is just importing a class (e.g. 'from pywikibot.excptions import UploadWarning').
Unfortunately __getattr__ is not available on module level, so the module must be wrapped in a class which redirects all accesses.
Change-Id: I3e83472ba780d9d99745c8dff864926432e9f254 --- M pywikibot/__init__.py M pywikibot/exceptions.py M pywikibot/page.py M pywikibot/tools.py 4 files changed, 86 insertions(+), 5 deletions(-)
Approvals: John Vandenberg: Looks good to me, approved jenkins-bot: Verified
diff --git a/pywikibot/__init__.py b/pywikibot/__init__.py index 0c9ac51..394043c 100644 --- a/pywikibot/__init__.py +++ b/pywikibot/__init__.py @@ -61,7 +61,7 @@
# pep257 doesn't understand when the first entry is on the next line __all__ = ('config', 'ui', 'UnicodeMixin', 'translate', - 'Page', 'FilePage', 'ImagePage', 'Category', 'Link', 'User', + 'Page', 'FilePage', 'Category', 'Link', 'User', 'ItemPage', 'PropertyPage', 'Claim', 'TimeStripper', 'html2unicode', 'url2unicode', 'unicode2html', 'stdout', 'output', 'warning', 'error', 'critical', 'debug', @@ -542,7 +542,6 @@ from .page import ( Page, FilePage, - ImagePage, Category, Link, User, @@ -714,3 +713,6 @@ # identification for debugging purposes _putthread.setName('Put-Thread') _putthread.setDaemon(True) + +wrapper = pywikibot.tools.ModuleDeprecationWrapper(__name__) +wrapper._add_deprecated_attr('ImagePage', FilePage) diff --git a/pywikibot/exceptions.py b/pywikibot/exceptions.py index ed620e1..7022935 100644 --- a/pywikibot/exceptions.py +++ b/pywikibot/exceptions.py @@ -374,6 +374,7 @@ pass
-# TODO: Warn about the deprecated usage import pywikibot.data.api -UploadWarning = pywikibot.data.api.UploadWarning +import pywikibot.tools +wrapper = pywikibot.tools.ModuleDeprecationWrapper(__name__) +wrapper._add_deprecated_attr('UploadWarning', pywikibot.data.api.UploadWarning) diff --git a/pywikibot/page.py b/pywikibot/page.py index 70f8dd1..c17dd9b 100644 --- a/pywikibot/page.py +++ b/pywikibot/page.py @@ -1930,7 +1930,9 @@ self, step=step, total=total, content=content)
-ImagePage = FilePage +import pywikibot.tools +wrapper = pywikibot.tools.ModuleDeprecationWrapper(__name__) +wrapper._add_deprecated_attr('ImagePage', FilePage)
class Category(Page): diff --git a/pywikibot/tools.py b/pywikibot/tools.py index 3ef95c4..c11d0d7 100644 --- a/pywikibot/tools.py +++ b/pywikibot/tools.py @@ -425,6 +425,82 @@ return Wrapper().call
+class ModuleDeprecationWrapper(object): + + """A wrapper for a module to deprecate classes or variables of it.""" + + def __init__(self, module): + """ + Initialise the wrapper. + + It will automatically overwrite the module with this instance in + C{sys.modules}. + + @param module: The module name or instance + @type module: str or module + """ + if isinstance(module, basestring): + module = sys.modules[module] + super(ModuleDeprecationWrapper, self).__setattr__('_deprecated', {}) + super(ModuleDeprecationWrapper, self).__setattr__('_module', module) + sys.modules[module.__name__] = self + + def _add_deprecated_attr(self, name, replacement=None, + replacement_name=None): + """ + Add the name to the local deprecated names dict. + + @param name: The name of the deprecated class or variable. It may not + be already deprecated. + @type name: str + @param replacement: The replacement value which should be returned + instead. If the name is already an attribute of that module this + must be None. If None it'll return the attribute of the module. + @type replacement: any + @param replacement_name: The name of the new replaced value. Required + if C{replacement} is not None and it has no __name__ attribute. + @type replacement_name: str + """ + if '.' in name: + raise ValueError('Deprecated name "{0}" may not contain ' + '".".'.format(name)) + if name in self._deprecated: + raise ValueError('Name "{0}" is already deprecated.'.format(name)) + if replacement is not None and hasattr(self._module, name): + raise ValueError('Module has already an attribute named ' + '"{0}".'.format(name)) + if replacement_name is None: + if hasattr(replacement, '__name__'): + replacement_name = replacement.__module__ + if hasattr(replacement, '__self__'): + replacement_name += '.' + replacement_name += replacement.__self__.__class__.__name__ + replacement_name += '.' + replacement.__name__ + else: + raise TypeError('Replacement must have a __name__ attribute ' + 'or a replacement name must be set ' + 'specifically.') + self._deprecated[name] = (replacement_name, replacement) + + def __setattr__(self, attr, value): + """Set a the value of the wrapped module.""" + setattr(self._module, attr, value) + + def __getattr__(self, attr): + """Return the attribute with a deprecation warning if required.""" + if attr in self._deprecated: + if self._deprecated[attr][0]: + warning(u"{0}.{1} is DEPRECATED, use {2} instead.".format( + self._module.__name__, attr, + self._deprecated[attr][0])) + if self._deprecated[attr][1]: + return self._deprecated[attr][1] + else: + warning(u"{0}.{1} is DEPRECATED.".format( + self._module.__name__, attr)) + return getattr(self._module, attr) + + if __name__ == "__main__": def _test(): import doctest
pywikibot-commits@lists.wikimedia.org