Skip to content

Commit a13b9c6

Browse files
authored
Merge pull request #164 from slimta/pytype
Consolidate plugin repos into main
2 parents 3dbb624 + 138cbe3 commit a13b9c6

Some content is hidden

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

51 files changed

+3438
-84
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ examples/cert.pem
1212
*.pyc
1313
*.pyo
1414
.*.swp
15+
.pytype/

.lvimrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
let g:ale_fix_on_save = 1
2+
let g:ale_fixers = {
3+
\ 'python': ['autopep8'],
4+
\}

.travis.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
language: python
22
python:
3-
- "2.7"
4-
- "3.5"
5-
- "3.6"
6-
- "3.7"
3+
- "2.7.18"
4+
- "3.5.9"
5+
- "3.6.12"
6+
- "3.7.9"
7+
- "3.8.6"
78
dist: xenial # https://github.com/travis-ci/travis-ci/issues/9069#issuecomment-425720905
89
install:
910
- travis_retry pip install -r test/requirements.txt
1011
- travis_retry pip install coveralls
1112
- travis_retry pip install -e .
12-
script: py.test --cov=slimta
13+
script:
14+
- py.test --cov=slimta
15+
- flake8 slimta
16+
- pytype -k
1317
after_success:
1418
- coveralls

setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ exclude_lines =
1313
pragma: no cover
1414
raise NotImplementedError
1515

16+
[pytype]
17+
inputs = slimta

setup.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424

2525
setup(name='python-slimta',
26-
version='4.0.11',
26+
version='4.1.0',
2727
author='Ian Good',
2828
author_email='icgood@gmail.com',
2929
description='Lightweight, asynchronous SMTP libraries.',
@@ -35,16 +35,23 @@
3535
'pysasl >= 0.4.0, < 0.5',
3636
'pycares < 3.0.0; python_version < "3.0"',
3737
'pycares >= 1; python_version >= "3.0"'],
38+
extras_require={'spf': ['pyspf', 'py3dns; python_version >= "3.0"',
39+
'pydns; python_version < "3.0"',
40+
'ipaddr; python_version < "3.0"'],
41+
'redis': ['redis'],
42+
'aws': ['boto'],
43+
'disk': ['pyaio >= 0.4; platform_system == "Linux"']},
3844
classifiers=['Development Status :: 3 - Alpha',
3945
'Topic :: Communications :: Email :: Mail Transport Agents',
4046
'Intended Audience :: Developers',
4147
'Intended Audience :: Information Technology',
4248
'License :: OSI Approved :: MIT License',
4349
'Programming Language :: Python',
4450
'Programming Language :: Python :: 2.7',
45-
'Programming Language :: Python :: 3.4',
4651
'Programming Language :: Python :: 3.5',
47-
'Programming Language :: Python :: 3.6'])
52+
'Programming Language :: Python :: 3.6',
53+
'Programming Language :: Python :: 3.7',
54+
'Programming Language :: Python :: 3.8'])
4855

4956

5057
# vim:et:fdm=marker:sts=4:sw=4:ts=4

slimta/cloudstorage/__init__.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Copyright (c) 2013 Ian C. Good
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
# THE SOFTWARE.
20+
#
21+
22+
"""Package containing a module for the different cloud service providers along
23+
with any necessary helper modules.
24+
25+
.. _Cloud Files: http://www.rackspace.com/cloud/files/
26+
.. _Cloud Queues: http://www.rackspace.com/cloud/queues/
27+
.. _S3: http://aws.amazon.com/s3/
28+
.. _SQS: http://aws.amazon.com/sqs/
29+
30+
"""
31+
32+
from __future__ import absolute_import
33+
34+
from slimta.queue import QueueError, QueueStorage
35+
from slimta import logging
36+
37+
__all__ = ['CloudStorageError', 'CloudStorage']
38+
39+
log = logging.getQueueStorageLogger(__name__)
40+
41+
42+
class CloudStorageError(QueueError):
43+
"""Base exception for all exceptions in the package.
44+
45+
"""
46+
pass
47+
48+
49+
class CloudStorage(QueueStorage):
50+
"""This class implements a :class:`~slimta.queue.QueueStorage` backend that
51+
uses cloud services to store messages. It coordinates the storage of
52+
messages and metadata (using `Cloud Files`_ or `S3`_) with the optional
53+
message queue mechanisms (using `Cloud Queues`_ or `SQS`_) that can alert
54+
other *slimta* processes that a new message is available in the object
55+
store.
56+
57+
:param object_store: The object used as the backend for storing message
58+
contents and metadata in the cloud. Currently this can
59+
be an instance of
60+
:class:`~rackspace.RackspaceCloudFiles` or
61+
:class:`~aws.SimpleStorageService`.
62+
:param message_queue: The optional object used
63+
as the backend for alerting other processes that a
64+
new message is in the object store. Currently this
65+
can be an instance of
66+
:class:`~rackspace.RackspaceCloudQueues` or
67+
:class:`~aws.SimpleQueueService`.
68+
69+
"""
70+
71+
def __init__(self, object_store, message_queue=None):
72+
super(CloudStorage, self).__init__()
73+
self.obj_store = object_store
74+
self.msg_queue = message_queue
75+
76+
def write(self, envelope, timestamp):
77+
storage_id = self.obj_store.write_message(envelope, timestamp)
78+
if self.msg_queue:
79+
try:
80+
self.msg_queue.queue_message(storage_id, timestamp)
81+
except Exception:
82+
logging.log_exception(__name__)
83+
log.write(storage_id, envelope)
84+
return storage_id
85+
86+
def set_timestamp(self, id, timestamp):
87+
self.obj_store.set_message_meta(id, timestamp=timestamp)
88+
log.update_meta(id, timestamp=timestamp)
89+
90+
def increment_attempts(self, id):
91+
meta = self.obj_store.get_message_meta(id)
92+
new_attempts = meta['attempts'] + 1
93+
self.obj_store.set_message_meta(id, attempts=new_attempts)
94+
log.update_meta(id, attempts=new_attempts)
95+
return new_attempts
96+
97+
def set_recipients_delivered(self, id, rcpt_indexes):
98+
meta = self.obj_store.get_message_meta(id)
99+
current = meta.get('delivered_indexes', [])
100+
new = current + rcpt_indexes
101+
self.obj_store.set_message_meta(id, delivered_indexes=new)
102+
log.update_meta(id, delivered_indexes=rcpt_indexes)
103+
104+
def load(self):
105+
return self.obj_store.list_messages()
106+
107+
def get(self, id):
108+
envelope, meta = self.obj_store.get_message(id)
109+
delivered_rcpts = meta.get('delivered_indexes', [])
110+
self._remove_delivered_rcpts(envelope, delivered_rcpts)
111+
return envelope, meta.get('attempts', 0)
112+
113+
def remove(self, id):
114+
self.obj_store.delete_message(id)
115+
log.remove(id)
116+
117+
def wait(self):
118+
if self.msg_queue:
119+
for timestamp, storage_id, message_id in self.msg_queue.poll():
120+
yield (timestamp, storage_id)
121+
self.msg_queue.delete(message_id)
122+
self.msg_queue.sleep()
123+
else:
124+
raise NotImplementedError()
125+
126+
127+
# vim:et:fdm=marker:sts=4:sw=4:ts=4

0 commit comments

Comments
 (0)