├── .github └── workflows │ └── python-package.yml ├── .gitignore ├── .pylintrc ├── .vscode └── settings.json ├── README.md ├── flyyer ├── __init__.py └── flyyer.py ├── poetry.lock ├── pyproject.toml └── tests ├── __init__.py └── test_flyyer.py /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Python package 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | strategy: 15 | matrix: 16 | python-version: [3.6, 3.7, 3.8, 3.9] 17 | poetry-version: [1.0, 1.1.2] 18 | os: [ubuntu-18.04, macos-latest, windows-latest] 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - uses: actions/checkout@v2 22 | - uses: actions/setup-python@v2 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | - name: Run image 26 | uses: abatilo/actions-poetry@v2.0.0 27 | with: 28 | poetry-version: ${{ matrix.poetry-version }} 29 | - run: poetry install --no-interaction 30 | # - run: poetry shell --no-interaction 31 | - run: poetry run pytest 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python-related 2 | venv 3 | .venv 4 | *.egg 5 | *.egg-info 6 | __pycache__ 7 | dist/ 8 | 9 | # Mac OS 10 | .DS_Store 11 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MESSAGES CONTROL] 2 | 3 | disable=missing-class-docstring,missing-function-docstring 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.linting.pylintEnabled": true, 3 | "python.linting.enabled": true, 4 | "python.formatting.provider": "black" 5 | } 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flyyer-python 2 | 3 |   4 | 5 | The AI-powered preview system built from your website (no effort required). 6 | 7 |  8 | 9 | **This module is agnostic to any Python framework.** 10 | 11 | ## Index 12 | 13 | - [Get started (5 minutes)](#get-started-5-minutes) 14 | - [Advanced usage](#advanced-usage) 15 | - [Flyyer Render](#flyyer-render) 16 | - [Development](#development) 17 | - [Test](#test) 18 | 19 | ## Get started (5 minutes) 20 | 21 | Haven't registered your website yet? Go to [Flyyer.io](https://flyyer.io?ref=flyyer-python) and create a project (e.g. `website-com`). 22 | 23 | ### 1. Install the library 24 | 25 | This module requires Python >= 3.6. 26 | 27 | Install it with [Poetry](https://python-poetry.org/). 28 | 29 | ```sh 30 | poetry add flyyer 31 | ``` 32 | 33 | Or install it with [pip](https://pip.pypa.io/en/stable/). 34 | 35 | ```sh 36 | pip install flyyer 37 | ``` 38 | 39 | ### 2. Get your Flyyer.io smart image link 40 | 41 | In your website code (e.g. your landing or product/post view file), set the following: 42 | 43 | ```python 44 | from flyyer import Flyyer 45 | 46 | flyyer = Flyyer( 47 | # Your project slug 48 | project="website-com", 49 | # The current path of your website 50 | path="/path/to/product", # In Django you can use {{ request.get_full_path }} 51 | # (Optional, Recommended) Default or main image for each page 52 | default="/static/image-1.png" # or https://your-site.com/static/image-1.png 53 | ) 54 | 55 | # Check: 56 | print(flyyer.href()) 57 | # > https://cdn.flyyer.io/v2/website-com/_/__v=1618281823&_def=%2Fstatic%2Fimage-1.png/path/to/product 58 | ``` 59 | 60 | ### 3. Put your smart image link in your `
` tags 61 | 62 | You'll get the best results like this: 63 | 64 | ```python 65 | 66 | 67 | 68 | ``` 69 | 70 | ### 4. Create a `rule` for your project 71 | 72 | Go to your dashboard [here](https://flyyer.io/dashboard/_/projects/_/manage) and create a rule like the following: 73 | 74 | [](https://flyyer.io/dashboard) 75 | 76 | Voilà! 77 | 78 | ## Advanced usage 79 | 80 | Advanced features include: 81 | 82 | - Custom variables: additional information for your preview that is not present in your website. [Note: if you need customization you should take a look at [Flyyer Render](#flyyer-render)] 83 | - Custom metadata: set custom width, height, resolution, and more (see example). 84 | - Signed URLs. 85 | 86 | Here you have a detailed full example for project `website-com` and path `/path/to/product`. 87 | 88 | ```python 89 | from flyyer import Flyyer, FlyyerMeta 90 | 91 | flyyer = Flyyer( 92 | # [Required] Your project slug, find it in your dashboard https://www.flyyer.io/dashboard/_/projects/_/integrate. 93 | project="website-com", 94 | # [Recommended] The current path of your website (by default it's `/`). 95 | path="/path/to/product", 96 | # [Optional] In case you want to provide information that is not present in your page set it here. 97 | variables={ 98 | "title": "Product name", 99 | "img": "https://flyyer.io/img/marketplace/flyyer-banner.png", 100 | }, 101 | # [Optional] Custom metadata for rendering the image. ID is recommended so we provide you with better statistics. 102 | meta=FlyyerMeta( 103 | id="jeans-123", # recommended for better stats 104 | v="12369420123", # specific handler version, by default it's a random number to circumvent platforms' cache, 105 | width=1200, 106 | height=600, 107 | resolution=0.9, # from 0.0 to 1.0 108 | agent="whatsapp", # force dimensions for specific platform 109 | ), 110 | ) 111 | 112 | # Check: 113 | print(flyyer.href()) 114 | # > https://cdn.flyyer.io/v2/website-com/_/__v=1618281823/path/to/product 115 | ``` 116 | 117 | For signed URLs, just provide your secret (find it in Dashboard > Project > Advanced settings) and choose a strategy (`HMAC` or `JWT`). 118 | 119 | ```python 120 | flyyer = Flyyer( 121 | project="website-com", 122 | path="/path/to/product", 123 | secret="your-secret-key", 124 | strategy="JWT", # or 'HMAC' 125 | ) 126 | 127 | print(flyyer.href()) 128 | # > https://cdn.flyyer.io/v2/website-com/jwt-eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXJhbXMiOnsiX19pZCI6ImplYW5zLTEyMyJ9LCJwYXRoIjoiXC9wYXRoXC90b1wvcHJvZHVjdCJ9.X8Vs5SGEA1-3M6bH-h24jhQnbwH95V_G0f-gPhTBTzE?__v=1618283086 129 | ``` 130 | 131 | ## Flyyer Render 132 | 133 | As you probably realized, **Flyyer** uses the [rules defined on your dashboard](https://flyyer.io/dashboard/_/projects) to decide how to handle every image based on path patterns. It analyses your website to obtain information and then render a content-rich image with no effort. Let's say _"Flyyer delivers images based on the content of this route"_. 134 | 135 | **Flyyer Render** instead requires you to explicitly declare template and variables for the images to render, **giving you more control for customization**. Let's say _"FlyyerRender delivers an image using this template and these explicit variables"_. 136 | 137 | ```python 138 | from flyyer import FlyyerRender 139 | 140 | flyyer = FlyyerRender( 141 | tenant="tenant", 142 | deck="deck", 143 | template="template", 144 | variables={"title": "Hello world!"}, 145 | ) 146 | 147 | # Use this image in yourtags 148 | url = flyyer.href() 149 | # > https://cdn.flyyer.io/render/v2/tenant/deck/template.jpeg?__v=1596906866&title=Hello+world%21 150 | ``` 151 | 152 | Variables can be complex arrays and hashes. 153 | 154 | ```python 155 | from flyyer import FlyyerRender, FlyyerMeta 156 | 157 | flyyer = FlyyerRender( 158 | tenant="tenant", 159 | deck="deck", 160 | template="template", 161 | variables={ 162 | "items": [ 163 | { "text": "Oranges", "count": 12 }, 164 | { "text": "Apples", "count": 14 }, 165 | ], 166 | }, 167 | meta=FlyyerMeta( 168 | id="slug-or-id", # To identify the resource in our analytics report 169 | ), 170 | ) 171 | ``` 172 | 173 | You can use signatures with Flyyer Render like below. 174 | 175 | ```python 176 | from flyyer import FlyyerRender 177 | 178 | flyyer = FlyyerRender( 179 | tenant="tenant", 180 | deck="deck", 181 | template="template", 182 | variables={"title": "Hello world!"}, 183 | secret=key, 184 | strategy="HMAC", # JWT 185 | ) 186 | 187 | # Use this image in your
tags
188 | url = flyyer.href()
189 | # > https://cdn.flyyer.io/render/v2/tenant/deck/template.jpeg?__v=d+&title=Hello+world%21&__hmac=1bea6d523496848c
190 | ```
191 |
192 | **IMPORTANT: variables must be serializable.**
193 |
194 | To decode the URL for debugging purposes:
195 |
196 | ```python
197 | from urllib.parse import unquote
198 |
199 | print(unquote(url))
200 | # > https://cdn.flyyer.io/render/v2/tenant/deck/template.jpeg?title=Hello+world!&__v=123
201 | ```
202 |
203 | ## Development
204 |
205 | Prepare the local environment:
206 |
207 | ```sh
208 | poetry install
209 | ```
210 |
211 | ```sh
212 | poetry shell
213 | ```
214 |
215 | Deploy with:
216 |
217 | ```sh
218 | # Set API Token
219 | poetry config pypi-token.pypi pypi-TOKEN
220 |
221 | poetry version X.Y.Z
222 | poetry build
223 | poetry publish
224 | ```
225 |
226 | ## Test
227 |
228 | Run tests with pytest:
229 |
230 | ```sh
231 | poetry run pytest
232 | ```
233 |
234 | Run [black](https://github.com/psf/black) linter:
235 |
236 | ```sh
237 | black .
238 | ```
239 |
--------------------------------------------------------------------------------
/flyyer/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = "1.0.0"
2 |
3 | from flyyer.flyyer import Flyyer, FlyyerMeta, to_query, FlyyerRender
4 |
--------------------------------------------------------------------------------
/flyyer/flyyer.py:
--------------------------------------------------------------------------------
1 | from time import time
2 | from urllib.parse import urlencode
3 | from typing import Optional, Mapping, Union, Any
4 | from typing_extensions import TypedDict
5 | from hashlib import sha256
6 | import hmac
7 | import jwt
8 |
9 |
10 | class FlyyerMeta(TypedDict, total=False):
11 | agent: str
12 | width: Union[str, int]
13 | height: Union[str, int]
14 | resolution: Union[str, int]
15 | id: Union[str, int]
16 | v: Union[str, int]
17 |
18 |
19 | class FlyyerRender:
20 | def __init__(
21 | self,
22 | tenant: str,
23 | deck: str,
24 | template: str,
25 | version: Optional[int] = None,
26 | extension: Optional[str] = None,
27 | variables: Optional[Mapping[Any, Any]] = None,
28 | meta: Optional[FlyyerMeta] = None,
29 | secret: Optional[str] = None,
30 | strategy: Optional[str] = None,
31 | ):
32 | self.tenant = tenant
33 | self.deck = deck
34 | self.template = template
35 | self.version = version
36 | self.extension = extension
37 | self.variables = variables if variables else {}
38 | self.meta = meta if meta else {}
39 | self.secret = secret
40 | self.strategy = strategy
41 | if strategy and strategy.lower() != "hmac" and strategy.lower() != "jwt":
42 | raise Exception("Invalid `strategy`. Valid options are `HMAC` or `JWT`.")
43 | if strategy and not secret:
44 | raise Exception(
45 | "Missing `secret`. You can find it in your project in Advanced settings."
46 | )
47 | if secret and not strategy:
48 | raise Exception(
49 | "Got `secret` but missing `strategy`. Valid options are `HMAC` or `JWT`."
50 | )
51 |
52 | def querystring(self) -> str:
53 | default_v = {
54 | "__v": self.meta.get("v", str(int(time())))
55 | } # This forces crawlers to refresh the image
56 | defaults_without_v = {
57 | "__id": self.meta.get("id"),
58 | "_w": self.meta.get("width"),
59 | "_h": self.meta.get("height"),
60 | "_res": self.meta.get("resolution"),
61 | "_ua": self.meta.get("agent"),
62 | }
63 | if self.strategy and self.secret:
64 | key = self.secret.encode("ASCII")
65 | if self.strategy.lower() == "hmac":
66 | data = "#".join(
67 | [
68 | self.deck,
69 | self.template,
70 | self.version or "",
71 | self.extension or "",
72 | to_query(
73 | {
74 | **defaults_without_v,
75 | **self.variables,
76 | }
77 | ),
78 | ],
79 | ).encode("ASCII")
80 | __hmac = hmac.new(key, data, sha256).hexdigest()[:16]
81 | return to_query(
82 | {
83 | **default_v,
84 | **defaults_without_v,
85 | **self.variables,
86 | "__hmac": __hmac,
87 | }
88 | )
89 | elif self.strategy.lower() == "jwt":
90 | jwt_defaults = {
91 | "i": self.meta.get("id"),
92 | "w": self.meta.get("width"),
93 | "h": self.meta.get("height"),
94 | "r": self.meta.get("resolution"),
95 | "u": self.meta.get("agent"),
96 | "var": self.variables,
97 | }
98 | data = {
99 | "d": self.deck,
100 | "t": self.template,
101 | "v": self.version,
102 | "e": self.extension,
103 | **jwt_defaults,
104 | }
105 | __jwt = jwt.encode(data, key, algorithm="HS256", headers=None)
106 | return to_query({"__jwt": __jwt, **default_v})
107 | else:
108 | return to_query({**default_v, **defaults_without_v, **self.variables})
109 |
110 | def href(self) -> str:
111 | query = self.querystring()
112 | base_href = "https://cdn.flyyer.io/render/v2"
113 | if self.strategy and self.strategy.lower() == "jwt":
114 | return f"{base_href}/{self.tenant}?{query}"
115 | final_href = f"{base_href}/{self.tenant}/{self.deck}/{self.template}"
116 | if self.version:
117 | final_href += f".{self.version}"
118 | if self.extension:
119 | final_href += f".{self.extension}"
120 | return f"{final_href}?{query}"
121 |
122 | def __str__(self):
123 | return self.href()
124 |
125 |
126 | class Flyyer:
127 | def __init__(
128 | self,
129 | project: str,
130 | path: Optional[str] = "/",
131 | secret: Optional[str] = None,
132 | strategy: Optional[str] = None,
133 | variables: Optional[Mapping[Any, Any]] = None,
134 | meta: Optional[FlyyerMeta] = None,
135 | default: Optional[str] = None,
136 | ):
137 | self.project = project
138 | self.path = path if path.startswith("/") else "/" + path
139 | self.default = default
140 | self.secret = secret
141 | self.strategy = strategy
142 | self.variables = variables if variables else {}
143 | self.meta = meta if meta else {}
144 | if strategy and strategy.lower() != "hmac" and strategy.lower() != "jwt":
145 | raise Exception("Invalid `strategy`. Valid options are `HMAC` or `JWT`.")
146 | if strategy and not secret:
147 | raise Exception(
148 | "Missing `secret`. You can find it in your project in Advanced settings."
149 | )
150 | if secret and not strategy:
151 | raise Exception(
152 | "Got `secret` but missing `strategy`. Valid options are `HMAC` or `JWT`."
153 | )
154 |
155 | def params_hash(self, ignoreV, isJWT=False) -> Union[str, dict]:
156 | if not isJWT:
157 | defaults = {
158 | "__v": self.meta.get(
159 | "v", str(int(time()))
160 | ), # This forces crawlers to refresh the image
161 | "__id": self.meta.get("id"),
162 | "_w": self.meta.get("width"),
163 | "_h": self.meta.get("height"),
164 | "_res": self.meta.get("resolution"),
165 | "_ua": self.meta.get("agent"),
166 | "_def": self.default,
167 | }
168 | if ignoreV:
169 | defaults.pop("__v", None)
170 | return {**defaults, **self.variables}
171 | else:
172 | jwt_defaults = {
173 | "path": self.path,
174 | "params": {
175 | "i": self.meta.get("id"),
176 | "w": self.meta.get("width"),
177 | "h": self.meta.get("height"),
178 | "r": self.meta.get("resolution"),
179 | "u": self.meta.get("agent"),
180 | "def": self.default,
181 | "var": self.variables,
182 | },
183 | }
184 | return jwt_defaults
185 |
186 | def querystring(self, ignoreV=False) -> str:
187 | params = self.params_hash(ignoreV)
188 | aux = to_query(params).split("&")
189 | aux.sort()
190 | return "&".join(aux)
191 |
192 | def sign(self) -> str:
193 | # strategy & secret consistency checked on init
194 | if self.strategy == None:
195 | return "_"
196 | key = self.secret.encode("ASCII")
197 | if self.strategy and self.strategy.lower() == "hmac":
198 | data = (self.project + self.path + self.querystring(True)).encode("ASCII")
199 | return hmac.new(key, data, sha256).hexdigest()[:16]
200 | elif self.strategy and self.strategy.lower() == "jwt":
201 | data = {k: v for k, v in self.params_hash(True, True).items() if v is not None}
202 | return jwt.encode(data, key, algorithm="HS256", headers=None)
203 |
204 | def href(self) -> str:
205 | signature = self.sign()
206 | if self.strategy and self.strategy.lower() == "jwt":
207 | final_version = self.meta.get("v", str(int(time())))
208 | return f"https://cdn.flyyer.io/v2/{self.project}/jwt-{signature}?__v={final_version}"
209 | else:
210 | query = self.querystring()
211 | return f"https://cdn.flyyer.io/v2/{self.project}/{signature}/{query}{self.path}"
212 |
213 | def __str__(self):
214 | return self.href()
215 |
216 |
217 | # From https://stackoverflow.com/a/43347067/3416691
218 | # Alternative: https://stackoverflow.com/a/4014164/3416691
219 | def to_query(params: Mapping[Any, Any]) -> str:
220 | g_encode_params = {}
221 |
222 | def _encode_params(params, p_key=None):
223 | encode_params = {}
224 | if params is None:
225 | pass # skip
226 | elif isinstance(params, dict):
227 | for key in params:
228 | encode_key = "{}[{}]".format(p_key, key)
229 | encode_params[encode_key] = params[key]
230 | elif isinstance(params, (list, tuple)):
231 | for offset, value in enumerate(params):
232 | encode_key = "{}[{}]".format(p_key, offset)
233 | encode_params[encode_key] = value
234 | elif isinstance(params, (bool)):
235 | g_encode_params[p_key] = str(params).lower()
236 | else:
237 | g_encode_params[p_key] = params
238 |
239 | for key in encode_params:
240 | value = encode_params[key]
241 | _encode_params(value, key)
242 |
243 | if isinstance(params, dict):
244 | for key in params:
245 | _encode_params(params[key], key)
246 |
247 | return urlencode(g_encode_params)
248 |
--------------------------------------------------------------------------------
/poetry.lock:
--------------------------------------------------------------------------------
1 | [[package]]
2 | name = "appdirs"
3 | version = "1.4.4"
4 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
5 | category = "dev"
6 | optional = false
7 | python-versions = "*"
8 |
9 | [[package]]
10 | name = "astroid"
11 | version = "2.4.2"
12 | description = "An abstract syntax tree for Python with inference support."
13 | category = "dev"
14 | optional = false
15 | python-versions = ">=3.5"
16 |
17 | [package.dependencies]
18 | lazy-object-proxy = ">=1.4.0,<1.5.0"
19 | six = ">=1.12,<2.0"
20 | typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""}
21 | wrapt = ">=1.11,<2.0"
22 |
23 | [[package]]
24 | name = "atomicwrites"
25 | version = "1.4.0"
26 | description = "Atomic file writes."
27 | category = "dev"
28 | optional = false
29 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
30 |
31 | [[package]]
32 | name = "attrs"
33 | version = "20.3.0"
34 | description = "Classes Without Boilerplate"
35 | category = "dev"
36 | optional = false
37 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
38 |
39 | [package.extras]
40 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"]
41 | docs = ["furo", "sphinx", "zope.interface"]
42 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
43 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"]
44 |
45 | [[package]]
46 | name = "black"
47 | version = "20.8b1"
48 | description = "The uncompromising code formatter."
49 | category = "dev"
50 | optional = false
51 | python-versions = ">=3.6"
52 |
53 | [package.dependencies]
54 | appdirs = "*"
55 | click = ">=7.1.2"
56 | dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""}
57 | mypy-extensions = ">=0.4.3"
58 | pathspec = ">=0.6,<1"
59 | regex = ">=2020.1.8"
60 | toml = ">=0.10.1"
61 | typed-ast = ">=1.4.0"
62 | typing-extensions = ">=3.7.4"
63 |
64 | [package.extras]
65 | colorama = ["colorama (>=0.4.3)"]
66 | d = ["aiohttp (>=3.3.2)", "aiohttp-cors"]
67 |
68 | [[package]]
69 | name = "click"
70 | version = "7.1.2"
71 | description = "Composable command line interface toolkit"
72 | category = "dev"
73 | optional = false
74 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
75 |
76 | [[package]]
77 | name = "colorama"
78 | version = "0.4.4"
79 | description = "Cross-platform colored terminal text."
80 | category = "dev"
81 | optional = false
82 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
83 |
84 | [[package]]
85 | name = "dataclasses"
86 | version = "0.6"
87 | description = "A backport of the dataclasses module for Python 3.6"
88 | category = "dev"
89 | optional = false
90 | python-versions = "*"
91 |
92 | [[package]]
93 | name = "importlib-metadata"
94 | version = "3.3.0"
95 | description = "Read metadata from Python packages"
96 | category = "dev"
97 | optional = false
98 | python-versions = ">=3.6"
99 |
100 | [package.dependencies]
101 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
102 | zipp = ">=0.5"
103 |
104 | [package.extras]
105 | docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
106 | testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
107 |
108 | [[package]]
109 | name = "isort"
110 | version = "5.6.4"
111 | description = "A Python utility / library to sort Python imports."
112 | category = "dev"
113 | optional = false
114 | python-versions = ">=3.6,<4.0"
115 |
116 | [package.extras]
117 | pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
118 | requirements_deprecated_finder = ["pipreqs", "pip-api"]
119 | colors = ["colorama (>=0.4.3,<0.5.0)"]
120 |
121 | [[package]]
122 | name = "lazy-object-proxy"
123 | version = "1.4.3"
124 | description = "A fast and thorough lazy object proxy."
125 | category = "dev"
126 | optional = false
127 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
128 |
129 | [[package]]
130 | name = "mccabe"
131 | version = "0.6.1"
132 | description = "McCabe checker, plugin for flake8"
133 | category = "dev"
134 | optional = false
135 | python-versions = "*"
136 |
137 | [[package]]
138 | name = "more-itertools"
139 | version = "8.6.0"
140 | description = "More routines for operating on iterables, beyond itertools"
141 | category = "dev"
142 | optional = false
143 | python-versions = ">=3.5"
144 |
145 | [[package]]
146 | name = "mypy-extensions"
147 | version = "0.4.3"
148 | description = "Experimental type system extensions for programs checked with the mypy typechecker."
149 | category = "dev"
150 | optional = false
151 | python-versions = "*"
152 |
153 | [[package]]
154 | name = "packaging"
155 | version = "20.8"
156 | description = "Core utilities for Python packages"
157 | category = "dev"
158 | optional = false
159 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
160 |
161 | [package.dependencies]
162 | pyparsing = ">=2.0.2"
163 |
164 | [[package]]
165 | name = "pathspec"
166 | version = "0.8.1"
167 | description = "Utility library for gitignore style pattern matching of file paths."
168 | category = "dev"
169 | optional = false
170 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
171 |
172 | [[package]]
173 | name = "pluggy"
174 | version = "0.13.1"
175 | description = "plugin and hook calling mechanisms for python"
176 | category = "dev"
177 | optional = false
178 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
179 |
180 | [package.dependencies]
181 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
182 |
183 | [package.extras]
184 | dev = ["pre-commit", "tox"]
185 |
186 | [[package]]
187 | name = "py"
188 | version = "1.10.0"
189 | description = "library with cross-python path, ini-parsing, io, code, log facilities"
190 | category = "dev"
191 | optional = false
192 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
193 |
194 | [[package]]
195 | name = "pyjwt"
196 | version = "2.0.1"
197 | description = "JSON Web Token implementation in Python"
198 | category = "main"
199 | optional = false
200 | python-versions = ">=3.6"
201 |
202 | [package.extras]
203 | crypto = ["cryptography (>=3.3.1,<4.0.0)"]
204 | dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1,<4.0.0)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"]
205 | docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
206 | tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"]
207 |
208 | [[package]]
209 | name = "pylint"
210 | version = "2.6.0"
211 | description = "python code static checker"
212 | category = "dev"
213 | optional = false
214 | python-versions = ">=3.5.*"
215 |
216 | [package.dependencies]
217 | astroid = ">=2.4.0,<=2.5"
218 | colorama = {version = "*", markers = "sys_platform == \"win32\""}
219 | isort = ">=4.2.5,<6"
220 | mccabe = ">=0.6,<0.7"
221 | toml = ">=0.7.1"
222 |
223 | [[package]]
224 | name = "pyparsing"
225 | version = "2.4.7"
226 | description = "Python parsing module"
227 | category = "dev"
228 | optional = false
229 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
230 |
231 | [[package]]
232 | name = "pytest"
233 | version = "5.4.3"
234 | description = "pytest: simple powerful testing with Python"
235 | category = "dev"
236 | optional = false
237 | python-versions = ">=3.5"
238 |
239 | [package.dependencies]
240 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
241 | attrs = ">=17.4.0"
242 | colorama = {version = "*", markers = "sys_platform == \"win32\""}
243 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""}
244 | more-itertools = ">=4.0.0"
245 | packaging = "*"
246 | pluggy = ">=0.12,<1.0"
247 | py = ">=1.5.0"
248 | wcwidth = "*"
249 |
250 | [package.extras]
251 | checkqa-mypy = ["mypy (==v0.761)"]
252 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
253 |
254 | [[package]]
255 | name = "regex"
256 | version = "2020.11.13"
257 | description = "Alternative regular expression module, to replace re."
258 | category = "dev"
259 | optional = false
260 | python-versions = "*"
261 |
262 | [[package]]
263 | name = "six"
264 | version = "1.15.0"
265 | description = "Python 2 and 3 compatibility utilities"
266 | category = "dev"
267 | optional = false
268 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
269 |
270 | [[package]]
271 | name = "toml"
272 | version = "0.10.2"
273 | description = "Python Library for Tom's Obvious, Minimal Language"
274 | category = "dev"
275 | optional = false
276 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
277 |
278 | [[package]]
279 | name = "typed-ast"
280 | version = "1.4.1"
281 | description = "a fork of Python 2 and 3 ast modules with type comment support"
282 | category = "dev"
283 | optional = false
284 | python-versions = "*"
285 |
286 | [[package]]
287 | name = "typing-extensions"
288 | version = "3.7.4.3"
289 | description = "Backported and Experimental Type Hints for Python 3.5+"
290 | category = "main"
291 | optional = false
292 | python-versions = "*"
293 |
294 | [[package]]
295 | name = "wcwidth"
296 | version = "0.2.5"
297 | description = "Measures the displayed width of unicode strings in a terminal"
298 | category = "dev"
299 | optional = false
300 | python-versions = "*"
301 |
302 | [[package]]
303 | name = "wrapt"
304 | version = "1.12.1"
305 | description = "Module for decorators, wrappers and monkey patching."
306 | category = "dev"
307 | optional = false
308 | python-versions = "*"
309 |
310 | [[package]]
311 | name = "zipp"
312 | version = "3.4.0"
313 | description = "Backport of pathlib-compatible object wrapper for zip files"
314 | category = "dev"
315 | optional = false
316 | python-versions = ">=3.6"
317 |
318 | [package.extras]
319 | docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
320 | testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
321 |
322 | [metadata]
323 | lock-version = "1.1"
324 | python-versions = "^3.6"
325 | content-hash = "e4b75ec69edf45cb9faac85d246785f4b414a01c5b8e0cdb68d74c33fe31fb56"
326 |
327 | [metadata.files]
328 | appdirs = [
329 | {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
330 | {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
331 | ]
332 | astroid = [
333 | {file = "astroid-2.4.2-py3-none-any.whl", hash = "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"},
334 | {file = "astroid-2.4.2.tar.gz", hash = "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703"},
335 | ]
336 | atomicwrites = [
337 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
338 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
339 | ]
340 | attrs = [
341 | {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"},
342 | {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"},
343 | ]
344 | black = [
345 | {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"},
346 | ]
347 | click = [
348 | {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
349 | {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
350 | ]
351 | colorama = [
352 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
353 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
354 | ]
355 | dataclasses = [
356 | {file = "dataclasses-0.6-py3-none-any.whl", hash = "sha256:454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f"},
357 | {file = "dataclasses-0.6.tar.gz", hash = "sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"},
358 | ]
359 | importlib-metadata = [
360 | {file = "importlib_metadata-3.3.0-py3-none-any.whl", hash = "sha256:bf792d480abbd5eda85794e4afb09dd538393f7d6e6ffef6e9f03d2014cf9450"},
361 | {file = "importlib_metadata-3.3.0.tar.gz", hash = "sha256:5c5a2720817414a6c41f0a49993908068243ae02c1635a228126519b509c8aed"},
362 | ]
363 | isort = [
364 | {file = "isort-5.6.4-py3-none-any.whl", hash = "sha256:dcab1d98b469a12a1a624ead220584391648790275560e1a43e54c5dceae65e7"},
365 | {file = "isort-5.6.4.tar.gz", hash = "sha256:dcaeec1b5f0eca77faea2a35ab790b4f3680ff75590bfcb7145986905aab2f58"},
366 | ]
367 | lazy-object-proxy = [
368 | {file = "lazy-object-proxy-1.4.3.tar.gz", hash = "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"},
369 | {file = "lazy_object_proxy-1.4.3-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442"},
370 | {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win32.whl", hash = "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4"},
371 | {file = "lazy_object_proxy-1.4.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a"},
372 | {file = "lazy_object_proxy-1.4.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d"},
373 | {file = "lazy_object_proxy-1.4.3-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a"},
374 | {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win32.whl", hash = "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e"},
375 | {file = "lazy_object_proxy-1.4.3-cp34-cp34m-win_amd64.whl", hash = "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357"},
376 | {file = "lazy_object_proxy-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50"},
377 | {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db"},
378 | {file = "lazy_object_proxy-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449"},
379 | {file = "lazy_object_proxy-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156"},
380 | {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531"},
381 | {file = "lazy_object_proxy-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb"},
382 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08"},
383 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383"},
384 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142"},
385 | {file = "lazy_object_proxy-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea"},
386 | {file = "lazy_object_proxy-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62"},
387 | {file = "lazy_object_proxy-1.4.3-cp38-cp38-win32.whl", hash = "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd"},
388 | {file = "lazy_object_proxy-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239"},
389 | ]
390 | mccabe = [
391 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
392 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
393 | ]
394 | more-itertools = [
395 | {file = "more-itertools-8.6.0.tar.gz", hash = "sha256:b3a9005928e5bed54076e6e549c792b306fddfe72b2d1d22dd63d42d5d3899cf"},
396 | {file = "more_itertools-8.6.0-py3-none-any.whl", hash = "sha256:8e1a2a43b2f2727425f2b5839587ae37093f19153dc26c0927d1048ff6557330"},
397 | ]
398 | mypy-extensions = [
399 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
400 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
401 | ]
402 | packaging = [
403 | {file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"},
404 | {file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"},
405 | ]
406 | pathspec = [
407 | {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"},
408 | {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"},
409 | ]
410 | pluggy = [
411 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
412 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
413 | ]
414 | py = [
415 | {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"},
416 | {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"},
417 | ]
418 | pyjwt = [
419 | {file = "PyJWT-2.0.1-py3-none-any.whl", hash = "sha256:b70b15f89dc69b993d8a8d32c299032d5355c82f9b5b7e851d1a6d706dffe847"},
420 | {file = "PyJWT-2.0.1.tar.gz", hash = "sha256:a5c70a06e1f33d81ef25eecd50d50bd30e34de1ca8b2b9fa3fe0daaabcf69bf7"},
421 | ]
422 | pylint = [
423 | {file = "pylint-2.6.0-py3-none-any.whl", hash = "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"},
424 | {file = "pylint-2.6.0.tar.gz", hash = "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210"},
425 | ]
426 | pyparsing = [
427 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
428 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
429 | ]
430 | pytest = [
431 | {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
432 | {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
433 | ]
434 | regex = [
435 | {file = "regex-2020.11.13-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85"},
436 | {file = "regex-2020.11.13-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70"},
437 | {file = "regex-2020.11.13-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee"},
438 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5"},
439 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7"},
440 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31"},
441 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa"},
442 | {file = "regex-2020.11.13-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6"},
443 | {file = "regex-2020.11.13-cp36-cp36m-win32.whl", hash = "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e"},
444 | {file = "regex-2020.11.13-cp36-cp36m-win_amd64.whl", hash = "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884"},
445 | {file = "regex-2020.11.13-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b"},
446 | {file = "regex-2020.11.13-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88"},
447 | {file = "regex-2020.11.13-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0"},
448 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1"},
449 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0"},
450 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512"},
451 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba"},
452 | {file = "regex-2020.11.13-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538"},
453 | {file = "regex-2020.11.13-cp37-cp37m-win32.whl", hash = "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4"},
454 | {file = "regex-2020.11.13-cp37-cp37m-win_amd64.whl", hash = "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444"},
455 | {file = "regex-2020.11.13-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f"},
456 | {file = "regex-2020.11.13-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d"},
457 | {file = "regex-2020.11.13-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af"},
458 | {file = "regex-2020.11.13-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f"},
459 | {file = "regex-2020.11.13-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b"},
460 | {file = "regex-2020.11.13-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8"},
461 | {file = "regex-2020.11.13-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5"},
462 | {file = "regex-2020.11.13-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b"},
463 | {file = "regex-2020.11.13-cp38-cp38-win32.whl", hash = "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c"},
464 | {file = "regex-2020.11.13-cp38-cp38-win_amd64.whl", hash = "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683"},
465 | {file = "regex-2020.11.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc"},
466 | {file = "regex-2020.11.13-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364"},
467 | {file = "regex-2020.11.13-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e"},
468 | {file = "regex-2020.11.13-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e"},
469 | {file = "regex-2020.11.13-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917"},
470 | {file = "regex-2020.11.13-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b"},
471 | {file = "regex-2020.11.13-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9"},
472 | {file = "regex-2020.11.13-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c"},
473 | {file = "regex-2020.11.13-cp39-cp39-win32.whl", hash = "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"},
474 | {file = "regex-2020.11.13-cp39-cp39-win_amd64.whl", hash = "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d"},
475 | {file = "regex-2020.11.13.tar.gz", hash = "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562"},
476 | ]
477 | six = [
478 | {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
479 | {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
480 | ]
481 | toml = [
482 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
483 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
484 | ]
485 | typed-ast = [
486 | {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"},
487 | {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"},
488 | {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"},
489 | {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"},
490 | {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"},
491 | {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"},
492 | {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"},
493 | {file = "typed_ast-1.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fcf135e17cc74dbfbc05894ebca928ffeb23d9790b3167a674921db19082401f"},
494 | {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"},
495 | {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"},
496 | {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"},
497 | {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"},
498 | {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"},
499 | {file = "typed_ast-1.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f208eb7aff048f6bea9586e61af041ddf7f9ade7caed625742af423f6bae3298"},
500 | {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"},
501 | {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"},
502 | {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"},
503 | {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"},
504 | {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"},
505 | {file = "typed_ast-1.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7e4c9d7658aaa1fc80018593abdf8598bf91325af6af5cce4ce7c73bc45ea53d"},
506 | {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"},
507 | {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"},
508 | {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"},
509 | {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c"},
510 | {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072"},
511 | {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91"},
512 | {file = "typed_ast-1.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d"},
513 | {file = "typed_ast-1.4.1-cp39-cp39-win32.whl", hash = "sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395"},
514 | {file = "typed_ast-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c"},
515 | {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"},
516 | ]
517 | typing-extensions = [
518 | {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"},
519 | {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
520 | {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
521 | ]
522 | wcwidth = [
523 | {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
524 | {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
525 | ]
526 | wrapt = [
527 | {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"},
528 | ]
529 | zipp = [
530 | {file = "zipp-3.4.0-py3-none-any.whl", hash = "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108"},
531 | {file = "zipp-3.4.0.tar.gz", hash = "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"},
532 | ]
533 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.poetry]
2 | name = "flyyer"
3 | version = "2.1.2"
4 | description = "FLYYER.io helper classes and methods"
5 | authors = ["Patricio López Juri