[Python-modules-commits] [flufl.testing] 01/03: import flufl.testing_0.5.orig.tar.gz
Barry Warsaw
barry at moszumanska.debian.org
Mon Dec 12 20:21:21 UTC 2016
This is an automated email from the git hooks/post-receive script.
barry pushed a commit to branch master
in repository flufl.testing.
commit d614f811f2d2a67a0f5df1d05304e5e064ae68b4
Author: Barry Warsaw <barry at python.org>
Date: Mon Dec 12 14:39:46 2016 -0500
import flufl.testing_0.5.orig.tar.gz
---
MANIFEST.in | 5 +
NEWS.rst | 28 +++++
PKG-INFO | 16 +++
README.rst | 177 ++++++++++++++++++++++++++++
flufl.testing.egg-info/PKG-INFO | 16 +++
flufl.testing.egg-info/SOURCES.txt | 14 +++
flufl.testing.egg-info/dependency_links.txt | 1 +
flufl.testing.egg-info/entry_points.txt | 3 +
flufl.testing.egg-info/top_level.txt | 1 +
flufl/__init__.py | 1 +
flufl/testing/__init__.py | 1 +
flufl/testing/imports.py | 136 +++++++++++++++++++++
flufl/testing/nose.py | 132 +++++++++++++++++++++
setup.cfg | 5 +
setup.py | 44 +++++++
setup_helpers.py | 138 ++++++++++++++++++++++
16 files changed, 718 insertions(+)
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..73b67b1
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,5 @@
+include *.py MANIFEST.in
+global-include *.txt *.rst *.po *.mo *.ini
+exclude .gitignore
+prune build
+prune .tox
diff --git a/NEWS.rst b/NEWS.rst
new file mode 100644
index 0000000..f450b2d
--- /dev/null
+++ b/NEWS.rst
@@ -0,0 +1,28 @@
+===============
+ flufl.testing
+===============
+
+Copyright (C) 2016 Barry A. Warsaw
+
+
+0.5 (2016-12-02)
+================
+* Fix namespace package compatibility.
+
+0.4 (2016-11-30)
+================
+* More fixes and documentation updates.
+
+0.3 (2016-11-29)
+================
+* Rename the ``unittest.cfg`` section to ``[flufl.testing]``.
+* Improve the documentation.
+
+0.2 (2016-11-28)
+================
+* Re-enable Python 3.4.
+* Update README.
+
+0.1 (2016-11-17)
+================
+* Initial release.
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..ebffb35
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,16 @@
+Metadata-Version: 1.1
+Name: flufl.testing
+Version: 0.5
+Summary: A small collection of test tool plugins
+Home-page: https://gitlab.com/warsaw/flufl.testing
+Author: Barry Warsaw
+Author-email: barry at python.org
+License: ASLv2
+Download-URL: https://pypi.python.org/pypi/flufl.testing
+Description: UNKNOWN
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Plugins
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 3
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..069796f
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,177 @@
+===============
+ flufl.testing
+===============
+
+This is a small collection of test helpers that I use in almost all my
+packages. Specifically, plugins for the following test tools are provided:
+
+* nose2_
+* flake8_
+
+Python 3.4 is the minimum supported version.
+
+
+Using test helpers
+==================
+
+You can use each of the plugins independently. For example, if you use flake8
+but you don't use nose2, just enable the flake8 plugin and ignore the rest.
+
+
+flake8 import order plugin
+--------------------------
+
+This flake8_ plugin enables import order checks as are used in the `GNU
+Mailman`_ project. Specifically, it enforces the following rules:
+
+* Non-``from`` imports must precede ``from``-imports.
+* Exactly one line must separate the block of non-``from`` imports from the
+ block of ``from`` imports.
+* Import exactly one module per non-``from`` import line.
+* Lines in the non-``from`` import block are sorted by length first, then
+ alphabetically. Dotted imports count toward both restrictions.
+* Lines in the ``from`` import block are sorted alphabetically.
+* Multiple names can be imported in a ``from`` import line, but those names
+ must be sorted alphabetically.
+* Names imported from the same module in a ``from`` import must appear in the
+ same import statement.
+
+It's so much easier to see an example::
+
+ import copy
+ import socket
+ import logging
+ import smtplib
+
+ from mailman import public
+ from mailman.config import config
+ from mailman.interfaces.mta import IMailTransportAgentDelivery
+ from mailman.mta.connection import Connection
+ from zope.interface import implementer
+
+To enable this plugin [#]_, add the following to your ``tox.ini`` or any other
+`flake8 recognized configuration file`_::
+
+ [flake8]
+ enable-extensions = U4
+
+
+nose2 plugin
+------------
+
+The `nose2`_ plugin enables a few helpful things for folks who use that test
+runner:
+
+* Implements better support for doctests, including supporting layers.
+* Enables sophisticated test pattern matching.
+* Provides test tracing.
+* A *log to stderr* flag that you can check. [#]_
+* Pluggable doctest setup/teardowns.
+
+To enable this plugin, add the following to your ``unittest.cfg`` file, in the
+``[unittest]`` section::
+
+ plugins = flufl.testing.nose
+
+You also need to add this section to ``unittest.cfg``, where *<package>* names
+the top-level package you want to test::
+
+ [flufl.testing]
+ always-on = True
+ package = <package>
+
+Now when you run your tests, you can include one or more ``-P`` options, which
+provide patterns to match your tests against. If given, only tests matching
+the given pattern are run. This is especially helpful if your test suite is
+huge. These patterns can match a test name, class, module, or filename, and
+follow Python's regexp syntax.
+
+The following options are also available by setting configuration variables in
+your ``unittest.cfg`` file, under the ``[flufl.testing]`` section.
+
+
+Doctests
+~~~~~~~~
+
+The plugin also provides some useful features for doctests. If you make a
+directory a package in your source tree (i.e. by adding an `__init__.py`), you
+can optionally also add specify a `nose2 layer`_ to use for the doctest. Bind
+the layer object you want to the ``layer`` attribute in the ``__init__.py``
+and it will be automatically assigned to the doctest's ``layer`` attribute for
+nose2 to find.
+
+Also for doctests, you can specify the ``setUp()`` and ``tearDown()`` methods
+you want by adding the following::
+
+ setup = my.package.namespace.setup
+ teardown = my.package.other.namespace.teardown
+
+The named packages will be imported, with the last path component naming an
+attribute in the module. This attribute should be a function taking a single
+argument, in the style used by the stdlib ``doctest.DocFileTest`` class [#]_.
+
+You can also name a default layer by setting::
+
+ default_layer = my.package.layers.DefaultLayer.
+
+This has the same format as the ``setup`` and ``teardown settings, except that
+it should name a class.
+
+
+Pre-test initialization
+~~~~~~~~~~~~~~~~~~~~~~~
+
+If you need to do anything before the tests starts, such as initialize
+database connections or acquire resources, set this::
+
+ start_run = my.package.initializer
+
+This has the same format as the ``setup`` and ``teardown`` settings, except
+that it takes a single argument which is the plugin instance. You can use
+this plugin instance for example to check if the ``-E`` option was given on
+the command line. This flag sets the ``stderr`` attribute to True on the
+plugin instance.
+
+
+Tracing
+~~~~~~~
+
+If you add this the plugin will also print some additional tracers to stderr
+for ever test as it starts and stops::
+
+ trace = True
+
+
+
+Author
+======
+
+``flufl.testing`` is Copyright (C) 2013-2016 Barry Warsaw <barry at python.org>
+
+Licensed under the terms of the Apache License, Version 2.0.
+
+
+Project details
+===============
+
+ * Project home: https://gitlab.com/warsaw/flufl.testing
+ * Report bugs at: https://gitlab.com/warsaw/flufl.testing/issues
+ * Code hosting: https://gitlab.com/warsaw/flufl.testing.git
+ * Documentation: https://gitlab.com/warsaw/flufl.testing/tree/master
+
+
+Footnotes
+=========
+
+.. [#] Note that flake8 3.1 or newer is required.
+.. [#] It's up to your application to do something with this flag.
+.. [#] This class is undocumented, so use the doctest_ module source to grok
+ the details.
+
+
+.. _flake8: http://flake8.pycqa.org/en/latest/index.html
+.. _`GNU Mailman`: http://www.list.org
+.. _`flake8 recognized configuration file`: http://flake8.pycqa.org/en/latest/user/configuration.html
+.. _nose2: http://nose2.readthedocs.io/en/latest/index.html
+.. _`nose2 layer`: http://nose2.readthedocs.io/en/latest/plugins/layers.html
+.. _doctest: https://docs.python.org/3/library/doctest.html
diff --git a/flufl.testing.egg-info/PKG-INFO b/flufl.testing.egg-info/PKG-INFO
new file mode 100644
index 0000000..ebffb35
--- /dev/null
+++ b/flufl.testing.egg-info/PKG-INFO
@@ -0,0 +1,16 @@
+Metadata-Version: 1.1
+Name: flufl.testing
+Version: 0.5
+Summary: A small collection of test tool plugins
+Home-page: https://gitlab.com/warsaw/flufl.testing
+Author: Barry Warsaw
+Author-email: barry at python.org
+License: ASLv2
+Download-URL: https://pypi.python.org/pypi/flufl.testing
+Description: UNKNOWN
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Environment :: Plugins
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 3
diff --git a/flufl.testing.egg-info/SOURCES.txt b/flufl.testing.egg-info/SOURCES.txt
new file mode 100644
index 0000000..501a889
--- /dev/null
+++ b/flufl.testing.egg-info/SOURCES.txt
@@ -0,0 +1,14 @@
+MANIFEST.in
+NEWS.rst
+README.rst
+setup.py
+setup_helpers.py
+flufl/__init__.py
+flufl.testing.egg-info/PKG-INFO
+flufl.testing.egg-info/SOURCES.txt
+flufl.testing.egg-info/dependency_links.txt
+flufl.testing.egg-info/entry_points.txt
+flufl.testing.egg-info/top_level.txt
+flufl/testing/__init__.py
+flufl/testing/imports.py
+flufl/testing/nose.py
\ No newline at end of file
diff --git a/flufl.testing.egg-info/dependency_links.txt b/flufl.testing.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/flufl.testing.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/flufl.testing.egg-info/entry_points.txt b/flufl.testing.egg-info/entry_points.txt
new file mode 100644
index 0000000..1641056
--- /dev/null
+++ b/flufl.testing.egg-info/entry_points.txt
@@ -0,0 +1,3 @@
+[flake8.extension]
+U4 = flufl.testing.imports:ImportOrder
+
diff --git a/flufl.testing.egg-info/top_level.txt b/flufl.testing.egg-info/top_level.txt
new file mode 100644
index 0000000..9ab661d
--- /dev/null
+++ b/flufl.testing.egg-info/top_level.txt
@@ -0,0 +1 @@
+flufl
diff --git a/flufl/__init__.py b/flufl/__init__.py
new file mode 100644
index 0000000..de40ea7
--- /dev/null
+++ b/flufl/__init__.py
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
diff --git a/flufl/testing/__init__.py b/flufl/testing/__init__.py
new file mode 100644
index 0000000..5a6f84c
--- /dev/null
+++ b/flufl/testing/__init__.py
@@ -0,0 +1 @@
+__version__ = '0.5'
diff --git a/flufl/testing/imports.py b/flufl/testing/imports.py
new file mode 100644
index 0000000..88e19e3
--- /dev/null
+++ b/flufl/testing/imports.py
@@ -0,0 +1,136 @@
+# Copyright (C) 2016 Barry A. Warsaw
+#
+# 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 ast import NodeVisitor
+from collections import namedtuple
+from enum import Enum
+
+
+class ImportType(Enum):
+ non_from = 0
+ from_import = 1
+
+
+ImportRecord = namedtuple('ImportRecord', 'itype lineno colno, module, names')
+
+
+NONFROM_FOLLOWS_FROM = 'U401 Non-from import follows from-import'
+NONFROM_MULTIPLE_NAMES = 'U402 Multiple names on non-from import'
+NONFROM_SHORTER_FOLLOWS = 'U403 Shorter non-from import follows longer'
+NONFROM_ALPHA_UNSORTED = (
+ 'U404 Same-length non-from imports not sorted alphabetically')
+NONFROM_EXTRA_BLANK_LINE = (
+ 'U405 Unexpected blank line since last non-from import')
+NONFROM_DOTTED_UNSORTED = (
+ 'U406 Dotted non-from import not sorted alphabetically')
+
+FROMIMPORT_MISSING_BLANK_LINE = (
+ 'U411 Expected one blank line since last non-from import')
+FROMIMPORT_ALPHA_UNSORTED = 'U412 from-import not sorted alphabetically'
+FROMIMPORT_MULTIPLE = 'U413 Multiple from-imports of same module'
+FROMIMPORT_NAMES_UNSORTED = (
+ 'U414 from-imported names are not sorted alphabetically')
+
+
+class ImportVisitor(NodeVisitor):
+ def __init__(self):
+ self.imports = []
+
+ def visit_Import(self, node):
+ if node.col_offset != 0:
+ # Ignore nested imports.
+ return
+ names = [alias.name for alias in node.names]
+ self.imports.append(
+ ImportRecord(ImportType.non_from, node.lineno, node.col_offset,
+ None, names))
+
+ def visit_ImportFrom(self, node):
+ if node.col_offset != 0:
+ # Ignore nested imports.
+ return
+ names = [alias.name for alias in node.names]
+ self.imports.append(
+ ImportRecord(ImportType.from_import, node.lineno, node.col_offset,
+ node.module, names))
+
+
+class ImportOrder:
+ name = 'flufl-import-order'
+ version = '0.2'
+ off_by_default = True
+
+ def __init__(self, tree, filename):
+ self.tree = tree
+ self.filename = filename
+
+ def _error(self, record, error):
+ code, space, text = error.partition(' ')
+ return (record.lineno, record.colno,
+ '{} {}'.format(code, text), ImportOrder)
+
+ def run(self):
+ visitor = ImportVisitor()
+ visitor.visit(self.tree)
+ last_import = None
+ for record in visitor.imports:
+ if last_import is None:
+ last_import = record
+ continue
+ if record.itype is ImportType.non_from:
+ if len(record.names) != 1:
+ yield self._error(record, NONFROM_MULTIPLE_NAMES)
+ if last_import.itype is ImportType.from_import:
+ yield self._error(record, NONFROM_FOLLOWS_FROM)
+ # Shorter imports should always precede longer import *except*
+ # when they are dotted imports and everything but the last
+ # path component are the same. In that case, they should be
+ # sorted alphabetically.
+ last_name = last_import.names[0]
+ this_name = record.names[0]
+ if '.' in last_name and '.' in this_name:
+ last_parts = last_name.split('.')
+ this_parts = this_name.split('.')
+ if (last_parts[:-1] == this_parts[:-1] and
+ last_parts[-1] > this_parts[-1]):
+ yield self._error(record, NONFROM_DOTTED_UNSORTED)
+ elif len(last_name) > len(this_name):
+ yield self._error(record, NONFROM_SHORTER_FOLLOWS)
+ # It's also possible that the imports are the same length, in
+ # which case they must be sorted alphabetically.
+ if (len(last_import.names[0]) == len(record.names[0]) and
+ last_import.names[0] > record.names[0]):
+ yield self._error(record, NONFROM_ALPHA_UNSORTED)
+ if last_import.lineno + 1 != record.lineno:
+ yield self._error(record, NONFROM_DOTTED_UNSORTED)
+ else:
+ assert record.itype is ImportType.from_import
+ if (last_import.itype is ImportType.non_from and
+ record.lineno != last_import.lineno + 2):
+ yield self._error(record, FROMIMPORT_MISSING_BLANK_LINE)
+ if last_import.itype is ImportType.non_from:
+ last_import = record
+ continue
+ if last_import.module > record.module:
+ yield self._error(record, FROMIMPORT_ALPHA_UNSORTED)
+ # All imports from the same module should show up in the same
+ # multiline import.
+ if last_import.module == record.module:
+ yield self._error(record, FROMIMPORT_MULTIPLE)
+ # Check the sort order of the imported names.
+ if sorted(record.names) != record.names:
+ yield self._error(record, FROMIMPORT_NAMES_UNSORTED)
+ # How to check for no blank lines between from imports?
+ # Update the last import.
+ last_import = record
diff --git a/flufl/testing/nose.py b/flufl/testing/nose.py
new file mode 100644
index 0000000..dc45354
--- /dev/null
+++ b/flufl/testing/nose.py
@@ -0,0 +1,132 @@
+# Copyright (C) 2013-2016 Barry A. Warsaw
+#
+# 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.
+
+"""nose2 test infrastructure."""
+
+import os
+import re
+import sys
+import doctest
+import importlib
+
+from nose2.events import Plugin
+
+
+DOT = '.'
+FLAGS = doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE | doctest.REPORT_NDIFF
+
+
+def as_object(path):
+ if path is None:
+ return None
+ # mod.ule.object -> import(mod.ule); mod.ule.object
+ module_name, dot, object_name = path.rpartition('.')
+ if dot != '.' or len(object_name) == 0:
+ return None
+ module = importlib.import_module(module_name)
+ return getattr(module, object_name, None)
+
+
+class NosePlugin(Plugin):
+ configSection = 'flufl.testing'
+
+ def __init__(self):
+ super().__init__()
+ self.patterns = []
+ self.stderr = False
+ def set_stderr(ignore): # noqa: E306
+ self.stderr = True
+ self.addArgument(self.patterns, 'P', 'pattern',
+ 'Add a test matching pattern')
+ self.addFlag(set_stderr, 'E', 'stderr',
+ 'Enable stderr logging to sub-runners')
+ # Get the topdir out of the plugin configuration file.
+ self.package = self.config.as_str('package')
+ if self.package is None:
+ raise RuntimeError('flufl.nose2 plugin missing "package" setting')
+
+ def startTestRun(self, event):
+ callback = as_object(self.config.get('start_run'))
+ if callback is not None:
+ callback(self)
+
+ def getTestCaseNames(self, event):
+ if len(self.patterns) == 0:
+ # No filter patterns, so everything should be tested.
+ return
+ # Does the pattern match the fully qualified class name?
+ for pattern in self.patterns:
+ full_class_name = '{}.{}'.format(
+ event.testCase.__module__, event.testCase.__name__)
+ if re.search(pattern, full_class_name):
+ # Don't suppress this test class.
+ return
+ names = filter(event.isTestMethod, dir(event.testCase))
+ for name in names:
+ full_test_name = '{}.{}.{}'.format(
+ event.testCase.__module__,
+ event.testCase.__name__,
+ name)
+ for pattern in self.patterns:
+ if re.search(pattern, full_test_name):
+ break
+ else:
+ event.excludedNames.append(name)
+
+ def handleFile(self, event):
+ package = importlib.import_module(self.package)
+ path = event.path[len(os.path.dirname(package.__file__))+1:]
+ if len(self.patterns) > 0:
+ for pattern in self.patterns:
+ if re.search(pattern, path):
+ break
+ else:
+ # Skip this doctest.
+ return
+ base, ext = os.path.splitext(path)
+ if ext != '.rst':
+ return
+ # Look to see if the package defines a test layer, otherwise use the
+ # default layer. First turn the file system path into a dotted Python
+ # module path.
+ parent = os.path.dirname(path)
+ dotted = '{}.{}'.format(
+ self.package, DOT.join(parent.split(os.path.sep)))
+ layer = None
+ default_layer = as_object(self.config.get('default_layer'))
+ try:
+ module = importlib.import_module(dotted)
+ except ImportError:
+ layer = default_layer
+ else:
+ layer = getattr(module, 'layer', default_layer)
+ setup = as_object(self.config.get('setup'))
+ teardown = as_object(self.config.get('teardown'))
+ test = doctest.DocFileTest(
+ path, package=self.package,
+ optionflags=FLAGS,
+ setUp=setup,
+ tearDown=teardown)
+ test.layer = layer
+ # Suppress the extra "Doctest: ..." line.
+ test.shortDescription = lambda: None
+ event.extraTests.append(test)
+
+ def startTest(self, event):
+ if self.config.as_bool('trace', False):
+ print('vvvvv', event.test, file=sys.stderr)
+
+ def stopTest(self, event):
+ if self.config.as_bool('trace', False):
+ print('^^^^^', event.test, file=sys.stderr)
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..861a9f5
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,5 @@
+[egg_info]
+tag_build =
+tag_date = 0
+tag_svn_revision = 0
+
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..755e299
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2016 Barry A. Warsaw
+#
+# 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 setup_helpers import get_version, require_python
+from setuptools import setup, find_packages
+
+
+require_python(0x030400f0)
+__version__ = get_version('flufl/testing/__init__.py')
+
+
+setup(
+ name='flufl.testing',
+ version=__version__,
+ packages=find_packages(),
+ include_package_data=True,
+ maintainer='Barry Warsaw',
+ maintainer_email='barry at python.org',
+ description='A small collection of test tool plugins',
+ license='ASLv2',
+ url='https://gitlab.com/warsaw/flufl.testing',
+ download_url='https://pypi.python.org/pypi/flufl.testing',
+ entry_points={
+ 'flake8.extension': ['U4 = flufl.testing.imports:ImportOrder'],
+ },
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Plugins',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: Apache Software License',
+ 'Programming Language :: Python :: 3',
+ ]
+ )
diff --git a/setup_helpers.py b/setup_helpers.py
new file mode 100644
index 0000000..11327e3
--- /dev/null
+++ b/setup_helpers.py
@@ -0,0 +1,138 @@
+# Copyright (C) 2016 Barry A. Warsaw
+#
+# 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.
+
+"""setup.py helper functions."""
+
+import os
+import re
+import sys
+
+
+DEFAULT_VERSION_RE = re.compile(
+ r'(?P<version>\d+\.\d+(?:\.\d+)?(?:(?:a|b|rc)\d+)?)')
+EMPTYSTRING = ''
+
+__version__ = '2.3'
+
+
+def require_python(minimum):
+ """Require at least a minimum Python version.
+
+ The version number is expressed in terms of `sys.hexversion`. E.g. to
+ require a minimum of Python 2.6, use::
+
+ >>> require_python(0x206000f0)
+
+ :param minimum: Minimum Python version supported.
+ :type minimum: integer
+ """
+ if sys.hexversion < minimum:
+ hversion = hex(minimum)[2:]
+ if len(hversion) % 2 != 0:
+ hversion = '0' + hversion
+ split = list(hversion)
+ parts = []
+ while split:
+ parts.append(int(''.join((split.pop(0), split.pop(0))), 16))
+ major, minor, micro, release = parts
+ if release == 0xf0:
+ print('Python {0}.{1}.{2} or better is required'.format(
+ major, minor, micro))
+ else:
+ print('Python {0}.{1}.{2} ({3}) or better is required'.format(
+ major, minor, micro, hex(release)[2:]))
+ sys.exit(1)
+
+
+def get_version(filename, pattern=None):
+ """Extract the __version__ from a file without importing it.
+
+ While you could get the __version__ by importing the module, the very act
+ of importing can cause unintended consequences. For example, Distribute's
+ automatic 2to3 support will break. Instead, this searches the file for a
+ line that starts with __version__, and extract the version number by
+ regular expression matching.
+
+ By default, two or three dot-separated digits are recognized, but by
+ passing a pattern parameter, you can recognize just about anything. Use
+ the `version` group name to specify the match group.
+
+ :param filename: The name of the file to search.
+ :type filename: string
+ :param pattern: Optional alternative regular expression pattern to use.
+ :type pattern: string
+ :return: The version that was extracted.
+ :rtype: string
+ """
+ if pattern is None:
+ cre = DEFAULT_VERSION_RE
+ else:
+ cre = re.compile(pattern)
+ with open(filename) as fp:
+ for line in fp:
+ if line.startswith('__version__'):
+ mo = cre.search(line)
+ assert mo, 'No valid __version__ string found'
+ return mo.group('version')
+ raise AssertionError('No __version__ assignment found')
+
+
+def find_doctests(start='.', extension='.rst'):
+ """Find separate-file doctests in the package.
+
+ This is useful for Distribute's automatic 2to3 conversion support. The
+ `setup()` keyword argument `convert_2to3_doctests` requires file names,
+ which may be difficult to track automatically as you add new doctests.
+
+ :param start: Directory to start searching in (default is cwd)
+ :type start: string
+ :param extension: Doctest file extension (default is .txt)
+ :type extension: string
+ :return: The doctest files found.
+ :rtype: list
+ """
+ doctests = []
+ for dirpath, dirnames, filenames in os.walk(start):
+ doctests.extend(os.path.join(dirpath, filename)
+ for filename in filenames
+ if filename.endswith(extension))
+ return doctests
+
+
+def long_description(*filenames):
+ """Provide a long description."""
+ res = ['']
+ for filename in filenames:
+ with open(filename) as fp:
+ for line in fp:
+ res.append(' ' + line)
+ res.append('')
+ res.append('\n')
+ return EMPTYSTRING.join(res)
+
+
+def description(filename):
+ """Provide a short description."""
+ # This ends up in the Summary header for PKG-INFO and it should be a
+ # one-liner. It will get rendered on the package page just below the
+ # package version header but above the long_description, which ironically
+ # gets stuff into the Description header. It should not include reST, so
+ # pick out the first single line after the double header.
+ with open(filename) as fp:
+ for lineno, line in enumerate(fp):
+ if lineno < 3:
+ continue
+ line = line.strip()
+ if len(line) > 0:
+ return line
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/flufl.testing.git
More information about the Python-modules-commits
mailing list