`_ in GitHub
162 | 6. Publish the new release in PyPI: ``python setup.py publish`` (Requires access to PyPI)
163 |
164 |
165 | References
166 | ----------
167 |
168 | * https://github.com/encode/django-rest-framework/blob/master/CONTRIBUTING.md
169 | * https://docs.djangoproject.com/en/dev/internals/contributing/
170 |
--------------------------------------------------------------------------------
/anon/base.py:
--------------------------------------------------------------------------------
1 | # stdlib
2 | from collections import OrderedDict
3 | from logging import getLogger
4 |
5 | # deps
6 | from chunkator import chunkator_page
7 |
8 | # local
9 | from anon.compat import bulk_update
10 |
11 |
12 | logger = getLogger(__name__)
13 |
14 |
15 | class OrderedDeclaration(object):
16 | """ Any classes inheriting from this will have an unique global counter
17 | associated with it. This counter is used to determine the order in
18 | which fields were declarated
19 |
20 | Idea taken from: https://stackoverflow.com/a/4460034/639465
21 | Also inspired by https://github.com/FactoryBoy/factory_boy
22 | """
23 |
24 | global_counter = 0
25 |
26 | def __init__(self):
27 | self._order = self.__class__.global_counter
28 | self.__class__.global_counter += 1
29 |
30 |
31 | class LazyAttribute(OrderedDeclaration):
32 | def __init__(self, lazy_fn):
33 | super(LazyAttribute, self).__init__()
34 | self.lazy_fn = lazy_fn
35 |
36 | def __call__(self, *args, **kwargs):
37 | return self.lazy_fn(*args, **kwargs)
38 |
39 |
40 | def lazy_attribute(lazy_fn):
41 | """ Returns LazyAttribute objects, that basically marks functions that
42 | should take `obj` as first parameter. This is useful when you need
43 | to take in consideration other values of `obj`
44 |
45 | Example:
46 |
47 | >>> full_name = lazy_attribute(o: o.first_name + o.last_name)
48 |
49 | """
50 | return LazyAttribute(lazy_fn)
51 |
52 |
53 | class BaseAnonymizer(object):
54 | def run(self, select_chunk_size=None, **bulk_update_kwargs):
55 | self._declarations = self.get_declarations()
56 |
57 | queryset = self.get_queryset()
58 | update_fields = list(self._declarations.keys())
59 |
60 | update_batch_size = bulk_update_kwargs.pop(
61 | "batch_size", self._meta.update_batch_size
62 | )
63 |
64 | if select_chunk_size is None:
65 | select_chunk_size = self._meta.select_chunk_size
66 |
67 | if update_batch_size > select_chunk_size:
68 | raise ValueError(
69 | "update_batch_size ({}) should not be higher than "
70 | "select_chunk_size ({})".format(update_batch_size, select_chunk_size)
71 | )
72 |
73 | # info used in log messages
74 | model_name = self._meta.model.__name__
75 | current_batch = 0
76 |
77 | for page in chunkator_page(queryset, chunk_size=select_chunk_size):
78 | logger.info(
79 | "Updating {}... {}-{}".format(
80 | model_name, current_batch, current_batch + select_chunk_size
81 | )
82 | )
83 | current_batch += select_chunk_size
84 |
85 | objs = []
86 | for obj in page:
87 | self.patch_object(obj)
88 | objs.append(obj)
89 |
90 | if update_fields:
91 | bulk_update(
92 | objs,
93 | update_fields,
94 | self.get_manager(),
95 | **dict(batch_size=update_batch_size, **bulk_update_kwargs)
96 | )
97 | else:
98 | logger.info(
99 | "Skiping bulk update for {}... No fields to update".format(
100 | model_name
101 | )
102 | )
103 |
104 | if current_batch == 0:
105 | logger.info("{} has no records".format(model_name))
106 |
107 | def get_meta(self):
108 | meta = self.Meta()
109 | if not hasattr(meta, "select_chunk_size"):
110 | # Chunk size to iterate over
111 | meta.select_chunk_size = 5000
112 | if not hasattr(meta, "update_batch_size"):
113 | # Batch size for bulk updates
114 | meta.update_batch_size = 200
115 | return meta
116 |
117 | _meta = property(get_meta)
118 |
119 | def get_manager(self):
120 | meta = self._meta
121 | return getattr(meta, "manager", meta.model.objects)
122 |
123 | _manager = property(get_manager)
124 |
125 | def get_queryset(self):
126 | """ Override this if you want to delimit the objects that should be
127 | affected by anonymization
128 | """
129 | return self.get_manager().all()
130 |
131 | def patch_object(self, obj):
132 | """ Update object attributes with fake data provided by replacers
133 | """
134 | # using obj.__dict__ instead of getattr for performance reasons
135 | # see https://stackoverflow.com/a/9791053/639465
136 | fields = [field for field in self._declarations if obj.__dict__[field]]
137 |
138 | for field in fields:
139 | replacer = self._declarations[field]
140 | if isinstance(replacer, LazyAttribute):
141 | # Pass in obj for LazyAttributes
142 | new_value = replacer(obj)
143 | elif callable(replacer):
144 | new_value = replacer()
145 | else:
146 | new_value = replacer
147 |
148 | obj.__dict__[field] = new_value
149 |
150 | self.clean(obj)
151 |
152 | def clean(self, obj):
153 | """ Use this function if you need to update additional data that may
154 | rely on multiple fields, or if you need to update multiple fields
155 | at once
156 | """
157 | pass
158 |
159 | def get_declarations(self):
160 | """ Returns ordered declarations. Any non-ordered declarations, for
161 | example any types that does not inherit from OrderedDeclaration
162 | will come first, as they are considered "raw" values and should
163 | not be affected by the order of other non-ordered declarations
164 | """
165 |
166 | def _sort_declaration(declaration):
167 | name, value = declaration
168 | if isinstance(value, OrderedDeclaration):
169 | return value._order
170 | else:
171 | # Any non-ordered declarations come first
172 | return -1
173 |
174 | declarations = self._get_class_attributes().items()
175 | sorted_declarations = sorted(declarations, key=_sort_declaration)
176 |
177 | return OrderedDict(sorted_declarations)
178 |
179 | def _get_class_attributes(self):
180 | """ Return list of class attributes, which also includes methods and
181 | subclasses, ignoring any magic methods and reserved attributes
182 | """
183 | reserved_names = list(BaseAnonymizer.__dict__.keys()) + ["Meta"]
184 |
185 | return {
186 | name: self.__class__.__dict__[name]
187 | for name, value in self.__class__.__dict__.items()
188 | if not name.startswith("__") and name not in reserved_names
189 | }
190 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | .. BANNERSTART
2 | .. Since PyPI does not support raw directives, we remove them from the README
3 | ..
4 | .. raw directives are only used to make README fancier on GitHub and do not
5 | .. contain relevant information to be displayed in PyPI, as they are not tied
6 | .. to the current version, but to the current development status
7 | .. raw:: html
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | django-anon
16 |
17 |
18 |
Anonymize production data so it can be safely used in not-so-safe environments
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | Install
40 |
41 | |
42 |
43 | Read Documentation
44 |
45 | |
46 |
47 | PyPI
48 |
49 | |
50 |
51 | Contribute
52 |
53 |
54 | .. BANNEREND
55 |
56 | **django-anon** will help you anonymize your production database so it can be
57 | shared among developers, helping to reproduce bugs and make performance improvements
58 | in a production-like environment.
59 |
60 | .. image:: https://raw.githubusercontent.com/Tesorio/django-anon/master/django-anon-recording.gif
61 |
62 | .. start-features
63 |
64 | Features
65 | ========
66 |
67 | .. start-features-table
68 |
69 | .. csv-table::
70 |
71 | "🚀", "**Really fast** data anonymization and database operations using bulk updates to operate over huge tables"
72 | "🍰", "**Flexible** to use your own anonymization functions or external libraries like `Faker `_"
73 | "🐩", "**Elegant** solution following consolidated patterns from projects like `Django `_ and `Factory Boy `_"
74 | "🔨", "**Powerful**. It can be used on any projects, not only Django, not only Python. Really!"
75 |
76 | .. end-features-table
77 | .. end-features
78 | .. start-table-of-contents
79 |
80 | Table of Contents
81 | =================
82 | .. contents::
83 | :local:
84 |
85 | .. end-table-of-contents
86 | .. start-introduction
87 |
88 |
89 | Installation
90 | ------------
91 |
92 | .. code::
93 |
94 | pip install django-anon
95 |
96 |
97 | Supported versions
98 | ------------------
99 |
100 | * Python (2.7, 3.7)
101 | * Django (1.11, 2.2, 3.0)
102 |
103 |
104 | License
105 | -------
106 |
107 | `MIT `_
108 |
109 | .. end-introduction
110 | .. start-usage
111 |
112 |
113 | Usage
114 | -----
115 |
116 | Use ``anon.BaseAnonymizer`` to define your anonymizer classes:
117 |
118 | .. code-block:: python
119 |
120 | import anon
121 |
122 | from your_app.models import Person
123 |
124 | class PersonAnonymizer(anon.BaseAnonymizer):
125 | email = anon.fake_email
126 |
127 | # You can use static values instead of callables
128 | is_admin = False
129 |
130 | class Meta:
131 | model = Person
132 |
133 | # run anonymizer: be cautious, this will affect your current database!
134 | PersonAnonymizer().run()
135 |
136 |
137 | Built-in functions
138 | ~~~~~~~~~~~~~~~~~~
139 |
140 | .. code:: python
141 |
142 | import anon
143 |
144 | anon.fake_word(min_size=_min_word_size, max_size=20)
145 | anon.fake_text(max_size=255, max_diff_allowed=5, separator=' ')
146 | anon.fake_small_text(max_size=50)
147 | anon.fake_name(max_size=15)
148 | anon.fake_username(max_size=10, separator='')
149 | anon.fake_email(max_size=40, suffix='@example.com')
150 | anon.fake_url(max_size=50, scheme='http://', suffix='.com')
151 | anon.fake_phone_number(format='999-999-9999')
152 |
153 |
154 | Lazy attributes
155 | ~~~~~~~~~~~~~~~
156 |
157 | Lazy attributes can be defined as inline lambdas or methods, as shown below,
158 | using the ``anon.lazy_attribute`` function/decorator.
159 |
160 | .. code-block:: python
161 |
162 | import anon
163 |
164 | from your_app.models import Person
165 |
166 | class PersonAnonymizer(anon.BaseAnonymizer):
167 | name = anon.lazy_attribute(lambda o: 'x' * len(o.name))
168 |
169 | @lazy_attribute
170 | def date_of_birth(self):
171 | # keep year and month
172 | return self.date_of_birth.replace(day=1)
173 |
174 | class Meta:
175 | model = Person
176 |
177 |
178 | The clean method
179 | ~~~~~~~~~~~~~~~~
180 |
181 | .. code-block:: python
182 |
183 | import anon
184 |
185 | class UserAnonymizer(anon.BaseAnonymizer):
186 | class Meta:
187 | model = User
188 |
189 | def clean(self, obj):
190 | obj.set_password('test')
191 | obj.save()
192 |
193 |
194 | Defining a custom QuerySet
195 | ~~~~~~~~~~~~~~~~~~~~~~~~~~
196 |
197 | A custom QuerySet can be used to select the rows that should be anonymized:
198 |
199 | .. code-block:: python
200 |
201 | import anon
202 |
203 | from your_app.models import Person
204 |
205 | class PersonAnonymizer(anon.BaseAnonymizer):
206 | email = anon.fake_email
207 |
208 | class Meta:
209 | model = Person
210 |
211 | def get_queryset(self):
212 | # keep admins unmodified
213 | return Person.objects.exclude(is_admin=True)
214 |
215 |
216 | High-quality fake data
217 | ~~~~~~~~~~~~~~~~~~~~~~
218 |
219 | In order to be really fast, **django-anon** uses it's own algorithm to generate fake data. It is
220 | really fast, but the generated data is not pretty. If you need something prettier in terms of data,
221 | we suggest using `Faker `_, which can be used
222 | out-of-the-box as the below:
223 |
224 | .. code-block:: python
225 |
226 | import anon
227 |
228 | from faker import Faker
229 | from your_app.models import Address
230 |
231 | faker = Faker()
232 |
233 | class PersonAnonymizer(anon.BaseAnonymizer):
234 | postalcode = faker.postalcode
235 |
236 | class Meta:
237 | model = Address
238 |
239 | .. end-usage
240 |
241 | Changelog
242 | ---------
243 |
244 | Check out `CHANGELOG.rst `_ for release notes
245 |
246 | Contributing
247 | ------------
248 |
249 | Check out `CONTRIBUTING.rst `_ for information about getting involved
250 |
251 | ----
252 |
253 | `Icon `_ made by `Eucalyp `_ from `www.flaticon.com `_
254 |
--------------------------------------------------------------------------------
/anon/utils.py:
--------------------------------------------------------------------------------
1 | # stdlib
2 | import itertools
3 | import random
4 |
5 |
6 | _WORD_LIST = [
7 | "a",
8 | "ab",
9 | "accusamus",
10 | "accusantium",
11 | "ad",
12 | "adipisci",
13 | "alias",
14 | "aliquam",
15 | "aliquid",
16 | "amet",
17 | "animi",
18 | "aperiam",
19 | "architecto",
20 | "asperiores",
21 | "aspernatur",
22 | "assumenda",
23 | "at",
24 | "atque",
25 | "aut",
26 | "autem",
27 | "beatae",
28 | "blanditiis",
29 | "commodi",
30 | "consectetur",
31 | "consequatur",
32 | "consequuntur",
33 | "corporis",
34 | "corrupti",
35 | "culpa",
36 | "cum",
37 | "cumque",
38 | "cupiditate",
39 | "debitis",
40 | "delectus",
41 | "deleniti",
42 | "deserunt",
43 | "dicta",
44 | "dignissimos",
45 | "distinctio",
46 | "dolor",
47 | "dolore",
48 | "dolorem",
49 | "doloremque",
50 | "dolores",
51 | "doloribus",
52 | "dolorum",
53 | "ducimus",
54 | "ea",
55 | "eaque",
56 | "earum",
57 | "eius",
58 | "eligendi",
59 | "enim",
60 | "eos",
61 | "error",
62 | "esse",
63 | "est",
64 | "et",
65 | "eum",
66 | "eveniet",
67 | "ex",
68 | "excepturi",
69 | "exercitationem",
70 | "expedita",
71 | "explicabo",
72 | "facere",
73 | "facilis",
74 | "fuga",
75 | "fugiat",
76 | "fugit",
77 | "harum",
78 | "hic",
79 | "id",
80 | "illo",
81 | "illum",
82 | "impedit",
83 | "in",
84 | "incidunt",
85 | "inventore",
86 | "ipsa",
87 | "ipsam",
88 | "ipsum",
89 | "iste",
90 | "itaque",
91 | "iure",
92 | "iusto",
93 | "labore",
94 | "laboriosam",
95 | "laborum",
96 | "laudantium",
97 | "libero",
98 | "magnam",
99 | "magni",
100 | "maiores",
101 | "maxime",
102 | "minima",
103 | "minus",
104 | "modi",
105 | "molestiae",
106 | "molestias",
107 | "mollitia",
108 | "nam",
109 | "natus",
110 | "necessitatibus",
111 | "nemo",
112 | "neque",
113 | "nesciunt",
114 | "nihil",
115 | "nisi",
116 | "nobis",
117 | "non",
118 | "nostrum",
119 | "nulla",
120 | "numquam",
121 | "occaecati",
122 | "odio",
123 | "odit",
124 | "officia",
125 | "officiis",
126 | "omnis",
127 | "optio",
128 | "pariatur",
129 | "perferendis",
130 | "perspiciatis",
131 | "placeat",
132 | "porro",
133 | "possimus",
134 | "praesentium",
135 | "provident",
136 | "quae",
137 | "quaerat",
138 | "quam",
139 | "quas",
140 | "quasi",
141 | "qui",
142 | "quia",
143 | "quibusdam",
144 | "quidem",
145 | "quis",
146 | "quisquam",
147 | "quo",
148 | "quod",
149 | "quos",
150 | "ratione",
151 | "recusandae",
152 | "reiciendis",
153 | "rem",
154 | "repellat",
155 | "repellendus",
156 | "reprehenderit",
157 | "repudiandae",
158 | "rerum",
159 | "saepe",
160 | "sapiente",
161 | "sed",
162 | "sequi",
163 | "similique",
164 | "sint",
165 | "sit",
166 | "soluta",
167 | "sunt",
168 | "suscipit",
169 | "tempora",
170 | "tempore",
171 | "temporibus",
172 | "tenetur",
173 | "totam",
174 | "ullam",
175 | "unde",
176 | "ut",
177 | "vel",
178 | "velit",
179 | "veniam",
180 | "veritatis",
181 | "vero",
182 | "vitae",
183 | "voluptas",
184 | "voluptate",
185 | "voluptatem",
186 | "voluptates",
187 | "voluptatibus",
188 | "voluptatum",
189 | ]
190 |
191 |
192 | try:
193 | xrange
194 | except NameError:
195 | # Python 2/3 proof
196 | xrange = range
197 |
198 |
199 | def _cycle_over_sample_range(start, end, sample_size):
200 | """
201 | Given a range (start, end), returns a generator that will cycle over a population
202 | sample with size specified by ``sample_size``
203 | """
204 | return itertools.cycle(random.sample(xrange(start, end), sample_size))
205 |
206 |
207 | def _trim_text(text, separator, max_size):
208 | limit = min(text.rindex(separator), max_size)
209 | return text[:limit]
210 |
211 |
212 | # Holds the maximum size of word sample
213 | _max_word_size = max(len(s) for s in _WORD_LIST)
214 |
215 | # Holds a generator that each iteration returns a different word
216 | _word_generator = itertools.cycle(_WORD_LIST)
217 |
218 | # Holds the size of smallest word in _WORD_LIST and is used to define bounds
219 | _min_word_size = len(sorted(_WORD_LIST, key=lambda w: len(w))[0])
220 |
221 | # Holds a generator that each iteration returns a different number
222 | _number_generator = itertools.cycle("86306894249026785203141")
223 |
224 | # Holds a generator for small integers, same as Django's PositiveSmallIntegerField
225 | _small_int_generator = _cycle_over_sample_range(0, 32767, 1000)
226 |
227 | # Holds a generator for small signed integers, same as Django's SmallIntegerField
228 | _small_signed_int_generator = _cycle_over_sample_range(-32768, 32767, 1000)
229 |
230 | # Holds a generator for integers, same as Django's PositiveIntegerField
231 | _int_generator = _cycle_over_sample_range(0, 2147483647, 10000)
232 |
233 | # Holds a generator for signed integers, same as Django's IntegerField
234 | _signed_int_generator = _cycle_over_sample_range(-2147483648, 2147483647, 100000)
235 |
236 |
237 | def fake_word(min_size=_min_word_size, max_size=20):
238 | """ Return fake word
239 |
240 | :min_size: Minimum number of chars
241 | :max_size: Maximum number of chars
242 |
243 | Example:
244 |
245 | >>> import django_anon as anon
246 | >>> print(anon.fake_word())
247 | adipisci
248 |
249 | """
250 | if min_size < _min_word_size:
251 | raise ValueError("no such word with this size < min_size")
252 |
253 | for word in _word_generator:
254 | if min_size <= len(word) <= max_size:
255 | return word
256 |
257 |
258 | def fake_text(max_size=255, max_diff_allowed=5, separator=" "):
259 | """ Return fake text
260 |
261 | :max_size: Maximum number of chars
262 | :max_diff_allowed: Maximum difference (fidelity) allowed, in chars number
263 | :separator: Word separator
264 |
265 | Example:
266 |
267 | >>> print(anon.fake_text())
268 | alias aliquam aliquid amet animi aperiam architecto asperiores aspernatur assumenda at atque aut autem beatae blanditiis commodi consectetur consequatur consequuntur corporis corrupti culpa cum cumque cupiditate debitis delectus deleniti deserunt dicta
269 |
270 | """
271 | if max_diff_allowed < 1:
272 | raise ValueError("max_diff_allowed must be > 0")
273 |
274 | num_words = max(1, int(max_size / _max_word_size))
275 | words = itertools.islice(_word_generator, num_words)
276 |
277 | text = separator.join(words)
278 | try:
279 | if len(text) > max_size:
280 | text = _trim_text(text, separator, max_size)
281 | except ValueError:
282 | text = text[:max_size]
283 |
284 | return text
285 |
286 |
287 | def fake_small_text(max_size=50):
288 | """ Preset for fake_text.
289 |
290 | :max_size: Maximum number of chars
291 |
292 | Example:
293 |
294 | >>> print(anon.fake_small_text())
295 | Distinctio Dolor Dolore Dolorem Doloremque Dolores
296 |
297 | """
298 | return fake_text(max_size=max_size).title()
299 |
300 |
301 | def fake_name(max_size=15):
302 | """ Preset for fake_text. Also returns capitalized words.
303 |
304 | :max_size: Maximum number of chars
305 |
306 | Example:
307 |
308 | >>> print(anon.fake_name())
309 | Doloribus Ea
310 |
311 | """
312 | return fake_text(max_size=max_size).title()
313 |
314 |
315 | def fake_username(max_size=10, separator=""):
316 | """ Returns fake username
317 |
318 | :max_size: Maximum number of chars
319 | :separator: Word separator
320 | :rand_range: Range to use when generating random number
321 |
322 | Example:
323 |
324 | >>> print(anon.fake_username())
325 | eius54455
326 |
327 | """
328 | random_number = str(next(_small_int_generator))
329 | min_size_allowed = _min_word_size + len(random_number)
330 |
331 | if max_size < min_size_allowed:
332 | raise ValueError("username must be >= {}".format(min_size_allowed))
333 | else:
334 | max_size -= len(random_number)
335 |
336 | return fake_text(max_size, separator=separator) + random_number
337 |
338 |
339 | def fake_email(max_size=40, suffix="@example.com"):
340 | """ Returns fake email address
341 |
342 | :max_size: Maximum number of chars
343 | :suffix: Suffix to add to email addresses (including @)
344 |
345 | Example:
346 |
347 | >>> print(anon.fake_email())
348 | enim120238@example.com
349 |
350 | """
351 | min_size_allowed = _min_word_size + len(suffix)
352 |
353 | if max_size + len(suffix) > 254:
354 | # an email address must not exceed 254 chars
355 | raise ValueError("email address must not exceed 254 chars")
356 | elif max_size < min_size_allowed:
357 | raise ValueError("max_size must be >= {}".format(min_size_allowed))
358 | else:
359 | max_size -= len(suffix)
360 |
361 | return fake_username(max_size, separator=".") + suffix
362 |
363 |
364 | def fake_url(max_size=50, scheme="http://", suffix=".com"):
365 | """ Returns fake URL
366 |
367 | :max_size: Maximum number of chars
368 | :scheme: URL scheme (http://)
369 | :suffix: Suffix to add to domain (including dot)
370 |
371 | Example:
372 |
373 | >>> print(anon.fake_url())
374 | http://facilis.fuga.fugiat.fugit.harum.hic.id.com
375 |
376 | """
377 | min_size_allowed = _min_word_size + len(scheme) + len(suffix)
378 |
379 | if max_size < min_size_allowed:
380 | raise ValueError("max_size must be >= {}".format(min_size_allowed))
381 | else:
382 | max_size -= len(scheme) + len(suffix)
383 |
384 | domain = fake_text(max_size=max_size, separator=".") + suffix
385 | return scheme + domain
386 |
387 |
388 | def fake_phone_number(format="999-999-9999"):
389 | """ Returns a fake phone number in the desired format
390 |
391 | :format: Format of phone number to generate
392 |
393 | Example:
394 |
395 | >>> print(anon.fake_phone_number())
396 | 863-068-9424
397 |
398 | """
399 | number = []
400 | for char in format:
401 | if char == "9":
402 | n = next(_number_generator)
403 | if not number:
404 | # do not start phone numbers with zero
405 | while n == "0":
406 | n = next(_number_generator)
407 | number.append(n)
408 | else:
409 | number.append(char)
410 | return "".join(number)
411 |
--------------------------------------------------------------------------------
/Pipfile.lock:
--------------------------------------------------------------------------------
1 | {
2 | "_meta": {
3 | "hash": {
4 | "sha256": "b830d2890d591353241f920dfd497d67cdf7eead00bb74a4bb49ad949592de72"
5 | },
6 | "pipfile-spec": 6,
7 | "requires": {
8 | "python_version": "3.7"
9 | },
10 | "sources": [
11 | {
12 | "name": "pypi",
13 | "url": "https://pypi.org/simple",
14 | "verify_ssl": true
15 | }
16 | ]
17 | },
18 | "default": {
19 | "asgiref": {
20 | "hashes": [
21 | "sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17",
22 | "sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0"
23 | ],
24 | "version": "==3.3.1"
25 | },
26 | "django": {
27 | "hashes": [
28 | "sha256:169e2e7b4839a7910b393eec127fd7cbae62e80fa55f89c6510426abf673fe5f",
29 | "sha256:c6c0462b8b361f8691171af1fb87eceb4442da28477e12200c40420176206ba7"
30 | ],
31 | "version": "==3.1.6"
32 | },
33 | "django-bulk-update": {
34 | "hashes": [
35 | "sha256:49a403392ae05ea872494d74fb3dfa3515f8df5c07cc277c3dc94724c0ee6985",
36 | "sha256:5ab7ce8a65eac26d19143cc189c0f041d5c03b9d1b290ca240dc4f3d6aaeb337"
37 | ],
38 | "index": "pypi",
39 | "version": "==2.2.0"
40 | },
41 | "django-chunkator": {
42 | "hashes": [
43 | "sha256:210713faf05d68035d067de9b015868b3d902d5405865401384a3c134f64c43a",
44 | "sha256:57d705966762e6ba6879c2344a1b9288045f64c638fa47cbe00467f0eeeadfaf"
45 | ],
46 | "index": "pypi",
47 | "version": "==2.0.0"
48 | },
49 | "pytz": {
50 | "hashes": [
51 | "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
52 | "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
53 | ],
54 | "version": "==2021.1"
55 | },
56 | "sqlparse": {
57 | "hashes": [
58 | "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
59 | "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
60 | ],
61 | "version": "==0.4.1"
62 | }
63 | },
64 | "develop": {
65 | "alabaster": {
66 | "hashes": [
67 | "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
68 | "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"
69 | ],
70 | "version": "==0.7.12"
71 | },
72 | "appdirs": {
73 | "hashes": [
74 | "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
75 | "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
76 | ],
77 | "version": "==1.4.4"
78 | },
79 | "argh": {
80 | "hashes": [
81 | "sha256:a9b3aaa1904eeb78e32394cd46c6f37ac0fb4af6dc488daa58971bdc7d7fcaf3",
82 | "sha256:e9535b8c84dc9571a48999094fda7f33e63c3f1b74f3e5f3ac0105a58405bb65"
83 | ],
84 | "version": "==0.26.2"
85 | },
86 | "babel": {
87 | "hashes": [
88 | "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5",
89 | "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"
90 | ],
91 | "version": "==2.9.0"
92 | },
93 | "bleach": {
94 | "hashes": [
95 | "sha256:6123ddc1052673e52bab52cdc955bcb57a015264a1c57d37bea2f6b817af0125",
96 | "sha256:98b3170739e5e83dd9dc19633f074727ad848cbedb6026708c8ac2d3b697a433"
97 | ],
98 | "index": "pypi",
99 | "version": "==3.3.0"
100 | },
101 | "certifi": {
102 | "hashes": [
103 | "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
104 | "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
105 | ],
106 | "version": "==2020.12.5"
107 | },
108 | "cffi": {
109 | "hashes": [
110 | "sha256:00a1ba5e2e95684448de9b89888ccd02c98d512064b4cb987d48f4b40aa0421e",
111 | "sha256:00e28066507bfc3fe865a31f325c8391a1ac2916219340f87dfad602c3e48e5d",
112 | "sha256:045d792900a75e8b1e1b0ab6787dd733a8190ffcf80e8c8ceb2fb10a29ff238a",
113 | "sha256:0638c3ae1a0edfb77c6765d487fee624d2b1ee1bdfeffc1f0b58c64d149e7eec",
114 | "sha256:105abaf8a6075dc96c1fe5ae7aae073f4696f2905fde6aeada4c9d2926752362",
115 | "sha256:155136b51fd733fa94e1c2ea5211dcd4c8879869008fc811648f16541bf99668",
116 | "sha256:1a465cbe98a7fd391d47dce4b8f7e5b921e6cd805ef421d04f5f66ba8f06086c",
117 | "sha256:1d2c4994f515e5b485fd6d3a73d05526aa0fcf248eb135996b088d25dfa1865b",
118 | "sha256:2c24d61263f511551f740d1a065eb0212db1dbbbbd241db758f5244281590c06",
119 | "sha256:51a8b381b16ddd370178a65360ebe15fbc1c71cf6f584613a7ea08bfad946698",
120 | "sha256:594234691ac0e9b770aee9fcdb8fa02c22e43e5c619456efd0d6c2bf276f3eb2",
121 | "sha256:5cf4be6c304ad0b6602f5c4e90e2f59b47653ac1ed9c662ed379fe48a8f26b0c",
122 | "sha256:64081b3f8f6f3c3de6191ec89d7dc6c86a8a43911f7ecb422c60e90c70be41c7",
123 | "sha256:6bc25fc545a6b3d57b5f8618e59fc13d3a3a68431e8ca5fd4c13241cd70d0009",
124 | "sha256:798caa2a2384b1cbe8a2a139d80734c9db54f9cc155c99d7cc92441a23871c03",
125 | "sha256:7c6b1dece89874d9541fc974917b631406233ea0440d0bdfbb8e03bf39a49b3b",
126 | "sha256:7ef7d4ced6b325e92eb4d3502946c78c5367bc416398d387b39591532536734e",
127 | "sha256:840793c68105fe031f34d6a086eaea153a0cd5c491cde82a74b420edd0a2b909",
128 | "sha256:8d6603078baf4e11edc4168a514c5ce5b3ba6e3e9c374298cb88437957960a53",
129 | "sha256:9cc46bc107224ff5b6d04369e7c595acb700c3613ad7bcf2e2012f62ece80c35",
130 | "sha256:9f7a31251289b2ab6d4012f6e83e58bc3b96bd151f5b5262467f4bb6b34a7c26",
131 | "sha256:9ffb888f19d54a4d4dfd4b3f29bc2c16aa4972f1c2ab9c4ab09b8ab8685b9c2b",
132 | "sha256:a5ed8c05548b54b998b9498753fb9cadbfd92ee88e884641377d8a8b291bcc01",
133 | "sha256:a7711edca4dcef1a75257b50a2fbfe92a65187c47dab5a0f1b9b332c5919a3fb",
134 | "sha256:af5c59122a011049aad5dd87424b8e65a80e4a6477419c0c1015f73fb5ea0293",
135 | "sha256:b18e0a9ef57d2b41f5c68beefa32317d286c3d6ac0484efd10d6e07491bb95dd",
136 | "sha256:b4e248d1087abf9f4c10f3c398896c87ce82a9856494a7155823eb45a892395d",
137 | "sha256:ba4e9e0ae13fc41c6b23299545e5ef73055213e466bd107953e4a013a5ddd7e3",
138 | "sha256:c6332685306b6417a91b1ff9fae889b3ba65c2292d64bd9245c093b1b284809d",
139 | "sha256:d5ff0621c88ce83a28a10d2ce719b2ee85635e85c515f12bac99a95306da4b2e",
140 | "sha256:d9efd8b7a3ef378dd61a1e77367f1924375befc2eba06168b6ebfa903a5e59ca",
141 | "sha256:df5169c4396adc04f9b0a05f13c074df878b6052430e03f50e68adf3a57aa28d",
142 | "sha256:ebb253464a5d0482b191274f1c8bf00e33f7e0b9c66405fbffc61ed2c839c775",
143 | "sha256:ec80dc47f54e6e9a78181ce05feb71a0353854cc26999db963695f950b5fb375",
144 | "sha256:f032b34669220030f905152045dfa27741ce1a6db3324a5bc0b96b6c7420c87b",
145 | "sha256:f60567825f791c6f8a592f3c6e3bd93dd2934e3f9dac189308426bd76b00ef3b",
146 | "sha256:f803eaa94c2fcda012c047e62bc7a51b0bdabda1cad7a92a522694ea2d76e49f"
147 | ],
148 | "version": "==1.14.4"
149 | },
150 | "chardet": {
151 | "hashes": [
152 | "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa",
153 | "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"
154 | ],
155 | "version": "==4.0.0"
156 | },
157 | "colorama": {
158 | "hashes": [
159 | "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b",
160 | "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"
161 | ],
162 | "version": "==0.4.4"
163 | },
164 | "cryptography": {
165 | "hashes": [
166 | "sha256:0003a52a123602e1acee177dc90dd201f9bb1e73f24a070db7d36c588e8f5c7d",
167 | "sha256:0e85aaae861d0485eb5a79d33226dd6248d2a9f133b81532c8f5aae37de10ff7",
168 | "sha256:594a1db4511bc4d960571536abe21b4e5c3003e8750ab8365fafce71c5d86901",
169 | "sha256:69e836c9e5ff4373ce6d3ab311c1a2eed274793083858d3cd4c7d12ce20d5f9c",
170 | "sha256:788a3c9942df5e4371c199d10383f44a105d67d401fb4304178020142f020244",
171 | "sha256:7e177e4bea2de937a584b13645cab32f25e3d96fc0bc4a4cf99c27dc77682be6",
172 | "sha256:83d9d2dfec70364a74f4e7c70ad04d3ca2e6a08b703606993407bf46b97868c5",
173 | "sha256:84ef7a0c10c24a7773163f917f1cb6b4444597efd505a8aed0a22e8c4780f27e",
174 | "sha256:9e21301f7a1e7c03dbea73e8602905a4ebba641547a462b26dd03451e5769e7c",
175 | "sha256:9f6b0492d111b43de5f70052e24c1f0951cb9e6022188ebcb1cc3a3d301469b0",
176 | "sha256:a69bd3c68b98298f490e84519b954335154917eaab52cf582fa2c5c7efc6e812",
177 | "sha256:b4890d5fb9b7a23e3bf8abf5a8a7da8e228f1e97dc96b30b95685df840b6914a",
178 | "sha256:c366df0401d1ec4e548bebe8f91d55ebcc0ec3137900d214dd7aac8427ef3030",
179 | "sha256:dc42f645f8f3a489c3dd416730a514e7a91a59510ddaadc09d04224c098d3302"
180 | ],
181 | "version": "==3.3.1"
182 | },
183 | "distlib": {
184 | "hashes": [
185 | "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb",
186 | "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"
187 | ],
188 | "version": "==0.3.1"
189 | },
190 | "docutils": {
191 | "hashes": [
192 | "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
193 | "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"
194 | ],
195 | "version": "==0.16"
196 | },
197 | "filelock": {
198 | "hashes": [
199 | "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59",
200 | "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"
201 | ],
202 | "version": "==3.0.12"
203 | },
204 | "idna": {
205 | "hashes": [
206 | "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
207 | "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
208 | ],
209 | "version": "==2.10"
210 | },
211 | "imagesize": {
212 | "hashes": [
213 | "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
214 | "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
215 | ],
216 | "version": "==1.2.0"
217 | },
218 | "importlib-metadata": {
219 | "hashes": [
220 | "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83",
221 | "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"
222 | ],
223 | "markers": "python_version < '3.8'",
224 | "version": "==1.7.0"
225 | },
226 | "jeepney": {
227 | "hashes": [
228 | "sha256:7d59b6622675ca9e993a6bd38de845051d315f8b0c72cca3aef733a20b648657",
229 | "sha256:aec56c0eb1691a841795111e184e13cad504f7703b9a64f63020816afa79a8ae"
230 | ],
231 | "markers": "sys_platform == 'linux'",
232 | "version": "==0.6.0"
233 | },
234 | "jinja2": {
235 | "hashes": [
236 | "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419",
237 | "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"
238 | ],
239 | "version": "==2.11.3"
240 | },
241 | "keyring": {
242 | "hashes": [
243 | "sha256:9acb3e1452edbb7544822b12fd25459078769e560fa51f418b6d00afaa6178df",
244 | "sha256:9f44660a5d4931bdc14c08a1d01ef30b18a7a8147380710d8c9f9531e1f6c3c0"
245 | ],
246 | "version": "==22.0.1"
247 | },
248 | "livereload": {
249 | "hashes": [
250 | "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"
251 | ],
252 | "version": "==2.6.3"
253 | },
254 | "markupsafe": {
255 | "hashes": [
256 | "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
257 | "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
258 | "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
259 | "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
260 | "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
261 | "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f",
262 | "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39",
263 | "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
264 | "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
265 | "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014",
266 | "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f",
267 | "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
268 | "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
269 | "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
270 | "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
271 | "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
272 | "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
273 | "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
274 | "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
275 | "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85",
276 | "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1",
277 | "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
278 | "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
279 | "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
280 | "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850",
281 | "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0",
282 | "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
283 | "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
284 | "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb",
285 | "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
286 | "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
287 | "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
288 | "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1",
289 | "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2",
290 | "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
291 | "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
292 | "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
293 | "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7",
294 | "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
295 | "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8",
296 | "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
297 | "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193",
298 | "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
299 | "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b",
300 | "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
301 | "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
302 | "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5",
303 | "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c",
304 | "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032",
305 | "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
306 | "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be",
307 | "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"
308 | ],
309 | "version": "==1.1.1"
310 | },
311 | "packaging": {
312 | "hashes": [
313 | "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
314 | "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
315 | ],
316 | "version": "==20.9"
317 | },
318 | "pathtools": {
319 | "hashes": [
320 | "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"
321 | ],
322 | "version": "==0.1.2"
323 | },
324 | "pkginfo": {
325 | "hashes": [
326 | "sha256:029a70cb45c6171c329dfc890cde0879f8c52d6f3922794796e06f577bb03db4",
327 | "sha256:9fdbea6495622e022cc72c2e5e1b735218e4ffb2a2a69cde2694a6c1f16afb75"
328 | ],
329 | "version": "==1.7.0"
330 | },
331 | "pluggy": {
332 | "hashes": [
333 | "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
334 | "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
335 | ],
336 | "version": "==0.13.1"
337 | },
338 | "port-for": {
339 | "hashes": [
340 | "sha256:b16a84bb29c2954db44c29be38b17c659c9c27e33918dec16b90d375cc596f1c"
341 | ],
342 | "version": "==0.3.1"
343 | },
344 | "py": {
345 | "hashes": [
346 | "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
347 | "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
348 | ],
349 | "version": "==1.10.0"
350 | },
351 | "pycparser": {
352 | "hashes": [
353 | "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
354 | "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
355 | ],
356 | "version": "==2.20"
357 | },
358 | "pygments": {
359 | "hashes": [
360 | "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435",
361 | "sha256:df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"
362 | ],
363 | "version": "==2.7.4"
364 | },
365 | "pyparsing": {
366 | "hashes": [
367 | "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
368 | "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
369 | ],
370 | "version": "==2.4.7"
371 | },
372 | "pytz": {
373 | "hashes": [
374 | "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da",
375 | "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"
376 | ],
377 | "version": "==2021.1"
378 | },
379 | "pyyaml": {
380 | "hashes": [
381 | "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf",
382 | "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696",
383 | "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393",
384 | "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77",
385 | "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922",
386 | "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5",
387 | "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8",
388 | "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10",
389 | "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc",
390 | "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018",
391 | "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e",
392 | "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253",
393 | "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183",
394 | "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb",
395 | "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185",
396 | "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db",
397 | "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46",
398 | "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b",
399 | "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63",
400 | "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df",
401 | "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"
402 | ],
403 | "version": "==5.4.1"
404 | },
405 | "readme-renderer": {
406 | "hashes": [
407 | "sha256:267854ac3b1530633c2394ead828afcd060fc273217c42ac36b6be9c42cd9a9d",
408 | "sha256:6b7e5aa59210a40de72eb79931491eaf46fefca2952b9181268bd7c7c65c260a"
409 | ],
410 | "version": "==28.0"
411 | },
412 | "requests": {
413 | "hashes": [
414 | "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804",
415 | "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"
416 | ],
417 | "version": "==2.25.1"
418 | },
419 | "requests-toolbelt": {
420 | "hashes": [
421 | "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f",
422 | "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"
423 | ],
424 | "version": "==0.9.1"
425 | },
426 | "rfc3986": {
427 | "hashes": [
428 | "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d",
429 | "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"
430 | ],
431 | "version": "==1.4.0"
432 | },
433 | "secretstorage": {
434 | "hashes": [
435 | "sha256:30cfdef28829dad64d6ea1ed08f8eff6aa115a77068926bcc9f5225d5a3246aa",
436 | "sha256:5c36f6537a523ec5f969ef9fad61c98eb9e017bc601d811e53aa25bece64892f"
437 | ],
438 | "markers": "sys_platform == 'linux'",
439 | "version": "==3.3.0"
440 | },
441 | "six": {
442 | "hashes": [
443 | "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
444 | "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
445 | ],
446 | "version": "==1.15.0"
447 | },
448 | "snowballstemmer": {
449 | "hashes": [
450 | "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2",
451 | "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"
452 | ],
453 | "version": "==2.1.0"
454 | },
455 | "sphinx": {
456 | "hashes": [
457 | "sha256:321d6d9b16fa381a5306e5a0b76cd48ffbc588e6340059a729c6fdd66087e0e8",
458 | "sha256:ce6fd7ff5b215af39e2fcd44d4a321f6694b4530b6f2b2109b64d120773faea0"
459 | ],
460 | "index": "pypi",
461 | "version": "==3.2.1"
462 | },
463 | "sphinx-autobuild": {
464 | "hashes": [
465 | "sha256:66388f81884666e3821edbe05dd53a0cfb68093873d17320d0610de8db28c74e",
466 | "sha256:e60aea0789cab02fa32ee63c7acae5ef41c06f1434d9fd0a74250a61f5994692"
467 | ],
468 | "index": "pypi",
469 | "version": "==0.7.1"
470 | },
471 | "sphinx-rtd-theme": {
472 | "hashes": [
473 | "sha256:22c795ba2832a169ca301cd0a083f7a434e09c538c70beb42782c073651b707d",
474 | "sha256:373413d0f82425aaa28fb288009bf0d0964711d347763af2f1b65cafcb028c82"
475 | ],
476 | "index": "pypi",
477 | "version": "==0.5.0"
478 | },
479 | "sphinxcontrib-applehelp": {
480 | "hashes": [
481 | "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
482 | "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
483 | ],
484 | "version": "==1.0.2"
485 | },
486 | "sphinxcontrib-devhelp": {
487 | "hashes": [
488 | "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
489 | "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
490 | ],
491 | "version": "==1.0.2"
492 | },
493 | "sphinxcontrib-htmlhelp": {
494 | "hashes": [
495 | "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f",
496 | "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"
497 | ],
498 | "version": "==1.0.3"
499 | },
500 | "sphinxcontrib-jsmath": {
501 | "hashes": [
502 | "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
503 | "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
504 | ],
505 | "version": "==1.0.1"
506 | },
507 | "sphinxcontrib-qthelp": {
508 | "hashes": [
509 | "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
510 | "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
511 | ],
512 | "version": "==1.0.3"
513 | },
514 | "sphinxcontrib-serializinghtml": {
515 | "hashes": [
516 | "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc",
517 | "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"
518 | ],
519 | "version": "==1.1.4"
520 | },
521 | "toml": {
522 | "hashes": [
523 | "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
524 | "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
525 | ],
526 | "version": "==0.10.2"
527 | },
528 | "tornado": {
529 | "hashes": [
530 | "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb",
531 | "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c",
532 | "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288",
533 | "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95",
534 | "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558",
535 | "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe",
536 | "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791",
537 | "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d",
538 | "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326",
539 | "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b",
540 | "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4",
541 | "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c",
542 | "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910",
543 | "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5",
544 | "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c",
545 | "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0",
546 | "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675",
547 | "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd",
548 | "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f",
549 | "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c",
550 | "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea",
551 | "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6",
552 | "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05",
553 | "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd",
554 | "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575",
555 | "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a",
556 | "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37",
557 | "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795",
558 | "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f",
559 | "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32",
560 | "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c",
561 | "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01",
562 | "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4",
563 | "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2",
564 | "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921",
565 | "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085",
566 | "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df",
567 | "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102",
568 | "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5",
569 | "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68",
570 | "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"
571 | ],
572 | "version": "==6.1"
573 | },
574 | "tox": {
575 | "hashes": [
576 | "sha256:17e61a93afe5c49281fb969ab71f7a3f22d7586d1c56f9a74219910f356fe7d3",
577 | "sha256:3d94b6921a0b6dc90fd8128df83741f30bb41ccd6cd52d131a6a6944ca8f16e6"
578 | ],
579 | "index": "pypi",
580 | "version": "==3.19.0"
581 | },
582 | "tqdm": {
583 | "hashes": [
584 | "sha256:4621f6823bab46a9cc33d48105753ccbea671b68bab2c50a9f0be23d4065cb5a",
585 | "sha256:fe3d08dd00a526850568d542ff9de9bbc2a09a791da3c334f3213d8d0bbbca65"
586 | ],
587 | "version": "==4.56.0"
588 | },
589 | "twine": {
590 | "hashes": [
591 | "sha256:34352fd52ec3b9d29837e6072d5a2a7c6fe4290e97bba46bb8d478b5c598f7ab",
592 | "sha256:ba9ff477b8d6de0c89dd450e70b2185da190514e91c42cc62f96850025c10472"
593 | ],
594 | "index": "pypi",
595 | "version": "==3.2.0"
596 | },
597 | "urllib3": {
598 | "hashes": [
599 | "sha256:1b465e494e3e0d8939b50680403e3aedaa2bc434b7d5af64dfd3c958d7f5ae80",
600 | "sha256:de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"
601 | ],
602 | "version": "==1.26.3"
603 | },
604 | "virtualenv": {
605 | "hashes": [
606 | "sha256:147b43894e51dd6bba882cf9c282447f780e2251cd35172403745fc381a0a80d",
607 | "sha256:2be72df684b74df0ea47679a7df93fd0e04e72520022c57b479d8f881485dbe3"
608 | ],
609 | "version": "==20.4.2"
610 | },
611 | "watchdog": {
612 | "hashes": [
613 | "sha256:016b01495b9c55b5d4126ed8ae75d93ea0d99377084107c33162df52887cee18",
614 | "sha256:101532b8db506559e52a9b5d75a308729b3f68264d930670e6155c976d0e52a0",
615 | "sha256:27d9b4666938d5d40afdcdf2c751781e9ce36320788b70208d0f87f7401caf93",
616 | "sha256:2f1ade0d0802503fda4340374d333408831cff23da66d7e711e279ba50fe6c4a",
617 | "sha256:376cbc2a35c0392b0fe7ff16fbc1b303fd99d4dd9911ab5581ee9d69adc88982",
618 | "sha256:57f05e55aa603c3b053eed7e679f0a83873c540255b88d58c6223c7493833bac",
619 | "sha256:5f1f3b65142175366ba94c64d8d4c8f4015825e0beaacee1c301823266b47b9b",
620 | "sha256:602dbd9498592eacc42e0632c19781c3df1728ef9cbab555fab6778effc29eeb",
621 | "sha256:68744de2003a5ea2dfbb104f9a74192cf381334a9e2c0ed2bbe1581828d50b61",
622 | "sha256:85e6574395aa6c1e14e0f030d9d7f35c2340a6cf95d5671354ce876ac3ffdd4d",
623 | "sha256:b1d723852ce90a14abf0ec0ca9e80689d9509ee4c9ee27163118d87b564a12ac",
624 | "sha256:d948ad9ab9aba705f9836625b32e965b9ae607284811cd98334423f659ea537a",
625 | "sha256:e2a531e71be7b5cc3499ae2d1494d51b6a26684bcc7c3146f63c810c00e8a3cc",
626 | "sha256:e7c73edef48f4ceeebb987317a67e0080e5c9228601ff67b3c4062fa020403c7",
627 | "sha256:ee21aeebe6b3e51e4ba64564c94cee8dbe7438b9cb60f0bb350c4fa70d1b52c2",
628 | "sha256:f1d0e878fd69129d0d68b87cee5d9543f20d8018e82998efb79f7e412d42154a",
629 | "sha256:f84146f7864339c8addf2c2b9903271df21d18d2c721e9a77f779493234a82b5"
630 | ],
631 | "version": "==1.0.2"
632 | },
633 | "webencodings": {
634 | "hashes": [
635 | "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78",
636 | "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"
637 | ],
638 | "version": "==0.5.1"
639 | },
640 | "zipp": {
641 | "hashes": [
642 | "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108",
643 | "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"
644 | ],
645 | "version": "==3.4.0"
646 | }
647 | }
648 | }
649 |
--------------------------------------------------------------------------------