[Python-modules-commits] [python-sievelib] 02/09: New upstream version 1.1.0
Michael Fladischer
fladi at moszumanska.debian.org
Tue Jan 9 07:14:33 UTC 2018
This is an automated email from the git hooks/post-receive script.
fladi pushed a commit to branch debian/master
in repository python-sievelib.
commit a79b0c5eaa885b62db93be71eec760a46625e334
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date: Tue Jan 9 08:03:31 2018 +0100
New upstream version 1.1.0
---
PKG-INFO | 3 ++-
README.rst | 1 +
setup.cfg | 1 -
setup.py | 3 ++-
sievelib.egg-info/PKG-INFO | 3 ++-
sievelib/commands.py | 55 +++++++++++++++++++++++++++++++++++++-----
sievelib/factory.py | 16 +++++++++++-
sievelib/parser.py | 17 +++++++------
sievelib/tests/test_factory.py | 50 ++++++++++++++++++++++++++------------
sievelib/tests/test_parser.py | 32 ++++++++++++++++++++++++
10 files changed, 147 insertions(+), 34 deletions(-)
diff --git a/PKG-INFO b/PKG-INFO
index cadc3f4..08d4832 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: sievelib
-Version: 1.0.0
+Version: 1.1.0
Summary: Client-side SIEVE library
Home-page: https://github.com/tonioo/sievelib
Author: Antoine Nguyen
@@ -44,6 +44,7 @@ Description: sievelib
The following extensions are also supported:
+ * Copying Without Side Effects (`RFC 3894 <https://tools.ietf.org/html/rfc3894>`_)
* Date and Index (`RFC 5260 <https://tools.ietf.org/html/rfc5260>`_)
* Vacation (`RFC 5230 <http://tools.ietf.org/html/rfc5230>`_)
diff --git a/README.rst b/README.rst
index 4583eab..34a9de4 100644
--- a/README.rst
+++ b/README.rst
@@ -36,6 +36,7 @@ supported.
The following extensions are also supported:
+* Copying Without Side Effects (`RFC 3894 <https://tools.ietf.org/html/rfc3894>`_)
* Date and Index (`RFC 5260 <https://tools.ietf.org/html/rfc5260>`_)
* Vacation (`RFC 5230 <http://tools.ietf.org/html/rfc5230>`_)
diff --git a/setup.cfg b/setup.cfg
index 861a9f5..8bfd5a1 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,4 @@
[egg_info]
tag_build =
tag_date = 0
-tag_svn_revision = 0
diff --git a/setup.py b/setup.py
index 79c3703..e2def9d 100644
--- a/setup.py
+++ b/setup.py
@@ -50,11 +50,12 @@ def parse_dependency_links(*filenames):
def read(fname):
return open(os.path.join(ROOT, fname)).read()
+
setup(
name="sievelib",
packages=find_packages(),
include_package_data=True,
- version="1.0.0",
+ version="1.1.0",
description="Client-side SIEVE library",
author="Antoine Nguyen",
author_email="tonio at ngyn.org",
diff --git a/sievelib.egg-info/PKG-INFO b/sievelib.egg-info/PKG-INFO
index cadc3f4..08d4832 100644
--- a/sievelib.egg-info/PKG-INFO
+++ b/sievelib.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: sievelib
-Version: 1.0.0
+Version: 1.1.0
Summary: Client-side SIEVE library
Home-page: https://github.com/tonioo/sievelib
Author: Antoine Nguyen
@@ -44,6 +44,7 @@ Description: sievelib
The following extensions are also supported:
+ * Copying Without Side Effects (`RFC 3894 <https://tools.ietf.org/html/rfc3894>`_)
* Date and Index (`RFC 5260 <https://tools.ietf.org/html/rfc5260>`_)
* Vacation (`RFC 5230 <http://tools.ietf.org/html/rfc5230>`_)
diff --git a/sievelib/commands.py b/sievelib/commands.py
index 4b482ba..494a432 100644
--- a/sievelib/commands.py
+++ b/sievelib/commands.py
@@ -29,8 +29,14 @@ import sys
from future.utils import python_2_unicode_compatible
+class CommandError(Exception):
+ """Base command exception class."""
+
+ pass
+
+
@python_2_unicode_compatible
-class UnknownCommand(Exception):
+class UnknownCommand(CommandError):
"""Specific exception raised when an unknown command is encountered"""
def __init__(self, name):
@@ -41,7 +47,7 @@ class UnknownCommand(Exception):
@python_2_unicode_compatible
-class BadArgument(Exception):
+class BadArgument(CommandError):
"""Specific exception raised when a bad argument is encountered"""
def __init__(self, command, seen, expected):
@@ -55,7 +61,7 @@ class BadArgument(Exception):
@python_2_unicode_compatible
-class BadValue(Exception):
+class BadValue(CommandError):
"""Specific exception raised when a bad argument value is encountered"""
def __init__(self, argument, value):
@@ -67,6 +73,16 @@ class BadValue(Exception):
% (self.value, self.argument)
+ at python_2_unicode_compatible
+class ExtensionNotLoaded(CommandError):
+ """Raised when an extension is not loaded."""
+
+ def __init__(self, name):
+ self.name = name
+
+ def __str__(self):
+ return "extension '{}' not loaded".format(self.name)
+
# Statement elements (see RFC, section 8.3)
# They are used in different commands.
comparator = {"name": "comparator",
@@ -150,7 +166,11 @@ class Command(object):
target.write(", ")
target.write(")")
else:
- target.write("[" + ((", ".join(['"%s"' % v.strip('"') for v in value]))) + "]")
+ target.write(
+ "[{}]".format(", ".join(
+ ['"%s"' % v.strip('"') for v in value])
+ )
+ )
continue
if isinstance(value, Command):
value.tosieve(indentlevel, target=target)
@@ -225,7 +245,8 @@ class Command(object):
for t in value:
t.dump(indentlevel, target)
else:
- self.__print("[" + (",".join(value)) + "]", indentlevel, target=target)
+ self.__print("[" + (",".join(value)) + "]",
+ indentlevel, target=target)
continue
if isinstance(value, Command):
value.dump(indentlevel, target)
@@ -288,7 +309,7 @@ class Command(object):
return True
return value.lower() in arg["values"]
- def check_next_arg(self, atype, avalue, add=True):
+ def check_next_arg(self, atype, avalue, add=True, check_extension=True):
"""Argument validity checking
This method is usually used by the parser to check if detected
@@ -312,6 +333,8 @@ class Command(object):
:param atype: the argument's type
:param avalue: the argument's value
:param add: indicates if this argument should be recorded on success
+ :param check_extension: raise ExtensionNotLoaded if extension not
+ loaded
:return: True on success, False otherwise
"""
if not self.has_arguments():
@@ -353,6 +376,12 @@ class Command(object):
break
if atype in curarg["type"]:
+ ext = curarg.get("extension")
+ condition = (
+ check_extension and ext and
+ ext not in RequireCommand.loaded_extensions)
+ if condition:
+ raise ExtensionNotLoaded(ext)
if self.__is_valid_value_for_arg(curarg, avalue):
if "extra_arg" in curarg:
self.curarg = curarg
@@ -368,6 +397,10 @@ class Command(object):
self.args_definition[pos]["type"])
return True
+ def __contains__(self, name):
+ """Check if argument is provided with command."""
+ return name in self.arguments
+
def __getitem__(self, name):
"""Shorcut to access a command argument
@@ -460,6 +493,11 @@ class ActionCommand(Command):
class FileintoCommand(ActionCommand):
is_extension = True
args_definition = [
+ {"name": "copy",
+ "type": ["tag"],
+ "values": [":copy"],
+ "required": False,
+ "extension": "copy"},
{"name": "mailbox",
"type": ["string"],
"required": True}
@@ -468,6 +506,11 @@ class FileintoCommand(ActionCommand):
class RedirectCommand(ActionCommand):
args_definition = [
+ {"name": "copy",
+ "type": ["tag"],
+ "values": [":copy"],
+ "required": False,
+ "extension": "copy"},
{"name": "address",
"type": ["string"],
"required": True}
diff --git a/sievelib/factory.py b/sievelib/factory.py
index 840e990..37721ba 100644
--- a/sievelib/factory.py
+++ b/sievelib/factory.py
@@ -97,6 +97,14 @@ class FiltersSet(object):
if name not in self.requires:
self.requires += [name]
+ def check_if_arg_is_extension(self, arg):
+ """Include extension if arg requires one."""
+ args_using_extensions = {
+ ":copy": "copy"
+ }
+ if arg in args_using_extensions:
+ self.require(args_using_extensions[arg])
+
def __gen_require_command(self):
"""Internal method to create a RequireCommand based on requirements
@@ -200,7 +208,13 @@ class FiltersSet(object):
if action.is_extension:
self.require(actdef[0])
for arg in actdef[1:]:
- action.check_next_arg("string", self.__quote_if_necessary(arg))
+ self.check_if_arg_is_extension(arg)
+ if arg.startswith(":"):
+ atype = "tag"
+ else:
+ atype = "string"
+ arg = self.__quote_if_necessary(arg)
+ action.check_next_arg(atype, arg, check_extension=False)
ifcontrol.addchild(action)
return ifcontrol
diff --git a/sievelib/parser.py b/sievelib/parser.py
index 137a9f9..1c5f86f 100755
--- a/sievelib/parser.py
+++ b/sievelib/parser.py
@@ -17,8 +17,7 @@ from future.utils import python_2_unicode_compatible, text_type
import six
from sievelib.commands import (
- get_command_instance, UnknownCommand, BadArgument, BadValue
-)
+ get_command_instance, CommandError, RequireCommand)
@python_2_unicode_compatible
@@ -136,6 +135,7 @@ class Parser(object):
self.__curstringlist = None
self.__expected = None
self.__opened_blocks = 0
+ RequireCommand.loaded_extensions = []
def __set_expected(self, *args, **kwargs):
"""Set the next expected token.
@@ -326,15 +326,17 @@ class Parser(object):
if ttype != "identifier":
return False
- command = get_command_instance(tvalue.decode("ascii"), self.__curcommand)
+ command = get_command_instance(
+ tvalue.decode("ascii"), self.__curcommand)
if command.get_type() == "test":
- raise ParseError("%s may not appear as a first command" % command.name)
+ raise ParseError(
+ "%s may not appear as a first command" % command.name)
if command.get_type() == "control" and command.accept_children \
- and command.has_arguments():
+ and command.has_arguments():
self.__set_expected("identifier")
if self.__curcommand is not None:
if not self.__curcommand.addchild(command):
- raise ParseError("%s unexpected after a %s" % \
+ raise ParseError("%s unexpected after a %s" %
(tvalue, self.__curcommand.name))
self.__curcommand = command
self.__cstate = self.__arguments
@@ -405,7 +407,7 @@ class Parser(object):
raise ParseError("end of script reached while %s expected" %
"|".join(self.__expected))
- except (ParseError, UnknownCommand, BadArgument, BadValue) as e:
+ except (ParseError, CommandError) as e:
self.error = "line %d: %s" % (self.lexer.curlineno(), str(e))
return False
return True
@@ -420,7 +422,6 @@ class Parser(object):
"""
with open(name, "rb") as fp:
return self.parse(fp.read())
-
def dump(self, target=sys.stdout):
"""Dump the parsing tree.
diff --git a/sievelib/tests/test_factory.py b/sievelib/tests/test_factory.py
index 061fa14..dfa0ff9 100644
--- a/sievelib/tests/test_factory.py
+++ b/sievelib/tests/test_factory.py
@@ -16,23 +16,41 @@ class FactoryTestCase(unittest.TestCase):
self.fs.addfilter(
"rule1",
[('Sender', ":is", 'toto at toto.com'), ],
- [("fileinto", 'Toto'), ])
+ [("fileinto", ":copy", "Toto"), ])
self.assertIsNot(self.fs.getfilter("rule1"), None)
self.fs.tosieve(output)
- self.assertEqual(output.getvalue(), """require ["fileinto"];
+ self.assertEqual(output.getvalue(), """require ["fileinto", "copy"];
# Filter: rule1
if anyof (header :is "Sender" "toto at toto.com") {
- fileinto "Toto";
+ fileinto :copy "Toto";
+}
+""")
+ output.close()
+
+ def test_use_action_with_tag(self):
+ output = six.StringIO()
+ self.fs.addfilter(
+ "rule1",
+ [('Sender', ":is", 'toto at toto.com'), ],
+ [("redirect", ":copy", "toto at titi.com"), ])
+ self.assertIsNot(self.fs.getfilter("rule1"), None)
+ self.fs.tosieve(output)
+ self.assertEqual(output.getvalue(), """require ["copy"];
+
+# Filter: rule1
+if anyof (header :is "Sender" "toto at toto.com") {
+ redirect :copy "toto at titi.com";
}
""")
output.close()
def test_add_header_filter_with_not(self):
output = six.StringIO()
- self.fs.addfilter("rule1",
- [('Sender', ":notcontains", 'toto at toto.com'),],
- [("fileinto", 'Toto'),])
+ self.fs.addfilter(
+ "rule1",
+ [('Sender', ":notcontains", 'toto at toto.com')],
+ [("fileinto", 'Toto')])
self.assertIsNot(self.fs.getfilter("rule1"), None)
self.fs.tosieve(output)
self.assertEqual(output.getvalue(), """require ["fileinto"];
@@ -47,8 +65,9 @@ if anyof (not header :contains "Sender" "toto at toto.com") {
output = six.StringIO()
self.fs.addfilter(
"rule1",
- [('exists', "list-help", "list-unsubscribe", "list-subscribe", "list-owner")],
- [("fileinto", 'Toto'),]
+ [('exists', "list-help", "list-unsubscribe",
+ "list-subscribe", "list-owner")],
+ [("fileinto", 'Toto')]
)
self.assertIsNot(self.fs.getfilter("rule1"), None)
self.fs.tosieve(output)
@@ -64,8 +83,9 @@ if anyof (exists ["list-help","list-unsubscribe","list-subscribe","list-owner"])
output = six.StringIO()
self.fs.addfilter(
"rule1",
- [('notexists', "list-help", "list-unsubscribe", "list-subscribe", "list-owner")],
- [("fileinto", 'Toto'),]
+ [('notexists', "list-help", "list-unsubscribe",
+ "list-subscribe", "list-owner")],
+ [("fileinto", 'Toto')]
)
self.assertIsNot(self.fs.getfilter("rule1"), None)
self.fs.tosieve(output)
@@ -82,7 +102,7 @@ if anyof (not exists ["list-help","list-unsubscribe","list-subscribe","list-owne
self.fs.addfilter(
"rule1",
[('size', ":over", "100k")],
- [("fileinto", 'Totoéé'),]
+ [("fileinto", 'Totoéé')]
)
self.assertIsNot(self.fs.getfilter("rule1"), None)
self.fs.tosieve(output)
@@ -96,8 +116,8 @@ if anyof (size :over 100k) {
def test_remove_filter(self):
self.fs.addfilter("rule1",
- [('Sender', ":is", 'toto at toto.com'),],
- [("fileinto", 'Toto'),])
+ [('Sender', ":is", 'toto at toto.com')],
+ [("fileinto", 'Toto')])
self.assertIsNot(self.fs.getfilter("rule1"), None)
self.assertEqual(self.fs.removefilter("rule1"), True)
self.assertIs(self.fs.getfilter("rule1"), None)
@@ -107,8 +127,8 @@ if anyof (size :over 100k) {
FIXME: Extra spaces are written between if and anyof, why?!
"""
self.fs.addfilter("rule1",
- [('Sender', ":is", 'toto at toto.com'),],
- [("fileinto", 'Toto'),])
+ [('Sender', ":is", 'toto at toto.com')],
+ [("fileinto", 'Toto')])
self.assertIsNot(self.fs.getfilter("rule1"), None)
self.assertEqual(self.fs.disablefilter("rule1"), True)
output = six.StringIO()
diff --git a/sievelib/tests/test_parser.py b/sievelib/tests/test_parser.py
index de268cf..1d79ca4 100644
--- a/sievelib/tests/test_parser.py
+++ b/sievelib/tests/test_parser.py
@@ -694,5 +694,37 @@ if allof (
""")
+class CopyWithoutSideEffectsTestCase(SieveTest):
+ """RFC3894 test cases."""
+
+ def test_redirect_with_copy(self):
+ self.compilation_ko(b"""
+if header :contains "subject" "test" {
+ redirect :copy "dev at null.com";
+}
+""")
+
+ self.compilation_ok(b"""require "copy";
+if header :contains "subject" "test" {
+ redirect :copy "dev at null.com";
+}
+""")
+
+ def test_fileinto_with_copy(self):
+ self.compilation_ko(b"""require "fileinto";
+if header :contains "subject" "test" {
+ fileinto :copy "Spam";
+}
+""")
+ self.assertEqual(
+ self.parser.error, "line 3: extension 'copy' not loaded")
+
+ self.compilation_ok(b"""require ["fileinto", "copy"];
+if header :contains "subject" "test" {
+ fileinto :copy "Spam";
+}
+""")
+
+
if __name__ == "__main__":
unittest.main()
--
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