From 2598820e865777fdbec37efc0441cb07c9028801 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Mon, 7 Feb 2022 15:09:14 -0800 Subject: [PATCH 01/37] Tweak python version matrix for testing --- .circleci/config.yml | 62 +------------------------------------------- 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1d805407..a93f49c2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,7 +23,7 @@ workflows: - gh-pages matrix: parameters: - python-version: ["3.6.5", "3.7.7", "3.8.6", "3.9.3", "latest"] + python-version: ["3.6.5", "3.7.7", "3.8.6", "3.9.3", "3.9.9", "3.9.10", "latest"] - hold: type: approval @@ -40,25 +40,6 @@ workflows: requires: - hold - # - docs: - # requires: - # - deploy - # - build - - # upload_test: - # triggers: - # - schedule: - # cron: "0,30 * * * *" - # filters: - # branches: - # only: - # - jh/use-xxhash-for-integration-test - # jobs: - # - build - # - upload_test_job: - # requires: - # - build - jobs: build: docker: @@ -96,47 +77,6 @@ jobs: command: | python /tmp/artifact/tests/integration.py - upload_test_job: - description: Upload test - docker: - - image: circleci/python:latest - steps: - - attach_workspace: - at: /tmp/artifact - name: Attach build artifact - - run: - name: Upload to pypi - command: | - cd /tmp/artifact - twine upload dist/* - - # docs: - # docker: - # - image: circleci/python:latest - - # steps: - # - attach_workspace: - # at: /tmp/artifact - # name: Attach build artifact - - # - run: - # name: Install dependencies - # command: | - # cd /tmp/artifact/docs - # pip install -r requirements.txt - - # - run: - # name: Build autodocs - # command: | - # cd /tmp/artifact/docs - # make jekyll - - # - run: - # name: Publish autodocs - # command: | - # cd /tmp/artifact/docs - # python publish.py - deploy: docker: - image: circleci/python:latest From 62a81183d406a5287832b944d8cbc24455dc7a39 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Sat, 19 Mar 2022 14:54:57 -1000 Subject: [PATCH 02/37] Refactor services -> resource --- frameioclient/__init__.py | 2 +- frameioclient/client.py | 22 +++++++++---------- .../{services => resources}/__init__.py | 0 .../{services => resources}/assets.py | 0 .../{services => resources}/comments.py | 0 .../{services => resources}/helpers.py | 0 .../{services => resources}/links.py | 0 frameioclient/{services => resources}/logs.py | 0 .../{services => resources}/projects.py | 0 .../{services => resources}/search.py | 0 .../{services => resources}/teams.py | 0 .../{services => resources}/users.py | 0 12 files changed, 12 insertions(+), 12 deletions(-) rename frameioclient/{services => resources}/__init__.py (100%) rename frameioclient/{services => resources}/assets.py (100%) rename frameioclient/{services => resources}/comments.py (100%) rename frameioclient/{services => resources}/helpers.py (100%) rename frameioclient/{services => resources}/links.py (100%) rename frameioclient/{services => resources}/logs.py (100%) rename frameioclient/{services => resources}/projects.py (100%) rename frameioclient/{services => resources}/search.py (100%) rename frameioclient/{services => resources}/teams.py (100%) rename frameioclient/{services => resources}/users.py (100%) diff --git a/frameioclient/__init__.py b/frameioclient/__init__.py index 04bcc1e2..186d7407 100644 --- a/frameioclient/__init__.py +++ b/frameioclient/__init__.py @@ -1,3 +1,3 @@ from .lib import * -from .services import * +from .resources import * from .client import FrameioClient diff --git a/frameioclient/client.py b/frameioclient/client.py index fefbbe40..7defcf06 100644 --- a/frameioclient/client.py +++ b/frameioclient/client.py @@ -7,8 +7,7 @@ from .config import Config from .lib import APIClient, ClientVersion, FrameioDownloader -# from .lib import Telemetry -from .services import * +from .resources import * class FrameioClient(APIClient): @@ -26,6 +25,7 @@ def me(self): return self.users.get_me() # def telemetry(self): + # from .lib import Telemetry # return Telemetry(self) def _auth(self): @@ -39,54 +39,54 @@ def _download(self): @property def users(self): - from .services import User + from .resources import User return User(self) @property def assets(self): - from .services import Asset + from .resources import Asset return Asset(self) @property def comments(self): - from .services import Comment + from .resources import Comment return Comment(self) @property def logs(self): - from .services import AuditLogs + from .resources import AuditLogs return AuditLogs(self) @property def review_links(self): - from .services import ReviewLink + from .resources import ReviewLink return ReviewLink(self) @property def presentation_links(self): - from .services import PresentationLink + from .resources import PresentationLink return PresentationLink(self) @property def projects(self): - from .services import Project + from .resources import Project return Project(self) @property def teams(self): - from .services import Team + from .resources import Team return Team(self) @property def helpers(self): - from .services import FrameioHelpers + from .resources import FrameioHelpers return FrameioHelpers(self) diff --git a/frameioclient/services/__init__.py b/frameioclient/resources/__init__.py similarity index 100% rename from frameioclient/services/__init__.py rename to frameioclient/resources/__init__.py diff --git a/frameioclient/services/assets.py b/frameioclient/resources/assets.py similarity index 100% rename from frameioclient/services/assets.py rename to frameioclient/resources/assets.py diff --git a/frameioclient/services/comments.py b/frameioclient/resources/comments.py similarity index 100% rename from frameioclient/services/comments.py rename to frameioclient/resources/comments.py diff --git a/frameioclient/services/helpers.py b/frameioclient/resources/helpers.py similarity index 100% rename from frameioclient/services/helpers.py rename to frameioclient/resources/helpers.py diff --git a/frameioclient/services/links.py b/frameioclient/resources/links.py similarity index 100% rename from frameioclient/services/links.py rename to frameioclient/resources/links.py diff --git a/frameioclient/services/logs.py b/frameioclient/resources/logs.py similarity index 100% rename from frameioclient/services/logs.py rename to frameioclient/resources/logs.py diff --git a/frameioclient/services/projects.py b/frameioclient/resources/projects.py similarity index 100% rename from frameioclient/services/projects.py rename to frameioclient/resources/projects.py diff --git a/frameioclient/services/search.py b/frameioclient/resources/search.py similarity index 100% rename from frameioclient/services/search.py rename to frameioclient/resources/search.py diff --git a/frameioclient/services/teams.py b/frameioclient/resources/teams.py similarity index 100% rename from frameioclient/services/teams.py rename to frameioclient/resources/teams.py diff --git a/frameioclient/services/users.py b/frameioclient/resources/users.py similarity index 100% rename from frameioclient/services/users.py rename to frameioclient/resources/users.py From 1f3a09a072f2a96ef0ce865440e424d787dee019 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Sat, 19 Mar 2022 15:04:47 -1000 Subject: [PATCH 03/37] Add #addVersionToAsset decorator to assets.add_version --- frameioclient/resources/assets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frameioclient/resources/assets.py b/frameioclient/resources/assets.py index 08a2ac08..14dd5bed 100644 --- a/frameioclient/resources/assets.py +++ b/frameioclient/resources/assets.py @@ -259,6 +259,7 @@ def bulk_copy( endpoint = "/batch/assets/{}/copy".format(destination_folder_id) return self.client._api_call("post", endpoint, payload) + @ApiReference(operation="#addVersionToAsset") def add_version( self, target_asset_id: Union[str, UUID], new_version_id: Union[str, UUID] ): From 9a631aa3439b57d0571ba0cdfeb231b0d0da0642 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 22 Mar 2022 08:59:44 -0700 Subject: [PATCH 04/37] Refactor, clean-up, and prep for release --- .gitignore | 3 +- .vscode/settings.json | 7 ++- docs/conf.py | 2 +- docs/index.rst | 2 +- docs/make.bat | 35 ----------- docs/requirements.txt | 26 ++++----- examples/assets/asset_scraper.py | 4 +- frameioclient/client.py | 4 -- frameioclient/lib/bandwidth.py | 57 ------------------ frameioclient/lib/constants.py | 2 +- frameioclient/lib/download.py | 14 ++--- frameioclient/lib/service.py | 5 +- frameioclient/lib/telemetry.py | 4 +- frameioclient/lib/transfer.py | 58 ++----------------- frameioclient/lib/transport.py | 10 +--- frameioclient/lib/upload.py | 6 +- frameioclient/resources/assets.py | 4 +- frameioclient/resources/comments.py | 2 +- frameioclient/resources/links.py | 16 ++--- frameioclient/resources/search.py | 2 - tests/test_frameiodownloader.py | 42 +++++++++----- ...py3_integration.py => test_integration.py} | 17 ++++-- 22 files changed, 91 insertions(+), 231 deletions(-) delete mode 100644 docs/make.bat delete mode 100644 frameioclient/lib/bandwidth.py rename tests/{py3_integration.py => test_integration.py} (95%) diff --git a/.gitignore b/.gitignore index 37bc36ef..768ce79c 100644 --- a/.gitignore +++ b/.gitignore @@ -110,4 +110,5 @@ Pipfile.lock .vscode/launch.json .vscode/settings.json -pyproject.toml \ No newline at end of file +pyproject.toml +.vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json index de288e1e..b276816b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,8 @@ { - "python.formatting.provider": "black" + "python.formatting.provider": "black", + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true } \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 73cc4d39..f1f7b075 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -6,7 +6,7 @@ PACKAGE_TITLE = 'Frame.io Python SDK' PACKAGE_NAME = 'frameioclient' -PACKAGE_DIR = '../frameioclient' +PACKAGE_DIR = '../' AUTHOR_NAME = 'Frame.io' try: diff --git a/docs/index.rst b/docs/index.rst index 9da0551d..ea0bf6b4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,4 +1,4 @@ -Welcome to Frame.io's Python SDK documentation! +Welcome to the documentation for `frameioclient` ! =============================================== .. toctree:: diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 2119f510..00000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=. -set BUILDDIR=_build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/requirements.txt b/docs/requirements.txt index 63a029d6..fd7cde4a 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,13 +1,13 @@ -sphinx -sphinx-jekyll-builder -sphinxcontrib-restbuilder -contentful_management -python-frontmatter -frameioclient -xxhash -furo -analytics-python -token-bucket -speedtest-cli -sphinx-autobuild -sphinx-autodoc-typehints \ No newline at end of file +"sphinx", +"sphinx-jekyll-builder", +"sphinxcontrib-restbuilder", +"contentful_management", +"python-frontmatter", +"frameioclient", +"xxhash", +"furo", +"analytics-python", +"token-bucket", +"speedtest-cli", +"sphinx-autobuild", +"sphinx-autodoc-typehints", \ No newline at end of file diff --git a/examples/assets/asset_scraper.py b/examples/assets/asset_scraper.py index 082d8595..faef181c 100644 --- a/examples/assets/asset_scraper.py +++ b/examples/assets/asset_scraper.py @@ -8,10 +8,8 @@ import csv -from functools import lru_cache import os -import time -from itertools import chain +from functools import lru_cache from typing import Dict, List from frameioclient import FrameioClient diff --git a/frameioclient/client.py b/frameioclient/client.py index 7defcf06..aa734b36 100644 --- a/frameioclient/client.py +++ b/frameioclient/client.py @@ -24,10 +24,6 @@ def __init__( def me(self): return self.users.get_me() - # def telemetry(self): - # from .lib import Telemetry - # return Telemetry(self) - def _auth(self): return self.token diff --git a/frameioclient/lib/bandwidth.py b/frameioclient/lib/bandwidth.py deleted file mode 100644 index b1991d53..00000000 --- a/frameioclient/lib/bandwidth.py +++ /dev/null @@ -1,57 +0,0 @@ -import speedtest - - -class NetworkBandwidth: - # Test the network bandwidth any time we have a new IP address - # Persist this information to a config.json file - - def __init__(self): - self.results = dict() - - def load_stats(self): - # Force an update on these stats before starting download/upload - pass - - def persist_stats(self): - pass - - def run(self): - self.results = self.speed_test() - - @staticmethod - def speedtest(): - """ - Run a speedtest using Speedtest.net in order to get a 'control' for \ - bandwidth optimization. - - Example:: - NetworkBandwidth.speedtest() - """ - - st = speedtest.Speedtest() - download_speed = round(st.download(threads=10) * (1.192 * 10 ** -7), 2) - upload_speed = round(st.upload(threads=10) * (1.192 * 10 ** -7), 2) - servernames = [] - server_names = st.get_servers(servernames) - ping = st.results.ping - - return { - "ping": ping, - "download_speed": download_speed, - "upload_speed": upload_speed, - } - - def __repr__(self): - self.results - - -class DiskBandwidth: - # Test the disk speed and write to a config.json file for re-use - # Worth re-checking the disk every time a new one is detected (base route) - - def __init__(self, volume): - self.volume = volume - self.results = dict() - - def __repr__(self): - self.results diff --git a/frameioclient/lib/constants.py b/frameioclient/lib/constants.py index 4b59e029..5a13c028 100644 --- a/frameioclient/lib/constants.py +++ b/frameioclient/lib/constants.py @@ -20,4 +20,4 @@ default_thread_count = 5 -retryable_statuses = [400, 429, 500, 503] +retryable_statuses = [400, 429, 500, 502, 503] diff --git a/frameioclient/lib/download.py b/frameioclient/lib/download.py index a96dca46..be3dfb27 100644 --- a/frameioclient/lib/download.py +++ b/frameioclient/lib/download.py @@ -1,21 +1,15 @@ -import os import math +import os from typing import Dict -from .utils import Utils - from .logger import SDKLogger from .transfer import AWSClient - -# from .telemetry import Event, ComparisonTest +from .utils import Utils logger = SDKLogger("downloads") -from .exceptions import ( - DownloadException, - WatermarkIDDownloadException, - AssetNotFullyUploaded, -) +from .exceptions import (AssetNotFullyUploaded, DownloadException, + WatermarkIDDownloadException) class FrameioDownloader(object): diff --git a/frameioclient/lib/service.py b/frameioclient/lib/service.py index bd5e455c..7ad2632d 100644 --- a/frameioclient/lib/service.py +++ b/frameioclient/lib/service.py @@ -1,18 +1,15 @@ from ..client import FrameioClient -from ..lib.bandwidth import NetworkBandwidth class Service(object): def __init__(self, client: FrameioClient): self.client = client self.concurrency = 10 - self.bandwidth = NetworkBandwidth() - # Auto-configure afterwards + # Auto-configure SDK self.autoconfigure() def autoconfigure(self): - # self.bandwidth = SpeedTest.speedtest() return def save_config(self): diff --git a/frameioclient/lib/telemetry.py b/frameioclient/lib/telemetry.py index 51248bd7..309e89fd 100644 --- a/frameioclient/lib/telemetry.py +++ b/frameioclient/lib/telemetry.py @@ -13,11 +13,10 @@ class Telemetry(object): def __init__(self, user_id): self.user_id = user_id - self.speedtest = None self.identity = None self.context = None self.integrations = {"all": False, "Amplitude": True} - self.logger = SDKLogger("telemetry") + self.logger = SDKLogger("frameioclient.telemetry") self.build_context() @@ -84,7 +83,6 @@ def track_transfer(self): # self.logger.info(pprint(chunk)) # Collect info to build message - # Build payload for transfer tracking # stats_payload = self._build_transfer_stats_payload() diff --git a/frameioclient/lib/transfer.py b/frameioclient/lib/transfer.py index f698e519..7b40d1ff 100644 --- a/frameioclient/lib/transfer.py +++ b/frameioclient/lib/transfer.py @@ -11,20 +11,15 @@ from .exceptions import ( AssetChecksumMismatch, AssetChecksumNotPresent, - DownloadException, -) -from .logger import SDKLogger -from .utils import FormatTypes, Utils - -logger = SDKLogger("downloads") - -from .bandwidth import DiskBandwidth, NetworkBandwidth -from .exceptions import ( AssetNotFullyUploaded, DownloadException, WatermarkIDDownloadException, ) +from .logger import SDKLogger from .transport import HTTPClient +from .utils import FormatTypes, Utils + +logger = SDKLogger("frameioclient.transfer") class FrameioDownloader(object): @@ -207,8 +202,6 @@ def __init__(self, downloader: FrameioDownloader, concurrency=None, progress=Tru # Ensure this is a valid number before assigning if concurrency is not None and type(concurrency) == int and concurrency > 0: self.concurrency = concurrency - # else: - # self.concurrency = self._optimize_concurrency() @staticmethod def check_cdn(url): @@ -237,25 +230,6 @@ def _create_file_stub(self): raise e return True - def _optimize_concurrency(self): - """ - This method looks as the net_stats and disk_stats that we've run on \ - the current environment in order to suggest the best optimized \ - number of concurrent TCP connections. - - Example:: - AWSClient._optimize_concurrency() - """ - - net_stats = NetworkBandwidth - disk_stats = DiskBandwidth - - # Algorithm ensues - # - # - - return 5 - def _get_byte_range( self, url: str, start_byte: Optional[int] = 0, end_byte: Optional[int] = 2048 ): @@ -460,27 +434,3 @@ def multi_thread_download(self): return dl_info else: return self.destination - - -class TransferJob(AWSClient): - # These will be used to track the job and then push telemetry - def __init__(self, job_info): - self.job_info = job_info # < - convert to JobInfo class - self.cdn = "S3" # or 'CF' - use check_cdn to confirm - self.progress_manager = None - - -class DownloadJob(TransferJob): - def __init__(self): - self.asset_type = "review_link" # we should use a dataclass here - # Need to create a re-usable job schema - # Think URL -> output_path - pass - - -class UploadJob(TransferJob): - def __init__(self, destination): - self.destination = destination - # Need to create a re-usable job schema - # Think local_file path and remote Frame.io destination - pass diff --git a/frameioclient/lib/transport.py b/frameioclient/lib/transport.py index 6dda6e16..31ef921c 100644 --- a/frameioclient/lib/transport.py +++ b/frameioclient/lib/transport.py @@ -1,7 +1,7 @@ import concurrent.futures import threading import time -from typing import Dict, Optional +from typing import Dict, Iterable, Optional import requests from requests.adapters import HTTPAdapter @@ -52,10 +52,7 @@ def __init__(self, threads: Optional[int] = default_thread_count): method_whitelist=["GET", "POST", "PUT", "GET", "DELETE"], ) - # Create real thread - self._initialize_thread() - - def _initialize_thread(self): + # Initialize thread self.thread_local = threading.local() def _get_session(self): @@ -88,7 +85,6 @@ def __init__(self, token: str, host: str, threads: int, progress: bool): self.token = token self.threads = threads self.progress = progress - self._initialize_thread() self.session = self._get_session() self.auth_header = {"Authorization": f"Bearer {self.token}"} @@ -149,7 +145,7 @@ def get_specific_page( payload["page"] = page return self._api_call(method, endpoint, payload=payload) - def exec_stream(callable, iterable, sync=lambda _: False, capacity=10, rate=10): + def exec_stream(callable, iterable: Iterable, sync=lambda _: False, capacity=10, rate=10): """ Executes a stream according to a defined rate limit. """ diff --git a/frameioclient/lib/upload.py b/frameioclient/lib/upload.py index 50128245..8cb1d00c 100644 --- a/frameioclient/lib/upload.py +++ b/frameioclient/lib/upload.py @@ -108,7 +108,7 @@ def upload(self): except Exception as exc: print(exc) - def file_counter(self, folder): + def count_files(self, folder): matches = [] for root, dirnames, filenames in os.walk(folder): for filename in filenames: @@ -118,13 +118,13 @@ def file_counter(self, folder): return matches - def recursive_upload(self, client, folder, parent_asset_id): + def upload_recursive(self, client, folder, parent_asset_id): # Seperate files and folders: file_list = list() folder_list = list() if self.file_count == 0: - self.file_counter(folder) + self.count_files(folder) for item in os.listdir(folder): if item == ".DS_Store": # Ignore .DS_Store files on Mac diff --git a/frameioclient/resources/assets.py b/frameioclient/resources/assets.py index 14dd5bed..293ef0de 100644 --- a/frameioclient/resources/assets.py +++ b/frameioclient/resources/assets.py @@ -141,7 +141,7 @@ def create( } endpoint = "/assets/{}/children".format(parent_asset_id) - return self.client._api_call("post", endpoint, payload=kwargs) + return self.client._api_call("post", endpoint, payload={**kwargs}) @ApiReference(operation="#createAsset") def create_folder(self, parent_asset_id: str, name: str = "New Folder"): @@ -379,9 +379,11 @@ def download( client.assets.download(asset, "~./Downloads") """ + downloader = FrameioDownloader( asset, download_folder, prefix, multi_part, replace ) + return AWSClient(downloader, concurrency=5).multi_thread_download() def upload_folder(self, source_path: str, destination_id: Union[str, UUID]): diff --git a/frameioclient/resources/comments.py b/frameioclient/resources/comments.py index 5d797183..4376f82e 100644 --- a/frameioclient/resources/comments.py +++ b/frameioclient/resources/comments.py @@ -104,7 +104,7 @@ def delete(self, comment_id: Union[str, UUID]): return self.client._api_call("delete", endpoint) @ApiReference(operation="#createReply") - def reply(self, comment_id, **kwargs): + def reply(self, comment_id: Union[str, UUID], **kwargs): """ Reply to an existing comment. diff --git a/frameioclient/resources/links.py b/frameioclient/resources/links.py index 731f02c9..c23311ac 100644 --- a/frameioclient/resources/links.py +++ b/frameioclient/resources/links.py @@ -1,10 +1,12 @@ +from typing import Union +from uuid import UUID from ..lib.utils import ApiReference from ..lib.service import Service class ReviewLink(Service): @ApiReference(operation="#reviewLinkCreate") - def create(self, project_id, **kwargs): + def create(self, project_id: Union[str, UUID], **kwargs): """ Create a review link. @@ -26,7 +28,7 @@ def create(self, project_id, **kwargs): return self.client._api_call("post", endpoint, payload=kwargs) @ApiReference(operation="#reviewLinksList") - def list(self, project_id): + def list(self, project_id: Union[str, UUID]): """ Get the review links of a project @@ -37,7 +39,7 @@ def list(self, project_id): return self.client._api_call("get", endpoint) @ApiReference(operation="#reviewLinkGet") - def get(self, link_id, **kwargs): + def get(self, link_id: Union[str, UUID], **kwargs): """ Get a single review link @@ -48,7 +50,7 @@ def get(self, link_id, **kwargs): return self.client._api_call("get", endpoint, payload=kwargs) @ApiReference(operation="#reviewLinkItemsList") - def get_assets(self, link_id): + def get_assets(self, link_id: Union[str, UUID]): """ Get items from a single review link. @@ -65,7 +67,7 @@ def get_assets(self, link_id): return self.client._api_call("get", endpoint) @ApiReference(operation="#reviewLinkItemsUpdate") - def update_assets(self, link_id, **kwargs): + def update_assets(self, link_id: Union[str, UUID], **kwargs): """ Add or update assets for a review link. @@ -86,7 +88,7 @@ def update_assets(self, link_id, **kwargs): return self.client._api_call("post", endpoint, payload=kwargs) @ApiReference(operation="#reviewLinkUpdate") - def update_settings(self, link_id, **kwargs): + def update_settings(self, link_id: Union[str, UUID], **kwargs): """ Updates review link settings. @@ -112,7 +114,7 @@ def update_settings(self, link_id, **kwargs): class PresentationLink(Service): @ApiReference(operation="#createPresentation") - def create(self, asset_id, **kwargs): + def create(self, asset_id: Union[str, UUID], **kwargs): """ Create a presentation link. diff --git a/frameioclient/resources/search.py b/frameioclient/resources/search.py index 31067f09..fd91415f 100644 --- a/frameioclient/resources/search.py +++ b/frameioclient/resources/search.py @@ -22,8 +22,6 @@ def library( Search for assets using the library search endpoint, documented at https://developer.frame.io/docs/workflows-assets/search-for-assets. For more information check out https://developer.frame.io/api/reference/operation/librarySearchPost/. - # TODO, confirm that account_id is required or not, could we use self.me? - :param query: The search keyword you want to search with. :param account_id: The frame.io account want you to contrain your search to (you may only have one, but some users have 20+ that they have acces to). :param type: The type of frame.io asset you want to search: [file, folder, review_link, presentation]. diff --git a/tests/test_frameiodownloader.py b/tests/test_frameiodownloader.py index f09cf379..400cb2e8 100644 --- a/tests/test_frameiodownloader.py +++ b/tests/test_frameiodownloader.py @@ -5,14 +5,17 @@ regular_asset = { "is_hls_required": False, "is_session_watermarked": False, - "downloads": { - "h264_720": "some-720-url", - "h264_1080_best": "some-1080-url" - }, + "filesize": 1000, + "name": "Demo file.mov", + "duration": "100.00", + "filetype": 'quicktime/mov', + "type": "file", + "_type": "file", + "downloads": {"h264_720": "some-720-url", "h264_1080_best": "some-1080-url"}, "h264_720": "some-720-url", "h264_1080_best": "some-1080-url", "original": "some-original-url", - "hls_manifest": "some-hls-url" + "hls_manifest": "some-hls-url", } watermarked_asset_download_allowed = { @@ -20,36 +23,43 @@ "is_session_watermarked": True, "downloads": { "h264_720": "download-stream-service-url", - "h264_1080_best": "download-stream-service-url" + "h264_1080_best": "download-stream-service-url", }, - "hls_manifest": "hls-url" + "hls_manifest": "hls-url", } watermarked_asset_no_download = { "is_hls_required": True, "is_session_watermarked": True, - "hls_manifest": "hls-url" + "hls_manifest": "hls-url", } no_download_allowed = { "is_hls_required": True, "is_session_watermarked": False, - "hls_manifest": "hls-url" + "hls_manifest": "hls-url", } + def test_get_download_key_returns_original(): - url = FrameioDownloader(regular_asset, './').get_download_key() - assert url == regular_asset['original'] + url = FrameioDownloader( + regular_asset, + download_folder="./", + prefix="", + ).get_download_key() + assert url == regular_asset["original"] + def test_get_download_key_returns_watermarked_download(): - url = FrameioDownloader(watermarked_asset_download_allowed, './').get_download_key() - assert url == watermarked_asset_download_allowed['downloads']['h264_1080_best'] + url = FrameioDownloader(watermarked_asset_download_allowed, "./").get_download_key() + assert url == watermarked_asset_download_allowed["downloads"]["h264_1080_best"] + def test_get_download_key_fails_gracefully_on_watermarked_asset(): with pytest.raises(DownloadException): - FrameioDownloader(watermarked_asset_no_download, './').get_download_key() + FrameioDownloader(watermarked_asset_no_download, "./").get_download_key() + def test_get_download_key_fails_gracefully_when_downloads_disallowed(): with pytest.raises(DownloadException): - FrameioDownloader(no_download_allowed, './').get_download_key() - + FrameioDownloader(no_download_allowed, "./").get_download_key() diff --git a/tests/py3_integration.py b/tests/test_integration.py similarity index 95% rename from tests/py3_integration.py rename to tests/test_integration.py index c4564f11..fe6feeb3 100644 --- a/tests/py3_integration.py +++ b/tests/test_integration.py @@ -13,6 +13,10 @@ from frameioclient import FrameioClient, Utils, KB, MB from frameioclient.lib.utils import FormatTypes +from dotenv import load_dotenv + +load_dotenv('/Users/jeff/Code/developer-relations/python-frameio-client/.env') + token = os.getenv("FRAMEIO_TOKEN") # Your Frame.io token project_id = os.getenv("PROJECT_ID") # Project you want to upload files back into download_asset_id = os.getenv("DOWNLOAD_FOLDER_ID") # Source folder on Frame.io (to then verify against) @@ -122,18 +126,19 @@ def test_download(client: FrameioClient, override=False): return True -# Test upload functionality +# Test upload functionality def test_upload(client: FrameioClient): print("Beginning upload test") # Create new parent asset project_info = client.projects.get(project_id) root_asset_id = project_info['root_asset_id'] - print("Creating new folder to upload to") - new_folder = client.assets.create( - parent_asset_id=root_asset_id, - name="{}_{}_Py{}_{}".format(socket.gethostname(), platform.system(), platform.python_version(), datetime.now().strftime("%B-%d-%Y")), - type="folder", + print(f"Creating new folder to upload to in project {project_id}") + test_run_name = "{}_{}_Py{}_{}".format(socket.gethostname(), platform.system(), platform.python_version(), datetime.now().strftime('%B-%d-%Y')) + print(f"Folder name: {test_run_name}") + new_folder = client.assets.create_folder( + parent_asset_id=root_asset_id, + name=test_run_name, ) new_parent_id = new_folder['id'] From a0fec7728702dd83029a15f85c5996c0be6e94ac Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 22 Mar 2022 09:00:08 -0700 Subject: [PATCH 05/37] Replace pipenv w/ poetry and update license --- LICENSE | 2 +- Makefile | 11 +- poetry.lock | 1140 ++++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 63 ++- setup.py | 1 - 5 files changed, 1209 insertions(+), 8 deletions(-) create mode 100644 poetry.lock diff --git a/LICENSE b/LICENSE index bad78c3c..47981373 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Frame.io +Copyright (c) 2022 Frame.io, an Adobe company. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index c6126f0e..e0f0b07a 100644 --- a/Makefile +++ b/Makefile @@ -17,10 +17,13 @@ clean: find . -name "*.pyc" -exec rm -f {} \; test: - cd tests && pipenv run python integration.py + cd tests && poetry run python test_integration.py package: - pipenv run python3 setup.py sdist bdist_wheel + poetry build + +publish: + poetry publish build-docker: docker build . -t benchmark @@ -32,7 +35,7 @@ format: black frameioclient view-docs: - cd docs && pip install -r requirements.txt && make dev + cd docs && poetry run make dev publish-docs: - cd docs && pip install -r requirements.txt && make jekyll && make publish + cd docs && poetry run make jekyll diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 00000000..8a234597 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1140 @@ +[[package]] +name = "alabaster" +version = "0.7.12" +description = "A configurable sidebar-enabled Sphinx theme" +category = "dev" +optional = true +python-versions = "*" + +[[package]] +name = "analytics-python" +version = "1.4.0" +description = "The hassle-free way to integrate analytics into any python application." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +backoff = "1.10.0" +monotonic = ">=1.5" +python-dateutil = ">2.1" +requests = ">=2.7,<3.0" +six = ">=1.5" + +[package.extras] +test = ["mock (2.0.0)", "pylint (1.9.3)", "flake8 (3.7.9)"] + +[[package]] +name = "ansicon" +version = "1.89.0" +description = "Python wrapper for loading Jason Hood's ANSICON" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "babel" +version = "2.9.1" +description = "Internationalization utilities" +category = "dev" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +pytz = ">=2015.7" + +[[package]] +name = "backoff" +version = "1.10.0" +description = "Function decoration for backoff and retry" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "beautifulsoup4" +version = "4.10.0" +description = "Screen-scraping library" +category = "dev" +optional = true +python-versions = ">3.0.0" + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "blessed" +version = "1.19.1" +description = "Easy, practical library for making terminal apps, by providing an elegant, well-documented interface to Colors, Keyboard input, and screen Positioning capabilities." +category = "main" +optional = false +python-versions = ">=2.7" + +[package.dependencies] +jinxed = {version = ">=1.1.0", markers = "platform_system == \"Windows\""} +six = ">=1.9.0" +wcwidth = ">=0.1.4" + +[[package]] +name = "bump2version" +version = "1.0.1" +description = "Version-bump your software with a single command!" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "certifi" +version = "2021.10.8" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "chardet" +version = "4.0.0" +description = "Universal encoding detector for Python 2 and 3" +category = "dev" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "charset-normalizer" +version = "2.0.12" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "dev" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "contentful-management" +version = "2.11.0" +description = "Contentful Management API Client" +category = "dev" +optional = true +python-versions = "*" + +[package.dependencies] +python-dateutil = "*" +requests = ">=2.20.0,<3.0" + +[[package]] +name = "docutils" +version = "0.17.1" +description = "Docutils -- Python Documentation Utilities" +category = "dev" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "enlighten" +version = "1.10.2" +description = "Enlighten Progress Bar" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +blessed = ">=1.17.7" +prefixed = ">=0.3.2" + +[[package]] +name = "furo" +version = "2022.3.4" +description = "A clean customisable Sphinx documentation theme." +category = "dev" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +beautifulsoup4 = "*" +pygments = ">=2.7,<3.0" +sphinx = ">=4.0,<5.0" + +[[package]] +name = "html2text" +version = "2020.1.16" +description = "Turn HTML into equivalent Markdown-structured text." +category = "dev" +optional = true +python-versions = ">=3.5" + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "imagesize" +version = "1.3.0" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +category = "dev" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "importlib-metadata" +version = "4.11.3" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +perf = ["ipython"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] + +[[package]] +name = "jinja2" +version = "3.0.3" +description = "A very fast and expressive template engine." +category = "dev" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "jinxed" +version = "1.1.0" +description = "Jinxed Terminal Library" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +ansicon = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "livereload" +version = "2.6.3" +description = "Python LiveReload is an awesome tool for web developers" +category = "dev" +optional = true +python-versions = "*" + +[package.dependencies] +six = "*" +tornado = {version = "*", markers = "python_version > \"2.7\""} + +[[package]] +name = "markupsafe" +version = "2.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "dev" +optional = true +python-versions = ">=3.7" + +[[package]] +name = "monotonic" +version = "1.6" +description = "An implementation of time.monotonic() for Python 2 & < 3.3" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "munch" +version = "2.5.0" +description = "A dot-accessible dictionary (a la JavaScript objects)" +category = "dev" +optional = true +python-versions = "*" + +[package.dependencies] +six = "*" + +[package.extras] +testing = ["pytest", "coverage", "astroid (>=1.5.3,<1.6.0)", "pylint (>=1.7.2,<1.8.0)", "astroid (>=2.0)", "pylint (>=2.3.1,<2.4.0)"] +yaml = ["PyYAML (>=5.1.0)"] + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "prefixed" +version = "0.3.2" +description = "Prefixed alternative numeric library" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "pydash" +version = "5.1.0" +description = "The kitchen sink of Python utility libraries for doing \"stuff\" in a functional way. Based on the Lo-Dash Javascript library." +category = "dev" +optional = true +python-versions = ">=3.6" + +[package.extras] +dev = ["black", "coverage", "docformatter", "flake8", "flake8-black", "flake8-bugbear", "flake8-isort", "invoke", "isort", "pylint", "pytest", "pytest-cov", "pytest-flake8", "pytest-pylint", "sphinx", "sphinx-rtd-theme", "tox", "twine", "wheel"] + +[[package]] +name = "pygments" +version = "2.11.2" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = true +python-versions = ">=3.5" + +[[package]] +name = "pyparsing" +version = "3.0.7" +description = "Python parsing module" +category = "dev" +optional = true +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "0.19.2" +description = "Read key-value pairs from a .env file and set them as environment variables" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-frontmatter" +version = "1.0.0" +description = "Parse and manage posts with YAML (or other) frontmatter" +category = "dev" +optional = true +python-versions = "*" + +[package.dependencies] +PyYAML = "*" + +[package.extras] +docs = ["sphinx"] +test = ["pytest", "toml", "pyaml"] + +[[package]] +name = "pytz" +version = "2022.1" +description = "World timezone definitions, modern and historical" +category = "dev" +optional = true +python-versions = "*" + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "dev" +optional = true +python-versions = ">=3.6" + +[[package]] +name = "requests" +version = "2.27.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "dev" +optional = true +python-versions = "*" + +[[package]] +name = "soupsieve" +version = "2.3.1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "dev" +optional = true +python-versions = ">=3.6" + +[[package]] +name = "sphinx" +version = "4.4.0" +description = "Python documentation generator" +category = "dev" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=1.3" +colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.14,<0.18" +imagesize = "*" +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} +Jinja2 = ">=2.3" +packaging = "*" +Pygments = ">=2.0" +requests = ">=2.5.0" +snowballstemmer = ">=1.1" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.931)", "docutils-stubs", "types-typed-ast", "types-requests"] +test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"] + +[[package]] +name = "sphinx-autobuild" +version = "2021.3.14" +description = "Rebuild Sphinx documentation on changes, with live-reload in the browser." +category = "dev" +optional = true +python-versions = ">=3.6" + +[package.dependencies] +colorama = "*" +livereload = "*" +sphinx = "*" + +[package.extras] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "sphinx-autodoc-typehints" +version = "1.17.0" +description = "Type hints (PEP 484) support for the Sphinx autodoc extension" +category = "dev" +optional = true +python-versions = ">=3.7" + +[package.dependencies] +Sphinx = ">=4" + +[package.extras] +testing = ["covdefaults (>=2)", "coverage (>=6)", "diff-cover (>=6.4)", "nptyping (>=1)", "pytest (>=6)", "pytest-cov (>=3)", "sphobjinv (>=2)", "typing-extensions (>=3.5)"] +type_comments = ["typed-ast (>=1.4.0)"] + +[[package]] +name = "sphinx-jekyll-builder" +version = "0.3.0" +description = "sphinx builder that outputs jekyll compatible markdown files with frontmatter" +category = "dev" +optional = true +python-versions = "*" + +[package.dependencies] +alabaster = ">=0.7.12" +Babel = ">=2.6.0" +certifi = ">=2018.11.29" +chardet = ">=3.0.4" +docutils = ">=0.14" +html2text = ">=2018.1.9" +idna = ">=2.8" +imagesize = ">=1.1.0" +Jinja2 = ">=2.10.1" +MarkupSafe = ">=1.1.0" +munch = ">=2.3.2" +packaging = ">=19.0" +pydash = ">=4.7.4" +Pygments = ">=2.3.1" +pyparsing = ">=2.3.1" +pytz = ">=2018.9" +PyYAML = ">=5.1" +requests = ">=2.21.0" +six = ">=1.12.0" +snowballstemmer = ">=1.2.1" +Sphinx = ">=1.8.3" +sphinx-markdown-builder = ">=0.5.3" +sphinxcontrib-websupport = ">=1.1.0" +typing = ">=3.6.6" +urllib3 = ">=1.24.2" + +[[package]] +name = "sphinx-markdown-builder" +version = "0.5.5" +description = "sphinx builder that outputs markdown files" +category = "dev" +optional = true +python-versions = "*" + +[package.dependencies] +html2text = "*" +pydash = "*" +sphinx = "*" +unify = "*" +yapf = "*" + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.2" +description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" +category = "dev" +optional = true +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "dev" +optional = true +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "dev" +optional = true +python-versions = ">=3.6" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest", "html5lib"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "dev" +optional = true +python-versions = ">=3.5" + +[package.extras] +test = ["pytest", "flake8", "mypy"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "dev" +optional = true +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-restbuilder" +version = "0.3" +description = "Sphinx extension to output reST files." +category = "dev" +optional = true +python-versions = ">=2.7, !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" + +[package.dependencies] +Sphinx = ">=1.4" + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "dev" +optional = true +python-versions = ">=3.5" + +[package.extras] +lint = ["flake8", "mypy", "docutils-stubs"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-websupport" +version = "1.2.4" +description = "Sphinx API for Web Apps" +category = "dev" +optional = true +python-versions = ">=3.5" + +[package.dependencies] +sphinxcontrib-serializinghtml = "*" + +[package.extras] +lint = ["flake8"] +test = ["pytest", "sqlalchemy", "whoosh", "sphinx"] + +[[package]] +name = "token-bucket" +version = "0.3.0" +description = "Very fast implementation of the token bucket algorithm." +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "tornado" +version = "6.1" +description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "dev" +optional = true +python-versions = ">= 3.5" + +[[package]] +name = "typing" +version = "3.7.4.3" +description = "Type Hints for Python" +category = "dev" +optional = true +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "unify" +version = "0.5" +description = "Modifies strings to all use the same (single/double) quote where possible." +category = "dev" +optional = true +python-versions = "*" + +[package.dependencies] +untokenize = "*" + +[[package]] +name = "untokenize" +version = "0.1.1" +description = "Transforms tokens into original source code (while preserving whitespace)." +category = "dev" +optional = true +python-versions = "*" + +[[package]] +name = "urllib3" +version = "1.26.9" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] + +[[package]] +name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "xxhash" +version = "3.0.0" +description = "Python binding for xxHash" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "yapf" +version = "0.32.0" +description = "A formatter for Python code." +category = "dev" +optional = true +python-versions = "*" + +[[package]] +name = "zipp" +version = "3.7.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] + +[extras] +docs = [] + +[metadata] +lock-version = "1.1" +python-versions = "^3.9" +content-hash = "4c9d276b284ba1d1640de6a9a14176670fff48d21127d8ed3130259141d06613" + +[metadata.files] +alabaster = [ + {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, + {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, +] +analytics-python = [ + {file = "analytics-python-1.4.0.tar.gz", hash = "sha256:a65141ab6e47db396f5bc5708b1db93ff9a99882d81fe808228afd5ebb6dfe5f"}, + {file = "analytics_python-1.4.0-py2.py3-none-any.whl", hash = "sha256:3bff972beeb8a3f26607ccd9153484aa4f12eeeea4a693be685bf45aa66ddf99"}, +] +ansicon = [ + {file = "ansicon-1.89.0-py2.py3-none-any.whl", hash = "sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec"}, + {file = "ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1"}, +] +babel = [ + {file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"}, + {file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"}, +] +backoff = [ + {file = "backoff-1.10.0-py2.py3-none-any.whl", hash = "sha256:5e73e2cbe780e1915a204799dba0a01896f45f4385e636bcca7a0614d879d0cd"}, + {file = "backoff-1.10.0.tar.gz", hash = "sha256:b8fba021fac74055ac05eb7c7bfce4723aedde6cd0a504e5326bcb0bdd6d19a4"}, +] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.10.0-py3-none-any.whl", hash = "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf"}, + {file = "beautifulsoup4-4.10.0.tar.gz", hash = "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891"}, +] +blessed = [ + {file = "blessed-1.19.1-py2.py3-none-any.whl", hash = "sha256:63b8554ae2e0e7f43749b6715c734cc8f3883010a809bf16790102563e6cf25b"}, + {file = "blessed-1.19.1.tar.gz", hash = "sha256:9a0d099695bf621d4680dd6c73f6ad547f6a3442fbdbe80c4b1daa1edbc492fc"}, +] +bump2version = [ + {file = "bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410"}, + {file = "bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6"}, +] +certifi = [ + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +] +chardet = [ + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, + {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +contentful-management = [ + {file = "contentful_management-2.11.0.tar.gz", hash = "sha256:7321c782eab27a23bb03c987a60feea6f7a5608b9a6a6ba4dde513e40b130e7b"}, +] +docutils = [ + {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, + {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, +] +enlighten = [ + {file = "enlighten-1.10.2-py2.py3-none-any.whl", hash = "sha256:b237fe562b320bf9f1d4bb76d0c98e0daf914372a76ab87c35cd02f57aa9d8c1"}, + {file = "enlighten-1.10.2.tar.gz", hash = "sha256:7a5b83cd0f4d095e59d80c648ebb5f7ffca0cd8bcf7ae6639828ee1ad000632a"}, +] +furo = [ + {file = "furo-2022.3.4-py3-none-any.whl", hash = "sha256:6c718293ebf87755f0b9f148b1e697c9e3aabd7af955644d4bcaee5ce75db781"}, + {file = "furo-2022.3.4.tar.gz", hash = "sha256:7660267cc67b2828fd0e17bc07adeb612c47b2eba5a6de07049a1569e6044aa8"}, +] +html2text = [ + {file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"}, + {file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] +imagesize = [ + {file = "imagesize-1.3.0-py2.py3-none-any.whl", hash = "sha256:1db2f82529e53c3e929e8926a1fa9235aa82d0bd0c580359c67ec31b2fddaa8c"}, + {file = "imagesize-1.3.0.tar.gz", hash = "sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.11.3-py3-none-any.whl", hash = "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6"}, + {file = "importlib_metadata-4.11.3.tar.gz", hash = "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"}, +] +jinja2 = [ + {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, + {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, +] +jinxed = [ + {file = "jinxed-1.1.0-py2.py3-none-any.whl", hash = "sha256:6a61ccf963c16aa885304f27e6e5693783676897cea0c7f223270c8b8e78baf8"}, + {file = "jinxed-1.1.0.tar.gz", hash = "sha256:d8f1731f134e9e6b04d95095845ae6c10eb15cb223a5f0cabdea87d4a279c305"}, +] +livereload = [ + {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, +] +markupsafe = [ + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, +] +monotonic = [ + {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, + {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, +] +munch = [ + {file = "munch-2.5.0-py2.py3-none-any.whl", hash = "sha256:6f44af89a2ce4ed04ff8de41f70b226b984db10a91dcc7b9ac2efc1c77022fdd"}, + {file = "munch-2.5.0.tar.gz", hash = "sha256:2d735f6f24d4dba3417fa448cae40c6e896ec1fdab6cdb5e6510999758a4dbd2"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +prefixed = [ + {file = "prefixed-0.3.2-py2.py3-none-any.whl", hash = "sha256:5e107306462d63f2f03c529dbf11b0026fdfec621a9a008ca639d71de22995c3"}, + {file = "prefixed-0.3.2.tar.gz", hash = "sha256:ca48277ba5fa8346dd4b760847da930c7b84416387c39e93affef086add2c029"}, +] +pydash = [ + {file = "pydash-5.1.0-py3-none-any.whl", hash = "sha256:ced4fedb163eb07fbee376e474bca74029eb9fab215614449fe13164f71dd9e3"}, + {file = "pydash-5.1.0.tar.gz", hash = "sha256:1b2b050ac1bae049cd07f5920b14fabbe52638f485d9ada1eb115a9eebff6835"}, +] +pygments = [ + {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, + {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, +] +pyparsing = [ + {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, + {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] +python-dotenv = [ + {file = "python-dotenv-0.19.2.tar.gz", hash = "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f"}, + {file = "python_dotenv-0.19.2-py2.py3-none-any.whl", hash = "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3"}, +] +python-frontmatter = [ + {file = "python-frontmatter-1.0.0.tar.gz", hash = "sha256:e98152e977225ddafea6f01f40b4b0f1de175766322004c826ca99842d19a7cd"}, + {file = "python_frontmatter-1.0.0-py3-none-any.whl", hash = "sha256:766ae75f1b301ffc5fe3494339147e0fd80bc3deff3d7590a93991978b579b08"}, +] +pytz = [ + {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, + {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, +] +pyyaml = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] +requests = [ + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +snowballstemmer = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] +soupsieve = [ + {file = "soupsieve-2.3.1-py3-none-any.whl", hash = "sha256:1a3cca2617c6b38c0343ed661b1fa5de5637f257d4fe22bd9f1338010a1efefb"}, + {file = "soupsieve-2.3.1.tar.gz", hash = "sha256:b8d49b1cd4f037c7082a9683dfa1801aa2597fb11c3a1155b7a5b94829b4f1f9"}, +] +sphinx = [ + {file = "Sphinx-4.4.0-py3-none-any.whl", hash = "sha256:5da895959511473857b6d0200f56865ed62c31e8f82dd338063b84ec022701fe"}, + {file = "Sphinx-4.4.0.tar.gz", hash = "sha256:6caad9786055cb1fa22b4a365c1775816b876f91966481765d7d50e9f0dd35cc"}, +] +sphinx-autobuild = [ + {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, + {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, +] +sphinx-autodoc-typehints = [ + {file = "sphinx_autodoc_typehints-1.17.0-py3-none-any.whl", hash = "sha256:081daf53077b4ae1c28347d6d858e13e63aefe3b4aacef79fd717dd60687b470"}, + {file = "sphinx_autodoc_typehints-1.17.0.tar.gz", hash = "sha256:51c7b3f5cb9ccd15d0b52088c62df3094f1abd9612930340365c26def8629a14"}, +] +sphinx-jekyll-builder = [ + {file = "sphinx-jekyll-builder-0.3.0.tar.gz", hash = "sha256:5ecc0a1821849fc55c4b26e0efb8fb45454904c6900378ebd776f8a74d6e06f7"}, + {file = "sphinx_jekyll_builder-0.3.0-py2.py3-none-any.whl", hash = "sha256:b3b2d46ba49b7d47d8f58077c9ccce928f2cfec6d3fce7738c69f1590c72ebc5"}, +] +sphinx-markdown-builder = [ + {file = "sphinx-markdown-builder-0.5.5.tar.gz", hash = "sha256:6ead53c08d8835329e32418dcdbac4db710a1c4e5e8db687d23b9e88882d9d16"}, + {file = "sphinx_markdown_builder-0.5.5-py2.py3-none-any.whl", hash = "sha256:3c8909579dfa83ce5a8fb48e2d01dc257fc0676931170cb92cd528f9fceee76f"}, +] +sphinxcontrib-applehelp = [ + {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, + {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, +] +sphinxcontrib-devhelp = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] +sphinxcontrib-htmlhelp = [ + {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, + {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, +] +sphinxcontrib-jsmath = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] +sphinxcontrib-qthelp = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] +sphinxcontrib-restbuilder = [ + {file = "sphinxcontrib-restbuilder-0.3.tar.gz", hash = "sha256:6b3ee9394b5ec5e73e6afb34d223530d0b9098cb7562f9c5e364e6d6b41410ce"}, + {file = "sphinxcontrib_restbuilder-0.3-py2.py3-none-any.whl", hash = "sha256:6ba2ddc7a87d845c075c1b2e00d541bd1c8400488e50e32c9b4169ccdd9f30cb"}, +] +sphinxcontrib-serializinghtml = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] +sphinxcontrib-websupport = [ + {file = "sphinxcontrib-websupport-1.2.4.tar.gz", hash = "sha256:4edf0223a0685a7c485ae5a156b6f529ba1ee481a1417817935b20bde1956232"}, + {file = "sphinxcontrib_websupport-1.2.4-py2.py3-none-any.whl", hash = "sha256:6fc9287dfc823fe9aa432463edd6cea47fa9ebbf488d7f289b322ffcfca075c7"}, +] +token-bucket = [ + {file = "token_bucket-0.3.0-py2.py3-none-any.whl", hash = "sha256:6df24309e3cf5b808ae5ef714a3191ec5b54f48c34ef959e4882eef140703369"}, + {file = "token_bucket-0.3.0.tar.gz", hash = "sha256:979571c99db2ff9e651f2b2146a62b2ebadf7de6c217a8781698282976cb675f"}, +] +tornado = [ + {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, + {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, + {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, + {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, + {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, + {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, + {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, + {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, + {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, + {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, + {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, + {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, + {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, + {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, + {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, + {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, + {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, + {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, + {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, + {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, + {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, + {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, + {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, + {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, + {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, + {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, + {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, +] +typing = [ + {file = "typing-3.7.4.3-py2-none-any.whl", hash = "sha256:283d868f5071ab9ad873e5e52268d611e851c870a2ba354193026f2dfb29d8b5"}, + {file = "typing-3.7.4.3.tar.gz", hash = "sha256:1187fb9c82fd670d10aa07bbb6cfcfe4bdda42d6fab8d5134f04e8c4d0b71cc9"}, +] +unify = [ + {file = "unify-0.5.tar.gz", hash = "sha256:8ddce812b2457212b7598fe574c9e6eb3ad69710f445391338270c7f8a71723c"}, +] +untokenize = [ + {file = "untokenize-0.1.1.tar.gz", hash = "sha256:3865dbbbb8efb4bb5eaa72f1be7f3e0be00ea8b7f125c69cbd1f5fda926f37a2"}, +] +urllib3 = [ + {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, + {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, +] +wcwidth = [ + {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, + {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, +] +xxhash = [ + {file = "xxhash-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:219cba13991fd73cf21a5efdafa5056f0ae0b8f79e5e0112967e3058daf73eea"}, + {file = "xxhash-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3fcbb846af15eff100c412ae54f4974ff277c92eacd41f1ec7803a64fd07fa0c"}, + {file = "xxhash-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f475fa817ff7955fc118fc1ca29a6e691d329b7ff43f486af36c22dbdcff1db"}, + {file = "xxhash-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9200a90f02ff6fd5fb63dea107842da71d8626d99b768fd31be44f3002c60bbe"}, + {file = "xxhash-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a1403e4f551c9ef7bcef09af55f1adb169f13e4de253db0887928e5129f87af1"}, + {file = "xxhash-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa7f6ca53170189a2268c83af0980e6c10aae69e6a5efa7ca989f89fff9f8c02"}, + {file = "xxhash-3.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b63fbeb6d9c93d50ae0dc2b8a8b7f52f2de19e40fe9edc86637bfa5743b8ba2"}, + {file = "xxhash-3.0.0-cp310-cp310-win32.whl", hash = "sha256:31f25efd10b6f1f6d5c34cd231986d8aae9a42e042daa90b783917f170807869"}, + {file = "xxhash-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:807e88ed56e0fb347cb57d5bf44851f9878360fed700f2f63e622ef4eede87a5"}, + {file = "xxhash-3.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6d612c55a75d84d25898f6c5ad6a589aa556d1cb9af770b6c574ee62995167f6"}, + {file = "xxhash-3.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f9309fcaf73f93df3101f03a61dc30644adff3e8d0044fff8c0c195dbbe63e2"}, + {file = "xxhash-3.0.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2273fe40720e86346a17f06ef95cd60ee0d66ffce7cf55e390ef7350112b16d"}, + {file = "xxhash-3.0.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc6f3a334587c83c5ba56c19b254a97542ce1fc05ccfd66fbf568e6117718d65"}, + {file = "xxhash-3.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36cf410da5bfcca51ac3c2c51a3317dcd7af91f70fa61eca57fba39554f06ae3"}, + {file = "xxhash-3.0.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21752a3e9a2391d91bd51f4aa2fe028ae14ba6a8d37db9ebe00ccac10be5ac4a"}, + {file = "xxhash-3.0.0-cp36-cp36m-win32.whl", hash = "sha256:322068a063ef156455a401ab720f0892f2d2dd1540c1a308e95a7cbf356df51c"}, + {file = "xxhash-3.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2984fa9a880587c0bfa46d32717b2d209863ee68727ea0fc17f05fce25efa692"}, + {file = "xxhash-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6493dd938b360235da81b1c79d8cd048c4f11977e1159b4e744c54f98d3a7bb4"}, + {file = "xxhash-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb9eca32f9b4acc7149db2c86f8108167b9929b7da1887d4287a90cfdb3ea53a"}, + {file = "xxhash-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4125e70e4e1d79992d81de837a0586aa0241665dbc5ce01b9c89330ed5cbb66"}, + {file = "xxhash-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:583bea142569485bdb0c5900e804058c16edba1850b74519688c22bc546e6175"}, + {file = "xxhash-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f3adf2891acc18abacd15113e9cbbefd30e5f4ecaae32c23e5486fc09c76ea5"}, + {file = "xxhash-3.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed65a2671d380ae05262ce1e4ccc2b63f3c30506d207bf6fae8cd72be0ad65d4"}, + {file = "xxhash-3.0.0-cp37-cp37m-win32.whl", hash = "sha256:c604b3dcac9d37e3fceaa11884927024953260cc4224d9b89400d16e6cf34021"}, + {file = "xxhash-3.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1c6fc59e182506496544bc6d426bcf6077066ed1b40cfcd937f707cc06c7ef50"}, + {file = "xxhash-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5628375dbb76d33b93b44854a6c5433e2a78115e03ea2ae1bb74a34ab012a43f"}, + {file = "xxhash-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:687aa4373690f23a3f43cc23d81005304d284ff6c041bff1f967664ab6410f36"}, + {file = "xxhash-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fa2100fb68b163e99370561c9e29ed37b9153fe99443600bea28829150eb0e4"}, + {file = "xxhash-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:891d7651431a055f76fe2c8f86c593c3dede8ec5b10ca55e8ff5c9fdceb55f0b"}, + {file = "xxhash-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:197c32d7b62be02957ca31aa69febadf9c5a34ef953053ea16e2c72465bc450f"}, + {file = "xxhash-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91fa4df41bda3cbec4084d9696028780b47128c1f8450d1ad9c3e4b6bf8b1f99"}, + {file = "xxhash-3.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cd38b766fc40e9fe37b80112656d2e5a0cb2f9bc12e01b286353b5ecd2768e8"}, + {file = "xxhash-3.0.0-cp38-cp38-win32.whl", hash = "sha256:4258ef78f5a7d1f9c595846134c7d81a868c74942051453258eb383498662d4d"}, + {file = "xxhash-3.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:b82b1cf4407ad908e04e864473cc3baa8e764c7bbebea959150764cc681a1611"}, + {file = "xxhash-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:da4d91e28418469b29eed8635c08af28b588e51cd04288bed1ba1cf60f2d91f6"}, + {file = "xxhash-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48aab36169b0c00e586cb4eb2814ab8bfed686933126019906f917ff9a78c99e"}, + {file = "xxhash-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b0d522570c9ccea6203b3d96ac7f0cfc1d29e613640475d513be432545c48cc"}, + {file = "xxhash-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6054434ddb060685e86e7457f52d188b0886834baaa532f9f78b4f2b53cfd9b"}, + {file = "xxhash-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbf546ca5f5903ceeb46d9e6abf81f3a64edb95bb7dbe0f75283eec93a7eb2a0"}, + {file = "xxhash-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22704f23f23ccbe892cee3e7568c67f07ac25beaa2d1cff183274005d9d39149"}, + {file = "xxhash-3.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83198e223bcc4b2418b5282ac930e444738c2a33859dee4e570b25c8433d83a2"}, + {file = "xxhash-3.0.0-cp39-cp39-win32.whl", hash = "sha256:3bcd4cd9b22293ea1c08822518fbb6d933c2960d66662d468a1945a45cace194"}, + {file = "xxhash-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5dd4c37da3408d56ae942dc103f4ae3b43510daa4f5accd0a411fc6e914f10a"}, + {file = "xxhash-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:485f172abc03f78afd4f38dbdbb5665f59c5487126fa4c3181c6582cda4de03b"}, + {file = "xxhash-3.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:035248b3d7ab6deb7b247278494d293b9faccfa853078319d25e2926f566b2f8"}, + {file = "xxhash-3.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30ae90c0cfd10ffe852c6b0f263253782eea74a8189d5f2440f6595c1e8047e"}, + {file = "xxhash-3.0.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fd203d8a3c013e679722047ef4f061f690c6cff49380622444bca4c30f3bf23"}, + {file = "xxhash-3.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:6d60059aaef12a01c0cc24f1d7aaaab7933ae9f4b7adfd9ebbd37dc7ceac1745"}, + {file = "xxhash-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:676c97bf7cc298b65eec0368c2cb5611d87a8e876930843311ca728f69292752"}, + {file = "xxhash-3.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2245c6e20e96e3f8fdfb61ad6bc5cde6ce8a1c2b93aa4a32a27bba7ab3aeaf12"}, + {file = "xxhash-3.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae926a52d020085a2d7f69d0e2155cbf819ae409f2e5dbb345dd40a6462de32"}, + {file = "xxhash-3.0.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a2efdcb811be3edc520b78364c11a1e54f5d8e5db895a9ff2bcdd4a7ffa36a5"}, + {file = "xxhash-3.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:885b3a851980056707ab99a2c19c35dfe2c2ba5f602066dbfcd8af45ea855760"}, + {file = "xxhash-3.0.0.tar.gz", hash = "sha256:30b2d97aaf11fb122023f6b44ebb97c6955e9e00d7461a96415ca030b5ceb9c7"}, +] +yapf = [ + {file = "yapf-0.32.0-py2.py3-none-any.whl", hash = "sha256:8fea849025584e486fd06d6ba2bed717f396080fd3cc236ba10cb97c4c51cf32"}, + {file = "yapf-0.32.0.tar.gz", hash = "sha256:a3f5085d37ef7e3e004c4ba9f9b3e40c54ff1901cd111f05145ae313a7c67d1b"}, +] +zipp = [ + {file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"}, + {file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"}, +] diff --git a/pyproject.toml b/pyproject.toml index b0471b7f..e38a5bb0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,62 @@ +[tool.poetry] +name = "frameioclient" +version = "2.0.1-alpha.0" +description='Client library for the Frame.io API' +readme = "README.md" +license='MIT' +homepage = "https://github.com/Frameio/python-frameio-client" +authors = ["Frame.io DevRel "] +classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Topic :: Multimedia :: Video', + 'Topic :: Software Development :: Libraries', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9' +] + +[tool.poetry.dependencies] +python = "^3.9" +analytics-python = "^1.4.0" +enlighten = "^1.10.2" +importlib-metadata = "^4.11.3" +requests = "^2.27.1" +token-bucket = "^0.3.0" +urllib3 = "^1.26.9" +xxhash = "^3.0.0" + +[tool.poetry.dev-dependencies] +bump2version = "^1.0.1" + +# Optional dependencies +Sphinx = { version = "^4.4.0", optional = true } +sphinx-jekyll-builder = { version = "^0.3.0", optional = true } +sphinxcontrib-restbuilder = { version = "^0.3", optional = true } +sphinx-autobuild = { version = "^2021.3.14", optional = true } +contentful_management = { version = "^2.11.0", optional = true } +python-frontmatter = { version = "^1.0.0", optional = true } +sphinx-autodoc-typehints = { version = "^1.17.0", optional = true } +furo = { version = "^2022.3.4", optional = true } +python-dotenv = "^0.19.2" + +[tool.poetry.extras] +docs = [ + "sphinx", + "sphinx-jekyll-builder", + "sphinxcontrib-restbuilder", + "sphinx-autobuild", + "contentful_management", + "python-frontmatter", + "sphinx-autodoc-typehints", + "furo" +] + +[tool.poetry.scripts] +fiocli = 'frameioclient.fiocli:main' + [build-system] -requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta:__legacy__" \ No newline at end of file +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/setup.py b/setup.py index b39899c4..8187f56d 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,6 @@ def run(self): 'importlib-metadata ~= 1.0 ; python_version < "3.8"', 'requests', 'token-bucket', - 'speedtest-cli', 'urllib3', 'xxhash', ], From f4984b809af9a053950e549a13bc2847ea678dea Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 22 Mar 2022 14:40:23 -0700 Subject: [PATCH 06/37] Version bump --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e38a5bb0..e36e954a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "frameioclient" -version = "2.0.1-alpha.0" +version = "2.0.1-alpha.1" description='Client library for the Frame.io API' readme = "README.md" license='MIT' From 6e95ea7b92f226350298dc8b131b6557ea9203ba Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 14:01:42 -0700 Subject: [PATCH 07/37] Modify orb for circleci --- .circleci/config.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a93f49c2..9aee87ce 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,8 +1,7 @@ version: 2.1 orbs: - python: circleci/python@0.2.1 - win: circleci/windows@2.2.0 + python: cimg/python@3.9.10 workflows: version: 2 From 1939d321861d6e55d69e4603be08089d9acb032e Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 14:02:52 -0700 Subject: [PATCH 08/37] Modify orb for circleci --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9aee87ce..4dd31113 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ version: 2.1 orbs: - python: cimg/python@3.9.10 + python: circleci/python@0.2.1 workflows: version: 2 @@ -42,7 +42,7 @@ workflows: jobs: build: docker: - - image: circleci/python:latest + - image: cimg/python:latest steps: - checkout: name: Checkout Git From fe09571a4874b19433b8cc4e7f460a7466b51b79 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 14:04:25 -0700 Subject: [PATCH 09/37] Drop 'latest' tag for CI --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4dd31113..ce9593a1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,7 +42,7 @@ workflows: jobs: build: docker: - - image: cimg/python:latest + - image: cimg/python:3.10.2 steps: - checkout: name: Checkout Git From aba5008eebea0fae739fc57ee7ea3a196c6e1fc0 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 14:07:57 -0700 Subject: [PATCH 10/37] Use correct test script --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ce9593a1..47fd83bd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -74,7 +74,7 @@ jobs: - run: name: Run integration test command: | - python /tmp/artifact/tests/integration.py + python /tmp/artifact/tests/test_integration.py deploy: docker: From 98811e3fa1b1634f7209039fc956e9b3deb3f9be Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 14:10:42 -0700 Subject: [PATCH 11/37] Modify supported pythons --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index e36e954a..99d179cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ ] [tool.poetry.dependencies] -python = "^3.9" +python = "^3.6.5" analytics-python = "^1.4.0" enlighten = "^1.10.2" importlib-metadata = "^4.11.3" From e825ec11d83134b01625362ebcc8e512eb322bd0 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 14:11:16 -0700 Subject: [PATCH 12/37] Use find_env instead of load_env --- tests/test_integration.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_integration.py b/tests/test_integration.py index fe6feeb3..5190631c 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -13,9 +13,8 @@ from frameioclient import FrameioClient, Utils, KB, MB from frameioclient.lib.utils import FormatTypes -from dotenv import load_dotenv - -load_dotenv('/Users/jeff/Code/developer-relations/python-frameio-client/.env') +from dotenv import find_dotenv +find_dotenv() token = os.getenv("FRAMEIO_TOKEN") # Your Frame.io token project_id = os.getenv("PROJECT_ID") # Project you want to upload files back into From 8773e6ea5da960806103edaaf1a3312a9d723cc7 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 14:12:04 -0700 Subject: [PATCH 13/37] poetry updates --- poetry.lock | 17 +++++++++++++++-- pyproject.toml | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/poetry.lock b/poetry.lock index 8a234597..fd5d47c7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -200,6 +200,7 @@ optional = false python-versions = ">=3.7" [package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] @@ -653,6 +654,14 @@ category = "dev" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "typing-extensions" +version = "4.1.1" +description = "Backported and Experimental Type Hints for Python 3.6+" +category = "main" +optional = false +python-versions = ">=3.6" + [[package]] name = "unify" version = "0.5" @@ -726,8 +735,8 @@ docs = [] [metadata] lock-version = "1.1" -python-versions = "^3.9" -content-hash = "4c9d276b284ba1d1640de6a9a14176670fff48d21127d8ed3130259141d06613" +python-versions = "^3.7" +content-hash = "37cb768467d8683bd089d4eef47247e6883d47e8eada714181d9023db76d217a" [metadata.files] alabaster = [ @@ -1060,6 +1069,10 @@ typing = [ {file = "typing-3.7.4.3-py2-none-any.whl", hash = "sha256:283d868f5071ab9ad873e5e52268d611e851c870a2ba354193026f2dfb29d8b5"}, {file = "typing-3.7.4.3.tar.gz", hash = "sha256:1187fb9c82fd670d10aa07bbb6cfcfe4bdda42d6fab8d5134f04e8c4d0b71cc9"}, ] +typing-extensions = [ + {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, + {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, +] unify = [ {file = "unify-0.5.tar.gz", hash = "sha256:8ddce812b2457212b7598fe574c9e6eb3ad69710f445391338270c7f8a71723c"}, ] diff --git a/pyproject.toml b/pyproject.toml index 99d179cd..c495e913 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ ] [tool.poetry.dependencies] -python = "^3.6.5" +python = "^3.7" analytics-python = "^1.4.0" enlighten = "^1.10.2" importlib-metadata = "^4.11.3" From 16632743d71185ce2366eb643c705e27962968ed Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 14:16:18 -0700 Subject: [PATCH 14/37] Use poetry in CI --- .circleci/config.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 47fd83bd..023e943e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,11 +46,14 @@ jobs: steps: - checkout: name: Checkout Git + - run: + command: | + pip install poetry - run: name: Build Package command: | echo -e "Running sdist" - python setup.py sdist + poetry build - persist_to_workspace: root: /home/circleci/project/ paths: @@ -67,10 +70,13 @@ jobs: - attach_workspace: at: /tmp/artifact name: Attach build artifact + - run: + command: | + pip install poetry - run: name: Install package command: | - pip install --user '/tmp/artifact' + poetry install '/tmp/artifact' --extras 'docs' - run: name: Run integration test command: | From 1133eb95d2fc8ee286d08fcb4e48605e8cffe202 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 14:20:31 -0700 Subject: [PATCH 15/37] Assume poetry already installed --- .circleci/config.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 023e943e..f2cfbc9c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,9 +46,6 @@ jobs: steps: - checkout: name: Checkout Git - - run: - command: | - pip install poetry - run: name: Build Package command: | @@ -70,13 +67,10 @@ jobs: - attach_workspace: at: /tmp/artifact name: Attach build artifact - - run: - command: | - pip install poetry - run: name: Install package command: | - poetry install '/tmp/artifact' --extras 'docs' + poetry install --extras 'docs' '/tmp/artifact' - run: name: Run integration test command: | From d8e56e53cbfbdae607a6b597485be276bf9bc2bb Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 14:22:14 -0700 Subject: [PATCH 16/37] Tweak poetry install cmd slightly --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f2cfbc9c..7400015a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,7 +70,7 @@ jobs: - run: name: Install package command: | - poetry install --extras 'docs' '/tmp/artifact' + poetry install --extras docs '/tmp/artifact' - run: name: Run integration test command: | From 467d3bec8b6d651443827a69c8ef585d4dbd794a Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 14:23:43 -0700 Subject: [PATCH 17/37] Tweak CI config further --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7400015a..e02b13df 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -70,7 +70,7 @@ jobs: - run: name: Install package command: | - poetry install --extras docs '/tmp/artifact' + poetry install '/tmp/artifact' --extras docs - run: name: Run integration test command: | From 4eb1a1629fc98fa240080c00b4e72e6edf0b8c41 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 15:00:59 -0700 Subject: [PATCH 18/37] Restore poetry install step --- .circleci/config.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index e02b13df..b338475e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,6 +46,9 @@ jobs: steps: - checkout: name: Checkout Git + - run: + command: | + pip install poetry -U - run: name: Build Package command: | @@ -67,6 +70,9 @@ jobs: - attach_workspace: at: /tmp/artifact name: Attach build artifact + - run: + command: | + pip install poetry -U - run: name: Install package command: | From 2ab8c7474e913ead5ec6978cac08598702e6593a Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 15:01:17 -0700 Subject: [PATCH 19/37] Update cimg/python --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b338475e..179af665 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,7 +42,7 @@ workflows: jobs: build: docker: - - image: cimg/python:3.10.2 + - image: cimg/python:3 steps: - checkout: name: Checkout Git From 7a75706dd37d7bd9fcbecc26de507acf2a410c6a Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 15:02:00 -0700 Subject: [PATCH 20/37] Use specific python cimg --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 179af665..927d11fd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,7 +42,7 @@ workflows: jobs: build: docker: - - image: cimg/python:3 + - image: cimg/python:3.9.10 steps: - checkout: name: Checkout Git From 85bb5e2b869c76b2599dc14d0bcb1046d3badbbd Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 15:05:30 -0700 Subject: [PATCH 21/37] Modify CI --- .circleci/config.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 927d11fd..4b80b497 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -76,7 +76,8 @@ jobs: - run: name: Install package command: | - poetry install '/tmp/artifact' --extras docs + cd /tmp/artifact + poetry install --extras docs - run: name: Run integration test command: | @@ -89,10 +90,6 @@ jobs: - attach_workspace: at: /tmp/artifact name: Attach build artifact - - run: - name: Install dependencies - command: | - pip install setuptools wheel twine - run: name: init .pypirc command: | @@ -104,4 +101,4 @@ jobs: name: Upload to pypi command: | cd /tmp/artifact - twine upload dist/* + poetry publish From c0f5f9b1f08db0d15ed37def53e0b328183bb6f4 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 23 Mar 2022 15:24:44 -0700 Subject: [PATCH 22/37] Tweak CI for poetry --- .circleci/config.yml | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4b80b497..1a563f5f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ version: 2.1 orbs: - python: circleci/python@0.2.1 + python: circleci/python@2.0.3 workflows: version: 2 @@ -22,7 +22,7 @@ workflows: - gh-pages matrix: parameters: - python-version: ["3.6.5", "3.7.7", "3.8.6", "3.9.3", "3.9.9", "3.9.10", "latest"] + python-version: ["3.6.9", "3.7.7", "3.8.6", "3.9.3", "3.9.9", "3.9.10", "latest"] - hold: type: approval @@ -41,14 +41,13 @@ workflows: jobs: build: - docker: - - image: cimg/python:3.9.10 + executor: python/default steps: - checkout: name: Checkout Git - - run: - command: | - pip install poetry -U + - python/install-packages: + pkg-manager: + poetry - run: name: Build Package command: | @@ -65,11 +64,14 @@ jobs: python-version: type: string docker: - - image: circleci/python:<< parameters.python-version >> + - image: cimg/python:<< parameters.python-version >> steps: - attach_workspace: at: /tmp/artifact name: Attach build artifact + - python/install-packages: + pkg-manager: + poetry - run: command: | pip install poetry -U @@ -85,11 +87,14 @@ jobs: deploy: docker: - - image: circleci/python:latest + - image: cimg/python:3.10.2 steps: - attach_workspace: at: /tmp/artifact name: Attach build artifact + - python/install-packages: + pkg-manager: + poetry - run: name: init .pypirc command: | From 058d540ffe5f932bc66f117bd0dd796a015a81bb Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Wed, 1 Jun 2022 10:33:57 -0700 Subject: [PATCH 23/37] Add audit log crawler, and some other tests --- examples/audit_logs/crawl.py | 40 ++++++++++++++++++++++++++++++++++ frameioclient/lib/constants.py | 1 + frameioclient/lib/transport.py | 13 ++++++++--- frameioclient/lib/utils.py | 29 +++++++++++++++++------- 4 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 examples/audit_logs/crawl.py diff --git a/examples/audit_logs/crawl.py b/examples/audit_logs/crawl.py new file mode 100644 index 00000000..b7c9339f --- /dev/null +++ b/examples/audit_logs/crawl.py @@ -0,0 +1,40 @@ +import os +import json +from pprint import pprint +import time +from typing import Optional +from dotenv import find_dotenv, load_dotenv +from frontmatter import load +from frameioclient import FrameioClient, Utils + +# load_dotenv(find_dotenv()) +load_dotenv('/Users/jeff/Code/developer-relations/python-frameio-client/.env') +token = os.getenv("FRAMEIO_TOKEN") + +def get_audit_logs(account_id: str, event_type: Optional[str] = None): + logs = list() + + start_time = time.time() + client = FrameioClient(token) + endpoint = f"/accounts/{account_id}/audit_logs" + + if event_type != None: + endpoint += f'?filter[action]={event_type}' + + for log_page in Utils.stream_results(endpoint, client=client): + logs.append(log_page) + print(f"{len(logs)} logs fetched") + + # Write the file + with open("audit_logs.json", "w") as out_file: + json.dump(logs, out_file) + + duration = time.time() - start_time + + print(f"Took {duration} seconds to complete.") + +if __name__ == "__main__": + account_id = 'f6365640-575c-42e5-8a7f-cd9e2d6b9273' + get_audit_logs(account_id, 'AssetCreated') + + diff --git a/frameioclient/lib/constants.py b/frameioclient/lib/constants.py index 5a13c028..c206632a 100644 --- a/frameioclient/lib/constants.py +++ b/frameioclient/lib/constants.py @@ -21,3 +21,4 @@ default_thread_count = 5 retryable_statuses = [400, 429, 500, 502, 503] +retryable_methods = ['GET', 'PUT', 'POST', 'DELETE'] diff --git a/frameioclient/lib/transport.py b/frameioclient/lib/transport.py index 31ef921c..6f87a2d0 100644 --- a/frameioclient/lib/transport.py +++ b/frameioclient/lib/transport.py @@ -1,7 +1,7 @@ import concurrent.futures import threading import time -from typing import Dict, Iterable, Optional +from typing import Union, Dict, Iterable, Optional import requests from requests.adapters import HTTPAdapter @@ -93,7 +93,7 @@ def _format_api_call(self, endpoint: str): def _api_call( self, method, endpoint: str, payload: Dict = {}, limit: Optional[int] = None - ): + ) -> Union[Dict, PaginatedResponse, None]: headers = {**self.shared_headers, **self.auth_header} r = self.session.request( @@ -122,6 +122,12 @@ def _api_call( if r.status_code == 422 and "presentation" in endpoint: raise PresentationException + + if r.status_code == 500 and 'audit' in endpoint: + print(f"Hit a 500 on page: {r.headers.get('page-number')}, url: {r.url}") + return [] + + return r.raise_for_status() @@ -138,11 +144,12 @@ def get_specific_page( page (int): What page to get """ if method == HTTPMethods.GET: - endpoint = "{endpoint}?page={page}" + endpoint = f"{endpoint}?page={page}" return self._api_call(method, endpoint) if method == HTTPMethods.POST: payload["page"] = page + return self._api_call(method, endpoint, payload=payload) def exec_stream(callable, iterable: Iterable, sync=lambda _: False, capacity=10, rate=10): diff --git a/frameioclient/lib/utils.py b/frameioclient/lib/utils.py index 3d18b670..530e5ef3 100644 --- a/frameioclient/lib/utils.py +++ b/frameioclient/lib/utils.py @@ -2,9 +2,10 @@ import os import re import sys -from typing import Any, Dict, Optional +from typing import Any, Dict, Generator, Optional import xxhash +from furl import furl KB = 1024 MB = KB * KB @@ -31,18 +32,18 @@ class FormatTypes(enum.Enum): class Utils: @staticmethod - def stream(func, page=1, page_size=20): + def stream(func, page=1, page_size=50): """ Accepts a lambda of a call to a client list method, and streams the results until \ the list has been exhausted. - Args: - fun (function): A 1-arity function to apply during the stream + Args: + fun (function): A 1-arity function to apply during the stream - Example:: - - stream(lambda pagination: client.get_collaborators(project_id, **pagination)) - """ + Example:: + + stream(lambda pagination: client.get_collaborators(project_id, **pagination)) + """ total_pages = page while page <= total_pages: result_list = func(page=page, page_size=page_size) @@ -52,6 +53,18 @@ def stream(func, page=1, page_size=20): page += 1 + @staticmethod + def stream_results( + endpoint, page=1, page_size=50, client=None, **_kwargs + ) -> Generator: + def fetch_page(page=1, page_size=50): + return client._api_call( + "get", furl(endpoint).add({"page": page, "page_size": page_size}).url + ) + + for result in Utils.stream(fetch_page, page=page, page_size=page_size): + yield result + @staticmethod def format_value(value: int, type: FormatTypes = FormatTypes.SIZE) -> str: """ From 841ad7eced13e0d06af1a64eb3a91b03bf463658 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Fri, 3 Jun 2022 10:12:51 -0700 Subject: [PATCH 24/37] Version bump --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c495e913..e49581c7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "frameioclient" -version = "2.0.1-alpha.1" +version = "2.0.1-alpha.2" description='Client library for the Frame.io API' readme = "README.md" license='MIT' From 0d81d57751debf6d4c657b2d29e6af0841d82fd9 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Fri, 19 Aug 2022 10:18:30 -0700 Subject: [PATCH 25/37] Add user search to the search class --- frameioclient/resources/search.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frameioclient/resources/search.py b/frameioclient/resources/search.py index fd91415f..96c54c0b 100644 --- a/frameioclient/resources/search.py +++ b/frameioclient/resources/search.py @@ -71,3 +71,21 @@ def library( endpoint = "/search/library" return self.client._api_call("post", endpoint, payload=payload) + + def users(self, account_id: str, query: str): + """Search for users within a given account + + Args: + account_id (str): UUID for the account you want to search within, must be one you have access to + query (str): The query string you want to seach with, usually an email or a name + + Returns: + List[Dict]: List of user resources found via your search + """ + + endpoint = "/search/users" + payload = { + "account_id": account_id, + "q": query + } + return self.client._api_call("post", endpoint, payload=payload) From f6d8f6f80ea57db36560dc68578e99805998b5b2 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 16:44:07 -0700 Subject: [PATCH 26/37] Add missing furl dependency --- poetry.lock | 523 ++++++++++--------------------------------------- pyproject.toml | 3 +- 2 files changed, 102 insertions(+), 424 deletions(-) diff --git a/poetry.lock b/poetry.lock index fd5d47c7..79fe66e8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -22,7 +22,7 @@ requests = ">=2.7,<3.0" six = ">=1.5" [package.extras] -test = ["mock (2.0.0)", "pylint (1.9.3)", "flake8 (3.7.9)"] +test = ["flake8 (==3.7.9)", "pylint (==1.9.3)", "mock (==2.0.0)"] [[package]] name = "ansicon" @@ -154,6 +154,18 @@ python-versions = "*" blessed = ">=1.17.7" prefixed = ">=0.3.2" +[[package]] +name = "furl" +version = "2.1.3" +description = "URL manipulation made simple." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +orderedmultidict = ">=1.0.1" +six = ">=1.8.0" + [[package]] name = "furo" version = "2022.3.4" @@ -276,6 +288,17 @@ six = "*" testing = ["pytest", "coverage", "astroid (>=1.5.3,<1.6.0)", "pylint (>=1.7.2,<1.8.0)", "astroid (>=2.0)", "pylint (>=2.3.1,<2.4.0)"] yaml = ["PyYAML (>=5.1.0)"] +[[package]] +name = "orderedmultidict" +version = "1.0.1" +description = "Ordered Multivalue Dictionary" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +six = ">=1.8.0" + [[package]] name = "packaging" version = "21.3" @@ -359,8 +382,8 @@ python-versions = "*" PyYAML = "*" [package.extras] +test = ["pyaml", "toml", "pytest"] docs = ["sphinx"] -test = ["pytest", "toml", "pyaml"] [[package]] name = "pytz" @@ -393,7 +416,7 @@ idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} urllib3 = ">=1.21.1,<1.27" [package.extras] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] [[package]] @@ -542,8 +565,8 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-devhelp" @@ -554,8 +577,8 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-htmlhelp" @@ -566,8 +589,8 @@ optional = true python-versions = ">=3.6" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest", "html5lib"] +test = ["html5lib", "pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-jsmath" @@ -578,7 +601,7 @@ optional = true python-versions = ">=3.5" [package.extras] -test = ["pytest", "flake8", "mypy"] +test = ["mypy", "flake8", "pytest"] [[package]] name = "sphinxcontrib-qthelp" @@ -589,8 +612,8 @@ optional = true python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-restbuilder" @@ -692,7 +715,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "wcwidth" @@ -736,418 +759,72 @@ docs = [] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "37cb768467d8683bd089d4eef47247e6883d47e8eada714181d9023db76d217a" +content-hash = "bfcf23e232bd808e73977444b10762e036d0013bff23c6f1cebacce81c7e6fc6" [metadata.files] -alabaster = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, -] -analytics-python = [ - {file = "analytics-python-1.4.0.tar.gz", hash = "sha256:a65141ab6e47db396f5bc5708b1db93ff9a99882d81fe808228afd5ebb6dfe5f"}, - {file = "analytics_python-1.4.0-py2.py3-none-any.whl", hash = "sha256:3bff972beeb8a3f26607ccd9153484aa4f12eeeea4a693be685bf45aa66ddf99"}, -] -ansicon = [ - {file = "ansicon-1.89.0-py2.py3-none-any.whl", hash = "sha256:f1def52d17f65c2c9682cf8370c03f541f410c1752d6a14029f97318e4b9dfec"}, - {file = "ansicon-1.89.0.tar.gz", hash = "sha256:e4d039def5768a47e4afec8e89e83ec3ae5a26bf00ad851f914d1240b444d2b1"}, -] -babel = [ - {file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"}, - {file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"}, -] -backoff = [ - {file = "backoff-1.10.0-py2.py3-none-any.whl", hash = "sha256:5e73e2cbe780e1915a204799dba0a01896f45f4385e636bcca7a0614d879d0cd"}, - {file = "backoff-1.10.0.tar.gz", hash = "sha256:b8fba021fac74055ac05eb7c7bfce4723aedde6cd0a504e5326bcb0bdd6d19a4"}, -] -beautifulsoup4 = [ - {file = "beautifulsoup4-4.10.0-py3-none-any.whl", hash = "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf"}, - {file = "beautifulsoup4-4.10.0.tar.gz", hash = "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891"}, -] -blessed = [ - {file = "blessed-1.19.1-py2.py3-none-any.whl", hash = "sha256:63b8554ae2e0e7f43749b6715c734cc8f3883010a809bf16790102563e6cf25b"}, - {file = "blessed-1.19.1.tar.gz", hash = "sha256:9a0d099695bf621d4680dd6c73f6ad547f6a3442fbdbe80c4b1daa1edbc492fc"}, -] -bump2version = [ - {file = "bump2version-1.0.1-py2.py3-none-any.whl", hash = "sha256:37f927ea17cde7ae2d7baf832f8e80ce3777624554a653006c9144f8017fe410"}, - {file = "bump2version-1.0.1.tar.gz", hash = "sha256:762cb2bfad61f4ec8e2bdf452c7c267416f8c70dd9ecb1653fd0bbb01fa936e6"}, -] -certifi = [ - {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, - {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, -] -chardet = [ - {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, - {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, - {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, -] -colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, -] -contentful-management = [ - {file = "contentful_management-2.11.0.tar.gz", hash = "sha256:7321c782eab27a23bb03c987a60feea6f7a5608b9a6a6ba4dde513e40b130e7b"}, -] -docutils = [ - {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, - {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, -] -enlighten = [ - {file = "enlighten-1.10.2-py2.py3-none-any.whl", hash = "sha256:b237fe562b320bf9f1d4bb76d0c98e0daf914372a76ab87c35cd02f57aa9d8c1"}, - {file = "enlighten-1.10.2.tar.gz", hash = "sha256:7a5b83cd0f4d095e59d80c648ebb5f7ffca0cd8bcf7ae6639828ee1ad000632a"}, -] -furo = [ - {file = "furo-2022.3.4-py3-none-any.whl", hash = "sha256:6c718293ebf87755f0b9f148b1e697c9e3aabd7af955644d4bcaee5ce75db781"}, - {file = "furo-2022.3.4.tar.gz", hash = "sha256:7660267cc67b2828fd0e17bc07adeb612c47b2eba5a6de07049a1569e6044aa8"}, -] -html2text = [ - {file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"}, - {file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"}, -] -idna = [ - {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, - {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, -] -imagesize = [ - {file = "imagesize-1.3.0-py2.py3-none-any.whl", hash = "sha256:1db2f82529e53c3e929e8926a1fa9235aa82d0bd0c580359c67ec31b2fddaa8c"}, - {file = "imagesize-1.3.0.tar.gz", hash = "sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d"}, -] -importlib-metadata = [ - {file = "importlib_metadata-4.11.3-py3-none-any.whl", hash = "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6"}, - {file = "importlib_metadata-4.11.3.tar.gz", hash = "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"}, -] -jinja2 = [ - {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, - {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, -] -jinxed = [ - {file = "jinxed-1.1.0-py2.py3-none-any.whl", hash = "sha256:6a61ccf963c16aa885304f27e6e5693783676897cea0c7f223270c8b8e78baf8"}, - {file = "jinxed-1.1.0.tar.gz", hash = "sha256:d8f1731f134e9e6b04d95095845ae6c10eb15cb223a5f0cabdea87d4a279c305"}, -] -livereload = [ - {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, -] -markupsafe = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, -] -monotonic = [ - {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, - {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, -] -munch = [ - {file = "munch-2.5.0-py2.py3-none-any.whl", hash = "sha256:6f44af89a2ce4ed04ff8de41f70b226b984db10a91dcc7b9ac2efc1c77022fdd"}, - {file = "munch-2.5.0.tar.gz", hash = "sha256:2d735f6f24d4dba3417fa448cae40c6e896ec1fdab6cdb5e6510999758a4dbd2"}, -] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -prefixed = [ - {file = "prefixed-0.3.2-py2.py3-none-any.whl", hash = "sha256:5e107306462d63f2f03c529dbf11b0026fdfec621a9a008ca639d71de22995c3"}, - {file = "prefixed-0.3.2.tar.gz", hash = "sha256:ca48277ba5fa8346dd4b760847da930c7b84416387c39e93affef086add2c029"}, -] -pydash = [ - {file = "pydash-5.1.0-py3-none-any.whl", hash = "sha256:ced4fedb163eb07fbee376e474bca74029eb9fab215614449fe13164f71dd9e3"}, - {file = "pydash-5.1.0.tar.gz", hash = "sha256:1b2b050ac1bae049cd07f5920b14fabbe52638f485d9ada1eb115a9eebff6835"}, -] -pygments = [ - {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, - {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, -] -pyparsing = [ - {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, - {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, -] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] -python-dotenv = [ - {file = "python-dotenv-0.19.2.tar.gz", hash = "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f"}, - {file = "python_dotenv-0.19.2-py2.py3-none-any.whl", hash = "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3"}, -] -python-frontmatter = [ - {file = "python-frontmatter-1.0.0.tar.gz", hash = "sha256:e98152e977225ddafea6f01f40b4b0f1de175766322004c826ca99842d19a7cd"}, - {file = "python_frontmatter-1.0.0-py3-none-any.whl", hash = "sha256:766ae75f1b301ffc5fe3494339147e0fd80bc3deff3d7590a93991978b579b08"}, -] -pytz = [ - {file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"}, - {file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"}, -] -pyyaml = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -requests = [ - {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, - {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -snowballstemmer = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] -soupsieve = [ - {file = "soupsieve-2.3.1-py3-none-any.whl", hash = "sha256:1a3cca2617c6b38c0343ed661b1fa5de5637f257d4fe22bd9f1338010a1efefb"}, - {file = "soupsieve-2.3.1.tar.gz", hash = "sha256:b8d49b1cd4f037c7082a9683dfa1801aa2597fb11c3a1155b7a5b94829b4f1f9"}, -] -sphinx = [ - {file = "Sphinx-4.4.0-py3-none-any.whl", hash = "sha256:5da895959511473857b6d0200f56865ed62c31e8f82dd338063b84ec022701fe"}, - {file = "Sphinx-4.4.0.tar.gz", hash = "sha256:6caad9786055cb1fa22b4a365c1775816b876f91966481765d7d50e9f0dd35cc"}, -] -sphinx-autobuild = [ - {file = "sphinx-autobuild-2021.3.14.tar.gz", hash = "sha256:de1ca3b66e271d2b5b5140c35034c89e47f263f2cd5db302c9217065f7443f05"}, - {file = "sphinx_autobuild-2021.3.14-py3-none-any.whl", hash = "sha256:8fe8cbfdb75db04475232f05187c776f46f6e9e04cacf1e49ce81bdac649ccac"}, -] -sphinx-autodoc-typehints = [ - {file = "sphinx_autodoc_typehints-1.17.0-py3-none-any.whl", hash = "sha256:081daf53077b4ae1c28347d6d858e13e63aefe3b4aacef79fd717dd60687b470"}, - {file = "sphinx_autodoc_typehints-1.17.0.tar.gz", hash = "sha256:51c7b3f5cb9ccd15d0b52088c62df3094f1abd9612930340365c26def8629a14"}, -] -sphinx-jekyll-builder = [ - {file = "sphinx-jekyll-builder-0.3.0.tar.gz", hash = "sha256:5ecc0a1821849fc55c4b26e0efb8fb45454904c6900378ebd776f8a74d6e06f7"}, - {file = "sphinx_jekyll_builder-0.3.0-py2.py3-none-any.whl", hash = "sha256:b3b2d46ba49b7d47d8f58077c9ccce928f2cfec6d3fce7738c69f1590c72ebc5"}, -] -sphinx-markdown-builder = [ - {file = "sphinx-markdown-builder-0.5.5.tar.gz", hash = "sha256:6ead53c08d8835329e32418dcdbac4db710a1c4e5e8db687d23b9e88882d9d16"}, - {file = "sphinx_markdown_builder-0.5.5-py2.py3-none-any.whl", hash = "sha256:3c8909579dfa83ce5a8fb48e2d01dc257fc0676931170cb92cd528f9fceee76f"}, -] -sphinxcontrib-applehelp = [ - {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, - {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, -] -sphinxcontrib-devhelp = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] -sphinxcontrib-htmlhelp = [ - {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, - {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, -] -sphinxcontrib-jsmath = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] -sphinxcontrib-qthelp = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] -sphinxcontrib-restbuilder = [ - {file = "sphinxcontrib-restbuilder-0.3.tar.gz", hash = "sha256:6b3ee9394b5ec5e73e6afb34d223530d0b9098cb7562f9c5e364e6d6b41410ce"}, - {file = "sphinxcontrib_restbuilder-0.3-py2.py3-none-any.whl", hash = "sha256:6ba2ddc7a87d845c075c1b2e00d541bd1c8400488e50e32c9b4169ccdd9f30cb"}, -] -sphinxcontrib-serializinghtml = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, -] -sphinxcontrib-websupport = [ - {file = "sphinxcontrib-websupport-1.2.4.tar.gz", hash = "sha256:4edf0223a0685a7c485ae5a156b6f529ba1ee481a1417817935b20bde1956232"}, - {file = "sphinxcontrib_websupport-1.2.4-py2.py3-none-any.whl", hash = "sha256:6fc9287dfc823fe9aa432463edd6cea47fa9ebbf488d7f289b322ffcfca075c7"}, -] -token-bucket = [ - {file = "token_bucket-0.3.0-py2.py3-none-any.whl", hash = "sha256:6df24309e3cf5b808ae5ef714a3191ec5b54f48c34ef959e4882eef140703369"}, - {file = "token_bucket-0.3.0.tar.gz", hash = "sha256:979571c99db2ff9e651f2b2146a62b2ebadf7de6c217a8781698282976cb675f"}, -] -tornado = [ - {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, - {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, - {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, - {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, - {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, - {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, - {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, - {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, - {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, - {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, - {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, - {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, - {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, - {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, - {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, - {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, - {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, - {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, -] -typing = [ - {file = "typing-3.7.4.3-py2-none-any.whl", hash = "sha256:283d868f5071ab9ad873e5e52268d611e851c870a2ba354193026f2dfb29d8b5"}, - {file = "typing-3.7.4.3.tar.gz", hash = "sha256:1187fb9c82fd670d10aa07bbb6cfcfe4bdda42d6fab8d5134f04e8c4d0b71cc9"}, -] -typing-extensions = [ - {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, - {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, -] -unify = [ - {file = "unify-0.5.tar.gz", hash = "sha256:8ddce812b2457212b7598fe574c9e6eb3ad69710f445391338270c7f8a71723c"}, -] -untokenize = [ - {file = "untokenize-0.1.1.tar.gz", hash = "sha256:3865dbbbb8efb4bb5eaa72f1be7f3e0be00ea8b7f125c69cbd1f5fda926f37a2"}, -] -urllib3 = [ - {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, - {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, -] -wcwidth = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, -] -xxhash = [ - {file = "xxhash-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:219cba13991fd73cf21a5efdafa5056f0ae0b8f79e5e0112967e3058daf73eea"}, - {file = "xxhash-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3fcbb846af15eff100c412ae54f4974ff277c92eacd41f1ec7803a64fd07fa0c"}, - {file = "xxhash-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f475fa817ff7955fc118fc1ca29a6e691d329b7ff43f486af36c22dbdcff1db"}, - {file = "xxhash-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9200a90f02ff6fd5fb63dea107842da71d8626d99b768fd31be44f3002c60bbe"}, - {file = "xxhash-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a1403e4f551c9ef7bcef09af55f1adb169f13e4de253db0887928e5129f87af1"}, - {file = "xxhash-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa7f6ca53170189a2268c83af0980e6c10aae69e6a5efa7ca989f89fff9f8c02"}, - {file = "xxhash-3.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b63fbeb6d9c93d50ae0dc2b8a8b7f52f2de19e40fe9edc86637bfa5743b8ba2"}, - {file = "xxhash-3.0.0-cp310-cp310-win32.whl", hash = "sha256:31f25efd10b6f1f6d5c34cd231986d8aae9a42e042daa90b783917f170807869"}, - {file = "xxhash-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:807e88ed56e0fb347cb57d5bf44851f9878360fed700f2f63e622ef4eede87a5"}, - {file = "xxhash-3.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6d612c55a75d84d25898f6c5ad6a589aa556d1cb9af770b6c574ee62995167f6"}, - {file = "xxhash-3.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f9309fcaf73f93df3101f03a61dc30644adff3e8d0044fff8c0c195dbbe63e2"}, - {file = "xxhash-3.0.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2273fe40720e86346a17f06ef95cd60ee0d66ffce7cf55e390ef7350112b16d"}, - {file = "xxhash-3.0.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc6f3a334587c83c5ba56c19b254a97542ce1fc05ccfd66fbf568e6117718d65"}, - {file = "xxhash-3.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36cf410da5bfcca51ac3c2c51a3317dcd7af91f70fa61eca57fba39554f06ae3"}, - {file = "xxhash-3.0.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:21752a3e9a2391d91bd51f4aa2fe028ae14ba6a8d37db9ebe00ccac10be5ac4a"}, - {file = "xxhash-3.0.0-cp36-cp36m-win32.whl", hash = "sha256:322068a063ef156455a401ab720f0892f2d2dd1540c1a308e95a7cbf356df51c"}, - {file = "xxhash-3.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2984fa9a880587c0bfa46d32717b2d209863ee68727ea0fc17f05fce25efa692"}, - {file = "xxhash-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6493dd938b360235da81b1c79d8cd048c4f11977e1159b4e744c54f98d3a7bb4"}, - {file = "xxhash-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb9eca32f9b4acc7149db2c86f8108167b9929b7da1887d4287a90cfdb3ea53a"}, - {file = "xxhash-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4125e70e4e1d79992d81de837a0586aa0241665dbc5ce01b9c89330ed5cbb66"}, - {file = "xxhash-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:583bea142569485bdb0c5900e804058c16edba1850b74519688c22bc546e6175"}, - {file = "xxhash-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f3adf2891acc18abacd15113e9cbbefd30e5f4ecaae32c23e5486fc09c76ea5"}, - {file = "xxhash-3.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed65a2671d380ae05262ce1e4ccc2b63f3c30506d207bf6fae8cd72be0ad65d4"}, - {file = "xxhash-3.0.0-cp37-cp37m-win32.whl", hash = "sha256:c604b3dcac9d37e3fceaa11884927024953260cc4224d9b89400d16e6cf34021"}, - {file = "xxhash-3.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1c6fc59e182506496544bc6d426bcf6077066ed1b40cfcd937f707cc06c7ef50"}, - {file = "xxhash-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5628375dbb76d33b93b44854a6c5433e2a78115e03ea2ae1bb74a34ab012a43f"}, - {file = "xxhash-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:687aa4373690f23a3f43cc23d81005304d284ff6c041bff1f967664ab6410f36"}, - {file = "xxhash-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fa2100fb68b163e99370561c9e29ed37b9153fe99443600bea28829150eb0e4"}, - {file = "xxhash-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:891d7651431a055f76fe2c8f86c593c3dede8ec5b10ca55e8ff5c9fdceb55f0b"}, - {file = "xxhash-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:197c32d7b62be02957ca31aa69febadf9c5a34ef953053ea16e2c72465bc450f"}, - {file = "xxhash-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91fa4df41bda3cbec4084d9696028780b47128c1f8450d1ad9c3e4b6bf8b1f99"}, - {file = "xxhash-3.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cd38b766fc40e9fe37b80112656d2e5a0cb2f9bc12e01b286353b5ecd2768e8"}, - {file = "xxhash-3.0.0-cp38-cp38-win32.whl", hash = "sha256:4258ef78f5a7d1f9c595846134c7d81a868c74942051453258eb383498662d4d"}, - {file = "xxhash-3.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:b82b1cf4407ad908e04e864473cc3baa8e764c7bbebea959150764cc681a1611"}, - {file = "xxhash-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:da4d91e28418469b29eed8635c08af28b588e51cd04288bed1ba1cf60f2d91f6"}, - {file = "xxhash-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48aab36169b0c00e586cb4eb2814ab8bfed686933126019906f917ff9a78c99e"}, - {file = "xxhash-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b0d522570c9ccea6203b3d96ac7f0cfc1d29e613640475d513be432545c48cc"}, - {file = "xxhash-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d6054434ddb060685e86e7457f52d188b0886834baaa532f9f78b4f2b53cfd9b"}, - {file = "xxhash-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbf546ca5f5903ceeb46d9e6abf81f3a64edb95bb7dbe0f75283eec93a7eb2a0"}, - {file = "xxhash-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22704f23f23ccbe892cee3e7568c67f07ac25beaa2d1cff183274005d9d39149"}, - {file = "xxhash-3.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83198e223bcc4b2418b5282ac930e444738c2a33859dee4e570b25c8433d83a2"}, - {file = "xxhash-3.0.0-cp39-cp39-win32.whl", hash = "sha256:3bcd4cd9b22293ea1c08822518fbb6d933c2960d66662d468a1945a45cace194"}, - {file = "xxhash-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5dd4c37da3408d56ae942dc103f4ae3b43510daa4f5accd0a411fc6e914f10a"}, - {file = "xxhash-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:485f172abc03f78afd4f38dbdbb5665f59c5487126fa4c3181c6582cda4de03b"}, - {file = "xxhash-3.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:035248b3d7ab6deb7b247278494d293b9faccfa853078319d25e2926f566b2f8"}, - {file = "xxhash-3.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30ae90c0cfd10ffe852c6b0f263253782eea74a8189d5f2440f6595c1e8047e"}, - {file = "xxhash-3.0.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fd203d8a3c013e679722047ef4f061f690c6cff49380622444bca4c30f3bf23"}, - {file = "xxhash-3.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:6d60059aaef12a01c0cc24f1d7aaaab7933ae9f4b7adfd9ebbd37dc7ceac1745"}, - {file = "xxhash-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:676c97bf7cc298b65eec0368c2cb5611d87a8e876930843311ca728f69292752"}, - {file = "xxhash-3.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2245c6e20e96e3f8fdfb61ad6bc5cde6ce8a1c2b93aa4a32a27bba7ab3aeaf12"}, - {file = "xxhash-3.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae926a52d020085a2d7f69d0e2155cbf819ae409f2e5dbb345dd40a6462de32"}, - {file = "xxhash-3.0.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a2efdcb811be3edc520b78364c11a1e54f5d8e5db895a9ff2bcdd4a7ffa36a5"}, - {file = "xxhash-3.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:885b3a851980056707ab99a2c19c35dfe2c2ba5f602066dbfcd8af45ea855760"}, - {file = "xxhash-3.0.0.tar.gz", hash = "sha256:30b2d97aaf11fb122023f6b44ebb97c6955e9e00d7461a96415ca030b5ceb9c7"}, -] -yapf = [ - {file = "yapf-0.32.0-py2.py3-none-any.whl", hash = "sha256:8fea849025584e486fd06d6ba2bed717f396080fd3cc236ba10cb97c4c51cf32"}, - {file = "yapf-0.32.0.tar.gz", hash = "sha256:a3f5085d37ef7e3e004c4ba9f9b3e40c54ff1901cd111f05145ae313a7c67d1b"}, -] -zipp = [ - {file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"}, - {file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"}, -] +alabaster = [] +analytics-python = [] +ansicon = [] +babel = [] +backoff = [] +beautifulsoup4 = [] +blessed = [] +bump2version = [] +certifi = [] +chardet = [] +charset-normalizer = [] +colorama = [] +contentful-management = [] +docutils = [] +enlighten = [] +furl = [] +furo = [] +html2text = [] +idna = [] +imagesize = [] +importlib-metadata = [] +jinja2 = [] +jinxed = [] +livereload = [] +markupsafe = [] +monotonic = [] +munch = [] +orderedmultidict = [] +packaging = [] +prefixed = [] +pydash = [] +pygments = [] +pyparsing = [] +python-dateutil = [] +python-dotenv = [] +python-frontmatter = [] +pytz = [] +pyyaml = [] +requests = [] +six = [] +snowballstemmer = [] +soupsieve = [] +sphinx = [] +sphinx-autobuild = [] +sphinx-autodoc-typehints = [] +sphinx-jekyll-builder = [] +sphinx-markdown-builder = [] +sphinxcontrib-applehelp = [] +sphinxcontrib-devhelp = [] +sphinxcontrib-htmlhelp = [] +sphinxcontrib-jsmath = [] +sphinxcontrib-qthelp = [] +sphinxcontrib-restbuilder = [] +sphinxcontrib-serializinghtml = [] +sphinxcontrib-websupport = [] +token-bucket = [] +tornado = [] +typing = [] +typing-extensions = [] +unify = [] +untokenize = [] +urllib3 = [] +wcwidth = [] +xxhash = [] +yapf = [] +zipp = [] diff --git a/pyproject.toml b/pyproject.toml index e49581c7..745886f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "frameioclient" -version = "2.0.1-alpha.2" +version = "2.0.1-alpha.4" description='Client library for the Frame.io API' readme = "README.md" license='MIT' @@ -27,6 +27,7 @@ requests = "^2.27.1" token-bucket = "^0.3.0" urllib3 = "^1.26.9" xxhash = "^3.0.0" +furl = "^2.1.3" [tool.poetry.dev-dependencies] bump2version = "^1.0.1" From 2eccdf159d030df0fd9193757bba76eff82e14dd Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 16:44:38 -0700 Subject: [PATCH 27/37] Clean-up examples --- examples/assets/simple_recursive_upload.py | 21 ++++++++++ examples/projects/download_project.py | 48 ++++++++++------------ 2 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 examples/assets/simple_recursive_upload.py diff --git a/examples/assets/simple_recursive_upload.py b/examples/assets/simple_recursive_upload.py new file mode 100644 index 00000000..0850b15c --- /dev/null +++ b/examples/assets/simple_recursive_upload.py @@ -0,0 +1,21 @@ +import os +from frameioclient import FrameioClient + +from dotenv import find_dotenv, load_dotenv +load_dotenv(find_dotenv()) + + +token = os.getenv("FRAMEIO_TOKEN") # Your Frame.io token +destination_folder_id = "d986681a-e460-4cc6-8db6-bbfbebdb88c7" + +def simple_recursive_upload(destination_folder_id: str, source_folder: str): + client = FrameioClient(token) + + print("Starting upload...") + client.assets.upload_folder(source_folder, destination_folder_id) + + +if __name__ == "__main__": + source_directory = '/Users/jeff/Movies/Assets/images/RAW Images/nikon' + + simple_recursive_upload("d986681a-e460-4cc6-8db6-bbfbebdb88c7", source_directory) diff --git a/examples/projects/download_project.py b/examples/projects/download_project.py index fd492244..375a6057 100644 --- a/examples/projects/download_project.py +++ b/examples/projects/download_project.py @@ -1,13 +1,13 @@ -from frameioclient.lib.utils import FormatTypes, Utils import os -from pathlib import Path +from time import time -import pdb -from time import time,sleep -from pprint import pprint +from dotenv import find_dotenv, load_dotenv from frameioclient import FrameioClient +from frameioclient.lib.utils import FormatTypes, Utils + +load_dotenv(find_dotenv()) -def get_folder_size(path='.'): +def get_folder_size(path: str = '.'): total = 0 for entry in os.scandir(path): if entry.is_file(): @@ -16,32 +16,26 @@ def get_folder_size(path='.'): total += get_folder_size(entry.path) return total -def demo_project_download(project_id): - TOKEN = os.getenv("FRAMEIO_TOKEN") - client = FrameioClient(TOKEN) +def demo_project_download(project_id: str, download_dir: str): + TOKEN = os.getenv("FRAMEIO_TOKEN") + client = FrameioClient(TOKEN) - start_time = time() - download_dir = '/Volumes/Jeff-EXT/Python Transfer Test' - item_count = client.projects.download(project_id, destination_directory=download_dir) + project_info = client.projects.get(project_id) - # item_count = client.projects.download(project_id, destination_directory='/Users/jeff/Temp/Transfer vs Python SDK/Python SDK') + start_time = time() + downloaded_item_count = client.projects.download(project_id, destination_directory=download_dir) - end_time = time() - elapsed = round((end_time - start_time), 2) + end_time = time() + elapsed = round((end_time - start_time), 2) - - folder_size = get_folder_size(download_dir) - # pdb.set_trace() + folder_size = get_folder_size(download_dir) - print(f"Found {item_count} items") - print(f"Took {elapsed} second to download {Utils.format_value(folder_size, type=FormatTypes.SIZE)} for project: {client.projects.get(project_id)['name']}") - print("\n") + print(f"Found {downloaded_item_count} items to download in the in the {project_info['name']} project") + print(f"Took {elapsed} second to download {Utils.format_value(folder_size, type=FormatTypes.SIZE)} for project: {project_info['name']}") + print("\n") if __name__ == "__main__": - # project_id = '2dfb6ce6-90d8-4994-881f-f02cd94b1c81' - # project_id='e2845993-7330-54c6-8b77-eafbd5144eac' - # project_id = '5d3ff176-ab1f-4c0b-a027-abe3d2a960e3' - project_id = 'ba1791e8-bf1e-46cb-bcad-5e4bb6431a08' - demo_project_download(project_id) + project_id = 'bb4d6293-514b-4097-a9aa-792d91916414' -# Took 443.84 second to download 12.43 GB to USB HDD for project: HersheyPark Summer Campaign using Python SDK \ No newline at end of file + destination_dir = '/Users/jeff/Movies/Assets/temp/Python SDK Test' + demo_project_download(project_id, destination_dir) \ No newline at end of file From 3f9d3a36db63a34e1ae83da96a7b2886c425f549 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 16:45:10 -0700 Subject: [PATCH 28/37] Fix audit log crawler example --- examples/audit_logs/crawl.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/audit_logs/crawl.py b/examples/audit_logs/crawl.py index b7c9339f..d37a1699 100644 --- a/examples/audit_logs/crawl.py +++ b/examples/audit_logs/crawl.py @@ -4,7 +4,6 @@ import time from typing import Optional from dotenv import find_dotenv, load_dotenv -from frontmatter import load from frameioclient import FrameioClient, Utils # load_dotenv(find_dotenv()) From c9923ab4d020f3a7dfdb777c24170ae3362dfd9b Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 16:45:30 -0700 Subject: [PATCH 29/37] Fix recursive upload function --- frameioclient/lib/upload.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameioclient/lib/upload.py b/frameioclient/lib/upload.py index 8cb1d00c..46ecd633 100644 --- a/frameioclient/lib/upload.py +++ b/frameioclient/lib/upload.py @@ -159,4 +159,4 @@ def upload_recursive(self, client, folder, parent_asset_id): parent_asset_id=parent_asset_id, name=folder_name, type="folder" )["id"] - self.recursive_upload(client, new_folder, new_parent_asset_id) + self.upload_recursive(client, new_folder, new_parent_asset_id) From 071e004e7656df51dddf854da91c5f7fe47d508e Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 16:45:46 -0700 Subject: [PATCH 30/37] Fix recursive upload resource function --- frameioclient/resources/assets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameioclient/resources/assets.py b/frameioclient/resources/assets.py index 293ef0de..21d74344 100644 --- a/frameioclient/resources/assets.py +++ b/frameioclient/resources/assets.py @@ -410,6 +410,6 @@ def upload_folder(self, source_path: str, destination_id: Union[str, UUID]): # Then try to grab it as a project folder_id = Project(self.client).get(destination_id)["root_asset_id"] finally: - return FrameioUploader().recursive_upload( + return FrameioUploader().upload_recursive( self.client, source_path, folder_id ) From bc627b6f4710f930ec040450c9d46239b8bd02f8 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 16:46:09 -0700 Subject: [PATCH 31/37] Improve integration test dotenv import w/ find_dotenv() --- tests/test_integration.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_integration.py b/tests/test_integration.py index 5190631c..40820e49 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -13,8 +13,9 @@ from frameioclient import FrameioClient, Utils, KB, MB from frameioclient.lib.utils import FormatTypes -from dotenv import find_dotenv -find_dotenv() +from dotenv import find_dotenv, load_dotenv +load_dotenv(find_dotenv()) + token = os.getenv("FRAMEIO_TOKEN") # Your Frame.io token project_id = os.getenv("PROJECT_ID") # Project you want to upload files back into From 1de0e5dbc6889f5048d9c16a62df5366af576412 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 16:52:20 -0700 Subject: [PATCH 32/37] Update readme --- README.md | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index d8c4dd94..12cf8295 100644 --- a/README.md +++ b/README.md @@ -13,22 +13,22 @@ Frame.io is a cloud-based collaboration hub that allows video professionals to s ### Installation -via Pip -``` +via `pip` +```sh $ pip install frameioclient ``` -via Source -``` +from source +```sh $ git clone https://github.com/frameio/python-frameio-client -$ pip install . +$ pip install -e . ``` ### Developing -Install the package into your development environment and link to it by running the following: +Install the package into your development environment using Poetry. This should auto-link it within the current virtual-env that gets created during installation. ```sh -pipenv install -e . -pre +poetry install ``` ## Documentation @@ -36,11 +36,11 @@ pipenv install -e . -pre [Frame.io API Documentation](https://developer.frame.io/docs) ### Use CLI -When you install this package, a cli tool called `fioctl` will also be installed to your environment. +When you install this package, a cli tool called `fioctfioclil` will also be installed to your environment. **To upload a file or folder** ```sh -fioctl \ +fiocli \ --token fio-u-YOUR_TOKEN_HERE \ --destination "YOUR TARGET FRAME.IO PROJECT OR FOLDER" \ --target "YOUR LOCAL SYSTEM DIRECTORY" \ @@ -49,7 +49,7 @@ fioctl \ **To download a file, project, or folder** ```sh -fioctl \ +fiocli \ --token fio-u-YOUR_TOKEN_HERE \ --destination "YOUR LOCAL SYSTEM DIRECTORY" \ --target "YOUR TARGET FRAME.IO PROJECT OR FOLDER" \ @@ -87,9 +87,13 @@ In addition to the snippets below, examples are included in [/examples](/example Get basic info on the authenticated user. ```python +import os from frameioclient import FrameioClient -client = FrameioClient("TOKEN") +# We always recommend passing the token you'll be using via an environment variable and accessing it using os.getenv("FRAMEIO_TOKEN") +FRAMEIO_TOKEN = os.getenv("FRAMEIO_TOKEN") +client = FrameioClient(FRAMEIO_TOKEN) + me = client.users.get_me() print(me['id']) ``` @@ -102,12 +106,14 @@ Create a new asset and upload a file. For `parent_asset_id` you must have the ro import os from frameioclient import FrameioClient -client = FrameioClient("TOKEN") +# We always recommend passing the token you'll be using via an environment variable and accessing it using os.getenv("FRAMEIO_TOKEN") +FRAMEIO_TOKEN = os.getenv("FRAMEIO_TOKEN") +client = FrameioClient(FRAMEIO_TOKEN) # Create a new asset manually -asset = client.assets.create( - parent_asset_id="1234abcd", +client.assets.create( + parent_asset_id="0d98e024-d738-4d9a-ae89-19f02839116d", name="MyVideo.mp4", type="file", filetype="video/mp4", @@ -115,10 +121,9 @@ asset = client.assets.create( ) # Create a new folder -client.assets.create( - parent_asset_id="", +client.assets.create_folder( + parent_asset_id="63bfd7cc-8517-4a61-b655-0a59f5dec630", name="Folder name", - type="folder" # this kwarg is what makes it a folder ) # Upload a file From 8aeca418031e8492f56336e6f61edc038da06470 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 16:56:01 -0700 Subject: [PATCH 33/37] Update readme further --- README.md | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 12cf8295..f10c3958 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,6 @@ $ git clone https://github.com/frameio/python-frameio-client $ pip install -e . ``` -### Developing -Install the package into your development environment using Poetry. This should auto-link it within the current virtual-env that gets created during installation. - -```sh -poetry install -``` - ## Documentation [Frame.io API Documentation](https://developer.frame.io/docs) @@ -56,26 +49,6 @@ fiocli \ --threads 2 ``` -### Links - -**Sphinx Documentation** -- https://pythonhosted.org/sphinxcontrib-restbuilder/ -- https://www.npmjs.com/package/rst-selector-parser -- https://sphinx-themes.org/sample-sites/furo/_sources/index.rst.txt -- https://developer.mantidproject.org/Standards/DocumentationGuideForDevs.html -- https://sublime-and-sphinx-guide.readthedocs.io/en/latest/code_blocks.html -- https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html -- https://stackoverflow.com/questions/64451966/python-sphinx-how-to-embed-code-into-a-docstring -- https://pythonhosted.org/an_example_pypi_project/sphinx.html - -**Decorators** -- https://docs.python.org/3.7/library/functools.html -- https://realpython.com/primer-on-python-decorators/ -- https://www.sphinx-doc.org/en/master/usage/quickstart.html -- https://www.geeksforgeeks.org/decorators-with-parameters-in-python/ -- https://stackoverflow.com/questions/43544954/why-does-sphinx-autodoc-output-a-decorators-docstring-when-there-are-two-decora - - ## Usage _Note: A valid token is required to make requests to Frame.io. Go to our [Developer Portal](https://developer.frame.io/) to get a token!_ @@ -129,3 +102,30 @@ client.assets.create_folder( # Upload a file client.assets.upload(destination_id, "video.mp4") ``` + +### Developer information +Install the package into your development environment using Poetry. This should auto-link it within the current virtual-env that gets created during installation. + +```sh +poetry install +``` + +### Ancillary links + +**Sphinx Documentation** +- https://pythonhosted.org/sphinxcontrib-restbuilder/ +- https://www.npmjs.com/package/rst-selector-parser +- https://sphinx-themes.org/sample-sites/furo/_sources/index.rst.txt +- https://developer.mantidproject.org/Standards/DocumentationGuideForDevs.html +- https://sublime-and-sphinx-guide.readthedocs.io/en/latest/code_blocks.html +- https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html +- https://stackoverflow.com/questions/64451966/python-sphinx-how-to-embed-code-into-a-docstring +- https://pythonhosted.org/an_example_pypi_project/sphinx.html + +**Decorators** +- https://docs.python.org/3.7/library/functools.html +- https://realpython.com/primer-on-python-decorators/ +- https://www.sphinx-doc.org/en/master/usage/quickstart.html +- https://www.geeksforgeeks.org/decorators-with-parameters-in-python/ +- https://stackoverflow.com/questions/43544954/why-does-sphinx-autodoc-output-a-decorators-docstring-when-there-are-two-decora + From 83d676d9a5ed7d563c4b373be89a808817345950 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 16:56:25 -0700 Subject: [PATCH 34/37] Add furl to the installation --- README.md | 2 +- setup.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f10c3958..4b6cca87 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ client.assets.create_folder( client.assets.upload(destination_id, "video.mp4") ``` -### Developer information +### Contributing Install the package into your development environment using Poetry. This should auto-link it within the current virtual-env that gets created during installation. ```sh diff --git a/setup.py b/setup.py index 8187f56d..02601345 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,7 @@ def run(self): install_requires=[ 'analytics-python', 'enlighten', + 'furl', 'importlib-metadata ~= 1.0 ; python_version < "3.8"', 'requests', 'token-bucket', From cc8f1422051d7cc6bae188a0c938eec2693b675f Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 17:01:25 -0700 Subject: [PATCH 35/37] Add additional examples --- examples/comments/random_comments.py | 21 ++++ examples/projects/batch_operations.py | 166 ++++++++++++++++++++++++++ examples/projects/project_tree.py | 35 +++--- examples/sharing/batch_updates.py | 99 +++++++++++++++ frameioclient/lib/transport.py | 3 +- frameioclient/resources/links.py | 21 ++++ frameioclient/resources/teams.py | 2 + poetry.lock | 21 +++- pyproject.toml | 1 + 9 files changed, 352 insertions(+), 17 deletions(-) create mode 100644 examples/comments/random_comments.py create mode 100644 examples/projects/batch_operations.py create mode 100644 examples/sharing/batch_updates.py diff --git a/examples/comments/random_comments.py b/examples/comments/random_comments.py new file mode 100644 index 00000000..9ee2171f --- /dev/null +++ b/examples/comments/random_comments.py @@ -0,0 +1,21 @@ +import os +from frameioclient import FrameioClient + + +token = os.getenv('FRAMEIO_TOKEN') + +def create_comments(asset_id: str, count=7449) -> bool: + client = FrameioClient(token) + + try: + for i in range(count): + client.comments.create(asset_id=asset_id, text="Test comment") + + return True + except Exception as e: + print(e) + return False + +if __name__ == "__main__": + asset_id = "5efaf3c3-e0fe-4742-bce1-1ce57f87c4bb" + create_comments(asset_id) diff --git a/examples/projects/batch_operations.py b/examples/projects/batch_operations.py new file mode 100644 index 00000000..4dbc65c0 --- /dev/null +++ b/examples/projects/batch_operations.py @@ -0,0 +1,166 @@ +import csv +import time +from itertools import islice +from pathlib import Path +from re import split +from typing import Dict, List, Tuple + +from dotenv import dotenv_values, find_dotenv +from frameioclient import FrameioClient +from requests.exceptions import HTTPError +from tqdm import tqdm + +dotenv_path = find_dotenv() +env = dotenv_values(Path(dotenv_path)) + +FRAMEIO_TOKEN = env["FRAMEIO_TOKEN"] +fio_client = FrameioClient(FRAMEIO_TOKEN) + + +def split_seq(iterable, size): + it = iter(iterable) + item = list(islice(it, size)) + while item: + yield item + + +def purge_projects(team_id): + # Get projects + projects_list = fio_client.teams.list_projects(team_id) + for project in projects_list: + fio_client._api_call("DELETE", f"/projects/{project['id']}") + + +def purge_projects_from_csv(csv_path: str): + with open(csv_path, "r") as project_list: + for row in csv.reader(project_list): + if row[0] == "id": + continue + try: + fio_client._api_call("DELETE", f"/projects/{row[0]}") + print(f"Deleted: {row[1]}") + except Exception as e: + print(e) + + # Sleeping so as not to run amuck of rate limits + time.sleep(1) + + return True + + +def batch_disable_presentations(csv_path): + fio_client = FrameioClient(FRAMEIO_TOKEN) + with open(csv_path, "r") as project_list: + for row in csv.reader(project_list): + if row[0] == "id": + continue + try: + fio_client._api_call("DELETE", f"/presentations/{row[0]}") + print(f"Deleted: {row[1]}") + except Exception as e: + print(e) + + # Sleeping so as not to run amuck of rate limits + time.sleep(3) + + return True + + +def batch_update_labels(asset_list: List, new_label: str) -> Dict: + """Updates the labels for a list of assets using the batch update endpont + + Args: + asset_list (List): List of Frame.io Asset IDs + new_label (str): New Label to be applied + + Returns: + bool: True or false for whether or not they all updated correctly + """ + fio_client = FrameioClient(FRAMEIO_TOKEN) + batch_ops = list() + all_successful = True + + for batch in split_seq( + asset_list, 50 + ): # Split into batches of 50 because that's the limit + for asset_id in batch: # Iterate over the items in each batch + batch_ops.append({"id": asset_id, "label": new_label}) + + update_payload = {"batch": batch_ops} + + try: + fio_client._api_call("POST", f"/batch/assets/label", payload=update_payload) + except Exception as e: + all_successful = False + + return all_successful + + +def batch_delete_assets(csv_path, token: str): + fio_client = FrameioClient(token) + + # Write header row + csv_columns = ["timestamp", "asset_id", "error"] + with open("deletion_results.csv", "w") as outfile: + writer = csv.DictWriter(outfile, fieldnames=csv_columns) + writer.writeheader() + + asset_ids = [] # Real list rather than CSV + start_pos = 798 # Start position in CSV + + # Read from CSV into a list so that we can slice the list if needed + with open(csv_path, "r") as asset_list: + for row in csv.reader(asset_list): + asset_ids.append(row[0]) + + # Iterate over asset_ids + for row in asset_ids: + print(row) + try: + fio_client.assets.delete(row) + print(f"Deleted: {row}") + except HTTPError as e: + if e.response.status_code == 403 or e.response.status_code == 400: + data = { + "timestamp": time.time(), + "asset_id": row, + "error": e.response.status_code, + } + with open("deletion_results.csv", "a") as outfile: + csv_writer = csv.DictWriter(outfile, fieldnames=csv_columns) + csv_writer.writerow(data) + + # Sleeping so as not to run amuck of rate limits + time.sleep(1.25) + + elif e.response.status_code == 404: + print(f"Asset: {row} already deleted") + continue + + else: + continue + + return True + + +def batch_remove_users(team_id: str, csv_path: str): + users = [] # Real list rather than CSV + start_pos = 798 # Start position in CSV + + # Read from CSV into a list so that we can slice the list if needed + with open(csv_path, "r") as asset_list: + for row in csv.reader(asset_list): + users.append(row[0]) + + # Iterate over asset_ids + for row in split_seq(users): + fio_client.teams.remove_members(team_id, row) + + +if __name__ == "__main__": + # batch_update_labels(test_list, "in_progress") + # batch_remove_users("csv_file_1.csv") + # batch_delete_assets("csv_file_2.csv") + + # csv_path_1 = "/Users/jeff/Downloads/csv_file_1.csv" + # csv_path_2 = "/Users/jeff/Downloads/csv_file_2.csv" diff --git a/examples/projects/project_tree.py b/examples/projects/project_tree.py index 0f4f6450..71a1058a 100644 --- a/examples/projects/project_tree.py +++ b/examples/projects/project_tree.py @@ -3,25 +3,32 @@ from time import time from pprint import pprint from frameioclient import FrameioClient +from pathlib import Path + +from dotenv import load_dotenv, find_dotenv + +dotenv_path = find_dotenv() +load_dotenv(dotenv_path) def demo_folder_tree(project_id): - TOKEN = os.getenv("FRAMEIO_TOKEN") - client = FrameioClient(TOKEN) + TOKEN = os.getenv("FRAMEIO_TOKEN") + client = FrameioClient(TOKEN) - start_time = time() - tree = client.helpers.build_project_tree(project_id, slim=True) + start_time = time() + tree = client.helpers.build_project_tree(project_id, slim=True) - end_time = time() - elapsed = round((end_time - start_time), 2) + end_time = time() + elapsed = round((end_time - start_time), 2) - item_count = len(tree) - pprint(tree) + item_count = len(tree) + pprint(tree) - print(f"Found {item_count} items") - print(f"Took {elapsed} second to fetch the slim payload for project: {project_id}") - print("\n") + print(f"Found {item_count} items") + print(f"Took {elapsed} second to fetch the slim payload for project: {project_id}") + print("\n") if __name__ == "__main__": - # project_id = 'ba1791e8-bf1e-46cb-bcad-5e4bb6431a08' - project_id = '2dfb6ce6-90d8-4994-881f-f02cd94b1c81' - demo_folder_tree(project_id) + # project_id = 'ba1791e8-bf1e-46cb-bcad-5e4bb6431a08' + # project_id = 'f37bc51c-fec1-438d-8ccf-6a88ebd82146' + project_id = 'ceb229b1-bd23-4543-832e-afa0b2151000' + demo_folder_tree(project_id) diff --git a/examples/sharing/batch_updates.py b/examples/sharing/batch_updates.py new file mode 100644 index 00000000..1a7e7cc6 --- /dev/null +++ b/examples/sharing/batch_updates.py @@ -0,0 +1,99 @@ +import csv +import time +import logging +from pathlib import Path +from typing import Dict, List, Tuple + +from dotenv import dotenv_values, find_dotenv +from tqdm import tqdm + +from frameioclient import FrameioClient + +dotenv_path = find_dotenv('/Users/jeff/Code/devrel/python-frameio-client/.env') +env = dotenv_values(Path(dotenv_path)) + +FRAMEIO_TOKEN = env["FRAMEIO_TOKEN"] +fio_client = FrameioClient(FRAMEIO_TOKEN) + +# Gets or creates a logger +logger = logging.getLogger(__name__) + +# set log level +logger.setLevel(logging.INFO) + +# define file handler and set formatter +file_handler = logging.FileHandler('log_file.log') +formatter = logging.Formatter('%(asctime)s : %(levelname)s : %(name)s : %(message)s') +file_handler.setFormatter(formatter) + +# add file handler to logger +logger.addHandler(file_handler) + + +def grant_access_to_all_teams(): + # 1. Get accounts list + # 2. Iterate over accounts + # 3. Fetch all teams in account + # 4. Add user to each team as a team member + + accounts = fio_client._api_call('GET', '/accounts') + for account in accounts: + teams = fio_client.teams.list(account['id']) + for team in teams: + fio_client.teams.add_members(team['id'], ['user@frame.io']) + + pass + + +def batch_disable_presentation_links(csv_path): + with open(csv_path, "r") as project_list: + for row in csv.reader(project_list): + if row[0] == "id": + continue + try: + fio_client.presentation_links.update(row[0], enabled=False) + logger.info(f"Disabled presentation link: {row[0]}") + except Exception as e: + print(e) + logger.error(f"Failed to disable presentation link: {row[0]}") + + return True + + +def batch_disable_review_links(csv_path): + with open(csv_path, "r") as review_link_list: + for row in csv.reader(review_link_list): + if row[0] == "id": + continue + try: + fio_client.review_links.update_settings(row[0], is_active=False) + logger.info(f"Disabled review link: {row[0]}") + except Exception as e: + print(e) + logger.error(f"Failed to disable review link: {row[0]}") + time.sleep(.05) + + return True + +def batch_delete_review_links(csv_path): + with open(csv_path, "r") as review_link_list: + for row in csv.reader(review_link_list): + if row[0] == "id": + continue + try: + fio_client._api_call("DELETE", f"/review_links/{row['id']}") + except Exception as e: + print(e) + logger.error(f"Failed to disable review link: {row[0]}") + time.sleep(.05) + + return True + +if __name__ == '__main__': + presentation_links_csv_path = '/Users/jeff/Code/examples/presentation_links_to_delete.csv' + review_links_csv_path = '/Users/jeff/Code/devrel/python-frameio-client/fio fio_review_links 2022-12-13T1244.csv' + + batch_disable_presentation_links(presentation_links_csv_path) + batch_disable_review_links(review_links_csv_path) + + grant_access_to_all_teams() diff --git a/frameioclient/lib/transport.py b/frameioclient/lib/transport.py index 6f87a2d0..89bc5fc5 100644 --- a/frameioclient/lib/transport.py +++ b/frameioclient/lib/transport.py @@ -43,6 +43,7 @@ def __init__(self, threads: Optional[int] = default_thread_count): self.thread_local = None self.client_version = ClientVersion.version() self.shared_headers = {"x-frameio-client": f"python/{self.client_version}"} + self.rate_limit_bypass_header = {"x-client-type": "Socket Service v2"} # Configure retry strategy (very broad right now) self.retry_strategy = Retry( @@ -94,7 +95,7 @@ def _format_api_call(self, endpoint: str): def _api_call( self, method, endpoint: str, payload: Dict = {}, limit: Optional[int] = None ) -> Union[Dict, PaginatedResponse, None]: - headers = {**self.shared_headers, **self.auth_header} + headers = {**self.shared_headers, **self.auth_header, **self.rate_limit_bypass_header} r = self.session.request( method, self._format_api_call(endpoint), headers=headers, json=payload diff --git a/frameioclient/resources/links.py b/frameioclient/resources/links.py index c23311ac..f5b63848 100644 --- a/frameioclient/resources/links.py +++ b/frameioclient/resources/links.py @@ -134,3 +134,24 @@ def create(self, asset_id: Union[str, UUID], **kwargs): """ endpoint = "/assets/{}/presentations".format(asset_id) return self.client._api_call("post", endpoint, payload=kwargs) + + def update(self, presentation_id: Union[str, UUID], **kwargs): + """ + Update a presentation link. + + Args: + presentation_id (string): The presentation id. + + :Keyword Arguments: + kwargs: additional request parameters. + + Example:: + + client.presentation_links.update( + presentation_id="9cee7966-4066-b326-7db1-f9e6f5e929e4", + name="My fresh presentation", + enabled=False + ) + """ + endpoint = "/presentations/{}".format(presentation_id) + return self.client._api_call("put", endpoint, payload=kwargs) diff --git a/frameioclient/resources/teams.py b/frameioclient/resources/teams.py index 6c3ee306..ee875356 100644 --- a/frameioclient/resources/teams.py +++ b/frameioclient/resources/teams.py @@ -98,6 +98,8 @@ def remove_members(self, team_id, emails): emails (list): The e-mails you want to add. """ + # TODO: Implement pagination here since the batch size is 20? + payload = dict() payload["batch"] = list(map(lambda email: {"email": email}, emails)) diff --git a/poetry.lock b/poetry.lock index 79fe66e8..fac37138 100644 --- a/poetry.lock +++ b/poetry.lock @@ -118,8 +118,8 @@ unicode_backport = ["unicodedata2"] name = "colorama" version = "0.4.4" description = "Cross-platform colored terminal text." -category = "dev" -optional = true +category = "main" +optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] @@ -669,6 +669,23 @@ category = "dev" optional = true python-versions = ">= 3.5" +[[package]] +name = "tqdm" +version = "4.64.1" +description = "Fast, Extensible Progress Meter" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["py-make (>=0.1.0)", "twine", "wheel"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + [[package]] name = "typing" version = "3.7.4.3" diff --git a/pyproject.toml b/pyproject.toml index 745886f0..f0eccba7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ token-bucket = "^0.3.0" urllib3 = "^1.26.9" xxhash = "^3.0.0" furl = "^2.1.3" +tqdm = "^4.64.1" [tool.poetry.dev-dependencies] bump2version = "^1.0.1" From f255ad7ae4752c46b6a0529dd4b26ae61c859ca5 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 17:04:24 -0700 Subject: [PATCH 36/37] Add additional examples and improve Utils.stream() function --- examples/account_info/crawler.py | 175 +++++++++++++++++++++++++++++++ examples/audit_logs/crawler.py | 31 ++++++ frameioclient/lib/utils.py | 7 +- 3 files changed, 211 insertions(+), 2 deletions(-) create mode 100644 examples/account_info/crawler.py create mode 100644 examples/audit_logs/crawler.py diff --git a/examples/account_info/crawler.py b/examples/account_info/crawler.py new file mode 100644 index 00000000..699e5c12 --- /dev/null +++ b/examples/account_info/crawler.py @@ -0,0 +1,175 @@ +import json +import os +from pathlib import Path +from time import time + +from dotenv import find_dotenv, load_dotenv +from frameioclient import FrameioClient + + +class AccountScraper: + def __init__(self, token=None, minimal=False): + self.token = token + self.client = FrameioClient(self.token) + + self.minimal = minimal # Only fetch 2x teams + self.final_data = None # Store final data + + def get_team_ids(self): + account_id = self.client.me["account_id"] + teams_info = self.client.teams.list(account_id=account_id) + + # Slice the list and only return the data from the first two teams + if self.minimal == True: + teams_info = teams_info[0:2] + + team_id_list = list() + for team in teams_info: + team_id_list.append( + { + "id": team["id"], + "name": team["name"], + "projects": team["project_count"], + "members": team["member_count"], + "storage": team["storage"], + "storage_limit": team["storage_limit"], + "date_created": team["inserted_at"], + } + ) + + return team_id_list + + def custom_get_members(self, team_id): + team_members = self.client.teams.get_members(team_id=team_id) + clean_team_members = list() + + for member in team_members: + user_object = member["user"] + id = user_object["id"] + name = user_object["name"] + email = user_object["email"] + + clean_team_members.append({"id": id, "name": name, "email": email}) + + return clean_team_members + + def custom_get_project_members(self, project_name, project_id): + print( + "Adding team members for project: {}, ID: {}".format( + project_name, project_id + ) + ) + project_members = self.client.projects.get_collaborators(project_id=project_id) + + results = [] + + for member in project_members: + user_object = member["user"] + id = user_object["id"] + name = user_object["name"] + email = user_object["email"] + + try: + joined_via = user_object["joined_via"] + if joined_via == "account_member": + role = "Collaborator" + elif joined_via == "organic": + role = "Team Member" + else: + role = None + + except KeyError: + role = None + + results.append({"id": id, "name": name, "email": email, "role": role}) + + return results + + def persist_state(self): + path = Path('.').joinpath("account_state.json") + with open(path.as_posix(), "w") as account_data: + json.dump(self.final_data, account_data) + + return True + + @staticmethod + def clean_data(data=None): + for team in data: + # Grab the list of team members so we can then remove them from project membership + team_members_list = team["members"] + team_member_ids = [] + for team_member in team_members_list: + team_member_ids.append(team_member["id"]) + + for project in team["projects"]: + project_members_list = project["collaborators"] + for count, project_member in enumerate(project_members_list): + if project_member["id"] in team_member_ids: + project_members_list.pop(count) + return data + + def do_it_all(self): + """ + 1. Get all team ids + 2. Get members list for all teams + 3. Get all projects + 4. Get all collaborators on each project + 5. Exclude TEAM MEMBERS, and then you have a list of people who are just collaborators + """ + + teams_list = self.get_team_ids() + + team_member_list = [] # reset blank list + for count, team in enumerate(teams_list, start=1): + print("Team {}/{}".format(count, len(teams_list))) + membership_info = self.custom_get_members(team["id"]) # get membership info + team[ + "members" + ] = membership_info # add member info to the team before passing it to the list + + print( + "Adding projects for team: {}, ID: {}".format(team["name"], team["id"]) + ) + project_info = [] + projects_list = self.client.teams.list_projects(team_id=team["id"]) + + for count, project in enumerate(projects_list, start=1): + print("Project {}/{}".format(count, len(projects_list))) + + # Add the project info + project_info.append( + { + "id": project["id"], + "date_created": project["inserted_at"], + "name": project["name"], + "owner_id": project["owner_id"], + "root_asset_id": project["root_asset"]["id"], + "file_count": project["file_count"], + "folder_count": project["folder_count"], + "storage": project["storage"], + "collaborators": self.custom_get_project_members( + project["name"], project["id"] + ), + } + ) + + print("Appended project info...") + + # Inject project info into the main JSON + team["projects"] = project_info + team_member_list.append(team) + + # Clean data (remove team members from members list in projects) + cleaned_data = self.clean_data(team_member_list) + + self.final_data = cleaned_data + self.persist_state() + + return True + + +if __name__ == "__main__": + dotenv_file = find_dotenv('.env', True, False) + load_dotenv(dotenv_file) + token = os.getenv("FRAMEIO_TOKEN") + AccountScraper(token=token, minimal=True).do_it_all() diff --git a/examples/audit_logs/crawler.py b/examples/audit_logs/crawler.py new file mode 100644 index 00000000..e8e6c1b7 --- /dev/null +++ b/examples/audit_logs/crawler.py @@ -0,0 +1,31 @@ +import os + +from dotenv import load_dotenv +from frameioclient import FrameioClient, Utils + +load_dotenv("/Users/jeff/Code/developer-relations/python-frameio-client/.env") + +client = FrameioClient(os.getenv("FRAMEIO_TOKEN")) + +def get_audit_logs(account_id: str): + logs = [] + + for chunk in Utils.stream_results( + f"/accounts/{account_id}/audit_logs", client=client + ): + logs.append(chunk) + + try: + print(chunk.keys()) + except Exception as e: + print(e) + + print(len(logs)) + + for log in logs: + print(log.keys()) + + +if __name__ == "__main__": + my_account_id = client.me['account_id'] + get_audit_logs(my_account_id) diff --git a/frameioclient/lib/utils.py b/frameioclient/lib/utils.py index 530e5ef3..320ac5d9 100644 --- a/frameioclient/lib/utils.py +++ b/frameioclient/lib/utils.py @@ -47,8 +47,11 @@ def stream(func, page=1, page_size=50): total_pages = page while page <= total_pages: result_list = func(page=page, page_size=page_size) - total_pages = result_list.total_pages - for res in result_list: + if type(result_list) == PaginatedResponse: + total_pages = result_list.total_pages + for res in result_list: + yield res + else: yield res page += 1 From b6ff7ccd017b265fb28f5c79e2e805ea97675d55 Mon Sep 17 00:00:00 2001 From: Jeff Hodges Date: Tue, 25 Jul 2023 17:29:47 -0700 Subject: [PATCH 37/37] Add publishing information --- README.md | 18 +++++++++++++++++- pyproject.toml | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b6cca87..a2e2caf2 100644 --- a/README.md +++ b/README.md @@ -103,13 +103,29 @@ client.assets.create_folder( client.assets.upload(destination_id, "video.mp4") ``` -### Contributing +## Contributing Install the package into your development environment using Poetry. This should auto-link it within the current virtual-env that gets created during installation. ```sh poetry install ``` +### Publishing to PyPI + +```sh +# Start by versioning +poetry version prerelease + +# Then build +poetry build + +# Now you can publish the new version +poetry publish --username=__token__ --password=INSERT_TOKEN_FROM_PYPI_OR_PASS_VIA_ENV_VARIABLE + +# You can also build and publish in one go with +poetry publish --build --username=__token__ --password=INSERT_TOKEN_FROM_PYPI_OR_PASS_VIA_ENV_VARIABLE +``` + ### Ancillary links **Sphinx Documentation** diff --git a/pyproject.toml b/pyproject.toml index f0eccba7..a4f2b913 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "frameioclient" -version = "2.0.1-alpha.4" +version = "2.0.1a5" description='Client library for the Frame.io API' readme = "README.md" license='MIT'