Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
manfred-kaiser committed Sep 24, 2020
2 parents c7e9e5d + 36fba3b commit 6e6601e
Show file tree
Hide file tree
Showing 46 changed files with 351 additions and 263 deletions.
77 changes: 38 additions & 39 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,41 @@
long_description = file.read()

setup(
name = "sifter",
version = "0.1",
author = "Gary Peck",
author_email = "gary@realify.com",
url = "https://github.com/garyp/sifter",
license = "BSD",
description = "Parser/evaluator for the Sieve filtering language (RFC 5228)",
long_description = long_description,
install_requires=[
"ply",
],
classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
"License :: OSI Approved :: BSD License",
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: System Administrators",
"Operating System :: OS Independent",
"Topic :: Communications :: Email :: Filters",
"Topic :: Software Development :: Interpreters",
"Topic :: Software Development :: Libraries :: Python Modules",
],
packages = [
"sifter",
"sifter.commands",
"sifter.comparators",
"sifter.extensions",
"sifter.grammar",
"sifter.t",
"sifter.tests",
"sifter.validators",
],
package_data = {
"sifter.t" : ["*.in", "*.out", "*.msg", "*.rules"],
},
)

name="sifter",
version="0.1",
author="Gary Peck",
author_email="gary@realify.com",
url="https://github.com/garyp/sifter",
license="BSD",
description="Parser/evaluator for the Sieve filtering language (RFC 5228)",
long_description=long_description,
install_requires=[
"ply",
],
classifiers=[
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
"License :: OSI Approved :: BSD License",
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: System Administrators",
"Operating System :: OS Independent",
"Topic :: Communications :: Email :: Filters",
"Topic :: Software Development :: Interpreters",
"Topic :: Software Development :: Libraries :: Python Modules",
],
packages=[
"sifter",
"sifter.commands",
"sifter.comparators",
"sifter.extensions",
"sifter.grammar",
"sifter.t",
"sifter.tests",
"sifter.validators",
],
package_data={
"sifter.t": ["*.in", "*.out", "*.msg", "*.rules"],
},
)
2 changes: 2 additions & 0 deletions sifter/commands/discard.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

__all__ = ('CommandDiscard',)


# section 4.4
class CommandDiscard(sifter.grammar.Command):

Expand All @@ -16,4 +17,5 @@ def __init__(self, arguments=None, tests=None, block=None):
def evaluate(self, message, state):
state.actions.cancel_implicit_keep()


CommandDiscard.register()
8 changes: 5 additions & 3 deletions sifter/commands/fileinto.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

__all__ = ('CommandFileInto',)


# section 4.1
class CommandFileInto(sifter.grammar.Command):

Expand All @@ -11,9 +12,9 @@ class CommandFileInto(sifter.grammar.Command):
def __init__(self, arguments=None, tests=None, block=None):
super(CommandFileInto, self).__init__(arguments, tests, block)
_, positional_args = self.validate_arguments(
{},
[ sifter.validators.StringList(length=1), ],
)
{},
[sifter.validators.StringList(length=1), ],
)
self.validate_tests_size(0)
self.validate_block_size(0)
self.file_dest = positional_args[0]
Expand All @@ -23,4 +24,5 @@ def evaluate(self, message, state):
state.actions.append('fileinto', self.file_dest)
state.actions.cancel_implicit_keep()


CommandFileInto.register()
4 changes: 4 additions & 0 deletions sifter/commands/if_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

__all__ = ('CommandIf', 'CommandElsIf', 'CommandElse',)


# section 3.1
class CommandIfBase(sifter.grammar.Command):

Expand All @@ -24,6 +25,7 @@ class CommandIf(CommandIfBase):

RULE_IDENTIFIER = 'IF'


CommandIf.register()


Expand All @@ -37,6 +39,7 @@ def evaluate(self, message, state):
else:
return super(CommandElsIf, self).evaluate(message, state)


CommandElsIf.register()


Expand All @@ -55,4 +58,5 @@ def evaluate(self, message, state):
else:
return self.block.evaluate(message, state)


CommandElse.register()
2 changes: 2 additions & 0 deletions sifter/commands/keep.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

__all__ = ('CommandKeep',)


# section 4.3
class CommandKeep(sifter.grammar.Command):

Expand All @@ -16,4 +17,5 @@ def __init__(self, arguments=None, tests=None, block=None):
def evaluate(self, message, state):
state.actions.append('keep').cancel_implicit_keep()


CommandKeep.register()
14 changes: 8 additions & 6 deletions sifter/commands/redirect.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

__all__ = ('CommandRedirect',)


# section 4.2
class CommandRedirect(sifter.grammar.Command):

Expand All @@ -13,9 +14,9 @@ class CommandRedirect(sifter.grammar.Command):
def __init__(self, arguments=None, tests=None, block=None):
super(CommandRedirect, self).__init__(arguments, tests, block)
_, positional_args = self.validate_arguments(
{},
[ sifter.validators.StringList(length=1), ],
)
{},
[sifter.validators.StringList(length=1), ],
)
self.validate_tests_size(0)
self.validate_block_size(0)
self.email_address = positional_args[0][0]
Expand All @@ -26,12 +27,13 @@ def __init__(self, arguments=None, tests=None, block=None):
realname, emailaddr = email.utils.parseaddr(self.email_address)
if emailaddr == "":
raise sifter.grammar.RuleSyntaxError(
"REDIRECT destination not a valid email address: %s"
% self.email_address
)
"REDIRECT destination not a valid email address: %s"
% self.email_address
)

def evaluate(self, message, state):
state.actions.append('redirect', self.email_address)
state.actions.cancel_implicit_keep()


CommandRedirect.register()
14 changes: 9 additions & 5 deletions sifter/commands/require.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

__all__ = ('CommandRequire',)


# section 3.2
class CommandRequire(sifter.grammar.Command):

Expand All @@ -12,18 +13,21 @@ class CommandRequire(sifter.grammar.Command):
def __init__(self, arguments=None, tests=None, block=None):
super(CommandRequire, self).__init__(arguments, tests, block)
_, positional_args = self.validate_arguments(
{},
[ sifter.validators.StringList(), ],
)
{},
[sifter.validators.StringList(), ],
)
self.validate_tests_size(0)
self.validate_block_size(0)
self.ext_names = positional_args[0]

def evaluate(self, message, state):
for ext_name in self.ext_names:
if not sifter.handler.get('extension', ext_name):
raise RuntimeError("Required extension '%s' not supported"
% ext_name)
raise RuntimeError(
"Required extension '%s' not supported"
% ext_name
)
state.require_extension(ext_name)


CommandRequire.register()
2 changes: 2 additions & 0 deletions sifter/commands/stop.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

__all__ = ('CommandStop',)


# section 3.3
class CommandStop(sifter.grammar.Command):

Expand All @@ -16,4 +17,5 @@ def __init__(self, arguments=None, tests=None, block=None):
def evaluate(self, message, state):
state.actions.append('stop')


CommandStop.register()
18 changes: 11 additions & 7 deletions sifter/comparator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@

__all__ = ('register', 'get_match_fn',)


def register(comparator_name, comparator_cls):
sifter.handler.register('comparator', comparator_name, comparator_cls)


def get_match_fn(comparator, match_type):
# section 2.7.3: default comparator is 'i;ascii-casemap'
if comparator is None: comparator = 'i;ascii-casemap'
if comparator is None:
comparator = 'i;ascii-casemap'
# RFC 4790, section 3.1: the special identifier 'default' refers to the
# implementation-defined default comparator
elif comparator == 'default': comparator = 'i;ascii-casemap'
elif comparator == 'default':
comparator = 'i;ascii-casemap'

# section 2.7.1: default match type is ":is"
if match_type is None: match_type = 'IS'
if match_type is None:
match_type = 'IS'

# TODO: support wildcard matching in comparator names (RFC 4790)
cmp_handler = sifter.handler.get('comparator', comparator)
Expand All @@ -24,9 +29,8 @@ def get_match_fn(comparator, match_type):
cmp_fn = getattr(cmp_handler, 'cmp_%s' % match_type.lower())
except AttributeError:
raise RuntimeError(
"':%s' matching not supported by comparator '%s'"
% (match_type, comparator)
)
"':%s' matching not supported by comparator '%s'"
% (match_type, comparator)
)

return (cmp_fn, comparator, match_type)

12 changes: 4 additions & 8 deletions sifter/comparators/ascii_casemap.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import string
try:
# Python 3
maketrans = str.maketrans
except AttributeError:
# Python 2
maketrans = string.maketrans

from sifter.comparators.octet import ComparatorOctet

maketrans = str.maketrans

__all__ = ('ComparatorASCIICasemap',)


class ComparatorASCIICasemap(ComparatorOctet):

COMPARATOR_ID = 'i;ascii-casemap'
Expand All @@ -18,4 +13,5 @@ class ComparatorASCIICasemap(ComparatorOctet):
def sort_key(cls, s):
return s.upper()


ComparatorASCIICasemap.register()
13 changes: 9 additions & 4 deletions sifter/comparators/octet.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

__all__ = ('ComparatorOctet',)


class ComparatorOctet(sifter.grammar.Comparator):

COMPARATOR_ID = 'i;octet'
Expand All @@ -29,8 +30,8 @@ def cmp_matches(cls, s, pattern, state):
elif c == "?":
re_pattern.append(".")
elif c == "\\":
if pattern[i:i+1] in ("\\*", "\\?"):
re_pattern.append(re.escape(pattern[i+1]))
if pattern[i:i + 1] in ("\\*", "\\?"):
re_pattern.append(re.escape(pattern[i + 1]))
i += 2
else:
re_pattern.append(re.escape(c))
Expand All @@ -39,7 +40,11 @@ def cmp_matches(cls, s, pattern, state):
re_pattern.append("\Z")
# TODO: compile and cache pattern for more efficient execution across
# multiple strings and messages
return re.match(''.join(re_pattern), cls.sort_key(s),
re.MULTILINE | re.DOTALL)
return re.match(
''.join(re_pattern),
cls.sort_key(s),
re.MULTILINE | re.DOTALL
)


ComparatorOctet.register()
1 change: 1 addition & 0 deletions sifter/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

__all__ = ('register',)


def register(extension_name):
sifter.handler.register('extension', extension_name, True)
13 changes: 8 additions & 5 deletions sifter/extensions/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@
import sifter.comparators.octet

import sifter.extension
list(map(sifter.extension.register,
('fileinto',
'comparator-i;ascii-casemap',
'comparator-i;octet',
)))
list(map(
sifter.extension.register,
(
'fileinto',
'comparator-i;ascii-casemap',
'comparator-i;octet',
)
))
1 change: 1 addition & 0 deletions sifter/grammar/actions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
__all__ = ('Actions',)


class Actions(list):

def __init__(self, implicit_keep=False):
Expand Down
Loading

0 comments on commit 6e6601e

Please sign in to comment.