Skip to content

Commit 890b58d

Browse files
Merge branch 'develop'
2 parents 9ce14b5 + 5419b4b commit 890b58d

47 files changed

Lines changed: 991 additions & 294 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ parser.out
55
parsetab.py
66
*.pyc
77
*.egg-info/
8+
.mypy_cache

setup.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,36 @@
11
#!/usr/bin/env python
22

3-
from distutils.core import setup
3+
from setuptools import setup
44

5-
with open('README.rst') as file:
6-
long_description = file.read()
5+
# read the contents of your README file
6+
from os import path
7+
this_directory = path.abspath(path.dirname(__file__))
8+
with open(path.join(this_directory, 'README.rst'), encoding='utf-8') as f:
9+
long_description = f.read()
710

811
setup(
912
name="sifter",
10-
version="0.1",
11-
author="Gary Peck",
12-
author_email="gary@realify.com",
13+
version="0.2.0",
14+
author="Gary Peck, Manfred Kaiser",
15+
author_email="gary@realify.com, manfred.kaiser@logfile.at",
1316
url="https://github.com/garyp/sifter",
1417
license="BSD",
15-
description="Parser/evaluator for the Sieve filtering language (RFC 5228)",
1618
long_description=long_description,
19+
long_description_content_type='text/x-rst',
20+
project_urls={
21+
'Source': 'https://github.com/garyp/sifter',
22+
'Tracker': 'https://github.com/garyp/sifter/issues',
23+
},
24+
python_requires='>= 3.6',
1725
install_requires=[
1826
"ply",
1927
],
2028
classifiers=[
2129
"Programming Language :: Python",
22-
"Programming Language :: Python :: 2",
2330
"Programming Language :: Python :: 3",
31+
"Programming Language :: Python :: 3.6",
32+
"Programming Language :: Python :: 3.7",
33+
"Programming Language :: Python :: 3.8",
2434
"License :: OSI Approved :: BSD License",
2535
"Development Status :: 4 - Beta",
2636
"Intended Audience :: Developers",
@@ -41,6 +51,7 @@
4151
"sifter.validators",
4252
],
4353
package_data={
54+
"sifter": ['py.typed'],
4455
"sifter.t": ["*.in", "*.out", "*.msg", "*.rules"],
4556
},
4657
)

sifter/commands/discard.py

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,45 @@
1-
import sifter.grammar
1+
from email.message import Message
2+
from typing import (
3+
TYPE_CHECKING,
4+
Optional,
5+
List,
6+
Union,
7+
SupportsInt,
8+
Text
9+
)
10+
11+
from sifter.grammar.actions import Actions
12+
from sifter.grammar.state import EvaluationState
13+
from sifter.grammar.command import Command
14+
from sifter.grammar.command_list import CommandList
15+
16+
if TYPE_CHECKING:
17+
from sifter.grammar.tag import Tag as TagGrammar
18+
from sifter.grammar.string import String
19+
from sifter.grammar.test import Test
220

321
__all__ = ('CommandDiscard',)
422

523

624
# section 4.4
7-
class CommandDiscard(sifter.grammar.Command):
25+
class CommandDiscard(Command):
826

927
RULE_IDENTIFIER = 'DISCARD'
1028

11-
def __init__(self, arguments=None, tests=None, block=None):
29+
def __init__(
30+
self,
31+
arguments: Optional[List[Union['TagGrammar', SupportsInt, List[Union[Text, 'String']]]]] = None,
32+
tests: Optional[List['Test']] = None,
33+
block: Optional[CommandList] = None
34+
) -> None:
1235
super(CommandDiscard, self).__init__(arguments, tests, block)
1336
self.validate_arguments()
1437
self.validate_tests_size(0)
1538
self.validate_block_size(0)
1639

17-
def evaluate(self, message, state):
40+
def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
1841
state.actions.cancel_implicit_keep()
42+
return None
1943

2044

2145
CommandDiscard.register()

sifter/commands/fileinto.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,52 @@
1-
import sifter.grammar
2-
import sifter.validators
1+
from email.message import Message
2+
from typing import (
3+
TYPE_CHECKING,
4+
List,
5+
Optional,
6+
Union,
7+
SupportsInt,
8+
Text
9+
)
10+
11+
from sifter.grammar.command import Command
12+
from sifter.grammar.command_list import CommandList
13+
from sifter.validators.stringlist import StringList
14+
from sifter.grammar.state import EvaluationState
15+
from sifter.grammar.actions import Actions
16+
17+
if TYPE_CHECKING:
18+
from sifter.grammar.tag import Tag as TagGrammar
19+
from sifter.grammar.string import String
20+
from sifter.grammar.test import Test
321

422
__all__ = ('CommandFileInto',)
523

624

725
# section 4.1
8-
class CommandFileInto(sifter.grammar.Command):
26+
class CommandFileInto(Command):
927

1028
RULE_IDENTIFIER = 'FILEINTO'
1129

12-
def __init__(self, arguments=None, tests=None, block=None):
30+
def __init__(
31+
self,
32+
arguments: Optional[List[Union['TagGrammar', SupportsInt, List[Union[Text, 'String']]]]] = None,
33+
tests: Optional[List['Test']] = None,
34+
block: Optional[CommandList] = None
35+
) -> None:
1336
super(CommandFileInto, self).__init__(arguments, tests, block)
1437
_, positional_args = self.validate_arguments(
1538
{},
16-
[sifter.validators.StringList(length=1), ],
39+
[StringList(length=1), ],
1740
)
1841
self.validate_tests_size(0)
1942
self.validate_block_size(0)
2043
self.file_dest = positional_args[0]
2144

22-
def evaluate(self, message, state):
45+
def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
2346
state.check_required_extension('fileinto', 'FILEINTO')
24-
state.actions.append('fileinto', self.file_dest)
47+
state.actions.append('fileinto', self.file_dest) # type: ignore
2548
state.actions.cancel_implicit_keep()
49+
return None
2650

2751

2852
CommandFileInto.register()

sifter/commands/if_cmd.py

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,46 @@
1-
import sifter.grammar
1+
from email.message import Message
2+
from typing import (
3+
TYPE_CHECKING,
4+
List,
5+
Optional,
6+
Union,
7+
SupportsInt,
8+
Text
9+
)
10+
11+
from sifter.grammar.command import Command
12+
from sifter.grammar.command_list import CommandList
13+
from sifter.grammar.state import EvaluationState
14+
from sifter.grammar.actions import Actions
15+
16+
if TYPE_CHECKING:
17+
from sifter.grammar.tag import Tag as TagGrammar
18+
from sifter.grammar.string import String
19+
from sifter.grammar.test import Test
220

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

523

624
# section 3.1
7-
class CommandIfBase(sifter.grammar.Command):
8-
9-
def __init__(self, arguments=None, tests=None, block=None):
25+
class CommandIfBase(Command):
26+
27+
def __init__(
28+
self,
29+
arguments: Optional[List[Union['TagGrammar', SupportsInt, List[Union[Text, 'String']]]]] = None,
30+
tests: Optional[List['Test']] = None,
31+
block: Optional[CommandList] = None
32+
) -> None:
1033
super(CommandIfBase, self).__init__(arguments, tests, block)
1134
self.validate_arguments()
1235
self.validate_tests_size(1)
1336

14-
def evaluate(self, message, state):
37+
def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
1538
if self.tests[0].evaluate(message, state):
1639
result = self.block.evaluate(message, state)
1740
state.last_if = True
1841
return result
19-
else:
20-
state.last_if = False
21-
return None
42+
state.last_if = False
43+
return None
2244

2345

2446
class CommandIf(CommandIfBase):
@@ -33,30 +55,33 @@ class CommandElsIf(CommandIfBase):
3355

3456
RULE_IDENTIFIER = 'ELSIF'
3557

36-
def evaluate(self, message, state):
58+
def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
3759
if state.last_if:
3860
return None
39-
else:
40-
return super(CommandElsIf, self).evaluate(message, state)
61+
return super(CommandElsIf, self).evaluate(message, state)
4162

4263

4364
CommandElsIf.register()
4465

4566

46-
class CommandElse(sifter.grammar.Command):
67+
class CommandElse(Command):
4768

4869
RULE_IDENTIFIER = 'ELSE'
4970

50-
def __init__(self, arguments=None, tests=None, block=None):
71+
def __init__(
72+
self,
73+
arguments: Optional[List[Union['TagGrammar', SupportsInt, List[Union[Text, 'String']]]]] = None,
74+
tests: Optional[List['Test']] = None,
75+
block: Optional[CommandList] = None
76+
) -> None:
5177
super(CommandElse, self).__init__(arguments, tests, block)
5278
self.validate_arguments()
5379
self.validate_tests_size(0)
5480

55-
def evaluate(self, message, state):
81+
def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
5682
if state.last_if:
5783
return None
58-
else:
59-
return self.block.evaluate(message, state)
84+
return self.block.evaluate(message, state)
6085

6186

6287
CommandElse.register()

sifter/commands/keep.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,47 @@
1-
import sifter.grammar
1+
from email.message import Message
2+
from typing import (
3+
TYPE_CHECKING,
4+
List,
5+
Optional,
6+
Union,
7+
SupportsInt,
8+
Text
9+
)
10+
11+
from sifter.grammar.command import Command
12+
from sifter.grammar.command_list import CommandList
13+
from sifter.grammar.state import EvaluationState
14+
from sifter.grammar.actions import Actions
15+
16+
if TYPE_CHECKING:
17+
from sifter.grammar.tag import Tag as TagGrammar
18+
from sifter.grammar.string import String
19+
from sifter.grammar.test import Test
20+
221

322
__all__ = ('CommandKeep',)
423

524

625
# section 4.3
7-
class CommandKeep(sifter.grammar.Command):
26+
class CommandKeep(Command):
827

928
RULE_IDENTIFIER = 'KEEP'
1029

11-
def __init__(self, arguments=None, tests=None, block=None):
30+
def __init__(
31+
self,
32+
arguments: Optional[List[Union['TagGrammar', SupportsInt, List[Union[Text, 'String']]]]] = None,
33+
tests: Optional[List['Test']] = None,
34+
block: Optional[CommandList] = None
35+
) -> None:
1236
super(CommandKeep, self).__init__(arguments, tests, block)
1337
self.validate_arguments()
1438
self.validate_tests_size(0)
1539
self.validate_block_size(0)
1640

17-
def evaluate(self, message, state):
18-
state.actions.append('keep').cancel_implicit_keep()
41+
def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
42+
state.actions.append('keep')
43+
state.actions.cancel_implicit_keep()
44+
return None
1945

2046

2147
CommandKeep.register()

sifter/commands/redirect.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,67 @@
11
import email.utils
2+
from email.message import Message
3+
from typing import (
4+
TYPE_CHECKING,
5+
List,
6+
Optional,
7+
Union,
8+
SupportsInt,
9+
Text
10+
)
211

3-
import sifter.grammar
4-
import sifter.validators
12+
from sifter.grammar.command import Command
13+
from sifter.grammar.command_list import CommandList
14+
from sifter.grammar.rule import RuleSyntaxError
15+
from sifter.validators.stringlist import StringList
16+
from sifter.grammar.state import EvaluationState
17+
from sifter.grammar.actions import Actions
18+
19+
if TYPE_CHECKING:
20+
from sifter.grammar.tag import Tag as TagGrammar
21+
from sifter.grammar.string import String
22+
from sifter.grammar.test import Test
523

624
__all__ = ('CommandRedirect',)
725

826

927
# section 4.2
10-
class CommandRedirect(sifter.grammar.Command):
28+
class CommandRedirect(Command):
1129

1230
RULE_IDENTIFIER = 'REDIRECT'
1331

14-
def __init__(self, arguments=None, tests=None, block=None):
32+
def __init__(
33+
self,
34+
arguments: Optional[List[Union['TagGrammar', SupportsInt, List[Union[Text, 'String']]]]] = None,
35+
tests: Optional[List['Test']] = None,
36+
block: Optional[CommandList] = None
37+
) -> None:
1538
super(CommandRedirect, self).__init__(arguments, tests, block)
1639
_, positional_args = self.validate_arguments(
1740
{},
18-
[sifter.validators.StringList(length=1), ],
41+
[StringList(length=1), ],
1942
)
2043
self.validate_tests_size(0)
2144
self.validate_block_size(0)
45+
if not isinstance(positional_args, list):
46+
raise ValueError("CommandRedirect positional argument error")
47+
if not isinstance(positional_args[0], list):
48+
raise ValueError("CommandRedirect positional argument error")
2249
self.email_address = positional_args[0][0]
2350
# TODO: section 2.4.2.3 constrains the email address to a limited
2451
# subset of valid address formats. need to check if python's
2552
# email.utils also uses this subset or if we need to do our own
2653
# parsing.
2754
realname, emailaddr = email.utils.parseaddr(self.email_address)
2855
if emailaddr == "":
29-
raise sifter.grammar.RuleSyntaxError(
56+
raise RuleSyntaxError(
3057
"REDIRECT destination not a valid email address: %s"
3158
% self.email_address
3259
)
3360

34-
def evaluate(self, message, state):
61+
def evaluate(self, message: Message, state: EvaluationState) -> Optional[Actions]:
3562
state.actions.append('redirect', self.email_address)
3663
state.actions.cancel_implicit_keep()
64+
return None
3765

3866

3967
CommandRedirect.register()

0 commit comments

Comments
 (0)