morp 5.2.1__tar.gz → 7.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- morp-7.0.0/PKG-INFO +212 -0
- morp-7.0.0/README.md +181 -0
- {morp-5.2.1 → morp-7.0.0}/morp/__init__.py +1 -1
- {morp-5.2.1 → morp-7.0.0}/morp/config.py +31 -17
- {morp-5.2.1 → morp-7.0.0}/morp/interface/__init__.py +22 -29
- morp-7.0.0/morp/interface/base.py +441 -0
- {morp-5.2.1 → morp-7.0.0}/morp/interface/dropfile.py +29 -24
- morp-7.0.0/morp/interface/postgres.py +368 -0
- {morp-5.2.1 → morp-7.0.0}/morp/interface/sqs.py +82 -46
- morp-7.0.0/morp/message.py +354 -0
- morp-7.0.0/morp.egg-info/PKG-INFO +212 -0
- {morp-5.2.1 → morp-7.0.0}/morp.egg-info/SOURCES.txt +2 -3
- morp-7.0.0/morp.egg-info/requires.txt +14 -0
- morp-7.0.0/morp.egg-info/top_level.txt +3 -0
- morp-7.0.0/pyproject.toml +70 -0
- morp-5.2.1/PKG-INFO +0 -138
- morp-5.2.1/README.md +0 -117
- morp-5.2.1/morp/__main__.py +0 -202
- morp-5.2.1/morp/interface/base.py +0 -325
- morp-5.2.1/morp/message.py +0 -313
- morp-5.2.1/morp.egg-info/PKG-INFO +0 -138
- morp-5.2.1/morp.egg-info/entry_points.txt +0 -2
- morp-5.2.1/morp.egg-info/requires.txt +0 -4
- morp-5.2.1/morp.egg-info/top_level.txt +0 -1
- morp-5.2.1/setup.py +0 -59
- {morp-5.2.1 → morp-7.0.0}/LICENSE.txt +0 -0
- {morp-5.2.1 → morp-7.0.0}/morp/compat.py +0 -0
- {morp-5.2.1 → morp-7.0.0}/morp/exception.py +0 -0
- {morp-5.2.1 → morp-7.0.0}/morp.egg-info/dependency_links.txt +0 -0
- {morp-5.2.1 → morp-7.0.0}/setup.cfg +0 -0
morp-7.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: morp
|
|
3
|
+
Version: 7.0.0
|
|
4
|
+
Summary: Send and receive messages without thinking about it
|
|
5
|
+
Author-email: Jay Marcyes <jay@marcyes.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, http://github.com/Jaymon/morp
|
|
8
|
+
Project-URL: Repository, https://github.com/Jaymon/morp
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Web Environment
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Topic :: Database
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
+
Classifier: Topic :: Utilities
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE.txt
|
|
20
|
+
Requires-Dist: dsnparse
|
|
21
|
+
Requires-Dist: datatypes
|
|
22
|
+
Provides-Extra: tests
|
|
23
|
+
Requires-Dist: testdata; extra == "tests"
|
|
24
|
+
Provides-Extra: postgres
|
|
25
|
+
Requires-Dist: psycopg; extra == "postgres"
|
|
26
|
+
Provides-Extra: sqs
|
|
27
|
+
Requires-Dist: boto3; extra == "sqs"
|
|
28
|
+
Provides-Extra: encryption
|
|
29
|
+
Requires-Dist: cryptography; extra == "encryption"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# Morp
|
|
33
|
+
|
|
34
|
+
Simple message processing without really thinking about it. Morp can use dropfiles (simple text files), Postgres, and [Amazon SQS](http://aws.amazon.com/sqs/).
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
Use pip to install the latest stable version:
|
|
40
|
+
|
|
41
|
+
pip install morp
|
|
42
|
+
|
|
43
|
+
Morp only supports the dropfiles interface out of the box, you'll need to install certain dependencies depending on what interface you want to use:
|
|
44
|
+
|
|
45
|
+
pip install morp[sqs]
|
|
46
|
+
pip install morp[postgres]
|
|
47
|
+
|
|
48
|
+
To install the development version:
|
|
49
|
+
|
|
50
|
+
pip install -U "git+https://github.com/Jaymon/morp#egg=morp"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
## 1 Minute Getting Started
|
|
54
|
+
|
|
55
|
+
Send and receive a `Foo` message.
|
|
56
|
+
|
|
57
|
+
First, let's set our environment variable to use dropfiles (local files suitable for development and prototyping) interface:
|
|
58
|
+
|
|
59
|
+
export MORP_DSN=dropfile:${TMPDIR}
|
|
60
|
+
|
|
61
|
+
Then, let's create three files in our working directory:
|
|
62
|
+
|
|
63
|
+
* `tasks.py` - We'll define our `Message` classes here.
|
|
64
|
+
* `send.py` - We'll send messages from this script.
|
|
65
|
+
* `recv.py` - We'll receive messages from this script.
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
Let's create our `Message` class in `tasks.py`:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
# tasks.py
|
|
72
|
+
from morp import Message
|
|
73
|
+
|
|
74
|
+
class Foo(Message):
|
|
75
|
+
some_field: int
|
|
76
|
+
some_other_field: str
|
|
77
|
+
|
|
78
|
+
def handle(self):
|
|
79
|
+
# this will be run when a Foo message is consumed
|
|
80
|
+
print(self.fields)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Now, let's flesh out our `recv.py` file:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# recv.py
|
|
87
|
+
|
|
88
|
+
import asyncio
|
|
89
|
+
|
|
90
|
+
# import our Foo message class from our tasks.py file
|
|
91
|
+
from tasks import Foo
|
|
92
|
+
|
|
93
|
+
# Foo's `process` method will call `Foo.handle` for each Foo instance received
|
|
94
|
+
asyncio.run(Foo.process())
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
And start it up:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
$ python recv.py
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
Finally, let's send some messages by fleshing out `send.py`:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
# send.py
|
|
108
|
+
|
|
109
|
+
import asyncio
|
|
110
|
+
|
|
111
|
+
from tasks import Foo
|
|
112
|
+
|
|
113
|
+
async def send_messages():
|
|
114
|
+
# create a message and send it manually
|
|
115
|
+
f = Foo()
|
|
116
|
+
f.some_field = 1
|
|
117
|
+
f.some_other_field = "one"
|
|
118
|
+
f.ignored_field = True
|
|
119
|
+
await f.send()
|
|
120
|
+
|
|
121
|
+
# quickly send a message
|
|
122
|
+
await Foo.create(
|
|
123
|
+
some_field=2,
|
|
124
|
+
some_other_field="two",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
asyncio.run(send_messages())
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
And running it in a separate shell from the shell running our `recv.py` script (it should send two messages):
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
$ python send.py
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
That's it! Our running `recv.py` script should've received the messages we sent when we ran our `send.py` script.
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
## DSN
|
|
140
|
+
|
|
141
|
+
You configure your connection using a dsn in the form:
|
|
142
|
+
|
|
143
|
+
InterfaceName://username:password@host:port/path?param1=value1¶m2=value2
|
|
144
|
+
|
|
145
|
+
So, to connect to [Amazon SQS](http://aws.amazon.com/sqs/), you would do:
|
|
146
|
+
|
|
147
|
+
sqs://${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}@
|
|
148
|
+
|
|
149
|
+
You can also override some default values like `region` and `read_lock`:
|
|
150
|
+
|
|
151
|
+
sqs://${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}@?region=${AWS_DEFAULT_REGION}&read_lock=120
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
### Serializers
|
|
155
|
+
|
|
156
|
+
* `pickle` (default)
|
|
157
|
+
* `json`
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
MORP_DSN="sqs://x:x@?serializer=json"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
## Encryption
|
|
165
|
+
|
|
166
|
+
You might need to install some dependencies:
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
pip install morp[encryption]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
If you would like to encrypt all your messages, you can pass in a `key` argument to your DSN and Morp will take care of encrypting and decrypting the messages for you transparently.
|
|
173
|
+
|
|
174
|
+
Let's just modify our DSN to pass in our key:
|
|
175
|
+
|
|
176
|
+
sqs://${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}@?key=jy4XWRuEsrH98RD2VeLG62uVLCPWpdUh
|
|
177
|
+
|
|
178
|
+
That's it, every message will now be encrypted on send and decrypted on receive. If you're using SQS you can also use [Amazon's key management service](https://github.com/Jaymon/morp/blob/master/docs/KMS.md) to handle the encryption for you.
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
## Environment configuration
|
|
182
|
+
|
|
183
|
+
### MORP_DISABLED
|
|
184
|
+
|
|
185
|
+
By default every message will be sent, if you just want to test functionality without actually sending the message you can set this environment variable to turn off all the queues.
|
|
186
|
+
|
|
187
|
+
MORP_DISABLED = 1 # queue is off
|
|
188
|
+
MORP_DISABLED = 0 # queue is on
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
### MORP_PREFIX
|
|
192
|
+
|
|
193
|
+
If you would like to have your queue names prefixed with something (eg, `prod` or `dev`) then you can set this environment variable and it will be prefixed to the queue name.
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
### MORP_DSN
|
|
197
|
+
|
|
198
|
+
Set this environment variable with your connection DSN so Morp can automatically configure itself when the interface is first requested.
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
## FAQ
|
|
202
|
+
|
|
203
|
+
### I would like to have multiple queues
|
|
204
|
+
|
|
205
|
+
By default, Morp will send any message from any `morp.Message` derived class to `Message.get_name()`, you can override this behavior by giving your child class a `._name` property:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from morp import Message
|
|
209
|
+
|
|
210
|
+
class childMsg(Message):
|
|
211
|
+
_name = "custom-queue-name"
|
|
212
|
+
```
|
morp-7.0.0/README.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Morp
|
|
2
|
+
|
|
3
|
+
Simple message processing without really thinking about it. Morp can use dropfiles (simple text files), Postgres, and [Amazon SQS](http://aws.amazon.com/sqs/).
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Use pip to install the latest stable version:
|
|
9
|
+
|
|
10
|
+
pip install morp
|
|
11
|
+
|
|
12
|
+
Morp only supports the dropfiles interface out of the box, you'll need to install certain dependencies depending on what interface you want to use:
|
|
13
|
+
|
|
14
|
+
pip install morp[sqs]
|
|
15
|
+
pip install morp[postgres]
|
|
16
|
+
|
|
17
|
+
To install the development version:
|
|
18
|
+
|
|
19
|
+
pip install -U "git+https://github.com/Jaymon/morp#egg=morp"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## 1 Minute Getting Started
|
|
23
|
+
|
|
24
|
+
Send and receive a `Foo` message.
|
|
25
|
+
|
|
26
|
+
First, let's set our environment variable to use dropfiles (local files suitable for development and prototyping) interface:
|
|
27
|
+
|
|
28
|
+
export MORP_DSN=dropfile:${TMPDIR}
|
|
29
|
+
|
|
30
|
+
Then, let's create three files in our working directory:
|
|
31
|
+
|
|
32
|
+
* `tasks.py` - We'll define our `Message` classes here.
|
|
33
|
+
* `send.py` - We'll send messages from this script.
|
|
34
|
+
* `recv.py` - We'll receive messages from this script.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
Let's create our `Message` class in `tasks.py`:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
# tasks.py
|
|
41
|
+
from morp import Message
|
|
42
|
+
|
|
43
|
+
class Foo(Message):
|
|
44
|
+
some_field: int
|
|
45
|
+
some_other_field: str
|
|
46
|
+
|
|
47
|
+
def handle(self):
|
|
48
|
+
# this will be run when a Foo message is consumed
|
|
49
|
+
print(self.fields)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Now, let's flesh out our `recv.py` file:
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
# recv.py
|
|
56
|
+
|
|
57
|
+
import asyncio
|
|
58
|
+
|
|
59
|
+
# import our Foo message class from our tasks.py file
|
|
60
|
+
from tasks import Foo
|
|
61
|
+
|
|
62
|
+
# Foo's `process` method will call `Foo.handle` for each Foo instance received
|
|
63
|
+
asyncio.run(Foo.process())
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
And start it up:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
$ python recv.py
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
Finally, let's send some messages by fleshing out `send.py`:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
# send.py
|
|
77
|
+
|
|
78
|
+
import asyncio
|
|
79
|
+
|
|
80
|
+
from tasks import Foo
|
|
81
|
+
|
|
82
|
+
async def send_messages():
|
|
83
|
+
# create a message and send it manually
|
|
84
|
+
f = Foo()
|
|
85
|
+
f.some_field = 1
|
|
86
|
+
f.some_other_field = "one"
|
|
87
|
+
f.ignored_field = True
|
|
88
|
+
await f.send()
|
|
89
|
+
|
|
90
|
+
# quickly send a message
|
|
91
|
+
await Foo.create(
|
|
92
|
+
some_field=2,
|
|
93
|
+
some_other_field="two",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
asyncio.run(send_messages())
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
And running it in a separate shell from the shell running our `recv.py` script (it should send two messages):
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
$ python send.py
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
That's it! Our running `recv.py` script should've received the messages we sent when we ran our `send.py` script.
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
## DSN
|
|
109
|
+
|
|
110
|
+
You configure your connection using a dsn in the form:
|
|
111
|
+
|
|
112
|
+
InterfaceName://username:password@host:port/path?param1=value1¶m2=value2
|
|
113
|
+
|
|
114
|
+
So, to connect to [Amazon SQS](http://aws.amazon.com/sqs/), you would do:
|
|
115
|
+
|
|
116
|
+
sqs://${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}@
|
|
117
|
+
|
|
118
|
+
You can also override some default values like `region` and `read_lock`:
|
|
119
|
+
|
|
120
|
+
sqs://${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}@?region=${AWS_DEFAULT_REGION}&read_lock=120
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
### Serializers
|
|
124
|
+
|
|
125
|
+
* `pickle` (default)
|
|
126
|
+
* `json`
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
MORP_DSN="sqs://x:x@?serializer=json"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
## Encryption
|
|
134
|
+
|
|
135
|
+
You might need to install some dependencies:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
pip install morp[encryption]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
If you would like to encrypt all your messages, you can pass in a `key` argument to your DSN and Morp will take care of encrypting and decrypting the messages for you transparently.
|
|
142
|
+
|
|
143
|
+
Let's just modify our DSN to pass in our key:
|
|
144
|
+
|
|
145
|
+
sqs://${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}@?key=jy4XWRuEsrH98RD2VeLG62uVLCPWpdUh
|
|
146
|
+
|
|
147
|
+
That's it, every message will now be encrypted on send and decrypted on receive. If you're using SQS you can also use [Amazon's key management service](https://github.com/Jaymon/morp/blob/master/docs/KMS.md) to handle the encryption for you.
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
## Environment configuration
|
|
151
|
+
|
|
152
|
+
### MORP_DISABLED
|
|
153
|
+
|
|
154
|
+
By default every message will be sent, if you just want to test functionality without actually sending the message you can set this environment variable to turn off all the queues.
|
|
155
|
+
|
|
156
|
+
MORP_DISABLED = 1 # queue is off
|
|
157
|
+
MORP_DISABLED = 0 # queue is on
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
### MORP_PREFIX
|
|
161
|
+
|
|
162
|
+
If you would like to have your queue names prefixed with something (eg, `prod` or `dev`) then you can set this environment variable and it will be prefixed to the queue name.
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
### MORP_DSN
|
|
166
|
+
|
|
167
|
+
Set this environment variable with your connection DSN so Morp can automatically configure itself when the interface is first requested.
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
## FAQ
|
|
171
|
+
|
|
172
|
+
### I would like to have multiple queues
|
|
173
|
+
|
|
174
|
+
By default, Morp will send any message from any `morp.Message` derived class to `Message.get_name()`, you can override this behavior by giving your child class a `._name` property:
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from morp import Message
|
|
178
|
+
|
|
179
|
+
class childMsg(Message):
|
|
180
|
+
_name = "custom-queue-name"
|
|
181
|
+
```
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
from __future__ import unicode_literals, division, print_function, absolute_import
|
|
3
|
-
import hashlib
|
|
4
|
-
import os
|
|
5
2
|
import base64
|
|
6
3
|
|
|
7
4
|
import dsnparse
|
|
8
|
-
from datatypes import
|
|
5
|
+
from datatypes import ReflectName
|
|
9
6
|
from datatypes import property as cachedproperty
|
|
10
7
|
from datatypes.config import Environ
|
|
11
8
|
|
|
@@ -14,9 +11,11 @@ from .compat import *
|
|
|
14
11
|
|
|
15
12
|
|
|
16
13
|
class Connection(object):
|
|
17
|
-
"""The base connection class, you will most likely always use DsnConnection
|
|
14
|
+
"""The base connection class, you will most likely always use DsnConnection
|
|
15
|
+
"""
|
|
18
16
|
name = ""
|
|
19
|
-
"""str, the name of this connection, handy when you have more than one used
|
|
17
|
+
"""str, the name of this connection, handy when you have more than one used
|
|
18
|
+
interface (eg, nsq)"""
|
|
20
19
|
|
|
21
20
|
username = ""
|
|
22
21
|
"""str, the username (if needed)"""
|
|
@@ -31,14 +30,15 @@ class Connection(object):
|
|
|
31
30
|
"""str, the path (if applicable)"""
|
|
32
31
|
|
|
33
32
|
interface_name = ""
|
|
34
|
-
"""str, full Interface class name -- the interface that will connect to the
|
|
33
|
+
"""str, full Interface class name -- the interface that will connect to the
|
|
34
|
+
messaging backend"""
|
|
35
35
|
|
|
36
36
|
options = None
|
|
37
37
|
"""dict, any other interface specific options you need"""
|
|
38
38
|
|
|
39
39
|
@property
|
|
40
40
|
def interface_class(self):
|
|
41
|
-
interface_class =
|
|
41
|
+
interface_class = ReflectName(self.interface_name).get_class()
|
|
42
42
|
return interface_class
|
|
43
43
|
|
|
44
44
|
@property
|
|
@@ -65,10 +65,10 @@ class Connection(object):
|
|
|
65
65
|
return key
|
|
66
66
|
|
|
67
67
|
def __init__(self, **kwargs):
|
|
68
|
-
"""
|
|
69
|
-
|
|
68
|
+
"""set all the values by passing them into this constructor, any
|
|
69
|
+
unrecognized kwargs get put into .options
|
|
70
70
|
|
|
71
|
-
:
|
|
71
|
+
:example:
|
|
72
72
|
c = Connection(
|
|
73
73
|
interface_name="...",
|
|
74
74
|
hosts=[("host", port), ("host2", port2)],
|
|
@@ -84,14 +84,23 @@ class Connection(object):
|
|
|
84
84
|
for key, val in kwargs.items():
|
|
85
85
|
if hasattr(self, key):
|
|
86
86
|
setattr(self, key, val)
|
|
87
|
+
|
|
87
88
|
else:
|
|
88
89
|
self.options[key] = val
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
self.options.setdefault('
|
|
91
|
+
# 1 hour to process message
|
|
92
|
+
self.options.setdefault('max_timeout', 3600)
|
|
93
|
+
|
|
94
|
+
# failure backoff multiplier
|
|
95
|
+
self.options.setdefault('backoff_multiplier', 5)
|
|
92
96
|
|
|
93
97
|
def get_netlocs(self, default_port):
|
|
94
|
-
return [
|
|
98
|
+
return [
|
|
99
|
+
"{}:{}".format(
|
|
100
|
+
h[0],
|
|
101
|
+
default_port if h[1] is None else h[1]
|
|
102
|
+
) for h in self.hosts
|
|
103
|
+
]
|
|
95
104
|
|
|
96
105
|
def get_option(self, key, default_val):
|
|
97
106
|
return getattr(self.options, key, default_val)
|
|
@@ -101,7 +110,7 @@ class DsnConnection(Connection):
|
|
|
101
110
|
"""
|
|
102
111
|
Create a connection object from a dsn in the form
|
|
103
112
|
|
|
104
|
-
InterfaceName://username:password@host:port?opt1=val1
|
|
113
|
+
InterfaceName://username:password@host:port?opt1=val1#connection_name
|
|
105
114
|
|
|
106
115
|
example -- connect to amazon SQS
|
|
107
116
|
|
|
@@ -116,7 +125,7 @@ class DsnConnection(Connection):
|
|
|
116
125
|
d = {'options': {}, 'hosts': []}
|
|
117
126
|
parser = dsnparse.parse(dsn)
|
|
118
127
|
p = parser.fields
|
|
119
|
-
|
|
128
|
+
d["dsn"] = parser.parser.dsn
|
|
120
129
|
|
|
121
130
|
# get the scheme, which is actually our interface_name
|
|
122
131
|
d['interface_name'] = self.normalize_scheme(p["scheme"])
|
|
@@ -137,7 +146,12 @@ class DsnConnection(Connection):
|
|
|
137
146
|
ret = v
|
|
138
147
|
d = {
|
|
139
148
|
"morp.interface.sqs:SQS": set(["sqs"]),
|
|
140
|
-
"morp.interface.dropfile:Dropfile": set(["dropfile"]),
|
|
149
|
+
"morp.interface.dropfile:Dropfile": set(["dropfile", "file"]),
|
|
150
|
+
"morp.interface.postgres:Postgres": set([
|
|
151
|
+
"postgres",
|
|
152
|
+
"postgresql",
|
|
153
|
+
"psql",
|
|
154
|
+
]),
|
|
141
155
|
}
|
|
142
156
|
|
|
143
157
|
kv = v.lower()
|
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import sys
|
|
5
|
-
from contextlib import contextmanager
|
|
6
|
-
import base64
|
|
7
|
-
import datetime
|
|
8
|
-
|
|
9
|
-
from cryptography.fernet import Fernet
|
|
2
|
+
|
|
10
3
|
import dsnparse
|
|
11
4
|
|
|
12
5
|
from ..compat import *
|
|
13
6
|
from ..config import DsnConnection
|
|
14
|
-
from ..exception import InterfaceError
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
logger = logging.getLogger(__name__)
|
|
18
7
|
|
|
19
8
|
|
|
20
9
|
interfaces = {}
|
|
@@ -47,20 +36,23 @@ def find_environ(dsn_env_name='MORP_DSN', connection_class=DsnConnection):
|
|
|
47
36
|
|
|
48
37
|
configure interfaces based on environment variables
|
|
49
38
|
|
|
50
|
-
by default, when morp is imported, it will look for MORP_DSN, and MORP_DSN_N
|
|
51
|
-
N is 1 through infinity) in the environment, if it finds them, it
|
|
52
|
-
are dsn urls that morp understands and will configure
|
|
53
|
-
don't want this behavior (ie, you want to
|
|
54
|
-
you don't have any environment
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
39
|
+
by default, when morp is imported, it will look for MORP_DSN, and MORP_DSN_N
|
|
40
|
+
(where N is 1 through infinity) in the environment, if it finds them, it
|
|
41
|
+
will assume they are dsn urls that morp understands and will configure
|
|
42
|
+
connections with them. If you don't want this behavior (ie, you want to
|
|
43
|
+
configure morp manually) then just make sure you don't have any environment
|
|
44
|
+
variables with matching names
|
|
45
|
+
|
|
46
|
+
The num checks (eg MORP_DSN_1, MORP_DSN_2) go in order, so you can't do
|
|
47
|
+
MORP_DSN_1, MORP_DSN_3, because it will fail on _2 and move on, so make sure
|
|
48
|
+
your num dsns are in order (eg, 1, 2, 3, ...)
|
|
49
|
+
|
|
50
|
+
:param dsn_env_name: str, the environment variable name to find the DSN,
|
|
51
|
+
this will aslo check for values of <dsn_env_name>_N where N is 1 -> N,
|
|
52
|
+
so you can configure multiple DSNs in the environment and this will pick
|
|
53
|
+
them all up
|
|
54
|
+
:param connection_class: Connection, the class that will receive the dsn
|
|
55
|
+
values
|
|
64
56
|
:returns: generator<connection_class>
|
|
65
57
|
"""
|
|
66
58
|
return dsnparse.parse_environs(dsn_env_name, parse_class=connection_class)
|
|
@@ -77,10 +69,11 @@ def configure(dsn, connection_class=DsnConnection):
|
|
|
77
69
|
"""
|
|
78
70
|
configure an interface to be used to send messages to a backend
|
|
79
71
|
|
|
80
|
-
you use this function to configure an Interface using a dsn, then you can
|
|
81
|
-
that interface using the get_interface() method
|
|
72
|
+
you use this function to configure an Interface using a dsn, then you can
|
|
73
|
+
get that interface using the get_interface() method
|
|
82
74
|
|
|
83
|
-
:param dsn: string, a properly formatted morp dsn, see DsnConnection for how
|
|
75
|
+
:param dsn: string, a properly formatted morp dsn, see DsnConnection for how
|
|
76
|
+
to format the dsn
|
|
84
77
|
"""
|
|
85
78
|
#global interfaces
|
|
86
79
|
c = dsnparse.parse(dsn, parse_class=connection_class)
|