debtcollector-1.13.0/0000775000567000056710000000000013070423715015564 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/.mailmap0000664000567000056710000000013013070423566017203 0ustar jenkinsjenkins00000000000000# Format is: # # debtcollector-1.13.0/setup.py0000664000567000056710000000200613070423566017300 0ustar jenkinsjenkins00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools # In python < 2.7.4, a lazy loading of package `pbr` will break # setuptools if some other modules registered functions in `atexit`. # solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) debtcollector-1.13.0/CONTRIBUTING.rst0000664000567000056710000000103713070423566020232 0ustar jenkinsjenkins00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in this page: http://docs.openstack.org/infra/manual/developers.html Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: http://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/debtcollectordebtcollector-1.13.0/tools/0000775000567000056710000000000013070423715016724 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/tools/tox_install.sh0000775000567000056710000000203613070423566021630 0ustar jenkinsjenkins00000000000000#!/usr/bin/env bash # Client constraint file contains this client version pin that is in conflict # with installing the client from source. We should remove the version pin in # the constraints file before applying it for from-source installation. CONSTRAINTS_FILE="$1" shift 1 set -e # NOTE(tonyb): Place this in the tox enviroment's log dir so it will get # published to logs.openstack.org for easy debugging. localfile="$VIRTUAL_ENV/log/upper-constraints.txt" if [[ "$CONSTRAINTS_FILE" != http* ]]; then CONSTRAINTS_FILE="file://$CONSTRAINTS_FILE" fi # NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep curl "$CONSTRAINTS_FILE" --insecure --progress-bar --output "$localfile" pip install -c"$localfile" openstack-requirements # This is the main purpose of the script: Allow local installation of # the current repo. It is listed in constraints file and thus any # install will be constrained and we need to unconstrain it. edit-constraints "$localfile" -- "$CLIENT_NAME" pip install -c"$localfile" -U "$@" exit $? debtcollector-1.13.0/tox.ini0000664000567000056710000000270313070423566017105 0ustar jenkinsjenkins00000000000000[tox] minversion = 2.0 envlist = py35,py27,pypy,pep8 [testenv] setenv = VIRTUAL_ENV={envdir} BRANCH_NAME=master CLIENT_NAME=debtcollector install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} deps = -r{toxinidir}/test-requirements.txt commands = python setup.py testr --slowest --testr-args='{posargs}' [testenv:debug] commands = oslo_debug_helper {posargs} [testenv:debug-py27] basepython = python2.7 commands = oslo_debug_helper {posargs} [testenv:debug-py34] basepython = python3.4 commands = oslo_debug_helper {posargs} [testenv:debug-py35] basepython = python3.5 commands = oslo_debug_helper {posargs} [testenv:pep8] commands = flake8 [testenv:venv] commands = {posargs} [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' [testenv:docs] commands = python setup.py build_sphinx [testenv:py27] deps = {[testenv]deps} commands = python setup.py testr --slowest --testr-args='{posargs}' sphinx-build -b doctest doc/source doc/build doc8 --ignore-path "doc/source/history.rst" doc/source [flake8] # E123, E125 skipped as they are invalid PEP-8. show-source = True ignore = E123,E125 exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build [testenv:releasenotes] commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html debtcollector-1.13.0/releasenotes/0000775000567000056710000000000013070423715020255 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/releasenotes/source/0000775000567000056710000000000013070423715021555 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/releasenotes/source/index.rst0000664000567000056710000000022313070423566023417 0ustar jenkinsjenkins00000000000000============================= debtcollector Release Notes ============================= .. toctree:: :maxdepth: 1 unreleased ocata debtcollector-1.13.0/releasenotes/source/conf.py0000664000567000056710000002161413070423566023064 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'oslosphinx', 'reno.sphinxext', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'debtcollector Release Notes' copyright = u'2016, debtcollector Developers' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. # The full version, including alpha/beta/rc tags. import pkg_resources release = pkg_resources.get_distribution('debtcollector').version # The short X.Y version. version = release # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'debtcollectorReleaseNotesDoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'debtcollectorReleaseNotes.tex', u'debtcollector Release Notes Documentation', u'debtcollector Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'debtcollectorReleaseNotes', u'debtcollector Release Notes Documentation', [u'debtcollector Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'debtcollectorReleaseNotes', u'debtcollector Release Notes Documentation', u'debtcollector Developers', 'debtcollectorReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] debtcollector-1.13.0/releasenotes/source/unreleased.rst0000664000567000056710000000014413070423566024441 0ustar jenkinsjenkins00000000000000========================== Unreleased Release Notes ========================== .. release-notes:: debtcollector-1.13.0/releasenotes/source/ocata.rst0000664000567000056710000000023013070423566023375 0ustar jenkinsjenkins00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata debtcollector-1.13.0/releasenotes/source/_static/0000775000567000056710000000000013070423715023203 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/releasenotes/source/_static/.placeholder0000664000567000056710000000000013070423566025460 0ustar jenkinsjenkins00000000000000debtcollector-1.13.0/releasenotes/source/_templates/0000775000567000056710000000000013070423715023712 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/releasenotes/source/_templates/.placeholder0000664000567000056710000000000013070423566026167 0ustar jenkinsjenkins00000000000000debtcollector-1.13.0/releasenotes/notes/0000775000567000056710000000000013070423715021405 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/releasenotes/notes/add-reno-996dd44974d53238.yaml0000664000567000056710000000007213070423566026007 0ustar jenkinsjenkins00000000000000--- other: - Introduce reno for deployer release notes. debtcollector-1.13.0/ChangeLog0000664000567000056710000000722713070423715017346 0ustar jenkinsjenkins00000000000000CHANGES ======= 1.13.0 ------ * Remove testscenarios from test-requirements.txt * Python 3.4 support has been removed 1.12.0 ------ * Updated from global requirements * Updated from global requirements * Update reno for stable/ocata 1.11.0 ------ * Add Constraints support * Show team and repo badges on README 1.10.0 ------ * Updated from global requirements * Typo fix for module debtcollector * Updated from global requirements * Add reno for release notes management * Trivial: Remove 'MANIFEST.in' * Updated from global requirements 1.9.0 ----- * Updated from global requirements * Update homepage with developer documentation page * Fix a typo in comment 1.8.0 ----- * Drop *openstack/common* in flake8 exclude list 1.7.0 ----- * Remove discover from test-requirements * Add Python 3.5 classifier and venv 1.6.0 ----- * Updated from global requirements 1.5.0 ----- * Updated from global requirements 1.4.0 ----- * Drop babel as requirement since its not used * Fix renamed_kwarg to preserve argspec * Add tests for decorated argspec preservation * Updated from global requirements * Updated from global requirements 1.3.0 ----- * Updated from global requirements * Add debug testenv in tox 1.2.0 ----- * Add updated_kwarg_default_value decorator * Allow replacing a keyword argument * Add 'removed_class' class decorator * py26/py33 are no longer supported by Infra's CI 1.1.0 ----- * Add removals.remove note about metaclasses 1.0.0 ----- * Updated from global requirements * Update get_class_name from olso.utils * Remove Python 2.6 classifier * Remove python 2.6 and cleanup tox.ini * Add 'moved_read_only_property' descriptor 0.10.0 ------ * Add ability to disable warnings being emitted 0.9.0 ----- * Add a 'removed_property' descriptor class * No need for Oslo Incubator Sync * docs - Set pbr 'warnerrors' option for doc build * Include changelog/history in docs * tweak language in readme * Enhance the README * Change ignore-errors to ignore_errors * Updated from global requirements * Activate pep8 check that _ is imported * Add a moved function helper 0.8.0 ----- * Updated from global requirements * Updated from global requirements * Updated from global requirements 0.7.0 ----- * Updated from global requirements * Expose a top level 'deprecate' function * Add @removals.removed_kwarg on an __init__ method * Improve + test keyword argument @classmethod removal * Add example for removing a @classmethod 0.6.0 ----- * Ensure doctesting and doc8 testing done in py27 env * Updated from global requirements * Fix quoting of examples * Updated from global requirements * Switch badges from 'pypip.in' to 'shields.io' 0.5.0 ----- * Remove oslo.utils dependency * Updated from global requirements * Ensure that the incoming 'new_class' is actually a class * Allow providing the deprecation category 0.4.0 ----- * Uncap library requirements for liberty * No longer need to workaround six issue/bug * Add pypi download + version badges * Updated from global requirements 0.3.0 ----- * Add a removed module deprecation helper * Updated from global requirements * Move to hacking 0.10 * Add a 'removed_kwarg' function/method decorator * Match the updated openstack-manuals description * Format the method/class removals messages like the others * Add examples of using the new removals decorator 0.2.0 ----- * Add a removal decorator * Add doctested examples into the documentation * Add universal wheel tag to setup.cfg 0.1.0 ----- * Upper case python * Fix up the docs into reasonable shape * Add a moved *instance* method deprecation pattern * Initial import of renames/moves + tests * Add a .gitreview file with correct values * Adjust summary of project * Initial commit debtcollector-1.13.0/test-requirements.txt0000664000567000056710000000101113070423566022022 0ustar jenkinsjenkins00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. hacking<0.11,>=0.10.0 coverage>=4.0 # Apache-2.0 python-subunit>=0.0.18 # Apache-2.0/BSD sphinx>=1.5.1 # BSD oslosphinx>=4.7.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 testrepository>=0.0.18 # Apache-2.0/BSD testtools>=1.4.0 # MIT fixtures>=3.0.0 # Apache-2.0/BSD doc8 # Apache-2.0 reno>=1.8.0 # Apache-2.0 debtcollector-1.13.0/setup.cfg0000664000567000056710000000172313070423715017410 0ustar jenkinsjenkins00000000000000[metadata] name = debtcollector summary = A collection of Python deprecation patterns and strategies that help you collect your technical debt in a non-destructive manner. description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = http://docs.openstack.org/developer/debtcollector classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 [files] packages = debtcollector [build_sphinx] source-dir = doc/source build-dir = doc/build all_files = 1 [upload_sphinx] upload-dir = doc/build/html [pbr] warnerrors = True [wheel] universal = 1 [egg_info] tag_build = tag_date = 0 debtcollector-1.13.0/LICENSE0000664000567000056710000002363613070423566016607 0ustar jenkinsjenkins00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. debtcollector-1.13.0/PKG-INFO0000664000567000056710000000465413070423715016672 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: debtcollector Version: 1.13.0 Summary: A collection of Python deprecation patterns and strategies that help you collect your technical debt in a non-destructive manner. Home-page: http://docs.openstack.org/developer/debtcollector Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/debtcollector.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on Debtcollector ============= .. image:: https://img.shields.io/pypi/v/debtcollector.svg :target: https://pypi.python.org/pypi/debtcollector/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/debtcollector.svg :target: https://pypi.python.org/pypi/debtcollector/ :alt: Downloads A collection of Python deprecation patterns and strategies that help you collect your technical debt in a non-destructive manner. The goal of this library is to provide well documented developer facing deprecation patterns that start of with a basic set and can expand into a larger set of patterns as time goes on. The desired output of these patterns is to apply the warnings module to emit DeprecationWarning or PendingDeprecationWarning or similar derivative to developers using libraries (or potentially applications) about future deprecations. * Free software: Apache license * Documentation: http://docs.openstack.org/developer/debtcollector * Source: http://git.openstack.org/cgit/openstack/debtcollector * Bugs: http://bugs.launchpad.net/debtcollector Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 debtcollector-1.13.0/HACKING.rst0000664000567000056710000000024413070423566017366 0ustar jenkinsjenkins00000000000000debtcollector Style Commandments =============================================== Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/debtcollector-1.13.0/AUTHORS0000664000567000056710000000166213070423715016641 0ustar jenkinsjenkins00000000000000Brant Knudson ChangBo Guo(gcb) Davanum Srinivas Doug Hellmann Flavio Percoco Graham Hayes Jamie Lennox Jamie Lennox Joe Gordon Joshua Harlow Joshua Harlow Joshua Harlow Matthew Treinish Monty Taylor OpenStack Release Bot Pierre-André MOREY Swapnil Kulkarni (coolsvap) THOMAS J. COCOZZELLO Tony Breeds Tony Xu Wei Li caoyue howardlee janonymous kavithahr debtcollector-1.13.0/.coveragerc0000664000567000056710000000020113070423566017702 0ustar jenkinsjenkins00000000000000[run] branch = True source = debtcollector omit = debtcollector/tests/*,debtcollector/openstack/* [report] ignore_errors = True debtcollector-1.13.0/babel.cfg0000664000567000056710000000002013070423566017306 0ustar jenkinsjenkins00000000000000[python: **.py] debtcollector-1.13.0/debtcollector/0000775000567000056710000000000013070423715020411 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/debtcollector/renames.py0000664000567000056710000000326713070423566022431 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2015 Yahoo! Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import wrapt from debtcollector import _utils _KWARG_RENAMED_POSTFIX_TPL = ", please use the '%s' argument instead" _KWARG_RENAMED_PREFIX_TPL = "Using the '%s' argument is deprecated" def renamed_kwarg(old_name, new_name, message=None, version=None, removal_version=None, stacklevel=3, category=None, replace=False): """Decorates a kwarg accepting function to deprecate a renamed kwarg.""" prefix = _KWARG_RENAMED_PREFIX_TPL % old_name postfix = _KWARG_RENAMED_POSTFIX_TPL % new_name out_message = _utils.generate_message( prefix, postfix=postfix, message=message, version=version, removal_version=removal_version) @wrapt.decorator def decorator(wrapped, instance, args, kwargs): if old_name in kwargs: _utils.deprecation(out_message, stacklevel=stacklevel, category=category) if replace: kwargs.setdefault(new_name, kwargs.pop(old_name)) return wrapped(*args, **kwargs) return decorator debtcollector-1.13.0/debtcollector/__init__.py0000664000567000056710000000426113070423566022531 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pbr.version from debtcollector import _utils __version__ = pbr.version.VersionInfo( 'debtcollector').version_string() def deprecate(prefix, postfix=None, message=None, version=None, removal_version=None, stacklevel=3, category=DeprecationWarning): """Helper to deprecate some thing using generated message format. :param prefix: prefix string used as the prefix of the output message :param postfix: postfix string used as the postfix of the output message :param message: message string used as ending contents of the deprecate message :param version: version string (represents the version this deprecation was created in) :param removal_version: version string (represents the version this deprecation will be removed in); a string of '?' will denote this will be removed in some future unknown version :param stacklevel: stacklevel used in the :func:`warnings.warn` function to locate where the users code is in the :func:`warnings.warn` call :param category: the :mod:`warnings` category to use, defaults to :py:class:`DeprecationWarning` if not provided """ out_message = _utils.generate_message(prefix, postfix=postfix, version=version, message=message, removal_version=removal_version) _utils.deprecation(out_message, stacklevel=stacklevel, category=category) debtcollector-1.13.0/debtcollector/moves.py0000664000567000056710000002035113070423566022121 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2015 Yahoo! Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import inspect import six import wrapt from debtcollector import _utils _KIND_MOVED_PREFIX_TPL = "%s '%s' has moved to '%s'" _CLASS_MOVED_PREFIX_TPL = "Class '%s' has moved to '%s'" _MOVED_CALLABLE_POSTFIX = "()" _FUNC_MOVED_PREFIX_TPL = "Function '%s' has moved to '%s'" def _moved_decorator(kind, new_attribute_name, message=None, version=None, removal_version=None, stacklevel=3, attr_postfix=None, category=None): """Decorates a method/property that was moved to another location.""" def decorator(f): fully_qualified, old_attribute_name = _utils.get_qualified_name(f) if attr_postfix: old_attribute_name += attr_postfix @wrapt.decorator def wrapper(wrapped, instance, args, kwargs): base_name = _utils.get_class_name(wrapped, fully_qualified=False) if fully_qualified: old_name = old_attribute_name else: old_name = ".".join((base_name, old_attribute_name)) new_name = ".".join((base_name, new_attribute_name)) prefix = _KIND_MOVED_PREFIX_TPL % (kind, old_name, new_name) out_message = _utils.generate_message( prefix, message=message, version=version, removal_version=removal_version) _utils.deprecation(out_message, stacklevel=stacklevel, category=category) return wrapped(*args, **kwargs) return wrapper(f) return decorator def moved_function(new_func, old_func_name, old_module_name, message=None, version=None, removal_version=None, stacklevel=3, category=None): """Deprecates a function that was moved to another location. This generates a wrapper around ``new_func`` that will emit a deprecation warning when called. The warning message will include the new location to obtain the function from. """ new_func_full_name = _utils.get_callable_name(new_func) new_func_full_name += _MOVED_CALLABLE_POSTFIX old_func_full_name = ".".join([old_module_name, old_func_name]) old_func_full_name += _MOVED_CALLABLE_POSTFIX prefix = _FUNC_MOVED_PREFIX_TPL % (old_func_full_name, new_func_full_name) out_message = _utils.generate_message(prefix, message=message, version=version, removal_version=removal_version) @six.wraps(new_func, assigned=_utils.get_assigned(new_func)) def old_new_func(*args, **kwargs): _utils.deprecation(out_message, stacklevel=stacklevel, category=category) return new_func(*args, **kwargs) old_new_func.__name__ = old_func_name old_new_func.__module__ = old_module_name return old_new_func class moved_read_only_property(object): """Descriptor for read-only properties moved to another location. This works like the ``@property`` descriptor but can be used instead to provide the same functionality and also interact with the :mod:`warnings` module to warn when a property is accessed, so that users of those properties can know that a previously read-only property at a prior location/name has moved to another location/name. :param old_name: old attribute location/name :param new_name: new attribute location/name :param version: version string (represents the version this deprecation was created in) :param removal_version: version string (represents the version this deprecation will be removed in); a string of '?' will denote this will be removed in some future unknown version :param stacklevel: stacklevel used in the :func:`warnings.warn` function to locate where the users code is when reporting the deprecation call (the default being 3) :param category: the :mod:`warnings` category to use, defaults to :py:class:`DeprecationWarning` if not provided """ def __init__(self, old_name, new_name, version=None, removal_version=None, stacklevel=3, category=None): self._old_name = old_name self._new_name = new_name self._message = _utils.generate_message( "Read-only property '%s' has moved" " to '%s'" % (self._old_name, self._new_name), version=version, removal_version=removal_version) self._stacklevel = stacklevel self._category = category def __get__(self, instance, owner): _utils.deprecation(self._message, stacklevel=self._stacklevel, category=self._category) # This handles the descriptor being applied on a # instance or a class and makes both work correctly... if instance is not None: real_owner = instance else: real_owner = owner return getattr(real_owner, self._new_name) def moved_method(new_method_name, message=None, version=None, removal_version=None, stacklevel=3, category=None): """Decorates an *instance* method that was moved to another location.""" if not new_method_name.endswith(_MOVED_CALLABLE_POSTFIX): new_method_name += _MOVED_CALLABLE_POSTFIX return _moved_decorator('Method', new_method_name, message=message, version=version, removal_version=removal_version, stacklevel=stacklevel, attr_postfix=_MOVED_CALLABLE_POSTFIX, category=category) def moved_property(new_attribute_name, message=None, version=None, removal_version=None, stacklevel=3, category=None): """Decorates an *instance* property that was moved to another location.""" return _moved_decorator('Property', new_attribute_name, message=message, version=version, removal_version=removal_version, stacklevel=stacklevel, category=category) def moved_class(new_class, old_class_name, old_module_name, message=None, version=None, removal_version=None, stacklevel=3, category=None): """Deprecates a class that was moved to another location. This creates a 'new-old' type that can be used for a deprecation period that can be inherited from. This will emit warnings when the old locations class is initialized, telling where the new and improved location for the old class now is. """ if not inspect.isclass(new_class): _qual, type_name = _utils.get_qualified_name(type(new_class)) raise TypeError("Unexpected class type '%s' (expected" " class type only)" % type_name) old_name = ".".join((old_module_name, old_class_name)) new_name = _utils.get_class_name(new_class) prefix = _CLASS_MOVED_PREFIX_TPL % (old_name, new_name) out_message = _utils.generate_message( prefix, message=message, version=version, removal_version=removal_version) def decorator(f): @six.wraps(f, assigned=_utils.get_assigned(f)) def wrapper(self, *args, **kwargs): _utils.deprecation(out_message, stacklevel=stacklevel, category=category) return f(self, *args, **kwargs) return wrapper old_class = type(old_class_name, (new_class,), {}) old_class.__module__ = old_module_name old_class.__init__ = decorator(old_class.__init__) return old_class debtcollector-1.13.0/debtcollector/_utils.py0000664000567000056710000001431213070423566022267 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2015 Yahoo! Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import inspect import types import warnings import six try: _TYPE_TYPE = types.TypeType except AttributeError: _TYPE_TYPE = type # See: https://docs.python.org/2/library/__builtin__.html#module-__builtin__ # and see https://docs.python.org/2/reference/executionmodel.html (and likely # others)... _BUILTIN_MODULES = ('builtins', '__builtin__', '__builtins__', 'exceptions') _enabled = True def deprecation(message, stacklevel=None, category=None): """Warns about some type of deprecation that has been (or will be) made. This helper function makes it easier to interact with the warnings module by standardizing the arguments that the warning function receives so that it is easier to use. This should be used to emit warnings to users (users can easily turn these warnings off/on, see https://docs.python.org/2/library/warnings.html as they see fit so that the messages do not fill up the users logs with warnings that they do not wish to see in production) about functions, methods, attributes or other code that is deprecated and will be removed in a future release (this is done using these warnings to avoid breaking existing users of those functions, methods, code; which a library should avoid doing by always giving at *least* N + 1 release for users to address the deprecation warnings). """ if not _enabled: return if category is None: category = DeprecationWarning if stacklevel is None: warnings.warn(message, category=category) else: warnings.warn(message, category=category, stacklevel=stacklevel) def get_qualified_name(obj): # Prefer the py3.x name (if we can get at it...) try: return (True, obj.__qualname__) except AttributeError: return (False, obj.__name__) def generate_message(prefix, postfix=None, message=None, version=None, removal_version=None): """Helper to generate a common message 'style' for deprecation helpers.""" message_components = [prefix] if version: message_components.append(" in version '%s'" % version) if removal_version: if removal_version == "?": message_components.append(" and will be removed in a future" " version") else: message_components.append(" and will be removed in version '%s'" % removal_version) if postfix: message_components.append(postfix) if message: message_components.append(": %s" % message) return ''.join(message_components) def get_assigned(decorator): """Helper to fix/workaround https://bugs.python.org/issue3445""" if six.PY3: return functools.WRAPPER_ASSIGNMENTS else: assigned = [] for attr_name in functools.WRAPPER_ASSIGNMENTS: if hasattr(decorator, attr_name): assigned.append(attr_name) return tuple(assigned) def get_class_name(obj, fully_qualified=True): """Get class name for object. If object is a type, fully qualified name of the type is returned. Else, fully qualified name of the type of the object is returned. For builtin types, just name is returned. """ if not isinstance(obj, six.class_types): obj = type(obj) try: built_in = obj.__module__ in _BUILTIN_MODULES except AttributeError: pass else: if built_in: return obj.__name__ if fully_qualified and hasattr(obj, '__module__'): return '%s.%s' % (obj.__module__, obj.__name__) else: return obj.__name__ def get_method_self(method): """Gets the ``self`` object attached to this method (or none).""" if not inspect.ismethod(method): return None try: return six.get_method_self(method) except AttributeError: return None def get_callable_name(function): """Generate a name from callable. Tries to do the best to guess fully qualified callable name. """ method_self = get_method_self(function) if method_self is not None: # This is a bound method. if isinstance(method_self, six.class_types): # This is a bound class method. im_class = method_self else: im_class = type(method_self) try: parts = (im_class.__module__, function.__qualname__) except AttributeError: parts = (im_class.__module__, im_class.__name__, function.__name__) elif inspect.ismethod(function) or inspect.isfunction(function): # This could be a function, a static method, a unbound method... try: parts = (function.__module__, function.__qualname__) except AttributeError: if hasattr(function, 'im_class'): # This is a unbound method, which exists only in python 2.x im_class = function.im_class parts = (im_class.__module__, im_class.__name__, function.__name__) else: parts = (function.__module__, function.__name__) else: im_class = type(function) if im_class is _TYPE_TYPE: im_class = function try: parts = (im_class.__module__, im_class.__qualname__) except AttributeError: parts = (im_class.__module__, im_class.__name__) # When running under sphinx it appears this can be none? if so just # don't include it... mod, rest = (parts[0], parts[1:]) if not mod: return '.'.join(rest) else: return '.'.join(parts) debtcollector-1.13.0/debtcollector/fixtures/0000775000567000056710000000000013070423715022262 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/debtcollector/fixtures/__init__.py0000664000567000056710000000000013070423566024365 0ustar jenkinsjenkins00000000000000debtcollector-1.13.0/debtcollector/fixtures/disable.py0000664000567000056710000000231213070423566024241 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2015 Yahoo! Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from __future__ import absolute_import import fixtures from debtcollector import _utils class DisableFixture(fixtures.Fixture): """Fixture that disables debtcollector triggered warnings. This does **not** disable warnings calls emitted by other libraries. This can be used like:: from debtcollector.fixtures import disable with disable.DisableFixture(): """ def _setUp(self): self.addCleanup(setattr, _utils, "_enabled", True) _utils._enabled = False debtcollector-1.13.0/debtcollector/removals.py0000664000567000056710000003310613070423566022622 0ustar jenkinsjenkins00000000000000# Copyright 2014 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import functools import inspect import six import wrapt from debtcollector import _utils def _get_qualified_name(obj): return _utils.get_qualified_name(obj)[1] def _fetch_first_result(fget, fset, fdel, apply_func, value_not_found=None): """Fetch first non-none/empty result of applying ``apply_func``.""" for f in filter(None, (fget, fset, fdel)): result = apply_func(f) if result: return result return value_not_found class removed_property(object): """Property descriptor that deprecates a property. This works like the ``@property`` descriptor but can be used instead to provide the same functionality and also interact with the :mod:`warnings` module to warn when a property is accessed, set and/or deleted. :param message: string used as ending contents of the deprecate message :param version: version string (represents the version this deprecation was created in) :param removal_version: version string (represents the version this deprecation will be removed in); a string of '?' will denote this will be removed in some future unknown version :param stacklevel: stacklevel used in the :func:`warnings.warn` function to locate where the users code is when reporting the deprecation call (the default being 3) :param category: the :mod:`warnings` category to use, defaults to :py:class:`DeprecationWarning` if not provided """ # Message templates that will be turned into real messages as needed. _PROPERTY_GONE_TPLS = { 'set': "Setting the '%s' property is deprecated", 'get': "Reading the '%s' property is deprecated", 'delete': "Deleting the '%s' property is deprecated", } def __init__(self, fget=None, fset=None, fdel=None, doc=None, stacklevel=3, category=DeprecationWarning, version=None, removal_version=None, message=None): self.fset = fset self.fget = fget self.fdel = fdel self.stacklevel = stacklevel self.category = category self.version = version self.removal_version = removal_version self.message = message if doc is None and inspect.isfunction(fget): doc = getattr(fget, '__doc__', None) self._message_cache = {} self.__doc__ = doc def _fetch_message_from_cache(self, kind): try: out_message = self._message_cache[kind] except KeyError: prefix_tpl = self._PROPERTY_GONE_TPLS[kind] prefix = prefix_tpl % _fetch_first_result( self.fget, self.fset, self.fdel, _get_qualified_name, value_not_found="???") out_message = _utils.generate_message( prefix, message=self.message, version=self.version, removal_version=self.removal_version) self._message_cache[kind] = out_message return out_message def __call__(self, fget, **kwargs): self.fget = fget self.message = kwargs.get('message', self.message) self.version = kwargs.get('version', self.version) self.removal_version = kwargs.get('removal_version', self.removal_version) self.stacklevel = kwargs.get('stacklevel', self.stacklevel) self.category = kwargs.get('category', self.category) self.__doc__ = kwargs.get('doc', getattr(fget, '__doc__', self.__doc__)) # Regenerate all the messages... self._message_cache.clear() return self def __delete__(self, obj): if self.fdel is None: raise AttributeError("can't delete attribute") out_message = self._fetch_message_from_cache('delete') _utils.deprecation(out_message, stacklevel=self.stacklevel, category=self.category) self.fdel(obj) def __set__(self, obj, value): if self.fset is None: raise AttributeError("can't set attribute") out_message = self._fetch_message_from_cache('set') _utils.deprecation(out_message, stacklevel=self.stacklevel, category=self.category) self.fset(obj, value) def __get__(self, obj, value): if obj is None: return self if self.fget is None: raise AttributeError("unreadable attribute") out_message = self._fetch_message_from_cache('get') _utils.deprecation(out_message, stacklevel=self.stacklevel, category=self.category) return self.fget(obj) def getter(self, fget): o = type(self)(fget, self.fset, self.fdel, self.__doc__) o.message = self.message o.version = self.version o.stacklevel = self.stacklevel o.removal_version = self.removal_version o.category = self.category return o def setter(self, fset): o = type(self)(self.fget, fset, self.fdel, self.__doc__) o.message = self.message o.version = self.version o.stacklevel = self.stacklevel o.removal_version = self.removal_version o.category = self.category return o def deleter(self, fdel): o = type(self)(self.fget, self.fset, fdel, self.__doc__) o.message = self.message o.version = self.version o.stacklevel = self.stacklevel o.removal_version = self.removal_version o.category = self.category return o def remove(f=None, message=None, version=None, removal_version=None, stacklevel=3, category=None): """Decorates a function, method, or class to emit a deprecation warning Due to limitations of the wrapt library (and python) itself, if this is applied to subclasses of metaclasses then it likely will not work as expected. More information can be found at bug #1520397 to see if this situation affects your usage of this *universal* decorator, for this specific scenario please use :py:func:`.removed_class` instead. :param str message: A message to include in the deprecation warning :param str version: Specify what version the removed function is present in :param str removal_version: What version the function will be removed. If '?' is used this implies an undefined future version :param int stacklevel: How many entries deep in the call stack before ignoring :param type category: warnings message category (this defaults to ``DeprecationWarning`` when none is provided) """ if f is None: return functools.partial(remove, message=message, version=version, removal_version=removal_version, stacklevel=stacklevel, category=category) @wrapt.decorator def wrapper(f, instance, args, kwargs): qualified, f_name = _utils.get_qualified_name(f) if qualified: if inspect.isclass(f): prefix_pre = "Using class" thing_post = '' else: prefix_pre = "Using function/method" thing_post = '()' if not qualified: prefix_pre = "Using function/method" base_name = None if instance is None: # Decorator was used on a class if inspect.isclass(f): prefix_pre = "Using class" thing_post = '' module_name = _get_qualified_name(inspect.getmodule(f)) if module_name == '__main__': f_name = _utils.get_class_name( f, fully_qualified=False) else: f_name = _utils.get_class_name( f, fully_qualified=True) # Decorator was a used on a function else: thing_post = '()' module_name = _get_qualified_name(inspect.getmodule(f)) if module_name != '__main__': f_name = _utils.get_callable_name(f) # Decorator was used on a classmethod or instancemethod else: thing_post = '()' base_name = _utils.get_class_name(instance, fully_qualified=False) if base_name: thing_name = ".".join([base_name, f_name]) else: thing_name = f_name else: thing_name = f_name if thing_post: thing_name += thing_post prefix = prefix_pre + " '%s' is deprecated" % (thing_name) out_message = _utils.generate_message( prefix, version=version, removal_version=removal_version, message=message) _utils.deprecation(out_message, stacklevel=stacklevel, category=category) return f(*args, **kwargs) return wrapper(f) def removed_kwarg(old_name, message=None, version=None, removal_version=None, stacklevel=3, category=None): """Decorates a kwarg accepting function to deprecate a removed kwarg.""" prefix = "Using the '%s' argument is deprecated" % old_name out_message = _utils.generate_message( prefix, postfix=None, message=message, version=version, removal_version=removal_version) @wrapt.decorator def wrapper(f, instance, args, kwargs): if old_name in kwargs: _utils.deprecation(out_message, stacklevel=stacklevel, category=category) return f(*args, **kwargs) return wrapper def removed_class(cls_name, replacement=None, message=None, version=None, removal_version=None, stacklevel=3, category=None): """Decorates a class to denote that it will be removed at some point.""" def _wrap_it(old_init, out_message): @six.wraps(old_init, assigned=_utils.get_assigned(old_init)) def new_init(self, *args, **kwargs): _utils.deprecation(out_message, stacklevel=stacklevel, category=category) return old_init(self, *args, **kwargs) return new_init def _check_it(cls): if not inspect.isclass(cls): _qual, type_name = _utils.get_qualified_name(type(cls)) raise TypeError("Unexpected class type '%s' (expected" " class type only)" % type_name) def _cls_decorator(cls): _check_it(cls) out_message = _utils.generate_message( "Using class '%s' (either directly or via inheritance)" " is deprecated" % cls_name, postfix=None, message=message, version=version, removal_version=removal_version) cls.__init__ = _wrap_it(cls.__init__, out_message) return cls return _cls_decorator def removed_module(module, replacement=None, message=None, version=None, removal_version=None, stacklevel=3, category=None): """Helper to be called inside a module to emit a deprecation warning :param str replacment: A location (or information about) of any potential replacement for the removed module (if applicable) :param str message: A message to include in the deprecation warning :param str version: Specify what version the removed module is present in :param str removal_version: What version the module will be removed. If '?' is used this implies an undefined future version :param int stacklevel: How many entries deep in the call stack before ignoring :param type category: warnings message category (this defaults to ``DeprecationWarning`` when none is provided) """ if inspect.ismodule(module): module_name = _get_qualified_name(module) elif isinstance(module, six.string_types): module_name = module else: _qual, type_name = _utils.get_qualified_name(type(module)) raise TypeError("Unexpected module type '%s' (expected string or" " module type only)" % type_name) prefix = "The '%s' module usage is deprecated" % module_name if replacement: postfix = ", please use %s instead" % replacement else: postfix = None out_message = _utils.generate_message(prefix, postfix=postfix, message=message, version=version, removal_version=removal_version) _utils.deprecation(out_message, stacklevel=stacklevel, category=category) debtcollector-1.13.0/debtcollector/tests/0000775000567000056710000000000013070423715021553 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/debtcollector/tests/__init__.py0000664000567000056710000000000013070423566023656 0ustar jenkinsjenkins00000000000000debtcollector-1.13.0/debtcollector/tests/test_deprecation.py0000664000567000056710000005236713070423566025502 0ustar jenkinsjenkins00000000000000# Copyright (C) 2014 Yahoo! Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import inspect import warnings import debtcollector from debtcollector.fixtures import disable from debtcollector import moves from debtcollector import removals from debtcollector import renames from debtcollector.tests import base as test_base from debtcollector import updating @renames.renamed_kwarg('blip', 'blop') def blip_blop(blip=1, blop=1): return (blip, blop) def blip_blop_unwrapped(blip=1, blop=1): return (blip, blop) @renames.renamed_kwarg('blip', 'blop', category=PendingDeprecationWarning) def blip_blop_2(blip=1, blop=1): return (blip, blop) @renames.renamed_kwarg('blip', 'blop', replace=True) def blip_blop_3(blop=1): return blop @updating.updated_kwarg_default_value('type', 'cat', 'feline') def blip_blop_blip(type='cat'): return "The %s meowed quietly" % type def blip_blop_blip_unwrapped(type='cat'): return "The %s meowed quietly" % type class WoofWoof(object): @property def bark(self): return 'woof' @property @moves.moved_property('bark') def burk(self): return self.bark @property @moves.moved_property('bark', category=PendingDeprecationWarning) def berk(self): return self.bark @removals.removed_kwarg('resp', message="Please use 'response' instead") @classmethod def factory(cls, resp=None, response=None): return 'super-duper' class KittyKat(object): @moves.moved_method('supermeow') def meow(self, volume=11): return self.supermeow(volume) @moves.moved_method('supermeow', category=PendingDeprecationWarning) def maow(self, volume=11): return self.supermeow(volume) def supermeow(self, volume=11): return 'supermeow' class Giraffe(object): color = 'orange' colour = moves.moved_read_only_property('colour', 'color') @property def height(self): return 2 heightt = moves.moved_read_only_property('heightt', 'height') class NewHotness(object): def hot(self): return 'cold' @removals.remove() def crimson_lightning(fake_input=None): return fake_input def crimson_lightning_unwrapped(fake_input=None): return fake_input @removals.remove(category=PendingDeprecationWarning) def crimson_lightning_to_remove(fake_input=None): return fake_input @removals.remove() def red_comet(): return True @removals.remove(category=PendingDeprecationWarning) def blue_comet(): return True def yellow_sun(): """Yellow.""" return True yellowish_sun = moves.moved_function(yellow_sun, 'yellowish_sun', __name__) @removals.remove() class EFSF(object): pass @removals.remove(category=PendingDeprecationWarning) class EFSF_2(object): pass @removals.removed_class("StarLord") class StarLord(object): def __init__(self): self.name = "star" class StarLordJr(StarLord): def __init__(self, name): super(StarLordJr, self).__init__() self.name = name class ThingB(object): @removals.remove() def black_tristars(self): pass @removals.removed_property def green_tristars(self): return 'green' @green_tristars.setter def green_tristars(self, value): pass @green_tristars.deleter def green_tristars(self): pass @removals.removed_property(message="stop using me") def green_blue_tristars(self): return 'green-blue' @removals.remove(category=PendingDeprecationWarning) def blue_tristars(self): pass @removals.remove() @classmethod def white_wolf(cls): pass @removals.remove(category=PendingDeprecationWarning) @classmethod def yellow_wolf(cls): pass @removals.remove() @staticmethod def blue_giant(): pass @removals.remove(category=PendingDeprecationWarning) @staticmethod def green_giant(): pass OldHotness = moves.moved_class(NewHotness, 'OldHotness', __name__) OldHotness2 = moves.moved_class(NewHotness, 'OldHotness', __name__, category=PendingDeprecationWarning) class DeprecateAnythingTest(test_base.TestCase): def test_generation(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") debtcollector.deprecate("Its broken") debtcollector.deprecate("Its really broken") self.assertEqual(2, len(capture)) class MovedInheritableClassTest(test_base.TestCase): def test_broken_type_class(self): self.assertRaises(TypeError, moves.moved_class, 'b', __name__) def test_basics(self): old = OldHotness() self.assertIsInstance(old, NewHotness) self.assertEqual('cold', old.hot()) def test_warnings_emitted_creation(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") OldHotness() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_warnings_emitted_creation_pending(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") OldHotness2() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) def test_existing_refer_subclass(self): class MyOldThing(OldHotness): pass with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") MyOldThing() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) class MovedPropertyTest(test_base.TestCase): def test_basics(self): dog = WoofWoof() self.assertEqual('woof', dog.burk) self.assertEqual('woof', dog.bark) def test_readonly_move(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual('orange', Giraffe.colour) g = Giraffe() self.assertEqual(2, g.heightt) self.assertEqual(2, len(capture)) def test_warnings_emitted(self): dog = WoofWoof() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual('woof', dog.burk) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_warnings_emitted_pending(self): dog = WoofWoof() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual('woof', dog.berk) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) def test_warnings_not_emitted(self): dog = WoofWoof() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual('woof', dog.bark) self.assertEqual(0, len(capture)) class DisabledTest(test_base.TestCase): def test_basics(self): dog = WoofWoof() c = KittyKat() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") with disable.DisableFixture(): self.assertTrue(yellowish_sun()) self.assertEqual('woof', dog.berk) self.assertEqual('supermeow', c.meow()) self.assertEqual(0, len(capture)) class MovedFunctionTest(test_base.TestCase): def test_basics(self): self.assertTrue(yellowish_sun()) self.assertTrue(yellow_sun()) self.assertEqual("Yellow.", yellowish_sun.__doc__) def test_warnings_emitted(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertTrue(yellowish_sun()) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) class MovedMethodTest(test_base.TestCase): def test_basics(self): c = KittyKat() self.assertEqual('supermeow', c.meow()) self.assertEqual('supermeow', c.supermeow()) def test_warnings_emitted(self): c = KittyKat() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual('supermeow', c.meow()) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_warnings_emitted_pending(self): c = KittyKat() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual('supermeow', c.maow()) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) def test_warnings_not_emitted(self): c = KittyKat() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual('supermeow', c.supermeow()) self.assertEqual(0, len(capture)) def test_keeps_argspec(self): self.assertEqual(inspect.getargspec(KittyKat.supermeow), inspect.getargspec(KittyKat.meow)) class RenamedKwargTest(test_base.TestCase): def test_basics(self): self.assertEqual((1, 1), blip_blop()) self.assertEqual((2, 1), blip_blop(blip=2)) self.assertEqual((1, 2), blip_blop(blop=2)) self.assertEqual((2, 2), blip_blop(blip=2, blop=2)) self.assertEqual(2, blip_blop_3(blip=2)) self.assertEqual(2, blip_blop_3(blop=2)) def test_warnings_emitted(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual((2, 1), blip_blop(blip=2)) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual(2, blip_blop_3(blip=2)) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_warnings_emitted_classmethod(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") WoofWoof.factory(resp="hi") self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") WoofWoof.factory(response="hi") self.assertEqual(0, len(capture)) def test_warnings_emitted_pending(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual((2, 1), blip_blop_2(blip=2)) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) def test_warnings_not_emitted(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual((1, 2), blip_blop(blop=2)) self.assertEqual(0, len(capture)) with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual(2, blip_blop_3(blop=2)) self.assertEqual(0, len(capture)) def test_argspec(self): # The decorated function keeps its argspec. self.assertEqual(inspect.getargspec(blip_blop_unwrapped), inspect.getargspec(blip_blop)) class UpdatedArgsTest(test_base.TestCase): def test_basic(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual('The cat meowed quietly', blip_blop_blip()) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(FutureWarning, w.category) def test_kwarg_set(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual( 'The kitten meowed quietly', blip_blop_blip(type='kitten')) self.assertEqual(0, len(capture)) def test_argspec_preserved(self): self.assertEqual(inspect.getargspec(blip_blop_blip_unwrapped), inspect.getargspec(blip_blop_blip)) class RemovalTests(test_base.TestCase): def test_function_args(self): self.assertEqual(666, crimson_lightning(666)) def test_function_noargs(self): self.assertTrue(red_comet()) def test_function_keeps_argspec(self): # The decorated function keeps its argspec. self.assertEqual( inspect.getargspec(crimson_lightning_unwrapped), inspect.getargspec(crimson_lightning)) def test_deprecated_kwarg(self): @removals.removed_kwarg('b') def f(b=2): return b with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual(3, f(b=3)) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual(2, f()) self.assertEqual(0, len(capture)) def test_removed_kwarg_keeps_argspec(self): @removals.removed_kwarg('b') def f(b=2): return b def f_unwrapped(b=2): return b self.assertEqual(inspect.getargspec(f_unwrapped), inspect.getargspec(f)) def test_pending_deprecated_kwarg(self): @removals.removed_kwarg('b', category=PendingDeprecationWarning) def f(b=2): return b with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual(3, f(b=3)) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual(2, f()) self.assertEqual(0, len(capture)) def test_warnings_emitted_property(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") o = ThingB() self.assertEqual('green', o.green_tristars) o.green_tristars = 'b' del o.green_tristars self.assertEqual(3, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_warnings_emitted_property_custom_message(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") o = ThingB() self.assertEqual('green-blue', o.green_blue_tristars) self.assertEqual(1, len(capture)) w = capture[0] self.assertIn('stop using me', str(w.message)) self.assertEqual(DeprecationWarning, w.category) def test_warnings_emitted_function_args(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual(666, crimson_lightning(666)) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_pending_warnings_emitted_function_args(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertEqual(666, crimson_lightning_to_remove(666)) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) def test_warnings_emitted_function_noargs(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertTrue(red_comet()) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_pending_warnings_emitted_function_noargs(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") self.assertTrue(blue_comet()) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) def test_warnings_emitted_class(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") EFSF() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_pending_warnings_emitted_class(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") EFSF_2() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) def test_pending_warnings_emitted_class_direct(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") s = StarLord() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) self.assertEqual("star", s.name) def test_pending_warnings_emitted_class_inherit(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") s = StarLordJr("star_jr") self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) self.assertEqual("star_jr", s.name) def test_warnings_emitted_instancemethod(self): zeon = ThingB() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") zeon.black_tristars() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_pending_warnings_emitted_instancemethod(self): zeon = ThingB() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") zeon.blue_tristars() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) def test_pending_warnings_emitted_classmethod(self): zeon = ThingB() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") zeon.yellow_wolf() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) def test_warnings_emitted_classmethod(self): zeon = ThingB() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") zeon.white_wolf() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_warnings_emitted_staticmethod(self): zeon = ThingB() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") zeon.blue_giant() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_pending_warnings_emitted_staticmethod(self): zeon = ThingB() with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") zeon.green_giant() self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) def test_removed_module(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") removals.removed_module(__name__) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(DeprecationWarning, w.category) def test_pending_removed_module(self): with warnings.catch_warnings(record=True) as capture: warnings.simplefilter("always") removals.removed_module(__name__, category=PendingDeprecationWarning) self.assertEqual(1, len(capture)) w = capture[0] self.assertEqual(PendingDeprecationWarning, w.category) def test_removed_module_bad_type(self): self.assertRaises(TypeError, removals.removed_module, 2) debtcollector-1.13.0/debtcollector/tests/base.py0000664000567000056710000000143213070423566023043 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # Copyright 2010-2011 OpenStack Foundation # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslotest import base class TestCase(base.BaseTestCase): """Test case base class for all unit tests.""" debtcollector-1.13.0/debtcollector/updating.py0000664000567000056710000000457613070423566022616 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # Copyright (C) 2015 Yahoo! Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import six import wrapt if six.PY3: import inspect Parameter = inspect.Parameter Signature = inspect.Signature get_signature = inspect.signature else: # Provide an equivalent but use funcsigs instead... import funcsigs Parameter = funcsigs.Parameter Signature = funcsigs.Signature get_signature = funcsigs.signature from debtcollector import _utils _KWARG_UPDATED_POSTFIX_TPL = (', please update the code to explicitly set %s ' 'as the value') _KWARG_UPDATED_PREFIX_TPL = ('The %s argument is changing its default value ' 'to %s') def updated_kwarg_default_value(name, old_value, new_value, message=None, version=None, stacklevel=3, category=FutureWarning): """Decorates a kwarg accepting function to change the default value""" prefix = _KWARG_UPDATED_PREFIX_TPL % (name, new_value) postfix = _KWARG_UPDATED_POSTFIX_TPL % old_value out_message = _utils.generate_message( prefix, postfix=postfix, message=message, version=version) def decorator(f): sig = get_signature(f) varnames = list(six.iterkeys(sig.parameters)) @wrapt.decorator def wrapper(wrapped, instance, args, kwargs): explicit_params = set( varnames[:len(args)] + list(kwargs.keys()) ) allparams = set(varnames) default_params = set(allparams - explicit_params) if name in default_params: _utils.deprecation(out_message, stacklevel=stacklevel, category=category) return wrapped(*args, **kwargs) return wrapper(f) return decorator debtcollector-1.13.0/requirements.txt0000664000567000056710000000054113070423566021054 0ustar jenkinsjenkins00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr>=2.0.0 # Apache-2.0 six>=1.9.0 # MIT wrapt>=1.7.0 # BSD License funcsigs>=0.4;python_version=='2.7' or python_version=='2.6' # Apache-2.0 debtcollector-1.13.0/debtcollector.egg-info/0000775000567000056710000000000013070423715022103 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/debtcollector.egg-info/SOURCES.txt0000664000567000056710000000224213070423715023767 0ustar jenkinsjenkins00000000000000.coveragerc .mailmap .testr.conf AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst babel.cfg requirements.txt setup.cfg setup.py test-requirements.txt tox.ini debtcollector/__init__.py debtcollector/_utils.py debtcollector/moves.py debtcollector/removals.py debtcollector/renames.py debtcollector/updating.py debtcollector.egg-info/PKG-INFO debtcollector.egg-info/SOURCES.txt debtcollector.egg-info/dependency_links.txt debtcollector.egg-info/not-zip-safe debtcollector.egg-info/pbr.json debtcollector.egg-info/requires.txt debtcollector.egg-info/top_level.txt debtcollector/fixtures/__init__.py debtcollector/fixtures/disable.py debtcollector/tests/__init__.py debtcollector/tests/base.py debtcollector/tests/test_deprecation.py doc/source/api.rst doc/source/conf.py doc/source/contributing.rst doc/source/examples.rst doc/source/history.rst doc/source/index.rst doc/source/installation.rst releasenotes/notes/add-reno-996dd44974d53238.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/ocata.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder tools/tox_install.shdebtcollector-1.13.0/debtcollector.egg-info/not-zip-safe0000664000567000056710000000000113070423673024334 0ustar jenkinsjenkins00000000000000 debtcollector-1.13.0/debtcollector.egg-info/top_level.txt0000664000567000056710000000001613070423715024632 0ustar jenkinsjenkins00000000000000debtcollector debtcollector-1.13.0/debtcollector.egg-info/pbr.json0000664000567000056710000000005613070423715023562 0ustar jenkinsjenkins00000000000000{"git_version": "154472d", "is_release": true}debtcollector-1.13.0/debtcollector.egg-info/PKG-INFO0000664000567000056710000000465413070423715023211 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: debtcollector Version: 1.13.0 Summary: A collection of Python deprecation patterns and strategies that help you collect your technical debt in a non-destructive manner. Home-page: http://docs.openstack.org/developer/debtcollector Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/debtcollector.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on Debtcollector ============= .. image:: https://img.shields.io/pypi/v/debtcollector.svg :target: https://pypi.python.org/pypi/debtcollector/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/debtcollector.svg :target: https://pypi.python.org/pypi/debtcollector/ :alt: Downloads A collection of Python deprecation patterns and strategies that help you collect your technical debt in a non-destructive manner. The goal of this library is to provide well documented developer facing deprecation patterns that start of with a basic set and can expand into a larger set of patterns as time goes on. The desired output of these patterns is to apply the warnings module to emit DeprecationWarning or PendingDeprecationWarning or similar derivative to developers using libraries (or potentially applications) about future deprecations. * Free software: Apache license * Documentation: http://docs.openstack.org/developer/debtcollector * Source: http://git.openstack.org/cgit/openstack/debtcollector * Bugs: http://bugs.launchpad.net/debtcollector Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 debtcollector-1.13.0/debtcollector.egg-info/dependency_links.txt0000664000567000056710000000000113070423715026151 0ustar jenkinsjenkins00000000000000 debtcollector-1.13.0/debtcollector.egg-info/requires.txt0000664000567000056710000000006113070423715024500 0ustar jenkinsjenkins00000000000000pbr>=2.0.0 six>=1.9.0 wrapt>=1.7.0 funcsigs>=0.4 debtcollector-1.13.0/.testr.conf0000664000567000056710000000047613070423566017665 0ustar jenkinsjenkins00000000000000[DEFAULT] test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--listdebtcollector-1.13.0/README.rst0000664000567000056710000000245513070423566017265 0ustar jenkinsjenkins00000000000000======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/debtcollector.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on Debtcollector ============= .. image:: https://img.shields.io/pypi/v/debtcollector.svg :target: https://pypi.python.org/pypi/debtcollector/ :alt: Latest Version .. image:: https://img.shields.io/pypi/dm/debtcollector.svg :target: https://pypi.python.org/pypi/debtcollector/ :alt: Downloads A collection of Python deprecation patterns and strategies that help you collect your technical debt in a non-destructive manner. The goal of this library is to provide well documented developer facing deprecation patterns that start of with a basic set and can expand into a larger set of patterns as time goes on. The desired output of these patterns is to apply the warnings module to emit DeprecationWarning or PendingDeprecationWarning or similar derivative to developers using libraries (or potentially applications) about future deprecations. * Free software: Apache license * Documentation: http://docs.openstack.org/developer/debtcollector * Source: http://git.openstack.org/cgit/openstack/debtcollector * Bugs: http://bugs.launchpad.net/debtcollector debtcollector-1.13.0/doc/0000775000567000056710000000000013070423715016331 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/doc/source/0000775000567000056710000000000013070423715017631 5ustar jenkinsjenkins00000000000000debtcollector-1.13.0/doc/source/history.rst0000664000567000056710000000003513070423566022066 0ustar jenkinsjenkins00000000000000.. include:: ../../ChangeLog debtcollector-1.13.0/doc/source/index.rst0000664000567000056710000000130313070423566021473 0ustar jenkinsjenkins00000000000000Welcome to debtcollector's documentation! ========================================= A collection of Python deprecation patterns and strategies that help you collect your technical debt in a non-destructive manner. .. note:: It should be noted that even though it is designed with OpenStack integration in mind, and that is where most of its *current* integration is it aims to be generally usable and useful in any project. Contents ======== .. toctree:: :maxdepth: 2 installation api examples contributing Release Notes ============= .. toctree:: :maxdepth: 1 history Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` debtcollector-1.13.0/doc/source/contributing.rst0000664000567000056710000000011413070423566023072 0ustar jenkinsjenkins00000000000000============ Contributing ============ .. include:: ../../CONTRIBUTING.rst debtcollector-1.13.0/doc/source/api.rst0000664000567000056710000000116613070423566021144 0ustar jenkinsjenkins00000000000000=== API === The currently documented publicly exposed API's for usage in your project are defined below. .. warning:: External usage of internal utility functions and modules should be kept to a **minimum** as they may be altered, refactored or moved to other locations **without** notice (and without the typical deprecation cycle). Helpers ------- .. automodule:: debtcollector Moves ----- .. automodule:: debtcollector.moves Renames ------- .. automodule:: debtcollector.renames Removals -------- .. automodule:: debtcollector.removals Fixtures -------- .. automodule:: debtcollector.fixtures.disable debtcollector-1.13.0/doc/source/conf.py0000664000567000056710000000527013070423566021140 0ustar jenkinsjenkins00000000000000# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import datetime import os import sys sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.doctest', 'oslosphinx', ] # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'debtcollector' copyright = u'%s, OpenStack Foundation' % datetime.date.today().year # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' # html_static_path = ['static'] # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', '%s.tex' % project, u'%s Documentation' % project, u'OpenStack Foundation', 'manual'), ] # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None} # -- Options for autoddoc ---------------------------------------------------- # Keep source order autodoc_member_order = 'bysource' # Always include members autodoc_default_flags = ['members', 'show-inheritance'] debtcollector-1.13.0/doc/source/installation.rst0000664000567000056710000000032013070423566023063 0ustar jenkinsjenkins00000000000000============ Installation ============ At the command line:: $ pip install debtcollector Or, if you have virtualenvwrapper installed:: $ mkvirtualenv debtcollector $ pip install debtcollector debtcollector-1.13.0/doc/source/examples.rst0000664000567000056710000003006313070423566022207 0ustar jenkinsjenkins00000000000000======== Examples ======== Removing a class/classmethod/method/function -------------------------------------------- To signal to a user that a method (staticmethod, classmethod, or regular instance method) or a class or function is going to be removed at some point in the future the :py:func:`~debtcollector.removals.remove` function/decorator can be used to achieve this in a non-destructive manner. A basic example to do just this (on a method/function): .. doctest:: >>> from debtcollector import removals >>> import warnings >>> warnings.simplefilter('always') >>> class Car(object): ... @removals.remove ... def start(self): ... pass ... >>> c = Car() >>> c.start() **Expected output:** .. testoutput:: __main__:1: DeprecationWarning: Using function/method 'Car.start()' is deprecated A basic example to do just this (on a class): .. doctest:: >>> from debtcollector import removals >>> import warnings >>> warnings.simplefilter('always') >>> @removals.removed_class("Pinto") ... class Pinto(object): ... pass ... >>> p = Pinto() **Expected output:** .. testoutput:: __main__:1: DeprecationWarning: Using class 'Pinto' (either directly or via inheritance) is deprecated A basic example to do just this (on a classmethod): .. doctest:: >>> from debtcollector import removals >>> import warnings >>> warnings.simplefilter("once") >>> class OldAndBusted(object): ... @removals.remove ... @classmethod ... def fix_things(cls): ... pass ... >>> OldAndBusted.fix_things() **Expected output:** .. testoutput:: __main__:1: DeprecationWarning: Using function/method 'OldAndBusted.fix_things()' is deprecated Removing a instance property ---------------------------- Use the :py:func:`~debtcollector.removals.removed_property` decorator to signal that an attribute of a class is deprecated. A basic example to do just this: .. doctest:: >>> import warnings >>> warnings.simplefilter("once") >>> from debtcollector import removals >>> class OldAndBusted(object): ... @removals.removed_property ... def thing(self): ... return 'old-and-busted' ... @thing.setter ... def thing(self, value): ... pass ... @thing.deleter ... def thing(self): ... pass ... >>> o = OldAndBusted() >>> o.thing 'old-and-busted' >>> o.thing = '2' >>> del o.thing .. testoutput:: __main__:1: DeprecationWarning: Reading the 'thing' property is deprecated __main__:1: DeprecationWarning: Setting the 'thing' property is deprecated __main__:1: DeprecationWarning: Deleting the 'thing' property is deprecated Removing a keyword argument --------------------------- A basic example to do just this (on a classmethod): .. doctest:: >>> import warnings >>> warnings.simplefilter("once") >>> from debtcollector import removals >>> class OldAndBusted(object): ... @removals.removed_kwarg('resp', message="Please use 'response' instead") ... @classmethod ... def factory(cls, resp=None, response=None): ... response = resp or response ... return response ... >>> OldAndBusted.factory(resp='super-duper') 'super-duper' .. testoutput:: __main__:1: DeprecationWarning: Using the 'resp' argument is deprecated: Please use 'response' instead A basic example to do just this (on a ``__init__`` method): .. doctest:: >>> import warnings >>> warnings.simplefilter("once") >>> from debtcollector import removals >>> class OldAndBusted(object): ... @removals.removed_kwarg('bleep') ... def __init__(self, bleep=None): ... self.bloop = bleep ... >>> o = OldAndBusted(bleep=2) .. testoutput:: __main__:1: DeprecationWarning: Using the 'bleep' argument is deprecated Changing the default value of a keyword argument ------------------------------------------------ A basic example to do just this: .. doctest:: >>> import warnings >>> warnings.simplefilter("once") >>> from debtcollector import updating >>> class OldAndBusted(object): ... ip = '127.0.0.1' ... @updating.updated_kwarg_default_value('type', 'http', 'https') ... def url(self, type='http'): ... response = '%s://%s' % (type, self.ip) ... return response ... >>> OldAndBusted().url() 'http://127.0.0.1' .. testoutput:: __main__:1: FutureWarning: The http argument is changing its default value to https, please update the code to explicitly set http as the value A basic classmethod example. .. note:: the @classmethod decorator is before the debtcollector one .. doctest:: >>> import warnings >>> warnings.simplefilter("once") >>> from debtcollector import updating >>> class OldAndBusted(object): ... ip = '127.0.0.1' ... @classmethod ... @updating.updated_kwarg_default_value('type', 'http', 'https') ... def url(cls, type='http'): ... response = '%s://%s' % (type, cls.ip) ... return response ... >>> OldAndBusted.url() 'http://127.0.0.1' .. testoutput:: __main__:1: FutureWarning: The http argument is changing its default value to https, please update the code to explicitly set http as the value Moving a function ----------------- To change the name or location of a regular function use the :py:func:`~debtcollector.moves.moved_function` function: .. doctest:: >>> from debtcollector import moves >>> import warnings >>> warnings.simplefilter('always') >>> def new_thing(): ... return "new thing" ... >>> old_thing = moves.moved_function(new_thing, 'old_thing', __name__) >>> new_thing() 'new thing' >>> old_thing() 'new thing' **Expected output:** .. testoutput:: __main__:1: DeprecationWarning: Function '__main__.old_thing()' has moved to '__main__.new_thing()' Moving a method --------------- To move a *instance* method from an existing one to a new one the :py:func:`~debtcollector.moves.moved_method` function/decorator can be used to achieve this in a non-destructive manner. A basic example to do just this: .. doctest:: >>> from debtcollector import moves >>> import warnings >>> warnings.simplefilter('always') >>> class Cat(object): ... @moves.moved_method('meow') ... def mewow(self): ... return self.meow() ... def meow(self): ... return 'kitty' ... >>> c = Cat() >>> c.mewow() 'kitty' >>> c.meow() 'kitty' **Expected output:** .. testoutput:: __main__:1: DeprecationWarning: Method 'Cat.mewow()' has moved to 'Cat.meow()' Moving a property ----------------- To move a *instance* property from an existing one to a new one the :py:func:`~debtcollector.moves.moved_property` function/decorator can be used to achieve this in a non-destructive manner. A basic example to do just this: .. doctest:: >>> from debtcollector import moves >>> import warnings >>> warnings.simplefilter('always') >>> class Dog(object): ... @property ... @moves.moved_property('bark') ... def burk(self): ... return self.bark ... @property ... def bark(self): ... return 'woof' ... >>> d = Dog() >>> d.burk 'woof' >>> d.bark 'woof' **Expected output:** .. testoutput:: __main__:1: DeprecationWarning: Property 'Dog.burk' has moved to 'Dog.bark' Moving a class -------------- To move a *class* from an existing one to a new one the :py:func:`~debtcollector.moves.moved_class` type generator function can be used to achieve this in a non-destructive manner. A basic example to do just this: .. doctest:: >>> from debtcollector import moves >>> import warnings >>> warnings.simplefilter('always') >>> class WizBang(object): ... pass ... >>> OldWizBang = moves.moved_class(WizBang, 'OldWizBang', __name__) >>> a = OldWizBang() >>> b = WizBang() **Expected output:** .. testoutput:: __main__:1: DeprecationWarning: Class '__main__.OldWizBang' has moved to '__main__.WizBang' Renaming a keyword argument --------------------------- To notify the user when a keyword argument has been replaced with a new and improved keyword argument and the user is still using the old keyword argument the :py:func:`~debtcollector.renames.renamed_kwarg` function/decorator can be used to achieve this in a non-destructive manner. A basic example to do just this: .. doctest:: >>> from debtcollector import renames >>> import warnings >>> warnings.simplefilter('always') >>> @renames.renamed_kwarg('snizzle', 'nizzle') ... def do_the_deed(snizzle=True, nizzle=True): ... return (snizzle, nizzle) ... >>> do_the_deed() (True, True) >>> do_the_deed(snizzle=False) (False, True) >>> do_the_deed(nizzle=False) (True, False) **Expected output:** .. testoutput:: __main__:1: DeprecationWarning: Using the 'snizzle' argument is deprecated, please use the 'nizzle' argument instead Further customizing the emitted messages ---------------------------------------- It is typically useful to tell the user when a deprecation has started and when the deprecated item will be officially removed (deleted or other). To enable this all the currently provided functions this library provides take a ``message``, ``version`` and ``removal_version`` keyword arguments. These are used in forming the message that is shown to the user when they trigger the deprecated activity. A basic example to do just this: .. doctest:: >>> from debtcollector import renames >>> import warnings >>> warnings.simplefilter('always') >>> @renames.renamed_kwarg('snizzle', 'nizzle', version="0.5", removal_version="0.7") ... def do_the_deed(snizzle=True, nizzle=True): ... pass ... >>> do_the_deed(snizzle=False) **Expected output:** .. testoutput:: __main__:1: DeprecationWarning: Using the 'snizzle' argument is deprecated in version '0.5' and will be removed in version '0.7', please use the 'nizzle' argument instead If the ``removal_version`` is unknown the special character ``?`` can be used instead (to denote that the deprecated activity will be removed sometime in the future). A basic example to do just this: .. doctest:: >>> from debtcollector import renames >>> import warnings >>> warnings.simplefilter('always') >>> @renames.renamed_kwarg('snizzle', 'nizzle', version="0.5", removal_version="?") ... def do_the_deed(snizzle=True, nizzle=True): ... pass ... >>> do_the_deed(snizzle=False) **Expected output:** .. testoutput:: __main__:1: DeprecationWarning: Using the 'snizzle' argument is deprecated in version '0.5' and will be removed in a future version, please use the 'nizzle' argument instead To further customize the message (with a special postfix) the ``message`` keyword argument can be provided. A basic example to do just this: .. doctest:: >>> from debtcollector import renames >>> import warnings >>> warnings.simplefilter('always') >>> @renames.renamed_kwarg('snizzle', 'nizzle', message="Pretty please stop using it") ... def do_the_deed(snizzle=True, nizzle=True): ... pass ... >>> do_the_deed(snizzle=False) **Expected output:** .. testoutput:: __main__:1: DeprecationWarning: Using the 'snizzle' argument is deprecated, please use the 'nizzle' argument instead: Pretty please stop using it Deprecating anything else ------------------------- For use-cases which do not fit the above decorators, properties other provided functionality the final option is to use debtcollectors the :py:func:`~debtcollector.deprecate` function to make your own messages (using the message building logic that debtcollector uses itself). A basic example to do just this: .. doctest:: >>> import warnings >>> warnings.simplefilter("always") >>> import debtcollector >>> debtcollector.deprecate("This is no longer supported", version="1.0") .. testoutput:: __main__:1: DeprecationWarning: This is no longer supported in version '1.0'