[Python-modules-commits] [python-sievelib] 01/02: Imported Upstream version 0.8+git20150929.71f2dee
Michael Fladischer
fladi at moszumanska.debian.org
Tue Sep 29 10:44:24 UTC 2015
This is an automated email from the git hooks/post-receive script.
fladi pushed a commit to branch master
in repository python-sievelib.
commit e2c299abedf441438fc41aaabe0b658b45476ae4
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date: Tue Sep 29 12:18:23 2015 +0200
Imported Upstream version 0.8+git20150929.71f2dee
---
.travis.yml | 10 +
COPYING | 13 +
MANIFEST.in | 2 +
README.rst | 154 ++++++++
requirements.txt | 1 +
setup.py | 76 ++++
sievelib/__init__.py | 0
sievelib/commands.py | 736 ++++++++++++++++++++++++++++++++++++
sievelib/digest_md5.py | 77 ++++
sievelib/factory.py | 390 +++++++++++++++++++
sievelib/managesieve.py | 683 +++++++++++++++++++++++++++++++++
sievelib/parser.py | 455 ++++++++++++++++++++++
sievelib/tests/__init__.py | 0
sievelib/tests/files/utf8_sieve.txt | 7 +
sievelib/tests/test_factory.py | 129 +++++++
sievelib/tests/test_parser.py | 691 +++++++++++++++++++++++++++++++++
16 files changed, 3424 insertions(+)
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..8f5e8ca
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: python
+python:
+ - "2.7"
+ - "3.4"
+
+install:
+ - pip install nose --use-mirrors
+ - python setup.py -q install
+
+script: nosetests
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..0027393
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,13 @@
+Copyright (c) 2011-2013, Antoine Nguyen <tonio at ngyn.org>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..9491f80
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+include README.rst
+include COPYING
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..431c0c5
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,154 @@
+sievelib
+========
+
+|travis| |latest-version| |downloads|
+
+Client-side Sieve and Managesieve library written in Python.
+
+* Sieve : An Email Filtering Language
+ (`RFC 5228 <http://tools.ietf.org/html/rfc5228>`_)
+* ManageSieve : A Protocol for Remotely Managing Sieve Scripts
+ (`RFC 5804 <http://tools.ietf.org/html/rfc5804>`_)
+
+Installation
+------------
+
+To install ``sievelib`` from PyPI::
+
+ pip install sievelib
+
+To install sievelib from git::
+
+ git clone git at github.com:tonioo/sievelib.git
+ cd sievelib
+ python ./setup.py install
+
+Sieve tools
+-----------
+
+What is supported
+^^^^^^^^^^^^^^^^^
+
+Currently, the provided parser supports most of the functionalities
+described in the RFC. The only exception concerns section
+*2.4.2.4. Encoding Characters Using "encoded-character"* which is not
+supported.
+
+The following extensions are also supported:
+
+* Date and Index (`RFC 5260 <https://tools.ietf.org/html/rfc5260>`_)
+* Vacation (`RFC 5230 <http://tools.ietf.org/html/rfc5230>`_)
+
+Extending the parser
+^^^^^^^^^^^^^^^^^^^^
+
+It is possible to extend the parser by adding new supported
+commands. For example::
+
+ import sievelib
+
+ def MyCommand(sievelib.commands.ActionCommand):
+ args_definition = [
+ {"name": "testtag",
+ "type": ["tag"],
+ "write_tag": True,
+ "values": [":testtag"],
+ "extra_arg": {"type": "number",
+ "required": False},
+ "required": False},
+ {"name": "recipients",
+ "type": ["string", "stringlist"],
+ "required": True}
+ ]
+
+ sievelib.commands.add_commands(MyCommand)
+
+Basic usage
+^^^^^^^^^^^
+
+The parser can either be used from the command-line::
+
+ $ cd sievelib
+ $ python parser.py test.sieve
+ Syntax OK
+ $
+
+Or can be used from a python environment (or script/module)::
+
+ >>> from sievelib.parser import Parser
+ >>> p = Parser()
+ >>> p.parse('require ["fileinto"];')
+ True
+ >>> p.dump()
+ require (type: control)
+ ["fileinto"]
+ >>>
+ >>> p.parse('require ["fileinto"]')
+ False
+ >>> p.error
+ 'line 1: parsing error: end of script reached while semicolon expected'
+ >>>
+
+Simple filters creation
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Some high-level classes are provided with the ``factory`` module, they
+make the generation of Sieve rules easier::
+
+ >>> from sievelib.factory import FiltersSet
+ >>> fs = FiltersSet("test")
+ >>> fs.addfilter("rule1",
+ ... [("Sender", ":is", "toto at toto.com"),],
+ ... [("fileinto", "Toto"),])
+ >>> fs.tosieve()
+ require ["fileinto"];
+
+ # Filter: rule1
+ if anyof (header :is "Sender" "toto at toto.com") {
+ fileinto "Toto";
+ }
+ >>>
+
+Additional documentation is available within source code.
+
+ManageSieve tools
+-----------------
+
+What is supported
+^^^^^^^^^^^^^^^^^
+
+All mandatory commands are supported. The ``RENAME`` extension is
+supported, with a simulated behaviour for server that do not support
+it.
+
+For the ``AUTHENTICATE`` command, supported mechanisms are ``DIGEST-MD5``,
+``PLAIN`` and ``LOGIN``.
+
+Basic usage
+^^^^^^^^^^^
+
+The ManageSieve client is intended to be used from another python
+application (there isn't any shell provided)::
+
+ >>> from sievelib.managesieve import Client
+ >>> c = Client("server.example.com")
+ >>> c.connect("user", "password", starttls=False, authmech="DIGEST-MD5")
+ True
+ >>> c.listscripts()
+ ("active_script", ["script1", "script2"])
+ >>> c.setactive("script1")
+ True
+ >>> c.havespace("script3", 45)
+ True
+ >>>
+
+Additional documentation is available with source code.
+
+.. |latest-version| image:: https://pypip.in/v/sievelib/badge.png
+ :alt: Latest version on Pypi
+ :target: https://crate.io/packages/sievelib/
+.. |downloads| image:: https://pypip.in/d/sievelib/badge.png
+ :alt: Downloads from Pypi
+ :target: https://crate.io/packages/sievelib/
+.. |travis| image:: https://travis-ci.org/tonioo/sievelib.png?branch=master
+ :target: https://travis-ci.org/tonioo/sievelib
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..ffe2fce
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+six
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..8171994
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,76 @@
+# coding=utf-8
+import os
+import re
+from setuptools import setup, find_packages
+
+ROOT = os.path.dirname(__file__)
+PIP_REQUIRES = os.path.join(ROOT, "requirements.txt")
+
+
+def parse_requirements(*filenames):
+ """
+ We generate our install_requires from the pip-requires and test-requires
+ files so that we don't have to maintain the dependency definitions in
+ two places.
+ """
+ requirements = []
+ for f in filenames:
+ for line in open(f, 'r').read().split('\n'):
+ # Comment lines. Skip.
+ if re.match(r'(\s*#)|(\s*$)', line):
+ continue
+ # Editable matches. Put the egg name into our reqs list.
+ if re.match(r'\s*-e\s+', line):
+ pkg = re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', line)
+ requirements.append("%s" % pkg)
+ # File-based installs not supported/needed. Skip.
+ elif re.match(r'\s*-f\s+', line):
+ pass
+ else:
+ requirements.append(line)
+ return requirements
+
+
+def parse_dependency_links(*filenames):
+ """
+ We generate our dependency_links from the pip-requires and test-requires
+ files for the dependencies pulled from github (prepended with -e).
+ """
+ dependency_links = []
+ for f in filenames:
+ for line in open(f, 'r').read().split('\n'):
+ if re.match(r'\s*-[ef]\s+', line):
+ line = re.sub(r'\s*-[ef]\s+', '', line)
+ line = re.sub(r'\s*git\+https', 'http', line)
+ line = re.sub(r'\.git#', '/tarball/master#', line)
+ dependency_links.append(line)
+ return dependency_links
+
+
+def read(fname):
+ return open(os.path.join(ROOT, fname)).read()
+
+setup(
+ name="sievelib",
+ packages=find_packages(),
+ include_package_data=True,
+ version="0.8",
+ description="Client-side SIEVE library",
+ author="Antoine Nguyen",
+ author_email="tonio at ngyn.org",
+ url="https://github.com/tonioo/sievelib",
+ license="MIT",
+ keywords=["sieve", "managesieve", "parser", "client"],
+ install_requires=parse_requirements(PIP_REQUIRES),
+ dependency_links=parse_dependency_links(PIP_REQUIRES),
+ classifiers=[
+ "Programming Language :: Python",
+ "Development Status :: 4 - Beta",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: OS Independent",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+ "Topic :: Communications :: Email :: Filters"
+ ],
+ long_description=read("README.rst")
+)
diff --git a/sievelib/__init__.py b/sievelib/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/sievelib/commands.py b/sievelib/commands.py
new file mode 100644
index 0000000..5a27f05
--- /dev/null
+++ b/sievelib/commands.py
@@ -0,0 +1,736 @@
+# coding: utf-8
+
+"""
+SIEVE commands representation
+
+This module contains classes that represent known commands. They all
+inherit from the Command class which provides generic method for
+command manipulation or parsing.
+
+There are three command types (each one represented by a class):
+ * control (ControlCommand) : Control structures are needed to allow
+ for multiple and conditional actions
+ * action (ActionCommand) : Actions that can be applied on emails
+ * test (TestCommand) : Tests are used in conditionals to decide which
+ part(s) of the conditional to execute
+
+Finally, each known command is represented by its own class which
+provides extra information such as:
+ * expected arguments,
+ * completion callback,
+ * etc.
+
+"""
+from __future__ import unicode_literals
+
+import sys
+from collections import Iterable
+
+
+class UnknownCommand(Exception):
+ """Specific exception raised when an unknown command is encountered"""
+
+ def __init__(self, name):
+ self.name = name
+
+ def __str__(self):
+ return "unknown command %s" % self.name
+
+
+class BadArgument(Exception):
+ """Specific exception raised when a bad argument is encountered"""
+
+ def __init__(self, command, seen, expected):
+ self.command = command
+ self.seen = seen
+ self.expected = expected
+
+ def __str__(self):
+ return "bad argument %s for command %s (%s expected)" \
+ % (self.seen, self.command, self.expected)
+
+
+class BadValue(Exception):
+ """Specific exception raised when a bad argument value is encountered"""
+
+ def __init__(self, argument, value):
+ self.argument = argument
+ self.value = value
+
+ def __str__(self):
+ return "bad value %s for argument %s" \
+ % (self.value, self.argument)
+
+
+# Statement elements (see RFC, section 8.3)
+# They are used in different commands.
+comparator = {"name": "comparator",
+ "type": ["tag"],
+ "values": [":comparator"],
+ "extra_arg": {"type": "string",
+ "values": ['"i;octet"', '"i;ascii-casemap"']},
+ "required": False}
+address_part = {"name": "address-part",
+ "values": [":localpart", ":domain", ":all"],
+ "type": ["tag"],
+ "required": False}
+match_type = {"name": "match-type",
+ "values": [":is", ":contains", ":matches"],
+ "type": ["tag"],
+ "required": False}
+
+
+class Command(object):
+ """Generic command representation.
+
+ A command is described as follow:
+ * A name
+ * A type
+ * A description of supported arguments
+ * Does it accept an unknown quantity of arguments? (ex: anyof, allof)
+ * Does it accept children? (ie. subcommands)
+ * Is it an extension?
+ * Must follow only certain commands
+
+ """
+ _type = None
+ variable_args_nb = False
+ accept_children = False
+ must_follow = None
+ is_extension = False
+
+ def __init__(self, parent=None):
+ self.parent = parent
+ self.arguments = {}
+ self.children = []
+
+ self.nextargpos = 0
+ self.required_args = -1
+ self.rargs_cnt = 0
+ self.curarg = None # for arguments that expect an argument :p (ex: :comparator)
+
+ self.name = self.__class__.__name__.replace("Command", "")
+ self.name = self.name.lower()
+
+ self.hash_comments = []
+
+ def __repr__(self):
+ return "%s (type: %s)" % (self.name, self._type)
+
+ def tosieve(self, indentlevel=0, target=sys.stdout):
+ """Generate the sieve syntax corresponding to this command
+
+ Recursive method.
+
+ :param indentlevel: current indentation level
+ :param target: opened file pointer where the content will be printed
+ """
+ self.__print(self.name, indentlevel, nocr=True, target=target)
+ if self.has_arguments():
+ for arg in self.args_definition:
+ if not arg["name"] in self.arguments:
+ continue
+ target.write(" ")
+ value = self.arguments[arg["name"]]
+
+ if "tag" in arg["type"] and arg.get("write_tag", False):
+ target.write("%s " % arg["values"][0])
+
+ if type(value) == list:
+ if self.__get_arg_type(arg["name"]) == ["testlist"]:
+ target.write("(")
+ for t in value:
+ t.tosieve(target=target)
+ if value.index(t) != len(value) - 1:
+ target.write(", ")
+ target.write(")")
+ else:
+ target.write("[" + ((", ".join(['"%s"' % v.strip('"') for v in value]))) + "]")
+ continue
+ if isinstance(value, Command):
+ value.tosieve(indentlevel, target=target)
+ continue
+
+ if "string" in arg["type"]:
+ target.write(value)
+ if not value.startswith('"'):
+ target.write("\n")
+ else:
+ target.write(value)
+
+ if not self.accept_children:
+ if self.get_type() != "test":
+ target.write(";\n")
+ return
+ if self.get_type() != "control":
+ return
+ target.write(" {\n")
+ for ch in self.children:
+ ch.tosieve(indentlevel + 4, target=target)
+ self.__print("}", indentlevel, target=target)
+
+ def __print(self, data, indentlevel, nocr=False, target=sys.stdout):
+ text = "%s%s" % (" " * indentlevel, data)
+ if nocr:
+ target.write(text)
+ else:
+ target.write(text + "\n")
+
+ def __get_arg_type(self, arg):
+ """Return the type corresponding to the given name.
+
+ :param arg: a defined argument name
+ """
+ for a in self.args_definition:
+ if a["name"] == arg:
+ return a["type"]
+ return None
+
+ def complete_cb(self):
+ """Completion callback
+
+ Called when a command is considered as complete by the parser.
+ """
+ pass
+
+ def get_expected_first(self):
+ """Return the first expected token for this command"""
+ return None
+
+ def has_arguments(self):
+ return len(self.args_definition) != 0
+
+ def dump(self, indentlevel=0, target=sys.stdout):
+ """Display the command
+
+ Pretty printing of this command and its eventual arguments and
+ children. (recursively)
+
+ :param indentlevel: integer that indicates indentation level to apply
+ """
+ self.__print(self, indentlevel, target=target)
+ indentlevel += 4
+ if self.has_arguments():
+ for arg in self.args_definition:
+ if not arg["name"] in self.arguments:
+ continue
+ value = self.arguments[arg["name"]]
+ if type(value) == list:
+ if self.__get_arg_type(arg["name"]) == ["testlist"]:
+ for t in value:
+ t.dump(indentlevel, target)
+ else:
+ self.__print("[" + (",".join(value)) + "]", indentlevel, target=target)
+ continue
+ if isinstance(value, Command):
+ value.dump(indentlevel, target)
+ continue
+ self.__print(str(value), indentlevel, target=target)
+ for ch in self.children:
+ ch.dump(indentlevel, target)
+
+ def addchild(self, child):
+ """Add a new child to the command
+
+ A child corresponds to a command located into a block (this
+ command's block). It can be either an action or a control.
+
+ :param child: the new child
+ :return: True on succes, False otherwise
+ """
+ if not self.accept_children:
+ return False
+ self.children += [child]
+ return True
+
+ def iscomplete(self):
+ """Check if the command is complete
+
+ Check if all required arguments have been encountered. For
+ commands that allow an undefined number of arguments, this
+ method always returns False.
+
+ :return: True if command is complete, False otherwise
+ """
+ if self.variable_args_nb:
+ return False
+ if self.required_args == -1:
+ self.required_args = 0
+ for arg in self.args_definition:
+ if arg["required"]:
+ self.required_args += 1
+ return (not self.curarg or not "extra_arg" in self.curarg) \
+ and (self.rargs_cnt == self.required_args)
+
+ def get_type(self):
+ """Return the command's type"""
+ if self._type is None:
+ raise NotImplementedError
+ return self._type
+
+ def __is_valid_value_for_arg(self, arg, value):
+ """Check if value is allowed for arg
+
+ Some commands only allow a limited set of values. The method
+ always returns True for methods that do not provide such a
+ set.
+
+ :param arg: the argument's name
+ :param value: the value to check
+ :return: True on succes, False otherwise
+ """
+ if not "values" in arg:
+ return True
+ return value.lower() in arg["values"]
+
+ def check_next_arg(self, atype, avalue, add=True):
+ """Argument validity checking
+
+ This method is usually used by the parser to check if detected
+ argument is allowed for this command.
+
+ We make a distinction between required and optional
+ arguments. Optional (or tagged) arguments can be provided
+ unordered but not the required ones.
+
+ A special handling is also done for arguments that require an
+ argument (example: the :comparator argument expects a string
+ argument).
+
+ The "testlist" type is checked separately as we can't know in
+ advance how many arguments will be provided.
+
+ If the argument is incorrect, the method raises the
+ appropriate exception, or return False to let the parser
+ handle the exception.
+
+ :param atype: the argument's type
+ :param avalue: the argument's value
+ :param add: indicates if this argument should be recorded on success
+ :return: True on success, False otherwise
+ """
+ if not self.has_arguments():
+ return False
+ if self.iscomplete():
+ return False
+
+ if self.curarg is not None and "extra_arg" in self.curarg:
+ if atype == self.curarg["extra_arg"]["type"]:
+ if not "values" in self.curarg["extra_arg"] \
+ or avalue in self.curarg["extra_arg"]["values"]:
+ if add:
+ self.arguments[self.curarg["name"]] = avalue
+ self.curarg = None
+ return True
+ raise BadValue(self.curarg["name"], avalue)
+
+ failed = False
+ pos = self.nextargpos
+ while pos < len(self.args_definition):
+ curarg = self.args_definition[pos]
+ if curarg["required"]:
+ if curarg["type"] == ["testlist"]:
+ if atype != "test":
+ failed = True
+ elif add:
+ if not curarg["name"] in self.arguments:
+ self.arguments[curarg["name"]] = []
+ self.arguments[curarg["name"]] += [avalue]
+ elif atype not in curarg["type"] or \
+ not self.__is_valid_value_for_arg(curarg, avalue):
+ failed = True
+ else:
+ self.curarg = curarg
+ self.rargs_cnt += 1
+ self.nextargpos = pos + 1
+ if add:
+ self.arguments[curarg["name"]] = avalue
+ break
+
+ if atype in curarg["type"]:
+ if self.__is_valid_value_for_arg(curarg, avalue):
+ if "extra_arg" in curarg:
+ self.curarg = curarg
+ break
+ if add:
+ self.arguments[curarg["name"]] = avalue
+ break
+
+ pos += 1
+
+ if failed:
+ raise BadArgument(self.name, avalue,
+ self.args_definition[pos]["type"])
+ return True
+
+ def __getitem__(self, name):
+ """Shorcut to access a command argument
+
+ :param name: the argument's name
+ """
+ found = False
+ for ad in self.args_definition:
+ if ad["name"] == name:
+ found = True
+ break
+ if not found:
+ raise KeyError(name)
+ if not name in self.arguments:
+ raise KeyError(name)
+ return self.arguments[name]
+
+
+class ControlCommand(Command):
+ """Indermediate class to represent "control" commands"""
+ _type = "control"
+
+
+class RequireCommand(ControlCommand):
+ """The 'require' command
+
+ This class has one big difference with others as it is used to
+ store loaded extension names. (The result is we can check for
+ unloaded extensions during the parsing)
+ """
+ args_definition = [
+ {"name": "capabilities",
+ "type": ["string", "stringlist"],
+ "required": True}
+ ]
+
+ loaded_extensions = []
+
+ def complete_cb(self):
+ if type(self.arguments["capabilities"]) == str:
+ exts = [self.arguments["capabilities"]]
+ else:
+ exts = self.arguments["capabilities"]
+ for ext in exts:
+ ext = ext.strip('"')
+ if not ext in RequireCommand.loaded_extensions:
+ RequireCommand.loaded_extensions += [ext]
+
+
+class StopCommand(ControlCommand):
+ args_definition = []
+
+
+class IfCommand(ControlCommand):
+ accept_children = True
+
+ args_definition = [
+ {"name": "test",
+ "type": ["test"],
+ "required": True}
+ ]
+
+ def get_expected_first(self):
+ return ["identifier"]
+
+
+class ElsifCommand(ControlCommand):
+ accept_children = True
+ must_follow = ["if", "elsif"]
+ args_definition = [
+ {"name": "test",
+ "type": ["test"],
+ "required": True}
+ ]
+
+ def get_expected_first(self):
+ return ["identifier"]
+
+
+class ElseCommand(ControlCommand):
+ accept_children = True
+ must_follow = ["if", "elsif"]
+ args_definition = []
+
+
+class ActionCommand(Command):
+ """Indermediate class to represent "action" commands"""
+ _type = "action"
+
+
+class FileintoCommand(ActionCommand):
+ is_extension = True
+ args_definition = [
+ {"name": "mailbox",
+ "type": ["string"],
+ "required": True}
+ ]
+
+
+class RedirectCommand(ActionCommand):
+ args_definition = [
+ {"name": "address",
+ "type": ["string"],
+ "required": True}
+ ]
+
+
+class RejectCommand(ActionCommand):
+ is_extension = True
+ args_definition = [
+ {"name": "text",
+ "type": ["string"],
+ "required": True}
+ ]
+
+
+class KeepCommand(ActionCommand):
+ args_definition = []
+
+
+class DiscardCommand(ActionCommand):
+ args_definition = []
+
+
+class TestCommand(Command):
+ """Indermediate class to represent "test" commands"""
+ _type = "test"
+
+
+class AddressCommand(TestCommand):
+ args_definition = [
+ comparator,
+ address_part,
+ match_type,
+ {"name": "header-list",
+ "type": ["string", "stringlist"],
+ "required": True},
+ {"name": "key-list",
+ "type": ["string", "stringlist"],
+ "required": True}
+ ]
+
+
+class AllofCommand(TestCommand):
+ accept_children = True
+ variable_args_nb = True
+
+ args_definition = [
+ {"name": "tests",
+ "type": ["testlist"],
+ "required": True}
+ ]
+
+ def get_expected_first(self):
+ return ["left_parenthesis"]
+
+
+class AnyofCommand(TestCommand):
+ accept_children = True
+ variable_args_nb = True
+
+ args_definition = [
+ {"name": "tests",
+ "type": ["testlist"],
+ "required": True}
+ ]
+
+ def get_expected_first(self):
+ return ["left_parenthesis"]
+
+
+class EnvelopeCommand(TestCommand):
+ args_definition = [
+ comparator,
+ address_part,
+ match_type,
+ {"name": "header-list",
+ "type": ["string", "stringlist"],
+ "required": True},
+ {"name": "key-list",
+ "type": ["string", "stringlist"],
+ "required": True}
+ ]
+
+
+class ExistsCommand(TestCommand):
+ args_definition = [
+ {"name": "header-names",
+ "type": ["stringlist"],
+ "required": True}
+ ]
+
+
+class TrueCommand(TestCommand):
+ args_definition = []
+
+
+class FalseCommand(TestCommand):
+ args_definition = []
+
+
+class HeaderCommand(TestCommand):
+ args_definition = [
+ comparator,
+ match_type,
+ {"name": "header-names",
+ "type": ["string", "stringlist"],
+ "required": True},
+ {"name": "key-list",
+ "type": ["string", "stringlist"],
+ "required": True}
+ ]
+
+
+class NotCommand(TestCommand):
+ accept_children = True
+
+ args_definition = [
+ {"name": "test",
+ "type": ["test"],
+ "required": True}
+ ]
+
+ def get_expected_first(self):
+ return ["identifier"]
+
+
+class SizeCommand(TestCommand):
+ args_definition = [
+ {"name": "comparator",
+ "type": ["tag"],
+ "values": [":over", ":under"],
+ "required": True},
+ {"name": "limit",
+ "type": ["number"],
+ "required": True},
+ ]
+
+
+class VacationCommand(ActionCommand):
+ args_definition = [
+ {"name": "subject",
+ "type": ["tag"],
+ "write_tag": True,
+ "values": [":subject"],
+ "extra_arg": {"type": "string"},
+ "required": False},
+ {"name": "days",
+ "type": ["tag"],
+ "write_tag": True,
+ "values": [":days"],
+ "extra_arg": {"type": "number"},
+ "required": False},
+ {"name": "from",
+ "type": ["tag"],
+ "write_tag": True,
+ "values": [":from"],
+ "extra_arg": {"type": "string"},
+ "required": False},
+ {"name": "addresses",
+ "type": ["tag"],
+ "write_tag": True,
+ "values": [":addresses"],
+ "extra_arg": {"type": "stringlist"},
+ "required": False},
+ {"name": "handle",
+ "type": ["tag"],
+ "write_tag": True,
+ "values": [":handle"],
+ "extra_arg": {"type": "string"},
+ "required": False},
+ {"name": "mime",
+ "type": ["tag"],
+ "write_tag": True,
+ "values": [":mime"],
+ "required": False},
+ {"name": "reason",
+ "type": ["string"],
+ "required": True},
+ ]
+
+class SetCommand(ControlCommand):
+ """currentdate command, part of the variables extension
+
+ http://tools.ietf.org/html/rfc5229
+ """
+ is_extension = True
+ args_definition = [
+ {"name": "startend",
+ "type": ["string"],
+ "required": True},
+ {"name": "date",
+ "type": ["string"],
+ "required": True}
+ ]
+
+class CurrentdateCommand(ControlCommand):
+ """currentdate command, part of the date extension
+
+ http://tools.ietf.org/html/rfc5260#section-5
+ """
+ is_extension = True
+ accept_children = True
+ args_definition = [
+ {"name": "zone",
+ "type": ["tag"],
+ "write_tag": True,
+ "values": [":zone"],
+ "extra_arg": {"type": "string"},
+ "required": False},
+ {"name": "match-value",
... 2538 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-sievelib.git
More information about the Python-modules-commits
mailing list