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 Oct 9, 2020
2 parents 105ed05 + a28571c commit 918cfd9
Show file tree
Hide file tree
Showing 44 changed files with 150 additions and 121 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ FEATURES

- Supports all of the base Sieve spec from RFC 5228, except for
features still listed under TODO below
- multiline strings (since version 0.2.2)
- multiline strings (since version 0.2.2)
- bracketed comments (since version 0.2.4)
- Extensions supported:
- regex (draft-ietf-sieve-regex-01)
- body (RFC 5173)
Expand Down Expand Up @@ -72,7 +73,6 @@ TODO
- Base spec features not yet implemented:
- encoded characters (section 2.4.2.4)
- multi-line strings (section 2.4.2)
- bracketed comments (section 2.3)
- message uniqueness (section 2.10.3)
- envelope test (section 5.4)
- handle message loops (section 10)
Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ FEATURES
features still listed under TODO below

- multiline strings (since version 0.2.2)
- bracketed comments (since version 0.2.4)

- Extensions supported:

Expand Down Expand Up @@ -76,7 +77,6 @@ TODO
- Base spec features not yet implemented:

- encoded characters (section 2.4.2.4)
- bracketed comments (section 2.3)
- message uniqueness (section 2.10.3)
- envelope test (section 5.4)
- handle message loops (section 10)
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

setup(
name="sifter3",
version="0.2.3",
version="0.2.4",
author="Manfred Kaiser, Gary Peck",
author_email="[email protected], [email protected]",
url="https://sifter3.readthedocs.io/en/latest/",
Expand Down Expand Up @@ -61,6 +61,7 @@
'notify = sifter.commands.notify:CommandNotify',
'redirect = sifter.commands.redirect:CommandRedirect',
'reject = sifter.commands.reject:CommandReject',
'ereject = sifter.commands.reject:CommandEReject',
'require = sifter.commands.require:CommandRequire',
'set = sifter.commands.variables:CommandSet',
'stop = sifter.commands.stop:CommandStop',
Expand Down
2 changes: 1 addition & 1 deletion sifter/commands/discard.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# section 4.4
class CommandDiscard(Command):

RULE_IDENTIFIER = 'DISCARD'
HANDLER_ID = 'DISCARD'

def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
state.actions.cancel_implicit_keep()
Expand Down
3 changes: 2 additions & 1 deletion sifter/commands/fileinto.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
# section 4.1
class CommandFileInto(Command):

RULE_IDENTIFIER = 'FILEINTO'
HANDLER_ID = 'FILEINTO'
EXTENSION_NAME = 'fileinto'
POSITIONAL_ARGS = [StringList(length=1)]

def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
Expand Down
6 changes: 3 additions & 3 deletions sifter/commands/if_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions

class CommandIf(CommandIfBase):

RULE_IDENTIFIER = 'IF'
HANDLER_ID = 'IF'


class CommandElsIf(CommandIfBase):

RULE_IDENTIFIER = 'ELSIF'
HANDLER_ID = 'ELSIF'

def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
if state.last_if:
Expand All @@ -40,7 +40,7 @@ def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions

class CommandElse(Command):

RULE_IDENTIFIER = 'ELSE'
HANDLER_ID = 'ELSE'
TESTS_MIN = 0
HAS_BLOCKS = False

Expand Down
9 changes: 6 additions & 3 deletions sifter/commands/imap4flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@

class CommandSetFlag(Command):

RULE_IDENTIFIER = 'SETFLAG'
HANDLER_ID = 'SETFLAG'
EXTENSION_NAME = 'imap4flags'
POSITIONAL_ARGS = [StringList()]

def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
Expand All @@ -28,7 +29,8 @@ def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions

class CommandRemoveFlag(Command):

RULE_IDENTIFIER = 'REMOVEFLAG'
HANDLER_ID = 'REMOVEFLAG'
EXTENSION_NAME = 'imap4flags'
POSITIONAL_ARGS = [StringList()]

def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
Expand All @@ -41,7 +43,8 @@ def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions

class CommandAddFlag(Command):

RULE_IDENTIFIER = 'ADDFLAG'
HANDLER_ID = 'ADDFLAG'
EXTENSION_NAME = 'imap4flags'
POSITIONAL_ARGS = [StringList()]

def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
Expand Down
2 changes: 1 addition & 1 deletion sifter/commands/keep.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# section 4.3
class CommandKeep(Command):

RULE_IDENTIFIER = 'KEEP'
HANDLER_ID = 'KEEP'
HAS_BLOCKS = False

def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
Expand Down
3 changes: 2 additions & 1 deletion sifter/commands/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
# RFC 5435
class CommandNotify(Command):

RULE_IDENTIFIER = 'NOTIFY'
HANDLER_ID = 'NOTIFY'
EXTENSION_NAME = 'enotify'
TAGGED_ARGS = {
'from': Tag('FROM', (StringList(1),)),
'importance': Tag('IMPORTANCE', (StringList(1),)),
Expand Down
2 changes: 1 addition & 1 deletion sifter/commands/redirect.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# section 4.2
class CommandRedirect(Command):

RULE_IDENTIFIER = 'REDIRECT'
HANDLER_ID = 'REDIRECT'
POSITIONAL_ARGS = [StringList(length=1)]

def __init__(
Expand Down
9 changes: 8 additions & 1 deletion sifter/commands/reject.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,18 @@
# section 3.2
class CommandReject(Command):

RULE_IDENTIFIER: Text = 'REJECT'
HANDLER_ID: Text = 'REJECT'
EXTENSION_NAME = 'reject'
POSITIONAL_ARGS = [StringList()]

def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
reject_message = self.positional_args[0][0] # type: ignore
state.actions.append('reject', reject_message)
state.actions.cancel_implicit_keep()
return None


class CommandEReject(CommandReject):

HANDLER_ID: Text = 'EREJECT'
EXTENSION_NAME = 'ereject'
2 changes: 1 addition & 1 deletion sifter/commands/require.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# section 3.2
class CommandRequire(Command):

RULE_IDENTIFIER: Text = 'REQUIRE'
HANDLER_ID: Text = 'REQUIRE'
POSITIONAL_ARGS = [StringList()]

def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
Expand Down
2 changes: 1 addition & 1 deletion sifter/commands/stop.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# section 3.3
class CommandStop(Command):

RULE_IDENTIFIER = 'STOP'
HANDLER_ID = 'STOP'

def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
state.actions.append('stop')
Expand Down
3 changes: 2 additions & 1 deletion sifter/commands/variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
# RFC 5229
class CommandSet(Command):

RULE_IDENTIFIER = 'SET'
HANDLER_ID = 'SET'
EXTENSION_NAME = 'variables'
TAGGED_ARGS = {
'lower': Tag('LOWER'),
'upper': Tag('UPPER'),
Expand Down
4 changes: 2 additions & 2 deletions sifter/comparators/ascii_casemap.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

class ComparatorASCIICasemap(ComparatorOctet):

COMPARATOR_ID = 'i;ascii-casemap'
HANDLER_ID = 'i;ascii-casemap'

@classmethod
def sort_key(cls, s: Text) -> Text:
Expand All @@ -21,7 +21,7 @@ def sort_key(cls, s: Text) -> Text:

class ComparatorASCIICasemapnoi(ComparatorOctet):

COMPARATOR_ID = ';ascii-casemap'
HANDLER_ID = ';ascii-casemap'

@classmethod
def sort_key(cls, s: Text) -> Text:
Expand Down
2 changes: 1 addition & 1 deletion sifter/comparators/octet.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

class ComparatorOctet(Comparator):

COMPARATOR_ID = 'i;octet'
HANDLER_ID = 'i;octet'

@classmethod
def cmp_is(cls, str1: Text, str2: Text, state: EvaluationState) -> bool:
Expand Down
29 changes: 13 additions & 16 deletions sifter/extensions/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import (
TYPE_CHECKING,
cast,
Dict,
List,
Text,
Expand All @@ -17,6 +18,7 @@
from sifter.grammar.test import Test
if TYPE_CHECKING:
from sifter.grammar.tag import Tag
from sifter.grammar.sieveobject import SieveObject


class ExtensionRegistry():
Expand All @@ -27,37 +29,32 @@ class ExtensionRegistry():
Text,
Union[
bool,
Type['Comparator'],
Type['Rule'],
Type['NotificationMethod']
Type['SieveObject']
]
]
] = {}
DEFAULT_EXTENSION: List[Text] = [
'body',
'comparator-i;ascii-casemap',
'comparator-i;octet',
'enotify',
'fileinto',
'imap4flags',
'regex',
'reject',
'variables',
'comparator-i;octet'
'regex'
]

def __init__(self) -> None:
for extension_name in self.DEFAULT_EXTENSION:
self.register_extension(extension_name)

for entry_point in pkg_resources.iter_entry_points('sifter_extensions'):
self.register_handler(entry_point.load())
sifter_extension_cls = cast(Type['SieveObject'], entry_point.load())
self.register_handler(sifter_extension_cls)
if sifter_extension_cls.EXTENSION_NAME is not None:
self.register_extension(sifter_extension_cls.EXTENSION_NAME)

@classmethod
def register_extension(cls, extension_name: Text) -> None:
cls.register('extension', extension_name, True)

@classmethod
def register_handler(cls, ext_cls: Union[Type['Comparator'], Type['Rule']]) -> None:
def register_handler(cls, ext_cls: Type['SieveObject']) -> None:
cls.register(ext_cls.handler_type(), ext_cls.handler_id(), ext_cls)

@classmethod
Expand Down Expand Up @@ -99,18 +96,18 @@ def register(
cls,
handler_type: Text,
handler_id: Text,
value: Union[bool, Type['Comparator'], Type['Rule']]
value: Union[bool, Type['SieveObject']]
) -> None:
cls._HANDLERS_MAP.setdefault(handler_type, {})[handler_id] = value

@classmethod
def unregister(
cls, handler_type: Text, handler_id: Text
) -> Optional[Union[bool, Type[NotificationMethod], Type['Comparator'], Type['Rule']]]:
) -> Optional[Union[bool, Type['SieveObject']]]:
return cls._HANDLERS_MAP.get(handler_type, {}).pop(handler_id, None)

@classmethod
def get(
cls, handler_type: Text, handler_id: Text
) -> Optional[Union[bool, Type[NotificationMethod], Type['Comparator'], Type['Rule']]]:
) -> Optional[Union[bool, Type['SieveObject']]]:
return cls._HANDLERS_MAP.get(handler_type, {}).get(handler_id, None)
4 changes: 2 additions & 2 deletions sifter/grammar/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

class Command(Rule):

RULE_TYPE: Text = 'command'
HANDLER_TYPE: Text = 'command'
HAS_BLOCKS: bool = True
BLOCKS_MAX: int = 0

Expand Down Expand Up @@ -53,7 +53,7 @@ def __str__(self) -> Text:
def validate_block_size(self, max_commands: int) -> None:
if len(self.block.commands) > max_commands:
raise RuleSyntaxError(
"%s takes no more than %d commands" % (self.RULE_IDENTIFIER, max_commands)
"%s takes no more than %d commands" % (self.HANDLER_ID, max_commands)
)

def validate(self) -> Tuple[
Expand Down
15 changes: 3 additions & 12 deletions sifter/grammar/comparator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,14 @@
)

import re
from sifter.grammar.sieveobject import SieveObject
from sifter.grammar.state import EvaluationState


# The official definition of comparators is in RFC 4790
class Comparator(object):
class Comparator(SieveObject):

COMPARATOR_ID: Optional[Text] = None

@classmethod
def handler_type(cls) -> Text:
return 'comparator'

@classmethod
def handler_id(cls) -> Text:
if cls.COMPARATOR_ID is None:
raise NotImplementedError('Rule must be implemented as subclass as COMPARATOR_ID must be set')
return cls.COMPARATOR_ID
HANDLER_TYPE = 'comparator'

@classmethod
def sort_key(cls, s: Text) -> Text:
Expand Down
12 changes: 6 additions & 6 deletions sifter/grammar/grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def make_parser(mod: Any, debug: bool = False) -> 'LRParser':
module=mod,
debug=True,
write_tables=False,
errorlog=NullLogger() if not debug else None
errorlog=None #NullLogger() if not debug else None
)

def parse(self, rules: Text, tracking: int = 0) -> CommandList:
Expand All @@ -51,14 +51,14 @@ def p_commands_list(self, p: 'YaccProduction') -> None:
p[0] = p[1]

# section 3.2: REQUIRE command must come before any other commands
if p[2].RULE_IDENTIFIER == 'REQUIRE':
if any(command.RULE_IDENTIFIER != 'REQUIRE' for command in p[0].commands):
if p[2].HANDLER_ID == 'REQUIRE':
if any(command.HANDLER_ID != 'REQUIRE' for command in p[0].commands):
print("REQUIRE command on line %d must come before any other non-REQUIRE commands" % p.lineno(2))
raise SyntaxError

# section 3.1: ELSIF and ELSE must follow IF or another ELSIF
elif p[2].RULE_IDENTIFIER in ('ELSIF', 'ELSE'):
if p[0].commands[-1].RULE_IDENTIFIER not in ('IF', 'ELSIF'):
elif p[2].HANDLER_ID in ('ELSIF', 'ELSE'):
if p[0].commands[-1].HANDLER_ID not in ('IF', 'ELSIF'):
print("ELSIF/ELSE command on line %d must follow an IF/ELSIF command" % p.lineno(2))
raise SyntaxError

Expand Down Expand Up @@ -94,7 +94,7 @@ def p_block(self, p: 'YaccProduction') -> None:
"""block : '{' commands '}' """
# section 3.2: REQUIRE command must come before any other commands,
# which means it can't be in the block of another command
if any(command.RULE_IDENTIFIER == 'REQUIRE' for command in p[2].commands):
if any(command.HANDLER_ID == 'REQUIRE' for command in p[2].commands):
print("REQUIRE command not allowed inside of a block (line %d)" % (p.lineno(2)))
raise SyntaxError
p[0] = p[2]
Expand Down
Loading

0 comments on commit 918cfd9

Please sign in to comment.