Retire Packaging Deb project repos
This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: I6ca27a94cfb35256d71966ea300245365d210350
This commit is contained in:
33
.gitignore
vendored
33
.gitignore
vendored
@@ -1,33 +0,0 @@
|
||||
# Compiled files
|
||||
*.py[co]
|
||||
*.a
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Sphinx
|
||||
_build
|
||||
|
||||
# Packages/installer info
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
|
||||
# Other
|
||||
.testrepository
|
||||
.tox
|
||||
.*.swp
|
||||
.coverage
|
||||
cover
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
|
||||
# Editors
|
||||
*~
|
||||
@@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack-dev/hacking.git
|
||||
3
.mailmap
3
.mailmap
@@ -1,3 +0,0 @@
|
||||
# Format is:
|
||||
# Name <preferred e-mail> <other e-mail >
|
||||
Joe Gordon <joe.gordon0@gmail.com> <jogo@cloudscaling.com>
|
||||
@@ -1,8 +0,0 @@
|
||||
[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=--list
|
||||
@@ -1,16 +0,0 @@
|
||||
If 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/hacking
|
||||
352
HACKING.rst
352
HACKING.rst
@@ -1,352 +0,0 @@
|
||||
.. _StyleGuide:
|
||||
|
||||
OpenStack Style Guidelines
|
||||
==========================
|
||||
|
||||
OpenStack has a set of style guidelines for clarity. OpenStack is a
|
||||
very large code base (over 1 Million lines of python), spanning dozens
|
||||
of git trees, with over a thousand developers contributing every 12
|
||||
months. As such common style helps developers understand code in
|
||||
reviews, move between projects smoothly, and overall make the code
|
||||
more maintainable.
|
||||
|
||||
|
||||
Step 0
|
||||
------
|
||||
|
||||
- Step 1: Read `pep8`_
|
||||
- Step 2: Read `pep8`_ again
|
||||
- Step 3: Read on
|
||||
|
||||
.. _`pep8`: http://www.python.org/dev/peps/pep-0008/
|
||||
|
||||
General
|
||||
-------
|
||||
- [H903] Use only UNIX style newlines (``\n``), not Windows style (``\r\n``)
|
||||
- It is preferred to wrap long lines in parentheses and not a backslash
|
||||
for line continuation.
|
||||
- [H201] Do not write ``except:``, use ``except Exception:`` at the very least.
|
||||
When catching an exception you should be as specific so you don't mistakenly
|
||||
catch unexpected exceptions.
|
||||
- [H101] Include your name with TODOs as in ``# TODO(yourname)``. This makes
|
||||
it easier to find out who the author of the comment was.
|
||||
- [H105] Don't use author tags. We use version control instead.
|
||||
- [H106] Don't put vim configuration in source files (off by default).
|
||||
- [H904] Delay string interpolations at logging calls (off by default).
|
||||
- Do not shadow a built-in or reserved word. Shadowing built -in or reserved
|
||||
words makes the code harder to understand. Example::
|
||||
|
||||
def list():
|
||||
return [1, 2, 3]
|
||||
|
||||
mylist = list() # BAD, shadows `list` built-in
|
||||
|
||||
class Foo(object):
|
||||
def list(self):
|
||||
return [1, 2, 3]
|
||||
|
||||
mylist = Foo().list() # OKAY, does not shadow built-in
|
||||
|
||||
|
||||
Imports
|
||||
-------
|
||||
|
||||
- Do not import objects, only modules (*)
|
||||
- [H301] Do not import more than one module per line (*)
|
||||
- [H303] Do not use wildcard ``*`` import (*)
|
||||
- [H304] Do not make relative imports
|
||||
- [H306] Alphabetically order your imports by the full module path.
|
||||
Organize your imports according to the `Import order
|
||||
template`_ and `Real-world Import Order Examples`_ below.
|
||||
|
||||
(*) exceptions are:
|
||||
|
||||
- imports from ``migrate`` package
|
||||
- imports from ``sqlalchemy`` package
|
||||
- function imports from ``i18n`` module
|
||||
|
||||
Import order template
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
{{stdlib imports in human alphabetical order}}
|
||||
\n
|
||||
{{third-party lib imports in human alphabetical order}}
|
||||
\n
|
||||
{{project imports in human alphabetical order}}
|
||||
\n
|
||||
\n
|
||||
{{begin your code}}
|
||||
|
||||
Real-world Import Order Examples
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Example::
|
||||
|
||||
import httplib
|
||||
import logging
|
||||
import random
|
||||
import StringIO
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import eventlet
|
||||
import webob.exc
|
||||
|
||||
import nova.api.ec2
|
||||
from nova.api import manager
|
||||
from nova.api import openstack
|
||||
from nova.auth import users
|
||||
from nova.endpoint import cloud
|
||||
import nova.flags
|
||||
from nova.i18n import _, _LC
|
||||
from nova import test
|
||||
|
||||
|
||||
Docstrings
|
||||
----------
|
||||
- [H401] Docstrings should not start with a space.
|
||||
- [H403] Multi line docstrings should end on a new line.
|
||||
- [H404] Multi line docstrings should start without a leading new line.
|
||||
- [H405] Multi line docstrings should start with a one line summary followed
|
||||
by an empty line.
|
||||
|
||||
Example::
|
||||
|
||||
"""A multi line docstring has a one-line summary, less than 80 characters.
|
||||
|
||||
Then a new paragraph after a newline that explains in more detail any
|
||||
general information about the function, class or method. Example usages
|
||||
are also great to have here if it is a complex class or function.
|
||||
|
||||
When writing the docstring for a class, an extra line should be placed
|
||||
after the closing quotations. For more in-depth explanations for these
|
||||
decisions see http://www.python.org/dev/peps/pep-0257/
|
||||
|
||||
If you are going to describe parameters and return values, use Sphinx, the
|
||||
appropriate syntax is as follows.
|
||||
|
||||
:param foo: the foo parameter
|
||||
:param bar: the bar parameter
|
||||
:returns: return_type -- description of the return value
|
||||
:returns: description of the return value
|
||||
:raises: AttributeError, KeyError
|
||||
"""
|
||||
|
||||
|
||||
Dictionaries/Lists
|
||||
------------------
|
||||
If a dictionary (dict) or list object is longer than 80 characters, its items
|
||||
should be split with newlines. Embedded iterables should have their items
|
||||
indented. Additionally, the last item in the dictionary should have a trailing
|
||||
comma. This increases readability and simplifies future diffs.
|
||||
|
||||
Example::
|
||||
|
||||
my_dictionary = {
|
||||
"image": {
|
||||
"name": "Just a Snapshot",
|
||||
"size": 2749573,
|
||||
"properties": {
|
||||
"user_id": 12,
|
||||
"arch": "x86_64",
|
||||
},
|
||||
"things": [
|
||||
"thing_one",
|
||||
"thing_two",
|
||||
],
|
||||
"status": "ACTIVE",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
- [H501] Do not use ``locals()`` or ``self.__dict__`` for formatting strings,
|
||||
it is not clear as using explicit dictionaries and can hide errors during
|
||||
refactoring.
|
||||
|
||||
Calling Methods
|
||||
---------------
|
||||
Calls to methods 80 characters or longer should format each argument with
|
||||
newlines. This is not a requirement, but a guideline::
|
||||
|
||||
unnecessarily_long_function_name('string one',
|
||||
'string two',
|
||||
kwarg1=constants.ACTIVE,
|
||||
kwarg2=['a', 'b', 'c'])
|
||||
|
||||
|
||||
Rather than constructing parameters inline, it is better to break things up::
|
||||
|
||||
list_of_strings = [
|
||||
'what_a_long_string',
|
||||
'not as long',
|
||||
]
|
||||
|
||||
dict_of_numbers = {
|
||||
'one': 1,
|
||||
'two': 2,
|
||||
'twenty four': 24,
|
||||
}
|
||||
|
||||
object_one.call_a_method('string three',
|
||||
'string four',
|
||||
kwarg1=list_of_strings,
|
||||
kwarg2=dict_of_numbers)
|
||||
|
||||
|
||||
Internationalization (i18n) Strings
|
||||
-----------------------------------
|
||||
In order to support multiple languages, we have a mechanism to support
|
||||
automatic translations of exception and log strings.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _("An error occurred")
|
||||
raise HTTPBadRequest(explanation=msg)
|
||||
|
||||
- [H702] If you have a variable to place within the string, first
|
||||
internationalize the template string then do the replacement.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _LE("Missing parameter: %s")
|
||||
LOG.error(msg, "flavor")
|
||||
|
||||
- [H703] If you have multiple variables to place in the string, use keyword
|
||||
parameters. This helps our translators reorder parameters when needed.
|
||||
|
||||
Example::
|
||||
|
||||
msg = _LE("The server with id %(s_id)s has no key %(m_key)s")
|
||||
LOG.error(msg, {"s_id": "1234", "m_key": "imageId"})
|
||||
|
||||
.. seealso::
|
||||
|
||||
* `oslo.i18n Guidelines <https://docs.openstack.org/oslo.i18n/latest/user/guidelines.html>`__
|
||||
|
||||
Python 3.x compatibility
|
||||
------------------------
|
||||
OpenStack code should become Python 3.x compatible. That means all Python 2.x-only
|
||||
constructs or dependencies should be avoided. In order to start making code
|
||||
Python 3.x compatible before it can be fully Python 3.x compatible, we have checks for Python 2.x-only constructs:
|
||||
|
||||
- [H231] ``except``. Instead of::
|
||||
|
||||
except x,y:
|
||||
|
||||
Use::
|
||||
|
||||
except x as y:
|
||||
|
||||
- [H232] Python 3.x has become more strict regarding octal string
|
||||
literals. Use ``0o755`` instead of ``0755``. Similarly, explicit use of long
|
||||
literals (``01234L``) should be avoided.
|
||||
|
||||
- [H233] The ``print`` operator can be avoided by using::
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
at the top of your module.
|
||||
|
||||
- [H234] ``assertEquals()`` logs a DeprecationWarning in Python 3.x, use
|
||||
``assertEqual()`` instead. The same goes for ``assertNotEquals()``.
|
||||
|
||||
- [H235] ``assert_()`` is deprecated in Python 3.x, use ``assertTrue()`` instead.
|
||||
|
||||
- [H236] Use ``six.add_metaclass`` instead of ``__metaclass__``.
|
||||
|
||||
Example::
|
||||
|
||||
import six
|
||||
|
||||
@six.add_metaclass(Meta)
|
||||
class YourClass():
|
||||
|
||||
- [H237] Don't use modules that were removed in Python 3. Removed module list:
|
||||
http://python3porting.com/stdlib.html#removed-modules
|
||||
|
||||
- [H238] Old style classes are deprecated and no longer available in Python 3
|
||||
(they are converted to new style classes). In order to avoid any unwanted side
|
||||
effects all classes should be declared using new style. See `the new-style
|
||||
class documentation <https://www.python.org/doc/newstyle/>`_ for reference on
|
||||
the differences.
|
||||
|
||||
Example::
|
||||
|
||||
class Foo(object):
|
||||
pass
|
||||
|
||||
Creating Unit Tests
|
||||
-------------------
|
||||
For every new feature, unit tests should be created that both test and
|
||||
(implicitly) document the usage of said feature. If submitting a patch for a
|
||||
bug that had no unit test, a new passing unit test should be added. If a
|
||||
submitted bug fix does have a unit test, be sure to add a new one that fails
|
||||
without the patch and passes with the patch.
|
||||
|
||||
Unit Tests and assertRaises
|
||||
---------------------------
|
||||
|
||||
A properly written test asserts that particular behavior occurs. This can
|
||||
be a success condition or a failure condition, including an exception.
|
||||
When asserting that a particular exception is raised, the most specific
|
||||
exception possible should be used.
|
||||
|
||||
- [H202] Testing for ``Exception`` being raised is almost always a
|
||||
mistake since it will match (almost) every exception, even those
|
||||
unrelated to the exception intended to be tested.
|
||||
|
||||
This applies to catching exceptions manually with a try/except block,
|
||||
or using ``assertRaises()``.
|
||||
|
||||
Example::
|
||||
|
||||
with self.assertRaises(exception.InstanceNotFound):
|
||||
db.instance_get_by_uuid(elevated, instance_uuid)
|
||||
|
||||
- [H203] Use assertIs(Not)None to check for None (off by default)
|
||||
Unit test assertions tend to give better messages for more specific
|
||||
assertions. As a result, ``assertIsNone(...)`` is preferred over
|
||||
``assertEqual(None, ...)`` and ``assertIs(None, ...)``, and
|
||||
``assertIsNotNone(...)`` is preferred over ``assertNotEqual(None, ...)``
|
||||
and ``assertIsNot(None, ...)``. Off by default.
|
||||
|
||||
OpenStack Trademark
|
||||
-------------------
|
||||
|
||||
OpenStack is a registered trademark of the OpenStack Foundation, and uses the
|
||||
following capitalization::
|
||||
|
||||
OpenStack
|
||||
|
||||
|
||||
OpenStack Licensing
|
||||
-------------------
|
||||
|
||||
- [H102 H103] Newly contributed Source Code should be licensed under the
|
||||
Apache 2.0 license. All source files should have the following header::
|
||||
|
||||
# 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.
|
||||
|
||||
- [H104] Files with no code shouldn't contain any license header nor comments,
|
||||
and must be left completely empty.
|
||||
|
||||
Commit Messages
|
||||
---------------
|
||||
Using a common format for commit messages will help keep our git history
|
||||
readable.
|
||||
|
||||
For further information on constructing high quality commit messages,
|
||||
and how to split up commits into a series of changes, consult the
|
||||
project wiki:
|
||||
https://wiki.openstack.org/GitCommitMessages
|
||||
176
LICENSE
176
LICENSE
@@ -1,176 +0,0 @@
|
||||
|
||||
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.
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
include README.rst
|
||||
include requirements.txt
|
||||
include test-requirements.txt
|
||||
exclude .gitignore
|
||||
exclude .gitreview
|
||||
|
||||
global-exclude *.pyc
|
||||
14
README
Normal file
14
README
Normal file
@@ -0,0 +1,14 @@
|
||||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For ongoing work on maintaining OpenStack packages in the Debian
|
||||
distribution, please see the Debian OpenStack packaging team at
|
||||
https://wiki.debian.org/OpenStack/.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
||||
131
README.rst
131
README.rst
@@ -1,131 +0,0 @@
|
||||
Introduction
|
||||
============
|
||||
|
||||
hacking is a set of flake8 plugins that test and enforce the :ref:`StyleGuide`.
|
||||
|
||||
Hacking pins its dependencies, as a new release of some dependency can break
|
||||
hacking based gating jobs. This is because new versions of dependencies can
|
||||
introduce new rules, or make existing rules stricter.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
hacking is available from pypi, so just run::
|
||||
|
||||
pip install hacking
|
||||
|
||||
This will install specific versions of ``flake8`` with the ``hacking``,
|
||||
``pep8``, ``mccabe`` and ``pyflakes`` plugins.
|
||||
|
||||
Origin
|
||||
======
|
||||
|
||||
Hacking started its life out as a text file in Nova's first commit. It was
|
||||
initially based on the `Google Python Style Guide`_, and over time more
|
||||
OpenStack specific rules were added. Hacking serves several purposes:
|
||||
|
||||
1. Agree on a common style guide so reviews don't get bogged down on style
|
||||
nit picks. (example: docstring guidelines)
|
||||
2. Make code written by many different authors easier to read by making the
|
||||
style more uniform. (example: unix vs windows newlines)
|
||||
3. Call out dangerous patterns and avoid them. (example: shadowing built-in
|
||||
or reserved words)
|
||||
|
||||
Initially the hacking style guide was enforced manually by reviewers, but this
|
||||
was a big waste of time so hacking, the tool, was born to automate
|
||||
the process and remove the extra burden from human reviewers.
|
||||
|
||||
.. _`Google Python Style Guide`: https://google.github.io/styleguide/pyguide.html
|
||||
|
||||
Versioning
|
||||
==========
|
||||
|
||||
hacking uses the ``major.minor.maintenance`` release notation, where maintenance
|
||||
releases cannot contain new checks. This way projects can gate on hacking
|
||||
by pinning on the ``major.minor`` number while accepting maintenance updates
|
||||
without being concerned that a new version will break the gate with a new
|
||||
check.
|
||||
|
||||
For example a project can depend on ``hacking>=0.10.0,<0.11.0``, and can know
|
||||
that ``0.10.1`` will not fail in places where ``0.10.0`` passed.
|
||||
|
||||
|
||||
Adding additional checks
|
||||
========================
|
||||
|
||||
Each check is a pep8 plugin so read
|
||||
|
||||
- https://github.com/jcrocholl/pep8/blob/master/docs/developer.rst#contribute
|
||||
|
||||
The focus of new or changed rules should be to do one of the following
|
||||
|
||||
- Substantially increase the reviewability of the code (eg: H301, H303)
|
||||
as they make it easy to understand where symbols come from)
|
||||
- Catch a common programming error that may arise in the future (H201)
|
||||
- Prevent a situation that would 100% of the time be -1ed by
|
||||
developers (H903)
|
||||
|
||||
But, as always, remember that these are Guidelines. Treat them as
|
||||
such. There are always times for exceptions. All new rules should
|
||||
support noqa.
|
||||
|
||||
If a check needs to be staged in, or it does not apply to every project or its
|
||||
branch, it can be added as off by default.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
- The check must already have community support. We do not want to dictate
|
||||
style, only enforce it.
|
||||
- The canonical source of the OpenStack Style Guidelines is :ref:`StyleGuide`,
|
||||
and hacking just enforces
|
||||
them; so when adding a new check, it must be in ``HACKING.rst``
|
||||
- False negatives are ok, but false positives are not
|
||||
- Cannot be project specific, project specific checks should be `Local Checks`_
|
||||
- Include extensive tests
|
||||
- Registered as entry_points in ``setup.cfg``
|
||||
- Error code must be in the relevant ``Hxxx`` group
|
||||
- The check should not attempt to import modules from the code being checked.
|
||||
Importing random modules, has caused all kinds of trouble for us in the past.
|
||||
|
||||
|
||||
Enabling off-by-default checks
|
||||
==============================
|
||||
|
||||
Some of the available checks are disabled by default. These checks are:
|
||||
|
||||
- [H106] Don't put vim configuration in source files.
|
||||
- [H203] Use assertIs(Not)None to check for None.
|
||||
- [H904] Delay string interpolations at logging calls.
|
||||
|
||||
To enable these checks, edit the ``flake8`` section of the ``tox.ini`` file.
|
||||
For example to enable H106 and H203:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[flake8]
|
||||
enable-extensions = H106,H203
|
||||
|
||||
Local Checks
|
||||
============
|
||||
|
||||
hacking supports having local changes in a source tree. They can be configured
|
||||
to run in two different ways. They can be registered individually, or with
|
||||
a factory function.
|
||||
|
||||
For individual registration, put a comma separated list of pep8 compatible
|
||||
check functions into the hacking section of tox.ini. E.g.:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[hacking]
|
||||
local-check = nova.tests.hacking.bad_code_is_terrible
|
||||
|
||||
Alternately, you can specify the location of a callable that will be called
|
||||
at registration time and will be passed the registration function. The callable
|
||||
should expect to call the passed in function on everything if wants to
|
||||
register. Such as:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[hacking]
|
||||
local-check-factory = nova.tests.hacking.factory
|
||||
@@ -1,61 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
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', 'openstackdocstheme']
|
||||
|
||||
# openstackdocstheme options
|
||||
repository_name = 'openstack-dev/hacking'
|
||||
bug_project = 'hacking'
|
||||
bug_tag = ''
|
||||
|
||||
# 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
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'hacking'
|
||||
copyright = u'2013, OpenStack Foundation'
|
||||
|
||||
# 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 --------------------------------------------------
|
||||
|
||||
html_theme = 'openstackdocs'
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# 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'),
|
||||
]
|
||||
@@ -1,14 +0,0 @@
|
||||
================================================
|
||||
hacking: OpenStack Hacking Guideline Enforcement
|
||||
================================================
|
||||
|
||||
hacking is a set of flake8 plugins that test and enforce the :ref:`StyleGuide`.
|
||||
|
||||
Hacking pins its dependencies, as a new release of some dependency can break
|
||||
hacking based gating jobs. This is because new versions of dependencies can
|
||||
introduce new rules, or make existing rules stricter.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
user/index
|
||||
@@ -1 +0,0 @@
|
||||
.. include:: ../../../HACKING.rst
|
||||
@@ -1,9 +0,0 @@
|
||||
==================
|
||||
User Documentation
|
||||
==================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
hacking
|
||||
usage
|
||||
@@ -1,5 +0,0 @@
|
||||
=============
|
||||
Using hacking
|
||||
=============
|
||||
|
||||
.. include:: ../../../README.rst
|
||||
@@ -1,182 +0,0 @@
|
||||
# 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 re
|
||||
import tokenize
|
||||
|
||||
from hacking import core
|
||||
|
||||
|
||||
AUTHOR_TAG_RE = (re.compile("^\s*#\s*@?(a|A)uthors?:"),
|
||||
re.compile("^\.\.\s+moduleauthor::"))
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_todo_format(physical_line, tokens):
|
||||
"""Check for 'TODO()'.
|
||||
|
||||
OpenStack HACKING guide recommendation for TODO:
|
||||
Include your name with TODOs as in "# TODO(termie)"
|
||||
|
||||
Okay: # TODO(sdague)
|
||||
H101: # TODO fail
|
||||
H101: # TODO
|
||||
H101: # TODO (jogo) fail
|
||||
Okay: TODO = 5
|
||||
"""
|
||||
# TODO(jogo): make the following doctests pass:
|
||||
# H101: #TODO(jogo fail
|
||||
# H101: #TODO(jogo
|
||||
# TODO(jogo): make this check docstrings as well (don't have to be at top
|
||||
# of function)
|
||||
for token_type, text, start_index, _, _ in tokens:
|
||||
if token_type == tokenize.COMMENT:
|
||||
pos = text.find('TODO')
|
||||
pos1 = text.find('TODO(')
|
||||
if (pos != pos1):
|
||||
return pos + start_index[1], "H101: Use TODO(NAME)"
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_has_license(physical_line, filename, lines, line_number):
|
||||
"""Check for Apache 2.0 license.
|
||||
|
||||
H102 license header not found
|
||||
"""
|
||||
# don't work about init files for now
|
||||
# TODO(sdague): enforce license in init file if it's not empty of content
|
||||
license_found = False
|
||||
|
||||
# skip files that are < 10 lines, which isn't enough for a license to fit
|
||||
# this allows us to handle empty files, as well as not fail on the Okay
|
||||
# doctests.
|
||||
if line_number is 1 and len(lines) > 10 and _project_is_apache():
|
||||
for idx, line in enumerate(lines):
|
||||
# if it's more than 10 characters in, it's probably not in the
|
||||
# header
|
||||
if 0 <= line.find('Licensed under the Apache License') < 10:
|
||||
license_found = True
|
||||
if not license_found:
|
||||
return (0, "H102: Apache 2.0 license header not found")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_has_correct_license(physical_line, filename, lines, line_number):
|
||||
"""Check for Apache 2.0 license.
|
||||
|
||||
H103 header does not match Apache 2.0 License notice
|
||||
"""
|
||||
# don't work about init files for now
|
||||
# TODO(sdague): enforce license in init file if it's not empty of content
|
||||
|
||||
# skip files that are < 10 lines, which isn't enough for a license to fit
|
||||
# this allows us to handle empty files, as well as not fail on the Okay
|
||||
# doctests.
|
||||
if line_number is 1 and len(lines) > 10 and _project_is_apache():
|
||||
for idx, line in enumerate(lines):
|
||||
column = line.find('Licensed under the Apache License')
|
||||
if (0 < column < 10 and not
|
||||
_check_for_exact_apache(idx, lines)):
|
||||
return (column, "H103: Header does not match Apache 2.0 "
|
||||
"License notice")
|
||||
|
||||
|
||||
EMPTY_LINE_RE = re.compile("^\s*(#.*|$)")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_has_only_comments(physical_line, filename, lines, line_number):
|
||||
"""Check for empty files with only comments
|
||||
|
||||
H104 empty file with only comments
|
||||
"""
|
||||
if line_number == 1 and all(map(EMPTY_LINE_RE.match, lines)):
|
||||
return (0, "H104: File contains nothing but comments")
|
||||
|
||||
|
||||
_is_apache_cache = None
|
||||
|
||||
|
||||
def _project_is_apache():
|
||||
"""Determine if a project is Apache.
|
||||
|
||||
Look for a key string in a set of possible license files to figure out
|
||||
if a project looks to be Apache. This is used as a precondition for
|
||||
enforcing license headers.
|
||||
"""
|
||||
|
||||
global _is_apache_cache
|
||||
if _is_apache_cache is not None:
|
||||
return _is_apache_cache
|
||||
license_files = ["LICENSE"]
|
||||
for filename in license_files:
|
||||
try:
|
||||
with open(filename, "r") as file:
|
||||
for line in file:
|
||||
if re.search('Apache License', line):
|
||||
_is_apache_cache = True
|
||||
return True
|
||||
except IOError:
|
||||
pass
|
||||
_is_apache_cache = False
|
||||
return False
|
||||
|
||||
|
||||
def _check_for_exact_apache(start, lines):
|
||||
"""Check for the Apache 2.0 license header.
|
||||
|
||||
We strip all the newlines and extra spaces so this license string
|
||||
should work regardless of indentation in the file.
|
||||
"""
|
||||
APACHE2 = """
|
||||
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."""
|
||||
|
||||
# out of all the formatting I've seen, a 12 line version seems to be the
|
||||
# longest in the source tree. So just take the 12 lines starting with where
|
||||
# the Apache starting words were found, strip all the '#' and collapse the
|
||||
# spaces.
|
||||
content = ''.join(lines[start:(start + 12)])
|
||||
content = re.sub('\#', '', content)
|
||||
content = re.sub('\s+', ' ', content).strip()
|
||||
stripped_apache2 = re.sub('\s+', ' ', APACHE2).strip()
|
||||
|
||||
if stripped_apache2 in content:
|
||||
return True
|
||||
else:
|
||||
print("<license>!=<apache2>:\n'%s' !=\n'%s'" %
|
||||
(content, stripped_apache2))
|
||||
return False
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_no_author_tags(physical_line):
|
||||
"""Check that no author tags are used.
|
||||
|
||||
H105 don't use author tags
|
||||
"""
|
||||
for regex in AUTHOR_TAG_RE:
|
||||
if regex.match(physical_line):
|
||||
physical_line = physical_line.lower()
|
||||
pos = physical_line.find('moduleauthor')
|
||||
if pos < 0:
|
||||
pos = physical_line.find('author')
|
||||
return (pos, "H105: Don't use author tags")
|
||||
@@ -1,46 +0,0 @@
|
||||
# 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 tokenize
|
||||
|
||||
from hacking import core
|
||||
|
||||
|
||||
LOCALS_TEXT_MAP = {
|
||||
'locals': 'locals()',
|
||||
'self': 'self.__dict__'
|
||||
}
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_no_locals(logical_line, tokens, noqa):
|
||||
"""Do not use locals() or self.__dict__ for string formatting.
|
||||
|
||||
Okay: 'locals()'
|
||||
Okay: 'locals'
|
||||
Okay: locals()
|
||||
Okay: print(locals())
|
||||
H501: print("%(something)" % locals())
|
||||
H501: LOG.info(_("%(something)") % self.__dict__)
|
||||
Okay: print("%(something)" % locals()) # noqa
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
for_formatting = False
|
||||
for token_type, text, start, _, _ in tokens:
|
||||
if text == "%" and token_type == tokenize.OP:
|
||||
for_formatting = True
|
||||
if for_formatting and token_type == tokenize.NAME:
|
||||
for k, v in LOCALS_TEXT_MAP.items():
|
||||
if text == k and v in logical_line:
|
||||
yield (start[1],
|
||||
"H501: Do not use %s for string formatting" % v)
|
||||
@@ -1,161 +0,0 @@
|
||||
# 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 tokenize
|
||||
|
||||
from hacking import core
|
||||
|
||||
START_DOCSTRING_TRIPLE = ['u"""', 'r"""', '"""', "u'''", "r'''", "'''"]
|
||||
END_DOCSTRING_TRIPLE = ['"""', "'''"]
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_docstring_start_space(physical_line, previous_logical, tokens):
|
||||
r"""Check for docstring not starting with space.
|
||||
|
||||
OpenStack HACKING guide recommendation for docstring:
|
||||
Docstring should not start with space
|
||||
|
||||
Okay: def foo():\n '''This is good.'''
|
||||
Okay: def foo():\n r'''This is good.'''
|
||||
Okay: def foo():\n a = ''' This is not a docstring.'''
|
||||
Okay: def foo():\n pass\n ''' This is not.'''
|
||||
H401: def foo():\n ''' This is not.'''
|
||||
H401: def foo():\n r''' This is not.'''
|
||||
"""
|
||||
docstring = is_docstring(tokens, previous_logical)
|
||||
if docstring:
|
||||
start, start_triple = _find_first_of(docstring, START_DOCSTRING_TRIPLE)
|
||||
if docstring[len(start_triple)] == ' ':
|
||||
# docstrings get tokenized on the last line of the docstring, so
|
||||
# we don't know the exact position.
|
||||
return (0, "H401: docstring should not start with"
|
||||
" a space")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_docstring_multiline_end(physical_line, previous_logical, tokens):
|
||||
r"""Check multi line docstring end.
|
||||
|
||||
OpenStack HACKING guide recommendation for docstring:
|
||||
Docstring should end on a new line
|
||||
|
||||
Okay: '''foobar\nfoo\nbar\n'''
|
||||
Okay: def foo():\n '''foobar\n\nfoo\nbar\n'''
|
||||
Okay: class Foo(object):\n '''foobar\n\nfoo\nbar\n'''
|
||||
Okay: def foo():\n a = '''not\na\ndocstring'''
|
||||
Okay: def foo():\n a = '''not\na\ndocstring''' # blah
|
||||
Okay: def foo():\n pass\n'''foobar\nfoo\nbar\n d'''
|
||||
H403: def foo():\n '''foobar\nfoo\nbar\ndocstring'''
|
||||
H403: def foo():\n '''foobar\nfoo\nbar\npretend raw: r'''
|
||||
H403: class Foo(object):\n '''foobar\nfoo\nbar\ndocstring'''\n\n
|
||||
"""
|
||||
docstring = is_docstring(tokens, previous_logical)
|
||||
if docstring:
|
||||
if '\n' not in docstring:
|
||||
# not a multi line
|
||||
return
|
||||
else:
|
||||
last_line = docstring.split('\n')[-1]
|
||||
pos = max(last_line.rfind(i) for i in END_DOCSTRING_TRIPLE)
|
||||
if len(last_line[:pos].strip()) > 0:
|
||||
# Something before the end docstring triple
|
||||
return (pos,
|
||||
"H403: multi line docstrings should end on a new line")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_docstring_multiline_start(physical_line, previous_logical, tokens):
|
||||
r"""Check multi line docstring starts immediately with summary.
|
||||
|
||||
OpenStack HACKING guide recommendation for docstring:
|
||||
Docstring should start with a one-line summary, less than 80 characters.
|
||||
|
||||
Okay: '''foobar\n\nfoo\nbar\n'''
|
||||
Okay: def foo():\n a = '''\nnot\na docstring\n'''
|
||||
H404: def foo():\n '''\nfoo\nbar\n'''\n\n
|
||||
H404: def foo():\n r'''\nfoo\nbar\n'''\n\n
|
||||
"""
|
||||
docstring = is_docstring(tokens, previous_logical)
|
||||
if docstring:
|
||||
if '\n' not in docstring:
|
||||
# single line docstring
|
||||
return
|
||||
start, start_triple = _find_first_of(docstring, START_DOCSTRING_TRIPLE)
|
||||
lines = docstring.split('\n')
|
||||
if lines[0].strip() == start_triple:
|
||||
# docstrings get tokenized on the last line of the docstring, so
|
||||
# we don't know the exact position.
|
||||
return (0, "H404: multi line docstring "
|
||||
"should start without a leading new line")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_docstring_summary(physical_line, previous_logical, tokens):
|
||||
r"""Check multi line docstring summary is separated with empty line.
|
||||
|
||||
OpenStack HACKING guide recommendation for docstring:
|
||||
Docstring should start with a one-line summary, less than 80 characters.
|
||||
|
||||
Okay: def foo():\n a = '''\nnot\na docstring\n'''
|
||||
Okay: '''foobar\n\nfoo\nbar\n'''
|
||||
H405: def foo():\n '''foobar\nfoo\nbar\n'''
|
||||
H405: def foo():\n r'''foobar\nfoo\nbar\n'''
|
||||
H405: def foo():\n '''foobar\n'''
|
||||
"""
|
||||
docstring = is_docstring(tokens, previous_logical)
|
||||
if docstring:
|
||||
if '\n' not in docstring:
|
||||
# not a multi line docstring
|
||||
return
|
||||
lines = docstring.split('\n')
|
||||
if len(lines) > 1 and len(lines[1].strip()) is not 0:
|
||||
# docstrings get tokenized on the last line of the docstring, so
|
||||
# we don't know the exact position.
|
||||
return (0, "H405: multi line docstring "
|
||||
"summary not separated with an empty line")
|
||||
|
||||
|
||||
def is_docstring(tokens, previous_logical):
|
||||
"""Return found docstring
|
||||
|
||||
'A docstring is a string literal that occurs as the first statement in a
|
||||
module, function, class,'
|
||||
http://www.python.org/dev/peps/pep-0257/#what-is-a-docstring
|
||||
"""
|
||||
for token_type, text, start, _, _ in tokens:
|
||||
if token_type == tokenize.STRING:
|
||||
break
|
||||
elif token_type != tokenize.INDENT:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
line = text.lstrip()
|
||||
start, start_triple = _find_first_of(line, START_DOCSTRING_TRIPLE)
|
||||
if (previous_logical.startswith("def ") or
|
||||
previous_logical.startswith("class ")):
|
||||
if start == 0:
|
||||
return text
|
||||
|
||||
|
||||
def _find_first_of(line, substrings):
|
||||
"""Find earliest occurrence of one of substrings in line.
|
||||
|
||||
Returns pair of index and found substring, or (-1, None)
|
||||
if no occurrences of any of substrings were found in line.
|
||||
"""
|
||||
starts = ((line.find(i), i) for i in substrings)
|
||||
found = [(i, sub) for i, sub in starts if i != -1]
|
||||
if found:
|
||||
return min(found)
|
||||
else:
|
||||
return -1, None
|
||||
@@ -1,224 +0,0 @@
|
||||
# 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.
|
||||
|
||||
# module cannot be called except since that is a reserved word
|
||||
|
||||
import ast
|
||||
import re
|
||||
|
||||
from six import PY2
|
||||
|
||||
from hacking import core
|
||||
|
||||
RE_ASSERT_RAISES_EXCEPTION = re.compile(r"self\.assertRaises\(Exception[,\)]")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_except_format(logical_line, noqa):
|
||||
r"""Check for 'except:'.
|
||||
|
||||
OpenStack HACKING guide recommends not using except:
|
||||
Do not write "except:", use "except Exception:" at the very least
|
||||
|
||||
Okay: try:\n pass\nexcept Exception:\n pass
|
||||
H201: try:\n pass\nexcept:\n pass
|
||||
H201: except:
|
||||
Okay: try:\n pass\nexcept: # noqa\n pass
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
if logical_line.startswith("except:"):
|
||||
yield 6, "H201: no 'except:' at least use 'except Exception:'"
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_except_format_assert(logical_line, noqa):
|
||||
r"""Check for 'assertRaises(Exception'.
|
||||
|
||||
OpenStack HACKING guide recommends not using assertRaises(Exception...):
|
||||
Do not use overly broad Exception type
|
||||
|
||||
Okay: self.assertRaises(NovaException, foo)
|
||||
Okay: self.assertRaises(ExceptionStrangeNotation, foo)
|
||||
H202: self.assertRaises(Exception, foo)
|
||||
H202: self.assertRaises(Exception)
|
||||
Okay: self.assertRaises(Exception) # noqa
|
||||
Okay: self.assertRaises(Exception, foo) # noqa
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
if RE_ASSERT_RAISES_EXCEPTION.search(logical_line):
|
||||
yield 1, "H202: assertRaises Exception too broad"
|
||||
|
||||
|
||||
def is_none(node):
|
||||
'''Check whether an AST node corresponds to None.
|
||||
|
||||
In Python 2 None uses the same ast.Name class that variables etc. use,
|
||||
but in Python 3 there is a new ast.NameConstant class.
|
||||
'''
|
||||
if PY2:
|
||||
return isinstance(node, ast.Name) and node.id == 'None'
|
||||
return isinstance(node, ast.NameConstant) and node.value is None
|
||||
|
||||
|
||||
def _get_local_func_name(node):
|
||||
if isinstance(node.func, ast.Attribute):
|
||||
return node.func.attr
|
||||
elif isinstance(node.func, ast.Name):
|
||||
return node.func.id
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class NoneArgChecker(ast.NodeVisitor):
|
||||
'''NodeVisitor to check function calls for None arguments.
|
||||
|
||||
:param func_name: only check calls to functions with this name
|
||||
:param num_args: number of arguments to check for None
|
||||
|
||||
self.none_found will be True if any None arguments were found.
|
||||
'''
|
||||
def __init__(self, func_name, num_args=2):
|
||||
self.func_name = func_name
|
||||
self.num_args = num_args
|
||||
self.none_found = False
|
||||
|
||||
def visit_Call(self, node):
|
||||
local_func_name = _get_local_func_name(node)
|
||||
|
||||
if local_func_name == self.func_name:
|
||||
args_to_check = node.args[:self.num_args]
|
||||
self.none_found |= any(is_none(x) for x in args_to_check)
|
||||
self.generic_visit(node)
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
@core.off_by_default
|
||||
def hacking_assert_is_none(logical_line, noqa):
|
||||
"""Use assertIs(Not)None to check for None in assertions.
|
||||
|
||||
Okay: self.assertEqual('foo', 'bar')
|
||||
Okay: self.assertNotEqual('foo', {}.get('bar', None))
|
||||
Okay: self.assertIs('foo', 'bar')
|
||||
Okay: self.assertIsNot('foo', 'bar', None)
|
||||
Okay: foo(self.assertIsNot('foo', 'bar'))
|
||||
H203: self.assertEqual(None, 'foo')
|
||||
H203: self.assertNotEqual('foo', None)
|
||||
H203: self.assertIs(None, 'foo', 'bar')
|
||||
H203: self.assertIsNot('foo', None, 'bar')
|
||||
H203: foo(self.assertIsNot('foo', None, 'bar'))
|
||||
Okay: self.assertEqual(None, 'foo') # noqa
|
||||
Okay: self.assertIs(None, 'foo') # noqa
|
||||
Okay: self.assertIsNone('foo')
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
for func_name in ('assertEqual', 'assertIs', 'assertNotEqual',
|
||||
'assertIsNot'):
|
||||
try:
|
||||
start = logical_line.index('.%s(' % func_name) + 1
|
||||
except ValueError:
|
||||
continue
|
||||
checker = NoneArgChecker(func_name)
|
||||
checker.visit(ast.parse(logical_line))
|
||||
if checker.none_found:
|
||||
yield start, "H203: Use assertIs(Not)None to check for None"
|
||||
|
||||
|
||||
class AssertTrueFalseChecker(ast.NodeVisitor):
|
||||
'''NodeVisitor to find "assert[True|False](some comparison)" statements.
|
||||
|
||||
:param method_names: methods to look for: assertTrue and/or assertFalse
|
||||
:param ops: list of comparisons we want to look for (objects from the ast
|
||||
module)
|
||||
'''
|
||||
def __init__(self, method_names, ops):
|
||||
self.method_names = method_names
|
||||
self.ops = tuple(ops)
|
||||
self.error = False
|
||||
|
||||
def visit_Call(self, node):
|
||||
# No need to keep visiting the AST if we already found something.
|
||||
if self.error:
|
||||
return
|
||||
|
||||
self.generic_visit(node)
|
||||
|
||||
local_func_name = _get_local_func_name(node)
|
||||
|
||||
if (local_func_name in self.method_names and
|
||||
len(node.args) == 1 and
|
||||
isinstance(node.args[0], ast.Compare) and
|
||||
len(node.args[0].ops) == 1 and
|
||||
isinstance(node.args[0].ops[0], self.ops)):
|
||||
self.error = True
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
@core.off_by_default
|
||||
def hacking_assert_equal(logical_line, noqa):
|
||||
r"""Check that self.assertEqual and self.assertNotEqual are used.
|
||||
|
||||
Okay: self.assertEqual(x, y)
|
||||
Okay: self.assertNotEqual(x, y)
|
||||
H204: self.assertTrue(x == y)
|
||||
H204: self.assertTrue(x != y)
|
||||
H204: self.assertFalse(x == y)
|
||||
H204: self.assertFalse(x != y)
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
|
||||
methods = ['assertTrue', 'assertFalse']
|
||||
for method in methods:
|
||||
start = logical_line.find('.%s' % method) + 1
|
||||
if start != 0:
|
||||
break
|
||||
else:
|
||||
return
|
||||
comparisons = [ast.Eq, ast.NotEq]
|
||||
checker = AssertTrueFalseChecker(methods, comparisons)
|
||||
checker.visit(ast.parse(logical_line))
|
||||
if checker.error:
|
||||
yield start, 'H204: Use assert(Not)Equal()'
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
@core.off_by_default
|
||||
def hacking_assert_greater_less(logical_line, noqa):
|
||||
r"""Check that self.assert{Greater,Less}[Equal] are used.
|
||||
|
||||
Okay: self.assertGreater(x, y)
|
||||
Okay: self.assertGreaterEqual(x, y)
|
||||
Okay: self.assertLess(x, y)
|
||||
Okay: self.assertLessEqual(x, y)
|
||||
H205: self.assertTrue(x > y)
|
||||
H205: self.assertTrue(x >= y)
|
||||
H205: self.assertTrue(x < y)
|
||||
H205: self.assertTrue(x <= y)
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
|
||||
methods = ['assertTrue', 'assertFalse']
|
||||
for method in methods:
|
||||
start = logical_line.find('.%s' % method) + 1
|
||||
if start != 0:
|
||||
break
|
||||
else:
|
||||
return
|
||||
comparisons = [ast.Gt, ast.GtE, ast.Lt, ast.LtE]
|
||||
checker = AssertTrueFalseChecker(methods, comparisons)
|
||||
checker.visit(ast.parse(logical_line))
|
||||
if checker.error:
|
||||
yield start, 'H205: Use assert{Greater,Less}[Equal]'
|
||||
@@ -1,104 +0,0 @@
|
||||
# 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 re
|
||||
|
||||
from hacking import core
|
||||
|
||||
RE_RELATIVE_IMPORT = re.compile('^from\s*[.]')
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_import_rules(logical_line, filename, noqa):
|
||||
r"""Check for imports.
|
||||
|
||||
OpenStack HACKING guide recommends one import per line:
|
||||
Do not import more than one module per line
|
||||
|
||||
Examples:
|
||||
Okay: from nova.compute import api
|
||||
H301: from nova.compute import api, utils
|
||||
|
||||
|
||||
Do not use wildcard import
|
||||
|
||||
Do not make relative imports
|
||||
|
||||
Examples:
|
||||
Okay: from os import path
|
||||
Okay: from os import path as p
|
||||
Okay: from os import (path as p)
|
||||
Okay: import os.path
|
||||
Okay: from nova.compute import rpcapi
|
||||
Okay: from six.moves.urllib import parse
|
||||
H303: from os.path import *
|
||||
H304: from .compute import rpcapi
|
||||
"""
|
||||
# TODO(jogo): make the following doctests pass:
|
||||
# H301: import os, sys
|
||||
# TODO(mordred: We need to split this into different checks so that they
|
||||
# can be disabled by command line switches properly
|
||||
|
||||
if noqa:
|
||||
return
|
||||
|
||||
split_line = logical_line.split()
|
||||
split_line_len = len(split_line)
|
||||
if (split_line_len > 1 and split_line[0] in ('import', 'from') and
|
||||
not core.is_import_exception(split_line[1])):
|
||||
pos = logical_line.find(',')
|
||||
if pos != -1:
|
||||
if split_line[0] == 'from':
|
||||
yield pos, "H301: one import per line"
|
||||
pos = logical_line.find('*')
|
||||
if pos != -1:
|
||||
yield pos, "H303: No wildcard (*) import."
|
||||
return
|
||||
|
||||
if split_line_len in (2, 4, 6) and split_line[1] != "__future__":
|
||||
if 'from' == split_line[0] and split_line_len > 3:
|
||||
mod = '.'.join((split_line[1], split_line[3]))
|
||||
if core.is_import_exception(mod):
|
||||
return
|
||||
if RE_RELATIVE_IMPORT.search(logical_line):
|
||||
yield logical_line.find('.'), (
|
||||
"H304: No relative imports. '%s' is a relative import"
|
||||
% logical_line)
|
||||
return
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_import_alphabetical(logical_line, blank_before, previous_logical,
|
||||
indent_level, previous_indent_level):
|
||||
r"""Check for imports in alphabetical order.
|
||||
|
||||
OpenStack HACKING guide recommendation for imports:
|
||||
imports in human alphabetical order
|
||||
|
||||
Okay: import os\nimport sys\n\nimport nova\nfrom nova import test
|
||||
Okay: import os\nimport sys
|
||||
H306: import sys\nimport os
|
||||
Okay: import sys\n\n# foo\nimport six
|
||||
"""
|
||||
# handle import x
|
||||
# use .lower since capitalization shouldn't dictate order
|
||||
if blank_before < 1 and indent_level == previous_indent_level:
|
||||
split_line = core.import_normalize(logical_line.
|
||||
strip()).lower().split()
|
||||
split_previous = core.import_normalize(previous_logical.
|
||||
strip()).lower().split()
|
||||
length = [2, 4]
|
||||
if (len(split_line) in length and len(split_previous) in length and
|
||||
split_line[0] == "import" and split_previous[0] == "import"):
|
||||
if split_line[1] < split_previous[1]:
|
||||
yield (0, "H306: imports not in alphabetical order (%s, %s)"
|
||||
% (split_previous[1], split_line[1]))
|
||||
@@ -1,141 +0,0 @@
|
||||
# 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 re
|
||||
import tokenize
|
||||
|
||||
from hacking import core
|
||||
|
||||
|
||||
FORMAT_RE = re.compile("%(?:"
|
||||
"%|" # Ignore plain percents
|
||||
"(\(\w+\))?" # mapping key
|
||||
"([#0 +-]?" # flag
|
||||
"(?:\d+|\*)?" # width
|
||||
"(?:\.\d+)?" # precision
|
||||
"[hlL]?" # length mod
|
||||
"\w))") # type
|
||||
|
||||
|
||||
class LocalizationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def check_i18n():
|
||||
"""Generator that checks token stream for localization errors.
|
||||
|
||||
Expects tokens to be ``send``ed one by one.
|
||||
Raises LocalizationError if some error is found.
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
token_type, text, _, _, line = yield
|
||||
except GeneratorExit:
|
||||
return
|
||||
|
||||
if text == "def" and token_type == tokenize.NAME:
|
||||
# explicitly ignore function definitions, as oslo defines these
|
||||
return
|
||||
if (token_type == tokenize.NAME and
|
||||
text in ["_", "_LI", "_LW", "_LE", "_LC"]):
|
||||
|
||||
while True:
|
||||
token_type, text, start, _, _ = yield
|
||||
if token_type != tokenize.NL:
|
||||
break
|
||||
if token_type != tokenize.OP or text != "(":
|
||||
continue # not a localization call
|
||||
|
||||
format_string = ''
|
||||
while True:
|
||||
token_type, text, start, _, _ = yield
|
||||
if token_type == tokenize.STRING:
|
||||
format_string += eval(text)
|
||||
elif token_type == tokenize.NL:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
|
||||
if not format_string:
|
||||
raise LocalizationError(
|
||||
start, "H701: Empty localization string")
|
||||
if token_type != tokenize.OP:
|
||||
raise LocalizationError(
|
||||
start, "H701: Invalid localization call")
|
||||
if text != ")":
|
||||
if text == "%":
|
||||
raise LocalizationError(
|
||||
start,
|
||||
"H702: Formatting operation should be outside"
|
||||
" of localization method call")
|
||||
elif text == "+":
|
||||
raise LocalizationError(
|
||||
start,
|
||||
"H702: Use bare string concatenation instead of +")
|
||||
else:
|
||||
raise LocalizationError(
|
||||
start, "H702: Argument to _, _LI, _LW, _LC, or _LE "
|
||||
"must be just a string")
|
||||
|
||||
format_specs = FORMAT_RE.findall(format_string)
|
||||
positional_specs = [(key, spec) for key, spec in format_specs
|
||||
if not key and spec]
|
||||
# not spec means %%, key means %(smth)s
|
||||
if len(positional_specs) > 1:
|
||||
raise LocalizationError(
|
||||
start, "H703: Multiple positional placeholders")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_localization_strings(logical_line, tokens, noqa):
|
||||
r"""Check localization in line.
|
||||
|
||||
Okay: _("This is fine")
|
||||
Okay: _LI("This is fine")
|
||||
Okay: _LW("This is fine")
|
||||
Okay: _LE("This is fine")
|
||||
Okay: _LC("This is fine")
|
||||
Okay: _("This is also fine %s")
|
||||
Okay: _("So is this %s, %(foo)s") % {foo: 'foo'}
|
||||
H701: _('')
|
||||
Okay: def _(msg):\n pass
|
||||
Okay: def _LE(msg):\n pass
|
||||
H701: _LI('')
|
||||
H701: _LW('')
|
||||
H701: _LE('')
|
||||
H701: _LC('')
|
||||
Okay: _('') # noqa
|
||||
H702: _("Bob" + " foo")
|
||||
H702: _LI("Bob" + " foo")
|
||||
H702: _LW("Bob" + " foo")
|
||||
H702: _LE("Bob" + " foo")
|
||||
H702: _LC("Bob" + " foo")
|
||||
Okay: _("Bob" + " foo") # noqa
|
||||
H702: _("Bob %s" % foo)
|
||||
H702: _LI("Bob %s" % foo)
|
||||
H702: _LW("Bob %s" % foo)
|
||||
H702: _LE("Bob %s" % foo)
|
||||
H702: _LC("Bob %s" % foo)
|
||||
H702: _("%s %s" % (foo, bar))
|
||||
H703: _("%s %s") % (foo, bar)
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
gen = check_i18n()
|
||||
next(gen)
|
||||
try:
|
||||
list(map(gen.send, tokens))
|
||||
gen.close()
|
||||
except LocalizationError as e:
|
||||
yield e.args
|
||||
|
||||
# TODO(jogo) Dict and list objects
|
||||
@@ -1,54 +0,0 @@
|
||||
# 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 re
|
||||
|
||||
from hacking import core
|
||||
|
||||
|
||||
log_string_interpolation = re.compile(r".*LOG\.(?:error|warn|warning|info"
|
||||
r"|critical|exception|debug)"
|
||||
r"\([^,]*%[^,]*[,)]")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_no_cr(physical_line):
|
||||
r"""Check that we only use newlines not carriage returns.
|
||||
|
||||
Okay: import os\nimport sys
|
||||
# pep8 doesn't yet replace \r in strings, will work on an
|
||||
# upstream fix
|
||||
H903 import os\r\nimport sys
|
||||
"""
|
||||
pos = physical_line.find('\r')
|
||||
if pos != -1 and pos == (len(physical_line) - 2):
|
||||
return (pos, "H903: Windows style line endings not allowed in code")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
@core.off_by_default
|
||||
def hacking_delayed_string_interpolation(logical_line, noqa):
|
||||
r"""String interpolation should be delayed at logging calls.
|
||||
|
||||
H904: LOG.debug('Example: %s' % 'bad')
|
||||
Okay: LOG.debug('Example: %s', 'good')
|
||||
"""
|
||||
msg = ("H904: String interpolation should be delayed to be "
|
||||
"handled by the logging code, rather than being done "
|
||||
"at the point of the logging call. "
|
||||
"Use ',' instead of '%'.")
|
||||
|
||||
if noqa:
|
||||
return
|
||||
|
||||
if log_string_interpolation.match(logical_line):
|
||||
yield 0, msg
|
||||
@@ -1,228 +0,0 @@
|
||||
# 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 re
|
||||
import tokenize
|
||||
|
||||
from hacking import core
|
||||
|
||||
RE_OCTAL = re.compile(r"0+([1-9]\d*)")
|
||||
RE_PRINT = re.compile(r"\bprint(?:$|\s+[^\(])")
|
||||
|
||||
|
||||
@core.skip_on_py3
|
||||
@core.flake8ext
|
||||
def hacking_python3x_except_compatible(logical_line, noqa):
|
||||
r"""Check for except statements to be Python 3.x compatible
|
||||
|
||||
As of Python 3.x, the construct 'except x,y:' has been removed.
|
||||
Use 'except x as y:' instead.
|
||||
|
||||
|
||||
Okay: try:\n pass\nexcept Exception:\n pass
|
||||
Okay: try:\n pass\nexcept (Exception, AttributeError):\n pass
|
||||
H231: try:\n pass\nexcept AttributeError, e:\n pass
|
||||
Okay: try:\n pass\nexcept AttributeError, e: # noqa\n pass
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
|
||||
def is_old_style_except(logical_line):
|
||||
return (',' in logical_line and
|
||||
')' not in logical_line.rpartition(',')[2])
|
||||
|
||||
if (logical_line.startswith("except ") and
|
||||
logical_line.endswith(':') and
|
||||
is_old_style_except(logical_line)):
|
||||
yield 0, "H231: Python 3.x incompatible 'except x,y:' construct"
|
||||
|
||||
|
||||
@core.skip_on_py3
|
||||
@core.flake8ext
|
||||
def hacking_python3x_octal_literals(logical_line, tokens, noqa):
|
||||
r"""Check for octal literals in Python 3.x compatible form.
|
||||
|
||||
As of Python 3.x, the construct "0755" has been removed.
|
||||
Use "0o755" instead".
|
||||
|
||||
|
||||
Okay: f(0o755)
|
||||
Okay: 'f(0755)'
|
||||
Okay: f(755)
|
||||
Okay: f(0)
|
||||
Okay: f(000)
|
||||
Okay: MiB = 1.0415
|
||||
H232: f(0755)
|
||||
Okay: f(0755) # noqa
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
|
||||
for token_type, text, _, _, _ in tokens:
|
||||
if token_type == tokenize.NUMBER:
|
||||
match = RE_OCTAL.match(text)
|
||||
if match:
|
||||
yield 0, ("H232: Python 3.x incompatible octal %s should be "
|
||||
"written as 0o%s " %
|
||||
(match.group(0)[1:], match.group(1)))
|
||||
|
||||
|
||||
@core.skip_on_py3
|
||||
@core.flake8ext
|
||||
def hacking_python3x_print_function(logical_line, noqa):
|
||||
r"""Check that all print occurrences look like print functions.
|
||||
|
||||
Check that all occurrences of print look like functions, not
|
||||
print operator. As of Python 3.x, the print operator has
|
||||
been removed.
|
||||
|
||||
|
||||
Okay: print(msg)
|
||||
Okay: print (msg)
|
||||
Okay: print msg # noqa
|
||||
Okay: print()
|
||||
H233: print msg
|
||||
H233: print >>sys.stderr, "hello"
|
||||
H233: print msg,
|
||||
H233: print
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
for match in RE_PRINT.finditer(logical_line):
|
||||
yield match.start(0), (
|
||||
"H233: Python 3.x incompatible use of print operator")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_no_assert_equals(logical_line, tokens, noqa):
|
||||
r"""assert(Not)Equals() is deprecated, use assert(Not)Equal instead.
|
||||
|
||||
Okay: self.assertEqual(0, 0)
|
||||
Okay: self.assertNotEqual(0, 1)
|
||||
H234: self.assertEquals(0, 0)
|
||||
H234: self.assertNotEquals(0, 1)
|
||||
Okay: self.assertEquals(0, 0) # noqa
|
||||
Okay: self.assertNotEquals(0, 1) # noqa
|
||||
"""
|
||||
|
||||
if noqa:
|
||||
return
|
||||
for token_type, text, start_index, _, _ in tokens:
|
||||
|
||||
if token_type == tokenize.NAME:
|
||||
if text == "assertEquals" or text == "assertNotEquals":
|
||||
yield (start_index[1],
|
||||
"H234: %s is deprecated, use %s" % (text, text[:-1]))
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_no_assert_underscore(logical_line, tokens, noqa):
|
||||
r"""assert_() is deprecated, use assertTrue instead.
|
||||
|
||||
Okay: self.assertTrue(foo)
|
||||
H235: self.assert_(foo)
|
||||
Okay: self.assert_(foo) # noqa
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
for token_type, text, start_index, _, _ in tokens:
|
||||
|
||||
if token_type == tokenize.NAME and text == "assert_":
|
||||
yield (
|
||||
start_index[1],
|
||||
"H235: assert_ is deprecated, use assertTrue")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_python3x_metaclass(logical_line, noqa):
|
||||
r"""Check for metaclass to be Python 3.x compatible.
|
||||
|
||||
Okay: @six.add_metaclass(Meta)\nclass Foo(object):\n pass
|
||||
Okay: @six.with_metaclass(Meta)\nclass Foo(object):\n pass
|
||||
Okay: class Foo(object):\n '''docstring\n\n __metaclass__ = Meta\n'''
|
||||
H236: class Foo(object):\n __metaclass__ = Meta
|
||||
H236: class Foo(object):\n foo=bar\n __metaclass__ = Meta
|
||||
H236: class Foo(object):\n '''docstr.'''\n __metaclass__ = Meta
|
||||
H236: class Foo(object):\n __metaclass__ = \\\n Meta
|
||||
Okay: class Foo(object):\n __metaclass__ = Meta # noqa
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
split_line = logical_line.split()
|
||||
if(len(split_line) > 2 and split_line[0] == '__metaclass__' and
|
||||
split_line[1] == '='):
|
||||
yield (logical_line.find('__metaclass__'),
|
||||
"H236: Python 3.x incompatible __metaclass__, "
|
||||
"use six.add_metaclass()")
|
||||
|
||||
|
||||
# NOTE(guochbo): This is removed module list:
|
||||
# http://python3porting.com/stdlib.html#removed-modules
|
||||
removed_modules = [
|
||||
'audiodev', 'Bastion', 'bsddb185', 'bsddb3',
|
||||
'Canvas', 'cfmfile', 'cl', 'commands', 'compiler'
|
||||
'dircache', 'dl', 'exception', 'fpformat',
|
||||
'htmllib', 'ihooks', 'imageop', 'imputil'
|
||||
'linuxaudiodev', 'md5', 'mhlib', 'mimetools'
|
||||
'MimeWriter', 'mimify', 'multifile', 'mutex',
|
||||
'new', 'popen2', 'posixfile', 'pure', 'rexec'
|
||||
'rfc822', 'sha', 'sgmllib', 'sre', 'stat'
|
||||
'stringold', 'sunaudio' 'sv', 'test.testall',
|
||||
'thread', 'timing', 'toaiff', 'user'
|
||||
]
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_no_removed_module(logical_line, noqa):
|
||||
r"""Check for removed modules in Python 3.
|
||||
|
||||
Examples:
|
||||
Okay: from os import path
|
||||
Okay: from os import path as p
|
||||
Okay: from os import (path as p)
|
||||
Okay: import os.path
|
||||
H237: import thread
|
||||
Okay: import thread # noqa
|
||||
H237: import commands
|
||||
H237: import md5 as std_md5
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
line = core.import_normalize(logical_line.strip())
|
||||
if line and line.split()[0] == 'import':
|
||||
module_name = line.split()[1].split('.')[0]
|
||||
if module_name in removed_modules:
|
||||
yield 0, ("H237: module %s is "
|
||||
"removed in Python 3" % module_name)
|
||||
|
||||
|
||||
RE_NEW_STYLE_CLASS = re.compile(r"^class [^(]+\(.+\):")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
def hacking_no_old_style_class(logical_line, noqa):
|
||||
r"""Check for old style classes.
|
||||
|
||||
Examples:
|
||||
Okay: class Foo(object):\n pass
|
||||
Okay: class Foo(Bar, Baz):\n pass
|
||||
Okay: class Foo(object, Baz):\n pass
|
||||
Okay: class Foo(somefunc()):\n pass
|
||||
H238: class Bar:\n pass
|
||||
H238: class Bar():\n pass
|
||||
"""
|
||||
if noqa:
|
||||
return
|
||||
line = core.import_normalize(logical_line.strip())
|
||||
if line.startswith("class ") and not RE_NEW_STYLE_CLASS.match(line):
|
||||
yield (0, "H238: old style class declaration, "
|
||||
"use new style (inherit from `object`)")
|
||||
@@ -1,39 +0,0 @@
|
||||
# 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 re
|
||||
|
||||
from hacking import core
|
||||
|
||||
|
||||
vim_header_re = re.compile(r"^#\s+vim?:.+")
|
||||
|
||||
|
||||
@core.flake8ext
|
||||
@core.off_by_default
|
||||
def no_vim_headers(physical_line, line_number, lines):
|
||||
r"""Check for vim editor configuration in source files.
|
||||
|
||||
By default vim modelines can only appear in the first or
|
||||
last 5 lines of a source file.
|
||||
|
||||
Examples:
|
||||
H106: # vim: set tabstop=4 shiftwidth=4\n#\n#\n#\n#\n#
|
||||
H106: # Lic\n# vim: set tabstop=4 shiftwidth=4\n#\n#\n#\n#\n#
|
||||
H106: # Lic\n#\n#\n#\n#\n#\n#\n#\n#\n# vim: set tabstop=4 shiftwidth=4
|
||||
Okay: # Lic\n#\n#\n#\n#\n#\n#\n#
|
||||
Okay: # viminal hill is located in Rome
|
||||
Okay: # vim, ze nemluvis cesky
|
||||
"""
|
||||
if ((line_number <= 5 or line_number > len(lines) - 5) and
|
||||
vim_header_re.match(physical_line)):
|
||||
return 0, "H106: Don't put vim configuration in source files"
|
||||
@@ -1,48 +0,0 @@
|
||||
# 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 six import moves
|
||||
|
||||
|
||||
class Config(object):
|
||||
def __init__(self, default_section=None, tox_file='tox.ini'):
|
||||
conf = moves.configparser.RawConfigParser()
|
||||
conf.read(tox_file)
|
||||
|
||||
self.conf = conf
|
||||
self.default_section = default_section
|
||||
|
||||
def get(self, option, section=None, default=None):
|
||||
section = section or self.default_section
|
||||
|
||||
if not self.conf.has_section(section):
|
||||
return default
|
||||
|
||||
if self.conf.has_option(section, option):
|
||||
return self.conf.get(section, option).strip()
|
||||
|
||||
return default
|
||||
|
||||
def get_multiple(self, option, section=None, default=None):
|
||||
section = section or self.default_section
|
||||
|
||||
values = self.get(option, section)
|
||||
if not values:
|
||||
return default
|
||||
|
||||
values = [v.strip() for v in values.split('\n') if v.strip()]
|
||||
result = []
|
||||
for vals in values:
|
||||
result.extend([v.strip() for v in vals.split(',') if v.strip()])
|
||||
|
||||
return result
|
||||
164
hacking/core.py
164
hacking/core.py
@@ -1,164 +0,0 @@
|
||||
# Copyright (c) 2012, Cloudscaling
|
||||
# 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.
|
||||
|
||||
"""OpenStack HACKING file compliance testing
|
||||
|
||||
Built as a sets of pep8 checks using flake8.
|
||||
"""
|
||||
|
||||
import gettext
|
||||
import sys
|
||||
|
||||
import pbr.util
|
||||
import pep8
|
||||
|
||||
from hacking import config
|
||||
|
||||
# Import tests need to inject _ properly into the builtins
|
||||
kwargs = {}
|
||||
if sys.version_info[0] < 3:
|
||||
# In Python2, ensure that the _() that gets installed into built-ins
|
||||
# always returns unicodes. This matches the default behavior under Python
|
||||
# 3, although the keyword argument is not present in the Python 3 API.
|
||||
kwargs['unicode'] = True
|
||||
gettext.install('hacking', **kwargs)
|
||||
|
||||
|
||||
def flake8ext(f):
|
||||
f.name = __name__
|
||||
f.version = '0.0.1'
|
||||
f.skip_on_py3 = False
|
||||
if not hasattr(f, 'off_by_default'):
|
||||
f.off_by_default = False
|
||||
return f
|
||||
|
||||
|
||||
def off_by_default(f):
|
||||
"""Decorator to turn check off by default.
|
||||
|
||||
To enable the check use the flake8 select setting in
|
||||
tox.ini.
|
||||
|
||||
flake8 documentation:
|
||||
http://flake8.readthedocs.org/en/latest/extensions.html.
|
||||
"""
|
||||
f.off_by_default = True
|
||||
return f
|
||||
|
||||
|
||||
def skip_on_py3(f):
|
||||
f.skip_on_py3 = True
|
||||
return f
|
||||
|
||||
# Error code block layout
|
||||
|
||||
# H1xx comments
|
||||
# H20x except
|
||||
# H23x Python 2.x -> 3.x portability issues
|
||||
# H3xx imports
|
||||
# H4xx docstrings
|
||||
# H5xx dictionaries/lists
|
||||
# H6xx calling methods
|
||||
# H7xx localization
|
||||
# H8xx git commit messages
|
||||
# H9xx other
|
||||
|
||||
|
||||
CONF = config.Config('hacking')
|
||||
|
||||
|
||||
DEFAULT_IMPORT_EXCEPTIONS = [
|
||||
'sqlalchemy',
|
||||
'migrate',
|
||||
]
|
||||
|
||||
IMPORT_EXCEPTIONS = CONF.get_multiple('import_exceptions', default=[])
|
||||
IMPORT_EXCEPTIONS += DEFAULT_IMPORT_EXCEPTIONS
|
||||
|
||||
|
||||
def is_import_exception(mod):
|
||||
"""Check module name to see if import has been whitelisted.
|
||||
|
||||
Import based rules should not run on any whitelisted module
|
||||
"""
|
||||
return (mod in IMPORT_EXCEPTIONS or
|
||||
any(mod.startswith(m + '.') for m in IMPORT_EXCEPTIONS))
|
||||
|
||||
|
||||
def import_normalize(line):
|
||||
# convert "from x import y" to "import x.y"
|
||||
# handle "from x import y as z" to "import x.y as z"
|
||||
split_line = line.split()
|
||||
if ("import" in line and line.startswith("from ") and "," not in line and
|
||||
split_line[2] == "import" and split_line[3] != "*" and
|
||||
split_line[1] != "__future__" and
|
||||
(len(split_line) == 4 or
|
||||
(len(split_line) == 6 and split_line[4] == "as"))):
|
||||
return "import %s.%s" % (split_line[1], split_line[3])
|
||||
else:
|
||||
return line
|
||||
|
||||
|
||||
class GlobalCheck(object):
|
||||
"""Base class for checks that should be run only once."""
|
||||
|
||||
name = None
|
||||
version = '0.0.1'
|
||||
skip_on_py3 = False
|
||||
_has_run = set()
|
||||
|
||||
def __init__(self, tree, *args):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
"""Make run a no-op if run() has been called before.
|
||||
|
||||
Store in a global registry the list of checks we've run. If we have
|
||||
run that one before, just skip doing anything the subsequent times.
|
||||
This way, since pep8 is file/line based, we don't wind up re-running
|
||||
a check on a git commit message over and over again.
|
||||
"""
|
||||
if self.name and self.name not in self.__class__._has_run:
|
||||
self.__class__._has_run.add(self.name)
|
||||
ret = self.run_once()
|
||||
if ret is not None:
|
||||
yield ret
|
||||
|
||||
def run_once(self):
|
||||
pass
|
||||
|
||||
|
||||
class ProxyChecks(GlobalCheck):
|
||||
"""Provide a mechanism for locally defined checks."""
|
||||
name = 'ProxyChecker'
|
||||
|
||||
@classmethod
|
||||
def add_options(cls, parser):
|
||||
# We're looking for local checks, so we need to include the local
|
||||
# dir in the search path
|
||||
sys.path.append('.')
|
||||
|
||||
local_check = CONF.get_multiple('local-check', default=[])
|
||||
for check_path in set(local_check):
|
||||
if check_path.strip():
|
||||
checker = pbr.util.resolve_name(check_path)
|
||||
pep8.register_check(checker)
|
||||
|
||||
local_check_fact = CONF.get('local-check-factory')
|
||||
if local_check_fact:
|
||||
factory = pbr.util.resolve_name(local_check_fact)
|
||||
factory(pep8.register_check)
|
||||
|
||||
sys.path.pop()
|
||||
@@ -1,58 +0,0 @@
|
||||
# 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.
|
||||
|
||||
import os
|
||||
|
||||
import fixtures
|
||||
import testtools
|
||||
|
||||
|
||||
_TRUE_VALUES = ('True', 'true', '1', 'yes')
|
||||
|
||||
|
||||
class TestCase(testtools.TestCase):
|
||||
"""Test case base class for all unit tests."""
|
||||
|
||||
def setUp(self):
|
||||
"""Run before each test method to initialize test environment."""
|
||||
super(TestCase, self).setUp()
|
||||
test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
|
||||
try:
|
||||
test_timeout = int(test_timeout)
|
||||
except ValueError:
|
||||
# If timeout value is invalid do not set a timeout.
|
||||
test_timeout = 0
|
||||
if test_timeout > 0:
|
||||
self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
|
||||
|
||||
if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES:
|
||||
stdout = self.useFixture(fixtures.StringStream('stdout')).stream
|
||||
self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
|
||||
if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES:
|
||||
stderr = self.useFixture(fixtures.StringStream('stderr')).stream
|
||||
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
|
||||
|
||||
def assertCheckFails(self, check_func, *args, **kwargs):
|
||||
if not list(check_func(*args, **kwargs)):
|
||||
raise AssertionError("Check %s did not fail." %
|
||||
check_func.__name__)
|
||||
|
||||
def assertCheckPasses(self, check_func, *args, **kwargs):
|
||||
try:
|
||||
self.assertCheckFails(check_func, *args, **kwargs)
|
||||
except AssertionError:
|
||||
return
|
||||
else:
|
||||
raise AssertionError("Check %s failed." % check_func.__name__)
|
||||
@@ -1,84 +0,0 @@
|
||||
# Copyright (c) 2013 eNovance
|
||||
#
|
||||
# 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 hacking.checks import comments
|
||||
from hacking import tests
|
||||
|
||||
|
||||
class CoreTestCase(tests.TestCase):
|
||||
def test_H104_regex(self):
|
||||
"""Verify that the H104 regex matches correct lines."""
|
||||
self.assertTrue(comments.hacking_has_only_comments(
|
||||
None,
|
||||
None,
|
||||
['# foo',
|
||||
'# bar'],
|
||||
1))
|
||||
self.assertTrue(comments.hacking_has_only_comments(
|
||||
None,
|
||||
None,
|
||||
['# foo',
|
||||
'# bar',
|
||||
''],
|
||||
1))
|
||||
self.assertTrue(comments.hacking_has_only_comments(
|
||||
None,
|
||||
None,
|
||||
['# foo',
|
||||
' ',
|
||||
'# bar'],
|
||||
1))
|
||||
|
||||
self.assertIsNone(comments.hacking_has_only_comments(
|
||||
None,
|
||||
None,
|
||||
['# foo',
|
||||
' ',
|
||||
'"""foobar"""'],
|
||||
1))
|
||||
self.assertIsNone(comments.hacking_has_only_comments(
|
||||
None,
|
||||
None,
|
||||
['# foo',
|
||||
'',
|
||||
'print(42)'],
|
||||
1))
|
||||
self.assertIsNone(comments.hacking_has_only_comments(
|
||||
None,
|
||||
None,
|
||||
['# foo'],
|
||||
100))
|
||||
|
||||
def test_H105(self):
|
||||
self.assertTrue(comments.hacking_no_author_tags(
|
||||
'# @author: Foo Bar'))
|
||||
|
||||
self.assertTrue(comments.hacking_no_author_tags(
|
||||
'# @Author: Foo Bar'))
|
||||
|
||||
self.assertTrue(comments.hacking_no_author_tags(
|
||||
'# author: Foo Bar'))
|
||||
|
||||
self.assertTrue(comments.hacking_no_author_tags(
|
||||
'# authors: Foo Bar'))
|
||||
|
||||
self.assertTrue(comments.hacking_no_author_tags(
|
||||
'# Author: Foo Bar'))
|
||||
|
||||
self.assertTrue(comments.hacking_no_author_tags(
|
||||
'# Authors: Foo Bar'))
|
||||
|
||||
self.assertTrue(comments.hacking_no_author_tags(
|
||||
'.. moduleauthor:: Foo Bar'))
|
||||
@@ -1,58 +0,0 @@
|
||||
# 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 os
|
||||
|
||||
import fixtures
|
||||
|
||||
from hacking import config
|
||||
from hacking import tests
|
||||
|
||||
|
||||
TEST_TOX_INI = """[hacking]
|
||||
option_1 = val_1
|
||||
option_2 =
|
||||
val_2
|
||||
option_3 =
|
||||
val_1,val_2, val_3,
|
||||
val_4 , val_5 , val_6,
|
||||
val_7
|
||||
,
|
||||
val_8
|
||||
val_9
|
||||
"""
|
||||
|
||||
|
||||
class ConfigTest(tests.TestCase):
|
||||
def setUp(self):
|
||||
tox_ini_path = os.path.join(self.useFixture(fixtures.TempDir()).path,
|
||||
'tox.ini')
|
||||
|
||||
with open(tox_ini_path, 'w') as tox_ini:
|
||||
tox_ini.write(TEST_TOX_INI)
|
||||
self.conf = config.Config('hacking', tox_ini_path)
|
||||
super(ConfigTest, self).setUp()
|
||||
|
||||
def test_get(self):
|
||||
self.assertEqual('val_1', self.conf.get('option_1'))
|
||||
self.assertEqual('val_2', self.conf.get('option_2'))
|
||||
self.assertEqual('val_3', self.conf.get('option_4', default='val_3'))
|
||||
|
||||
def test_get_multiple(self):
|
||||
self.assertEqual(['val_1', 'val_2', 'val_3', 'val_4', 'val_5', 'val_6',
|
||||
'val_7', 'val_8', 'val_9'],
|
||||
self.conf.get_multiple('option_3'))
|
||||
|
||||
self.assertEqual(['val_1', 'val_2'],
|
||||
self.conf.get_multiple('option_4',
|
||||
default=['val_1', 'val_2']))
|
||||
@@ -1,97 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# 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.
|
||||
|
||||
import re
|
||||
|
||||
from flake8 import engine
|
||||
import pep8
|
||||
import pkg_resources
|
||||
import six
|
||||
import testscenarios
|
||||
from testtools import content
|
||||
from testtools import matchers
|
||||
|
||||
import hacking
|
||||
import hacking.tests
|
||||
|
||||
SELFTEST_REGEX = re.compile(r'\b(Okay|[HEW]\d{3}):\s(.*)')
|
||||
# Each scenario is (name, dict(lines=.., options=..., code=...))
|
||||
file_cases = []
|
||||
|
||||
|
||||
class HackingTestCase(hacking.tests.TestCase):
|
||||
|
||||
scenarios = file_cases
|
||||
|
||||
def test_pep8(self):
|
||||
|
||||
# NOTE(jecarey): Add tests marked as off_by_default to enable testing
|
||||
turn_on = set(['H106', 'H203', 'H904', 'H204', 'H205'])
|
||||
if self.options.select:
|
||||
turn_on.update(self.options.select)
|
||||
self.options.select = tuple(turn_on)
|
||||
|
||||
report = pep8.BaseReport(self.options)
|
||||
checker = pep8.Checker(lines=self.lines, options=self.options,
|
||||
report=report)
|
||||
checker.check_all()
|
||||
self.addDetail('doctest', content.text_content(self.raw))
|
||||
if self.code == 'Okay':
|
||||
self.assertThat(
|
||||
len(report.counters),
|
||||
matchers.Not(matchers.GreaterThan(
|
||||
len(self.options.benchmark_keys))),
|
||||
"incorrectly found %s" % ', '.join(
|
||||
[key for key in report.counters
|
||||
if key not in self.options.benchmark_keys]))
|
||||
else:
|
||||
self.addDetail('reason',
|
||||
content.text_content("Failed to trigger rule %s" %
|
||||
self.code))
|
||||
self.assertIn(self.code, report.counters)
|
||||
|
||||
|
||||
def _get_lines(check):
|
||||
for line in check.__doc__.splitlines():
|
||||
line = line.lstrip()
|
||||
match = SELFTEST_REGEX.match(line)
|
||||
if match is None:
|
||||
continue
|
||||
yield (line, match.groups())
|
||||
|
||||
|
||||
def load_tests(loader, tests, pattern):
|
||||
|
||||
flake8_style = engine.get_style_guide(parse_argv=False,
|
||||
# Ignore H104 otherwise it's
|
||||
# raised on doctests.
|
||||
ignore=('F', 'H104'))
|
||||
options = flake8_style.options
|
||||
|
||||
for entry in pkg_resources.iter_entry_points('flake8.extension'):
|
||||
if not entry.module_name.startswith('hacking.'):
|
||||
continue
|
||||
check = entry.load()
|
||||
name = entry.attrs[0]
|
||||
if check.skip_on_py3 and six.PY3:
|
||||
continue
|
||||
for (lineno, (raw, (code, source))) in enumerate(_get_lines(check)):
|
||||
lines = [part.replace(r'\t', '\t') + '\n'
|
||||
for part in source.split(r'\n')]
|
||||
file_cases.append(("%s-%s-line-%s" % (entry.name, name, lineno),
|
||||
dict(lines=lines, raw=raw, options=options,
|
||||
code=code)))
|
||||
return testscenarios.load_tests_apply_scenarios(loader, tests, pattern)
|
||||
@@ -1,53 +0,0 @@
|
||||
# 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 os
|
||||
|
||||
import fixtures
|
||||
|
||||
from hacking import config
|
||||
from hacking import core
|
||||
from hacking import tests
|
||||
|
||||
|
||||
TEST_TOX_INI = """[hacking]
|
||||
import_exceptions =
|
||||
a.b.c
|
||||
z.x
|
||||
"""
|
||||
|
||||
|
||||
class ImportExceptionsTest(tests.TestCase):
|
||||
def _setUpConfig(self, content):
|
||||
tox_ini_path = os.path.join(self.useFixture(fixtures.TempDir()).path,
|
||||
'tox.ini')
|
||||
|
||||
with open(tox_ini_path, 'w') as tox_ini:
|
||||
tox_ini.write(content)
|
||||
|
||||
return config.Config('hacking', tox_ini_path)
|
||||
|
||||
def setUp(self):
|
||||
super(ImportExceptionsTest, self).setUp()
|
||||
|
||||
def test_default_import_exceptions(self):
|
||||
conf = self._setUpConfig("")
|
||||
self.assertEqual(core.DEFAULT_IMPORT_EXCEPTIONS,
|
||||
conf.get_multiple(
|
||||
'import_exceptions',
|
||||
default=core.DEFAULT_IMPORT_EXCEPTIONS))
|
||||
|
||||
def test_import_exceptions(self):
|
||||
conf = self._setUpConfig(TEST_TOX_INI)
|
||||
self.assertEqual(['a.b.c', 'z.x'],
|
||||
conf.get_multiple('import_exceptions'))
|
||||
@@ -1,36 +0,0 @@
|
||||
# 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 flake8 import engine
|
||||
import pep8
|
||||
|
||||
import hacking.tests
|
||||
|
||||
|
||||
def check(physical_line):
|
||||
"""Test check to make sure local-checks are working."""
|
||||
if physical_line.strip() == "#this-is-the-test-phrase":
|
||||
return (0, "L100: Found local-check test case")
|
||||
|
||||
|
||||
class HackingTestCase(hacking.tests.TestCase):
|
||||
def test_local_check(self):
|
||||
flake8_style = engine.get_style_guide(parse_argv=False, ignore='F')
|
||||
report = pep8.BaseReport(flake8_style.options)
|
||||
line = ["#this-is-the-test-phrase"]
|
||||
checker = pep8.Checker(lines=line, options=flake8_style.options,
|
||||
report=report)
|
||||
checker.check_all()
|
||||
self.assertIn("L100", report.counters)
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Usage: test.sh openstack keystone path-to-repo
|
||||
# path-to-repo is an optional parameter, if it exists
|
||||
# no cloning will happen and the local directory will be used,
|
||||
# the first two parameter get ignored.
|
||||
# Note: you can clone from a local file with REPO_ROOT=file:////~/path/to/repo
|
||||
|
||||
set -x
|
||||
set -e
|
||||
|
||||
REPO_ROOT=${REPO_ROOT:-git://git.openstack.org}
|
||||
|
||||
if [[ $# -lt 2 ]] ; then
|
||||
echo "Script needs at least two arguments:"
|
||||
echo "$0 organization name [path-to-repo]"
|
||||
exit 1
|
||||
fi
|
||||
org=$1
|
||||
project=$2
|
||||
|
||||
if [[ $# -eq 3 ]] ; then
|
||||
projectdir=$3
|
||||
clone=0
|
||||
else
|
||||
projectdir=$project
|
||||
clone=1
|
||||
fi
|
||||
|
||||
if [ "$clone" = "1" ] ; then
|
||||
|
||||
tempdir="$(mktemp -d)"
|
||||
|
||||
trap "rm -rf $tempdir" EXIT
|
||||
pushd $tempdir
|
||||
git clone $REPO_ROOT/$org/$project --depth=1
|
||||
fi
|
||||
|
||||
pushd $projectdir
|
||||
set +e
|
||||
flake8 --select H --statistics
|
||||
popd
|
||||
|
||||
if [ "$clone" = "1" ] ; then
|
||||
popd
|
||||
fi
|
||||
@@ -1,283 +0,0 @@
|
||||
# -*- 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
|
||||
|
||||
# Hacking Release Notes documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue Nov 3 17:40:50 2015.
|
||||
#
|
||||
# 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 = [
|
||||
'openstackdocstheme',
|
||||
'reno.sphinxext',
|
||||
]
|
||||
|
||||
# openstackdocstheme options
|
||||
repository_name = 'openstack-dev/hacking'
|
||||
bug_project = 'hacking'
|
||||
bug_tag = ''
|
||||
|
||||
# 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'Hacking Release Notes'
|
||||
copyright = u'2015, Hacking 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.
|
||||
|
||||
version_info = pbr.version.VersionInfo('hacking')
|
||||
# The short X.Y version.
|
||||
release = version_info.version_string_with_vcs()
|
||||
# The short X.Y version.
|
||||
version = version_info.canonical_version_string()
|
||||
|
||||
# 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 = 'openstackdocs'
|
||||
|
||||
# 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
|
||||
# "<project> v<release> 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 = '%Y-%m-%d %H:%M'
|
||||
|
||||
# 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 <link> 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 = 'HackingReleaseNotesdoc'
|
||||
|
||||
|
||||
# -- 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', 'HackingReleaseNotes.tex',
|
||||
u'Hacking Release Notes Documentation',
|
||||
u'Hacking 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', 'hackingreleasenotes', u'Hacking Release Notes Documentation',
|
||||
[u'Hacking 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', 'HackingReleaseNotes', u'Hacking Release Notes Documentation',
|
||||
u'Hacking Developers', 'HackingReleaseNotes',
|
||||
'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/']
|
||||
@@ -1,8 +0,0 @@
|
||||
=======================
|
||||
Hacking Release Notes
|
||||
=======================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
unreleased
|
||||
@@ -1,5 +0,0 @@
|
||||
==============================
|
||||
Current Series Release Notes
|
||||
==============================
|
||||
|
||||
.. release-notes::
|
||||
@@ -1,11 +0,0 @@
|
||||
# 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>=1.8 # Apache-2.0
|
||||
|
||||
pep8==1.5.7 # MIT
|
||||
pyflakes==0.8.1 # MIT
|
||||
flake8<2.6.0,>=2.5.4 # MIT
|
||||
mccabe==0.2.1 # MIT License
|
||||
|
||||
six>=1.9.0 # MIT
|
||||
77
setup.cfg
77
setup.cfg
@@ -1,77 +0,0 @@
|
||||
[metadata]
|
||||
name = hacking
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
summary = OpenStack Hacking Guideline Enforcement
|
||||
description-file =
|
||||
README.rst
|
||||
home-page = https://docs.openstack.org/hacking/latest/
|
||||
classifier =
|
||||
Development Status :: 4 - Beta
|
||||
Environment :: Console
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Developers
|
||||
Intended Audience :: Information Technology
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.4
|
||||
Programming Language :: Python :: 3.5
|
||||
|
||||
[files]
|
||||
packages =
|
||||
hacking
|
||||
|
||||
[entry_points]
|
||||
flake8.extension =
|
||||
H000 = hacking.core:ProxyChecks
|
||||
H101 = hacking.checks.comments:hacking_todo_format
|
||||
H102 = hacking.checks.comments:hacking_has_license
|
||||
H103 = hacking.checks.comments:hacking_has_correct_license
|
||||
H104 = hacking.checks.comments:hacking_has_only_comments
|
||||
H105 = hacking.checks.comments:hacking_no_author_tags
|
||||
H106 = hacking.checks.vim_check:no_vim_headers
|
||||
H201 = hacking.checks.except_checks:hacking_except_format
|
||||
H202 = hacking.checks.except_checks:hacking_except_format_assert
|
||||
H203 = hacking.checks.except_checks:hacking_assert_is_none
|
||||
H204 = hacking.checks.except_checks:hacking_assert_equal
|
||||
H205 = hacking.checks.except_checks:hacking_assert_greater_less
|
||||
H231 = hacking.checks.python23:hacking_python3x_except_compatible
|
||||
H232 = hacking.checks.python23:hacking_python3x_octal_literals
|
||||
H233 = hacking.checks.python23:hacking_python3x_print_function
|
||||
H234 = hacking.checks.python23:hacking_no_assert_equals
|
||||
H235 = hacking.checks.python23:hacking_no_assert_underscore
|
||||
H236 = hacking.checks.python23:hacking_python3x_metaclass
|
||||
H237 = hacking.checks.python23:hacking_no_removed_module
|
||||
H238 = hacking.checks.python23:hacking_no_old_style_class
|
||||
H301 = hacking.checks.imports:hacking_import_rules
|
||||
H306 = hacking.checks.imports:hacking_import_alphabetical
|
||||
H401 = hacking.checks.docstrings:hacking_docstring_start_space
|
||||
H403 = hacking.checks.docstrings:hacking_docstring_multiline_end
|
||||
H404 = hacking.checks.docstrings:hacking_docstring_multiline_start
|
||||
H405 = hacking.checks.docstrings:hacking_docstring_summary
|
||||
H501 = hacking.checks.dictlist:hacking_no_locals
|
||||
H700 = hacking.checks.localization:hacking_localization_strings
|
||||
H903 = hacking.checks.other:hacking_no_cr
|
||||
H904 = hacking.checks.other:hacking_delayed_string_interpolation
|
||||
|
||||
[extras]
|
||||
pep257 =
|
||||
flake8-docstrings==0.2.1.post1 # MIT
|
||||
|
||||
[build_sphinx]
|
||||
all_files = 1
|
||||
build-dir = doc/build
|
||||
source-dir = doc/source
|
||||
warning-is-error = 1
|
||||
|
||||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
tag_svn_revision = 0
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
||||
29
setup.py
29
setup.py
@@ -1,29 +0,0 @@
|
||||
# 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>=1.8'],
|
||||
pbr=True)
|
||||
@@ -1,21 +0,0 @@
|
||||
# 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.
|
||||
coverage>=4.0 # Apache-2.0
|
||||
fixtures>=3.0.0 # Apache-2.0/BSD
|
||||
mock>=2.0 # BSD
|
||||
python-subunit>=0.0.18 # Apache-2.0/BSD
|
||||
sphinx>=1.6.2 # BSD
|
||||
openstackdocstheme>=1.11.0 # Apache-2.0
|
||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||
testscenarios>=0.4 # Apache-2.0/BSD
|
||||
testtools>=1.4.0 # MIT
|
||||
|
||||
# hacking doesn't use this anywhere, but nova imports this in nova/__init__.py
|
||||
# since eventlet is such a common universal import, add it to the hacking test
|
||||
# virtualenv, so importing things like 'nova.hacking.checks.factory' will just
|
||||
# work.
|
||||
# See https://bugs.launchpad.net/hacking/+bug/1403270
|
||||
eventlet!=0.18.3,>=0.18.2 # MIT
|
||||
|
||||
reno>=1.8.0 # Apache-2.0
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/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 $?
|
||||
48
tox.ini
48
tox.ini
@@ -1,48 +0,0 @@
|
||||
[tox]
|
||||
minversion = 2.0
|
||||
skipsdist = True
|
||||
envlist = py{35,34,27},pep8,pypy
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
BRANCH_NAME=master
|
||||
CLIENT_NAME=hacking
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
commands =
|
||||
python setup.py testr --slowest --testr-args='{posargs}'
|
||||
|
||||
[tox:jenkins]
|
||||
sitepackages = True
|
||||
|
||||
[testenv:pep8]
|
||||
commands = flake8 {posargs}
|
||||
|
||||
[testenv:integration]
|
||||
whitelist_externals = bash
|
||||
commands =
|
||||
bash integration-test/test.sh {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
commands =
|
||||
python setup.py testr --coverage
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[testenv:releasenotes]
|
||||
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||
|
||||
[flake8]
|
||||
exclude = .venv,.tox,dist,doc,*.egg,build
|
||||
show-source = true
|
||||
enable-extensions = H106
|
||||
|
||||
[hacking]
|
||||
local-check = hacking.tests.test_local.check
|
||||
Reference in New Issue
Block a user