-
Notifications
You must be signed in to change notification settings - Fork 2
/
aws.py
177 lines (145 loc) · 5.14 KB
/
aws.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import gettext
import logging
import os
from boto.s3 import connection as s3_conn
from boto.s3 import key as s3_key
from . import core
LANGUAGE = "LANGUAGE"
LANG = "LANG"
LC_MESSAGES = "LC_MESSAGES"
LC_ALL = "LC_ALL"
DEFAULT_ENVVARS = [
LANGUAGE,
LC_ALL,
LC_MESSAGES,
LANG
]
_default_localedir = os.path.join("locale")
logger = logging.getLogger(__name__)
class S3FileHandler(core.FileHandler):
"""
A custom file handler to search for and load translations files from an S3
bucket.
The bucket name is pass through via the "options" argument to the core
FileHandler's init function.
The `find` function mimics the default gettext `find` but searches for
file paths inside of an S3 bucket, rather than on the file system itself.
The `open` function simply sets a filename for usage in the `read` function.
The `read` function pulls file contents from an S3 bucket.
The `close` function just nulls out the filename set by `open`.
"""
def __init__(self, *args, **kwargs):
"""
Pulls bucket name and an optional "default_localedir" from the options
dictionary.
Initializes empty filename and connection variables.
"""
super(S3FileHandler, self).__init__(*args, **kwargs)
self._bucket_name = self._options["bucket_name"]
self._aws_access_key_id = self._options.get(
"aws_access_key_id",
os.environ.get("AWS_ACCESS_KEY_ID")
)
self._aws_secret_access_key = self._options.get(
"aws_secret_access_key",
os.environ.get("AWS_SECRET_ACCESS_KEY")
)
self._default_localedir = self._options.get(
"default_localedir",
_default_localedir
)
self._filename = None
self._connection = None
def _get_conn(self):
"""
Open an S3 connection and caches on this instance.
:return: the S3 connection.
"""
if self._connection is None:
self._connection = s3_conn.S3Connection(
aws_access_key_id=self._aws_access_key_id,
aws_secret_access_key=self._aws_secret_access_key
)
return self._connection
def find(self, localedir=None, languages=None, all=0):
"""
Mimic gettext.find almost exactly -- os.path.exists is replaced with
assembling an S3 key and checking for its existence instead.
:param localedir: an optional localedir where translations are found.
:param languages: which languages to search for.
:param all: whether or not to read all found files, or just the first.
:return: a list of file paths or a single file path in S3.
"""
conn = self._get_conn()
bucket = conn.get_bucket(self._bucket_name)
if localedir is None:
localedir = self._default_localedir
if languages is None:
languages = []
for envar in DEFAULT_ENVVARS:
val = os.environ.get(envar)
if val:
languages = val.split(":")
break
if "C" not in languages:
languages.append("C")
nelangs = []
for lang in languages:
for nelang in getattr(gettext, "_expand_lang")(lang):
if nelang not in nelangs:
nelangs.append(nelang)
result = [] if all else None
domain_mo = "%s.mo" % self._domain
for lang in nelangs:
if lang == "C":
break
mofile = os.path.join(
localedir,
lang,
LC_MESSAGES,
domain_mo
)
mofile_lp = os.path.join(
"locale-langpack",
lang,
LC_MESSAGES,
domain_mo
)
key = s3_key.Key(bucket=bucket, name=mofile)
if key.exists():
if all:
result.append(mofile)
else:
return mofile
key = s3_key.Key(bucket=bucket, name=mofile_lp)
if key.exists():
if all:
result.append(mofile_lp)
else:
return mofile_lp
return result
def open(self, filename):
"""
"Opens" a given S3 file.
This just sets the _filename variable for usage in `read`.
:param filename: the filename to 'open'.
"""
self._filename = filename
def read(self):
"""
Get an S3 connection and attempt to read the file.
If the file doesn't exist, return an iterable empty string and let
core gettext blow up as it would on a regular empty file.
:return: file contents or empty string if not found.
"""
conn = self._get_conn()
bucket = conn.get_bucket(self._bucket_name)
key = s3_key.Key(bucket=bucket, name=self._filename)
if key.exists():
return key.get_contents_as_string()
return ""
def close(self):
"""
Clear the _filename variable.
"""
self._filename = None