[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