[Python-modules-commits] [python-parse-type] 01/02: import parse_type-0.3.4.tar.gz

Brian May bam at moszumanska.debian.org
Tue May 16 09:22:34 UTC 2017


This is an automated email from the git hooks/post-receive script.

bam pushed a commit to branch debian/master
in repository python-parse-type.

commit cfb172fe8fa6a4c28b27a0d42846f3c52da5c1fa
Author: Brian May <brian at linuxpenguins.xyz>
Date:   Tue May 16 19:10:13 2017 +1000

    import parse_type-0.3.4.tar.gz
---
 .bumpversion.cfg                  |    6 +
 .coveragerc                       |   41 ++
 .editorconfig                     |   26 +
 .gitignore                        |   40 ++
 .travis.yml                       |   13 +
 LICENSE                           |   27 +
 MANIFEST.in                       |   17 +
 README.rst                        |  273 +++++++++
 _paver_ext/__init__.py            |    1 +
 _paver_ext/paver_consume_args.py  |   46 ++
 _paver_ext/paver_patch.py         |  105 ++++
 _paver_ext/paver_require.py       |   36 ++
 _paver_ext/pip_download.py        |   93 ++++
 _paver_ext/python_bundle.py       |  107 ++++
 _paver_ext/python_checker.py      |   91 +++
 _paver_ext/python_requirements.py |   52 ++
 ez_setup.py                       |  370 ++++++++++++
 parse_type/__init__.py            |   28 +
 parse_type/builder.py             |  307 ++++++++++
 parse_type/cardinality.py         |  210 +++++++
 parse_type/cardinality_field.py   |  179 ++++++
 parse_type/cfparse.py             |   84 +++
 parse_type/parse.py               | 1114 +++++++++++++++++++++++++++++++++++++
 parse_type/parse_util.py          |  175 ++++++
 pavement.py                       |  193 +++++++
 pytest.ini                        |   22 +
 requirements/all.txt              |   12 +
 requirements/basic.txt            |   13 +
 requirements/develop.txt          |   18 +
 requirements/docs.txt             |    6 +
 setup.cfg                         |   22 +
 setup.py                          |  120 ++++
 tests/__init__.py                 |    0
 tests/parse_type_test.py          |  137 +++++
 tests/test_builder.py             |  551 ++++++++++++++++++
 tests/test_cardinality.py         |  555 ++++++++++++++++++
 tests/test_cardinality_field.py   |  328 +++++++++++
 tests/test_cardinality_field0.py  |  148 +++++
 tests/test_cfparse.py             |  186 +++++++
 tests/test_parse_decorator.py     |  144 +++++
 tests/test_parse_type_parse.py    |  757 +++++++++++++++++++++++++
 tests/test_parse_util.py          |  392 +++++++++++++
 tox.ini                           |  125 +++++
 43 files changed, 7170 insertions(+)

diff --git a/.bumpversion.cfg b/.bumpversion.cfg
new file mode 100644
index 0000000..241e96a
--- /dev/null
+++ b/.bumpversion.cfg
@@ -0,0 +1,6 @@
+[bumpversion]
+current_version = 0.3.4
+files = setup.py parse_type/__init__.py .bumpversion.cfg
+commit = True
+tag = True
+
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..f6b08c0
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,41 @@
+# =========================================================================
+# COVERAGE CONFIGURATION FILE: .coveragerc
+# =========================================================================
+# LANGUAGE: Python
+# SEE ALSO:
+#  * http://nedbatchelder.com/code/coverage/
+#  * http://nedbatchelder.com/code/coverage/config.html
+# =========================================================================
+
+[run]
+data_file = .coverage
+source   = parse_type
+branch   = True
+parallel = False
+omit = mock.py, ez_setup.py, distribute.py
+
+
+[report]
+ignore_errors = False
+show_missing  = True
+exclude_lines =
+    # pragma: no cover
+    # pragma: recursive coverage
+    def __repr__
+
+    raise AssertionError
+    raise NotImplementedError
+
+    if 0:
+    if False:
+    if __name__ == .__main__.:
+
+[html]
+directory = build/coverage.html
+title = Coverage Report: parse_type
+
+[xml]
+outfile = build/coverage.xml
+
+
+
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..d262338
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,26 @@
+# =============================================================================
+# EDITOR CONFIGURATION: http://editorconfig.org
+# =============================================================================
+
+root = true
+
+# -- DEFAULT: Unix-style newlines with a newline ending every file.
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{py,rst,ini,txt}]
+indent_style = space
+indent_size = 4
+
+[*.feature]
+indent_style = space
+indent_size = 2
+
+[**/makefile]
+indent_style = tab
+
+[*.{cmd,bat}]
+end_of_line = crlf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cea1620
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,40 @@
+*.py[cod]
+
+# C extensions
+*.so
+
+# Packages
+MANIFEST
+*.egg
+*.egg-info
+dist
+build
+downloads
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+__pycache__
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.cache/
+.idea/
+.tox/
+.coverage
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..c40df0f
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: python
+python:
+  - 2.6
+  - 2.7
+  - 3.2
+  - 3.3
+  - pypy
+
+install:
+  - if [[ $TRAVIS_PYTHON_VERSION == '2.6' ]]; then pip install --use-mirrors unittest2; fi
+  - python setup.py -q install
+script:
+  - py.test tests
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..420adb4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013, jenisys
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+  Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+  Redistributions in binary form must reproduce the above copyright notice, this
+  list of conditions and the following disclaimer in the documentation and/or
+  other materials provided with the distribution.
+
+  Neither the name of the {organization} nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..3967cff
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,17 @@
+include README.rst
+include LICENSE
+include .coveragerc
+include .editorconfig
+include *.py
+include *.rst
+include *.txt
+include *.ini
+include *.cfg
+
+recursive-include bin           *.sh *.py
+recursive-include docs          *.rst *.txt *.py
+recursive-include requirements  *.txt
+recursive-include tests         *.py
+
+prune .tox
+
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..7a90413
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,273 @@
+===============================================================================
+parse_type
+===============================================================================
+
+.. image:: https://pypip.in/v/parse_type/badge.png
+    :target: https://crate.io/packages/parse_type/
+    :alt: Latest PyPI version
+
+.. image:: https://pypip.in/d/parse_type/badge.png
+    :target: https://crate.io/packages/parse_type/
+    :alt: Number of PyPI downloads
+
+.. image:: https://travis-ci.org/jenisys/parse_type.png?branch=master
+    :target: https://travis-ci.org/jenisys/parse_type
+    :alt: Travis CI Build Status
+
+
+`parse_type`_ extends the `parse`_ module (opposite of `string.format()`_)
+with the following features:
+
+    * build type converters for common use cases (enum/mapping, choice)
+    * build a type converter with a cardinality constraint (0..1, 0..*, 1..*)
+      from the type converter with cardinality=1.
+    * compose a type converter from other type converters
+    * an extended parser that supports the CardinalityField naming schema
+      and creates missing type variants (0..1, 0..*, 1..*) from the
+      primary type converter
+
+.. _parse_type: http://pypi.python.org/pypi/parse_type
+.. _parse:      http://pypi.python.org/pypi/parse
+.. _`string.format()`: http://docs.python.org/library/string.html#format-string-syntax
+
+
+Definitions
+-------------------------------------------------------------------------------
+
+*type converter*
+    A type converter function that converts a textual representation
+    of a value type into instance of this value type.
+    In addition, a type converter function is often annotated with attributes
+    that allows the `parse`_ module to use it in a generic way.
+    A type converter is also called a *parse_type* (a definition used here).
+
+*cardinality field*
+    A naming convention for related types that differ in cardinality.
+    A cardinality field is a type name suffix in the format of a field.
+    It allows parse format expression, ala::
+
+        "{person:Person}"     #< Cardinality: 1    (one; the normal case)
+        "{person:Person?}"    #< Cardinality: 0..1 (zero or one  = optional)
+        "{persons:Person*}"   #< Cardinality: 0..* (zero or more = many0)
+        "{persons:Person+}"   #< Cardinality: 1..* (one  or more = many)
+
+    This naming convention mimics the relationship descriptions in UML diagrams.
+
+
+Basic Example
+-------------------------------------------------------------------------------
+
+Define an own type converter for numbers (integers):
+
+.. code-block:: python
+
+    # -- USE CASE:
+    def parse_number(text):
+        return int(text)
+    parse_number.pattern = r"\d+"  # -- REGULAR EXPRESSION pattern for type.
+
+This is equivalent to:
+
+.. code-block:: python
+
+    import parse
+
+    @parse.with_pattern(r"\d+")
+    def parse_number(text):
+         return int(text)
+    assert hasattr(parse_number, "pattern")
+    assert parse_number.pattern == r"\d+"
+
+
+.. code-block:: python
+
+    # -- USE CASE: Use the type converter with the parse module.
+    schema = "Hello {number:Number}"
+    parser = parse.Parser(schema, dict(Number=parse_number))
+    result = parser.parse("Hello 42")
+    assert result is not None, "REQUIRE: text matches the schema."
+    assert result["number"] == 42
+
+    result = parser.parse("Hello XXX")
+    assert result is None, "MISMATCH: text does not match the schema."
+
+.. hint::
+
+    The described functionality above is standard functionality
+    of the `parse`_ module. It serves as introduction for the remaining cases.
+
+
+Cardinality
+-------------------------------------------------------------------------------
+
+Create an type converter for "ManyNumbers" (List, separated with commas)
+with cardinality "1..* = 1+" (many) from the type converter for a "Number".
+
+.. code-block:: python
+
+    # -- USE CASE: Create new type converter with a cardinality constraint.
+    # CARDINALITY: many := one or more (1..*)
+    from parse import Parser
+    from parse_type import TypeBuilder
+    parse_numbers = TypeBuilder.with_many(parse_number, listsep=",")
+
+    schema = "List: {numbers:ManyNumbers}"
+    parser = Parser(schema, dict(ManyNumbers=parse_numbers))
+    result = parser.parse("List: 1, 2, 3")
+    assert result["numbers"] == [1, 2, 3]
+
+
+Create an type converter for an "OptionalNumbers" with cardinality "0..1 = ?"
+(optional) from the type converter for a "Number".
+
+.. code-block:: python
+
+    # -- USE CASE: Create new type converter with cardinality constraint.
+    # CARDINALITY: optional := zero or one (0..1)
+    from parse import Parser
+    from parse_type import TypeBuilder
+
+    parse_optional_number = TypeBuilder.with_optional(parse_number)
+    schema = "Optional: {number:OptionalNumber}"
+    parser = Parser(schema, dict(OptionalNumber=parse_optional_number))
+    result = parser.parse("Optional: 42")
+    assert result["number"] == 42
+    result = parser.parse("Optional: ")
+    assert result["number"] == None
+
+
+Enumeration (Name-to-Value Mapping)
+-------------------------------------------------------------------------------
+
+Create an type converter for an "Enumeration" from the description of
+the mapping as dictionary.
+
+.. code-block:: python
+
+    # -- USE CASE: Create a type converter for an enumeration.
+    from parse import Parser
+    from parse_type import TypeBuilder
+
+    parse_enum_yesno = TypeBuilder.make_enum({"yes": True, "no": False})
+    parser = Parser("Answer: {answer:YesNo}", dict(YesNo=parse_enum_yesno))
+    result = parser.parse("Answer: yes")
+    assert result["answer"] == True
+
+
+Create an type converter for an "Enumeration" from the description of
+the mapping as an enumeration class (`Python 3.4 enum`_ or the `enum34`_
+backport; see also: `PEP-0435`_).
+
+.. code-block:: python
+
+    # -- USE CASE: Create a type converter for enum34 enumeration class.
+    # NOTE: Use Python 3.4 or enum34 backport.
+    from parse import Parser
+    from parse_type import TypeBuilder
+    from enum import Enum
+
+    class Color(Enum):
+        red   = 1
+        green = 2
+        blue  = 3
+
+    parse_enum_color = TypeBuilder.make_enum(Color)
+    parser = Parser("Select: {color:Color}", dict(Color=parse_enum_color))
+    result = parser.parse("Select: red")
+    assert result["color"] is Color.red
+
+.. _`Python 3.4 enum`: http://docs.python.org/3.4/library/enum.html#module-enum
+.. _enum34:   http://pypi.python.org/pypi/enum34
+.. _PEP-0435: http://www.python.org/dev/peps/pep-0435
+
+
+Choice (Name Enumeration)
+-------------------------------------------------------------------------------
+
+A Choice data type allows to select one of several strings.
+
+Create an type converter for an "Choice" list, a list of unique names
+(as string).
+
+.. code-block:: python
+
+    from parse import Parser
+    from parse_type import TypeBuilder
+
+    parse_choice_yesno = TypeBuilder.make_choice(["yes", "no"])
+    schema = "Answer: {answer:ChoiceYesNo}"
+    parser = Parser(schema, dict(ChoiceYesNo=parse_choice_yesno))
+    result = parser.parse("Answer: yes")
+    assert result["answer"] == "yes"
+
+
+Variant (Type Alternatives)
+-------------------------------------------------------------------------------
+
+Sometimes you need a type converter that can accept text for multiple
+type converter alternatives. This is normally called a "variant" (or: union).
+
+Create an type converter for an "Variant" type that accepts:
+
+  * Numbers (positive numbers, as integer)
+  * Color enum values (by name)
+
+.. code-block:: python
+
+    from parse import Parser, with_pattern
+    from parse_type import TypeBuilder
+    from enum import Enum
+
+    class Color(Enum):
+        red   = 1
+        green = 2
+        blue  = 3
+
+    @with_pattern(r"\d+")
+    def parse_number(text):
+        return int(text)
+
+    # -- MAKE VARIANT: Alternatives of different type converters.
+    parse_color = TypeBuilder.make_enum(Color)
+    parse_variant = TypeBuilder.make_variant([parse_number, parse_color])
+    schema = "Variant: {variant:Number_or_Color}"
+    parser = Parser(schema, dict(Number_or_Color=parse_variant))
+
+    # -- TEST VARIANT: With number, color and mismatch.
+    result = parser.parse("Variant: 42")
+    assert result["variant"] == 42
+    result = parser.parse("Variant: blue")
+    assert result["variant"] is Color.blue
+    result = parser.parse("Variant: __MISMATCH__")
+    assert not result
+
+
+
+Extended Parser with CardinalityField support
+-------------------------------------------------------------------------------
+
+The parser extends the ``parse.Parser`` and adds the following functionality:
+
+   * supports the CardinalityField naming scheme
+   * automatically creates missing type variants for types with
+     a CardinalityField by using the primary type converter for cardinality=1
+   * extends the provide type converter dictionary with new type variants.
+
+Example:
+
+.. code-block:: python
+
+    # -- USE CASE: Parser with CardinalityField support.
+    # NOTE: Automatically adds missing type variants with CardinalityField part.
+    # USE:  parse_number() type converter from above.
+    from parse_type.cfparse import Parser
+
+    # -- PREPARE: parser, adds missing type variant for cardinality 1..* (many)
+    type_dict = dict(Number=parse_number)
+    schema = "List: {numbers:Number+}"
+    parser = Parser(schema, type_dict)
+    assert "Number+" in type_dict, "Created missing type variant based on: Number"
+
+    # -- USE: parser.
+    result = parser.parse("List: 1, 2, 3")
+    assert result["numbers"] == [1, 2, 3]
diff --git a/_paver_ext/__init__.py b/_paver_ext/__init__.py
new file mode 100644
index 0000000..7c68785
--- /dev/null
+++ b/_paver_ext/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
\ No newline at end of file
diff --git a/_paver_ext/paver_consume_args.py b/_paver_ext/paver_consume_args.py
new file mode 100644
index 0000000..45b6b77
--- /dev/null
+++ b/_paver_ext/paver_consume_args.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+"""
+
+"""
+
+class Cmdline(object):
+    """
+    Simplify to process consuming args of a task w/o specifying the command line.
+    Splits up into options and args.
+
+    SPECIFICATION:
+      * An option starts with a dash ('-') or dashdash ('--').
+      * If an option has a value long options should be used, like:
+            --my-long-option=value
+
+    EXAMPLE:
+        @task
+        @consume_args
+        def hello(args):
+            cmdline = Cmdline.consume(args, default_args=options.default_args)
+            ...
+    """
+    def __init__(self, args=None, options=None):
+        self.args = args or []
+        self.options = options or []
+
+    def join_args(self, separator=" "):
+        return separator.join(self.args)
+
+    def join_options(self, separator=" "):
+        return separator.join(self.options)
+
+    @classmethod
+    def consume(cls, args, default_args=None, default_options=None):
+        args_ = []
+        options_ = []
+        for arg in args:
+            if arg.startswith("-"):
+                options_.append(arg)
+            else:
+                args_.append(arg)
+        if not args_:
+            args_ = default_args
+        if not options_:
+            options_ = default_options
+        return cls(args_, options_)
diff --git a/_paver_ext/paver_patch.py b/_paver_ext/paver_patch.py
new file mode 100644
index 0000000..5bf94f0
--- /dev/null
+++ b/_paver_ext/paver_patch.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+# ============================================================================
+# PAVER EXTENSION: paver_patch
+# ============================================================================
+"""
+Provide some patch functionality to decouple "pavement.py" from
+concrete installed paver version.
+
+"""
+
+from paver.easy import info
+
+# -----------------------------------------------------------------------------
+# PATCH: PMETHODS, ala path.xxx_p()
+# -----------------------------------------------------------------------------
+_PATH_PMETHOD_NAMES = [
+    'makedirs_p',
+    'mkdir_p',
+    'remove_p',
+    'removedirs_p',
+    'rmdir_p',
+    'rmtree_p',
+    'unlink_p',
+]
+
+def ensure_path_with_pmethods(path_class):
+    """
+    Adds "..._p()" methods to path class.
+    Earlier versions did not have those.
+
+    EXAMPLE:
+        # -- file:pavement.py
+        from paver.easy import *
+        sys.path.insert(0, ".")
+
+        from paver_ext import paver_patch
+        paver_patch.ensure_path_with_pmethods(path)
+
+    :since: paver.path >= 1.2
+    """
+    for pmethod_name in _PATH_PMETHOD_NAMES:
+        assert pmethod_name.endswith("_p")
+        method_name = pmethod_name[:-2]
+        pfunc = getattr(path_class, pmethod_name, None)
+        if not pfunc:
+            # -- PATCH PATH-CLASS: Set path_class.pmethod = method
+            # NOTE: Old method had sanity check that is now provided by pmethod.
+            info("PATCH: path.%s = %s" % (pmethod_name, method_name))
+            func = getattr(path_class, method_name)
+            setattr(path_class, pmethod_name, func)
+
+# -----------------------------------------------------------------------------
+# PATCH: SMETHODS (silent methods), ala path.xxx_s()
+# -----------------------------------------------------------------------------
+_PATH_SMETHOD_CREATE_NAMES = [
+    'makedirs_s',
+    'mkdir_s',
+]
+_PATH_SMETHOD_DESTROY_NAMES = [
+    'remove_s',
+    'unlink_s',
+    'removedirs_s',
+    'rmdir_s',
+    'rmtree_s',
+]
+
+def ensure_path_with_smethods(path_class):
+    """
+    Adds/patches silent path methods to path class, ala "..._s()".
+    They are executed (and printed) only if something needs to be done.
+
+    EXAMPLE:
+        # -- file:pavement.py
+        from paver.easy import *
+        sys.path.insert(0, ".")
+
+        from paver_ext import paver_patch
+        paver_patch.ensure_path_with_smethods(path)
+
+    .. note::
+        This is similar to Paver < 1.2 behaviour.
+    """
+    def _make_screate_func(func):
+        def wrapped(self, *args, **kwargs):
+            if not self.exists():
+                return func(self, *args, **kwargs)
+        return wrapped
+
+    def _make_sdestroy_func(func):
+        def wrapped(self, *args, **kwargs):
+            if self.exists():
+                return func(self, *args, **kwargs)
+        return wrapped
+
+    smethod_names = _PATH_SMETHOD_CREATE_NAMES + _PATH_SMETHOD_DESTROY_NAMES
+    for smethod_name in smethod_names:
+        assert smethod_name.endswith("_s")
+        pmethod_name = "%s_p" % smethod_name[:-2]
+        func = getattr(path_class, pmethod_name)
+        if smethod_name in _PATH_SMETHOD_CREATE_NAMES:
+            screate_func = _make_screate_func(func)
+            setattr(path_class, smethod_name, screate_func)
+        else:
+            sdestroy_func = _make_sdestroy_func(func)
+            setattr(path_class, smethod_name, sdestroy_func)
diff --git a/_paver_ext/paver_require.py b/_paver_ext/paver_require.py
new file mode 100644
index 0000000..d549af0
--- /dev/null
+++ b/_paver_ext/paver_require.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+# ============================================================================
+# PAVER EXTENSION: paver_require
+# ============================================================================
+"""
+Ensure that "pavement.py" are run with the expected paver version.
+
+EXAMPLE:
+
+    # -- file:pavement.py
+    from paver.easy import *
+    sys.path.insert(0, ".")
+    from paver_ext import paver_require
+
+    paver_require.min_version("1.2")
+"""
+
+from paver.easy import error
+from paver.release import VERSION as paver_version
+import sys
+
+def min_version(min_version):
+    """
+    Utility function to ensure that a minimal paver version is used.
+    Aborts paver execution if expectation is not met.
+
+    :param min_version: Minimum paver version that is required (as string).
+    """
+    if not (paver_version >= min_version):
+        error("REQUIRE: paver >= %s (actually: %s)" % (min_version, paver_version))
+        error("ABORT: Here.")
+        sys.exit(1)
+
+def assert_min_version(min_version):
+    assert paver_version >= min_version, \
+           "REQUIRE: paver >= %s (actually: %s)" % (min_version, paver_version)
diff --git a/_paver_ext/pip_download.py b/_paver_ext/pip_download.py
new file mode 100644
index 0000000..0f34283
--- /dev/null
+++ b/_paver_ext/pip_download.py
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+# ============================================================================
+# PAVER EXTENSION: Download dependencies with pip via requirements files
+# ============================================================================
+"""
+A paver extension that provides pip related tasks:
+  - download dependent packages
+  - build a local packages index for downloaded packages
+
+EXPECTED OPTIONS STRUCTURE:
+   options.pip
+       .requirements_files     -- List of requirements files to use.
+       .download_dir           -- Directory for downloaded packages.
+
+REQUIRES:
+  * paver  >= 1.2
+  * pip    >= 1.1
+  * bin/make_localpi.py (script)
+
+SEE ALSO:
+  * http://www.blueskyonmars.com/projects/paver/
+  * http://pypi.python.org/pypi/Paver/
+  * http://pypi.python.org/pypi/pip/
+"""
+
+from paver.easy import path, sh, task, call_task, consume_args
+from paver.easy import info, options, BuildFailure
+
+# ----------------------------------------------------------------------------
+# CONSTANTS:
+# ----------------------------------------------------------------------------
+HERE = path(__file__).dirname()
+MAKE_LOCALPI_SCRIPT = HERE.joinpath("..", "bin", "make_localpi.py").normpath()
+
+
+# ----------------------------------------------------------------------------
+# TASKS:
+# ----------------------------------------------------------------------------
+ at task
+ at consume_args
+def download_deps(args):
+    """Download all dependencies (python packages) with pip."""
+    download_dir = options.pip.get("download_dir", "$HOME/.pip/downloads")
+    requirements_files = None
+    if args:
+        info("DOWNLOAD DEPENDENCIES: %s" % ", ".join(args))
+    else:
+        info("DOWNLOAD ALL DEPENDENCIES: %s/" % download_dir)
+        requirements_files = options.pip.requirements_files
+
+    pip_download(download_dir, args=args, requirements_files=requirements_files)
+    call_task("localpi")
+
+
+ at task
+def localpi():
+    """Make local python package index from download_dir contents."""
+    download_dir = path(options.pip.download_dir)
+    if not download_dir.exists():
+        call_task("download_depends")
+
+    require_script(MAKE_LOCALPI_SCRIPT)
+    info("MAKE LOCAL PACKAGE-INDEX: %s/" % download_dir)
+    sh("%s %s" % (MAKE_LOCALPI_SCRIPT, download_dir))
+
+
+# ----------------------------------------------------------------------------
+# UTILS:
+# ----------------------------------------------------------------------------
+def require_script(script_path):
+    script_path = path(script_path)
+    if not script_path.exists():
+        message = "REQUIRE: '%s' => NOT-FOUND" % script_path
+        raise BuildFailure(message)
+
+def pip_download(download_dir, cmdopts="", args=None, requirements_files=None):
+    """Download all dependencies with pip by using requirement files, etc."""
+    requirements = []
+    if args:
+        requirements.extend(args)
+    if requirements_files:
+        requirements.extend([ "-r %s" % f for f in requirements_files ])
+    assert requirements, "No requirements provided."
+
+    # -- NORMAL-CASE:
+    # NOTE: --exists-action option requires pip >= 1.1
+    download_dir = path(download_dir)
+    download_dir.makedirs_p()
+    pip_download_cmd  = "pip install --use-mirrors --exists-action=i"
+    pip_download_cmd += " --download=%s" % download_dir
+    for requirement in requirements:
+        # sh("{pip_download} {cmdopts} {requirement}".format(
+        sh("%s %s %s" % (pip_download_cmd, cmdopts, requirement))
diff --git a/_paver_ext/python_bundle.py b/_paver_ext/python_bundle.py
new file mode 100644
index 0000000..105aeb0
--- /dev/null
+++ b/_paver_ext/python_bundle.py
@@ -0,0 +1,107 @@
+# ============================================================================
+# PAVER EXTENSION (pavement.py)
+# ============================================================================
+# REQUIRES: paver >= 1.0
+# DESCRIPTION:
+#   Provides some tasks to bundle python packages.
+#
+# SEE ALSO:
+#  * http://pypi.python.org/pypi/Paver/
+#  * http://www.blueskyonmars.com/projects/paver/
+# ============================================================================
+
+from paver.easy import path, task, options, debug, info, error
+import os
+
+# ----------------------------------------------------------------------------
+# CONFIGURATION:
+# ----------------------------------------------------------------------------
+#options(
+#    bundle=Bunch(
+#        archive  = "python-bundle.zip",
+#        packages = [ "sphinx", "jinja2" ]
+#    ),
+#)
+
+# ----------------------------------------------------------------------------
+# TASKS:
+# ----------------------------------------------------------------------------
+ at task
+def bundle():
+    """Bundle required Python packages as ZIP archive."""
+    packages = options.bundle.packages
+    builddir = path("build")/"bundle"
+    builddir.makedirs()
+    errors = 0
+    for package in packages:
+        try:
+            info("bundle %s ..." % package)
+            module   = __import__(package)
+            pkgfile  = path(module.__file__)
+            basedir  = pkgfile.dirname()
+            destdir  = builddir/package
+            if destdir.exists():
+                destdir.rmtree()
+            basedir.copytree(destdir)
+        except StandardError, e:
+            info("FAILED %s: %s" % (package, e))
+            errors += 1
+
+    archive = options.bundle.get("archive", "python-bundle.zip")
+    stored_files = make_zip_archive(archive, builddir)
+
+    # -- SUMMARY:
+    message1  = "bundle {pkgno} packages into {archive} "
+    message1 += "(with {errorno} errors, files: {fileno})"
+    message   = message1.format(archive=archive, pkgno=len(packages),
+                    errorno=errors, fileno=stored_files)
+    output  = info
+    if errors:
+        message = "FAILED: {message}".format(message=message)
+        output  = error
+    output(message)
+
+# ----------------------------------------------------------------------------
+# UTILS:
+# ----------------------------------------------------------------------------
+def make_zip_archive(archive_name, basedir, files=None, file_pattern="*"):
+    import zipfile
+    archive_name = path(archive_name).abspath()
+    curdir = os.getcwd()
+    os.chdir(basedir)
+    files_count = 0
+
+    try:
+        # -- STEP: Collect files.
+        files2 = []
+        if files is None:
+            dirs = path(".").listdir(pattern="*")
+            files2.extend(dirs)
+        else:
+            for file_ in files:
+                if "*" in file_:
+                    parts = path(".").glob(pattern=file_)
+                    parts = [ part.normpath()   for part in parts ]
+                    files2.extend(parts)
+                else:
+                    file_ = path(".")/file_
+                    files2.append(file_.normpath())
+        files3 = []
+        for file_ in files2:
+            if file_.isdir():
+                files3.extend(file_.walkfiles(file_pattern))
+            else:
+                files3.append(file_)
+        files = files3
+
+        # -- STEP: Store files in archive.
+        archive = zipfile.ZipFile(archive_name, "w", zipfile.ZIP_DEFLATED)
+        for filename in files:
+            debug("ZIP: Store %s ..." % filename)
+            archive.write(filename)
+        files_count = len(archive.namelist())
+        archive.close()
+    finally:
+        os.chdir(curdir)
+    return files_count
+
diff --git a/_paver_ext/python_checker.py b/_paver_ext/python_checker.py
new file mode 100644
index 0000000..c78fd84
--- /dev/null
+++ b/_paver_ext/python_checker.py
@@ -0,0 +1,91 @@
+# -*- coding: utf-8 -*-
+# ============================================================================
+# PAVER EXTENSION: Tasks for pychecker, pylint, ...
+# ============================================================================
+"""
+A paver extension that provides pip related tasks:
+  - download dependent packages
+  - build a local packages index for downloaded packages
+
+EXPECTED OPTIONS STRUCTURE:
+   options.pychecker
+       .default_args        -- Default args to use (as string).
+   options.pylint
+       .default_args        -- Default args to use (as string).
+
+REQUIRES:
+  * paver  >= 1.0.4
+  * pychecker >= 0.8.18
+  * pylint >= 0.25
+
+SEE ALSO:
+  * http://www.blueskyonmars.com/projects/paver/
+  * http://pypi.python.org/pypi/Paver/
+  * http://pypi.python.org/pypi/pylint/
+  * http://pypi.python.org/pypi/pychecker/
+"""
+
... 6477 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-parse-type.git



More information about the Python-modules-commits mailing list