diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 96bc615..e909a42 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,5 +17,5 @@ ] } }, - "postCreateCommand": "poetry install" + "postCreateCommand": "poetry install && poetry run pre-commit install" } diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..0ce541b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,14 @@ +repos: + - repo: local + hooks: + - id: black + name: black + entry: poetry run black + language: system + types: [python] + + - id: isort + name: isort + entry: poetry run isort + language: system + types: [python] diff --git a/README.md b/README.md index 49a2e5f..33e96c9 100644 --- a/README.md +++ b/README.md @@ -203,13 +203,16 @@ pip install poetry # Install dependencies poetry install + +# Set up pre-commit hooks to keep your code formatted +poetry run pre-commit install ``` Check out [Important commands](#important-commands) below for next steps. ### Developing using a GitHub Codespace -This project uses a custom Development Container supported by GitHub Codespaces. Creating a new Codespace automatically takes care of installing all supported Python interpreters, the Poetry package manager, and Python dependencies for you. +This project uses a custom Development Container supported by GitHub Codespaces. Creating a new Codespace automatically takes care of installing all supported Python interpreters, the Poetry package manager, Python dependencies, and pre-commit hooks for you. To create a new Codespace: 1. Click on the `<> Code` dropdown on the GitHub UI. diff --git a/edgar_tool/cli.py b/edgar_tool/cli.py index 06b8ae5..a335a69 100644 --- a/edgar_tool/cli.py +++ b/edgar_tool/cli.py @@ -1,17 +1,18 @@ import sys import time -from datetime import date, timedelta, datetime +from datetime import date, datetime, timedelta from typing import List, Optional from warnings import warn + from edgar_tool.constants import ( SUPPORTED_OUTPUT_EXTENSIONS, TEXT_SEARCH_CATEGORY_FORM_GROUPINGS, TEXT_SEARCH_FILING_VS_MAPPING_CATEGORIES_MAPPING, ) +from edgar_tool.page_fetcher import NoResultsFoundError from edgar_tool.rss import fetch_rss_feed from edgar_tool.text_search import EdgarTextSearcher from edgar_tool.utils import parse_location_input -from edgar_tool.page_fetcher import NoResultsFoundError def _validate_text_search_args( @@ -57,15 +58,16 @@ def _validate_text_search_args( ): raise ValueError( f"Filing form group must be one of: {'; '.join(TEXT_SEARCH_FILING_VS_MAPPING_CATEGORIES_MAPPING.keys())}" - ) + ) if single_forms: - single_list = [item for sublist in TEXT_SEARCH_CATEGORY_FORM_GROUPINGS.values() for item in - sublist] + single_list = [ + item + for sublist in TEXT_SEARCH_CATEGORY_FORM_GROUPINGS.values() + for item in sublist + ] invalid_forms = [form for form in single_forms if form not in single_list] if invalid_forms: - raise ValueError( - f"Single forms must be one or more of: {single_list}" - ) + raise ValueError(f"Single forms must be one or more of: {single_list}") class SecEdgarScraperCli: @@ -135,7 +137,9 @@ def text_search( scraper.text_search( keywords=keywords, entity_id=entity_id, - filing_form=TEXT_SEARCH_FILING_VS_MAPPING_CATEGORIES_MAPPING.get(filing_form), + filing_form=TEXT_SEARCH_FILING_VS_MAPPING_CATEGORIES_MAPPING.get( + filing_form + ), single_forms=single_forms, start_date=start_date, end_date=end_date, @@ -144,7 +148,7 @@ def text_search( retries=retries, destination=output, peo_in=peo_in, - inc_in=inc_in + inc_in=inc_in, ) @staticmethod diff --git a/edgar_tool/io.py b/edgar_tool/io.py index d87b71a..2ab62cc 100644 --- a/edgar_tool/io.py +++ b/edgar_tool/io.py @@ -1,12 +1,10 @@ import csv import json -from typing import List, Dict, Any, Iterator +from typing import Any, Dict, Iterator, List import jsonlines -from edgar_tool.constants import ( - SUPPORTED_OUTPUT_EXTENSIONS, -) +from edgar_tool.constants import SUPPORTED_OUTPUT_EXTENSIONS def write_results_to_file( diff --git a/edgar_tool/main.py b/edgar_tool/main.py index 15f8d05..1b59768 100644 --- a/edgar_tool/main.py +++ b/edgar_tool/main.py @@ -1,6 +1,7 @@ -from edgar_tool.cli import SecEdgarScraperCli import fire +from edgar_tool.cli import SecEdgarScraperCli + def main_entrypoint(): fire.Fire(SecEdgarScraperCli) diff --git a/edgar_tool/page_fetcher.py b/edgar_tool/page_fetcher.py index 1208c07..4b31a60 100644 --- a/edgar_tool/page_fetcher.py +++ b/edgar_tool/page_fetcher.py @@ -1,10 +1,10 @@ import time import uuid from random import uniform -from typing import Callable, Any, Optional +from typing import Any, Callable, Optional -from tenacity import retry, wait_fixed, stop_after_attempt import requests +from tenacity import retry, stop_after_attempt, wait_fixed def fetch_page( @@ -23,6 +23,7 @@ def fetch_page( :param stop_after_n: how many times to retry the request before failing :return: wrapper function that takes a check method and retries the request if the page load fails """ + @retry( wait=wait_fixed(uniform(min_wait_seconds, max_wait_seconds)), stop=stop_after_attempt(stop_after_n), @@ -57,5 +58,6 @@ class ResultsTableNotFoundError(Exception): class PageCheckFailedError(Exception): pass + class NoResultsFoundError(Exception): - pass \ No newline at end of file + pass diff --git a/edgar_tool/rss.py b/edgar_tool/rss.py index 79b0215..4fa570f 100644 --- a/edgar_tool/rss.py +++ b/edgar_tool/rss.py @@ -1,7 +1,7 @@ import json import uuid from pathlib import Path -from typing import List, Any, Dict, Iterator, Tuple +from typing import Any, Dict, Iterator, List, Tuple import requests import xmltodict diff --git a/edgar_tool/text_search.py b/edgar_tool/text_search.py index 656bd7a..018438f 100644 --- a/edgar_tool/text_search.py +++ b/edgar_tool/text_search.py @@ -4,24 +4,23 @@ import urllib.parse from datetime import date, timedelta from math import ceil -from typing import List, Optional, Dict, Any, Iterator +from typing import Any, Dict, Iterator, List, Optional - -from edgar_tool.page_fetcher import ( - fetch_page, - PageCheckFailedError, - ResultsTableNotFoundError, - NoResultsFoundError -) from edgar_tool.constants import ( TEXT_SEARCH_BASE_URL, TEXT_SEARCH_CATEGORY_FORM_GROUPINGS, - TEXT_SEARCH_SPLIT_BATCHES_NUMBER, TEXT_SEARCH_CSV_FIELDS_NAMES, TEXT_SEARCH_FORM_MAPPING, TEXT_SEARCH_LOCATIONS_MAPPING, + TEXT_SEARCH_SPLIT_BATCHES_NUMBER, ) from edgar_tool.io import write_results_to_file +from edgar_tool.page_fetcher import ( + NoResultsFoundError, + PageCheckFailedError, + ResultsTableNotFoundError, + fetch_page, +) from edgar_tool.utils import split_date_range_in_n, unpack_singleton_list @@ -130,7 +129,11 @@ def _parse_row(row: Dict[str, Any]) -> Dict[str, Any]: places_of_business = _source.get("biz_locations") places_of_business = [ - f"{split[0]}, {TEXT_SEARCH_LOCATIONS_MAPPING.get(split[1])}" if len(split) == 2 else f"{split[0]}" + ( + f"{split[0]}, {TEXT_SEARCH_LOCATIONS_MAPPING.get(split[1])}" + if len(split) == 2 + else f"{split[0]}" + ) for place in places_of_business if (split := place.rsplit(", ", maxsplit=1)) ] @@ -226,25 +229,31 @@ def _generate_request_args( # Add optional parameters if peo_in and inc_in: - raise ValueError("use only one of peo_in or inc_in, not both") ## because SEC API doesn't support + raise ValueError( + "use only one of peo_in or inc_in, not both" + ) ## because SEC API doesn't support else: if peo_in: request_args["locationCodes"] = peo_in if inc_in: request_args["locationCodes"] = inc_in request_args["locationType"] = "incorporated" - + if entity_id: request_args["entityName"] = entity_id # Handle forms and single forms - part_filing_form = [] if filing_form is None else TEXT_SEARCH_CATEGORY_FORM_GROUPINGS[filing_form] + part_filing_form = ( + [] + if filing_form is None + else TEXT_SEARCH_CATEGORY_FORM_GROUPINGS[filing_form] + ) part_single_forms = [] if single_forms is None else single_forms # Join the filing_forms and single forms and remove duplicates forms = ",".join(list(set(part_filing_form + part_single_forms))) if forms != "": request_args["forms"] = forms - + # URL-encode the request arguments request_args = urllib.parse.urlencode(request_args) @@ -373,7 +382,9 @@ def _generate_search_requests( # If we have 10000 results, split date range in two separate requests and fetch first page again, do so until # we have a set of date ranges for which none of the requests have 10000 results if num_results == 0: - print(f"No results found for query in date range {start_date} -> {end_date}.") + print( + f"No results found for query in date range {start_date} -> {end_date}." + ) elif num_results < 10000: print( f"Less than 10000 ({num_results}) results found for range {start_date} -> {end_date}, " @@ -475,7 +486,7 @@ def text_search( print( f"Skipping search request due to an unexpected {e.__class__.__name__} for request parameters '{r}': {e}" ) - if(search_requests_results == []): + if search_requests_results == []: raise NoResultsFoundError(f"No results found for the search query") write_results_to_file( itertools.chain(*search_requests_results), @@ -518,4 +529,4 @@ def _fetch_first_page_results_number( raise NoResultsFoundError( f"\nExecution aborting due to a {e.__class__.__name__} error raised " f"while parsing number of results for first page at URL {url}: {e}" - ) from e \ No newline at end of file + ) from e diff --git a/edgar_tool/utils.py b/edgar_tool/utils.py index d72bd11..271bebc 100644 --- a/edgar_tool/utils.py +++ b/edgar_tool/utils.py @@ -2,7 +2,8 @@ import re from datetime import date -from typing import Any, Iterator, Dict, List, Union, Optional +from typing import Any, Dict, Iterator, List, Optional, Union + from edgar_tool.constants import TEXT_SEARCH_LOCATIONS_MAPPING @@ -36,16 +37,19 @@ def safe_get(d: Dict, *keys) -> Any: return None return d + def unpack_singleton_list(l: Optional[List]) -> Union[str, List[str]]: return l if (l is None) or (len(l) != 1) else l[0] -def invert_dict(d:dict)->dict: + +def invert_dict(d: dict) -> dict: """ - Returns an inverted dictionary such that values are keys and keys are values. - If there are duplicate values, the last occurring key-value pair will prevail. + Returns an inverted dictionary such that values are keys and keys are values. + If there are duplicate values, the last occurring key-value pair will prevail. """ return {v: k for k, v in d.items()} + def replace_ignore_case_whitespace(s, location, replacement): """ Perform a case-insensitive and whitespace-insensitive replacement of a substring in a string. @@ -59,12 +63,15 @@ def replace_ignore_case_whitespace(s, location, replacement): str: The modified string with the replacements made. """ # Create a regex pattern that ignores whitespace and is case-insensitive - location_pattern = re.compile(r'\s*'.join(re.escape(char) for char in location), re.IGNORECASE) + location_pattern = re.compile( + r"\s*".join(re.escape(char) for char in location), re.IGNORECASE + ) return location_pattern.sub(replacement, s) -def replace_substrings_in_string(s)->str: + +def replace_substrings_in_string(s) -> str: """ - Takes a string like "New York, OH" and returns a string with the full + Takes a string like "New York, OH" and returns a string with the full location names converted to codes such as "NY,OH". Returns an unmodified string if there are no full location names present. Note that matching full location names shall be case and whitespace insensitive. @@ -76,58 +83,61 @@ def replace_substrings_in_string(s)->str: str: The modified string with substrings replaced. """ locations2codes = invert_dict(TEXT_SEARCH_LOCATIONS_MAPPING) - locations2codes = {k.replace(" ", "").lower(): v for k, v in locations2codes.items()} + locations2codes = { + k.replace(" ", "").lower(): v for k, v in locations2codes.items() + } for location in locations2codes.keys(): if location in s.replace(" ", "").lower(): - s = replace_ignore_case_whitespace(s,location, locations2codes[location]) + s = replace_ignore_case_whitespace(s, location, locations2codes[location]) return s - + + def parse_location_input(location_input: str | tuple | None) -> str | None: """ Handles text search input for --peo_in or --inc_in. - This function processes the input to ensure it is in an acceptable format + This function processes the input to ensure it is in an acceptable format for location searches. Because CLI input like --peo_in "NY, OH" yields - runtime value ('NY','OH'), this function supports single or multiple locations - provided as a string or a tuple. If the input is a tuple, it converts the tuple - to a comma-separated string. It also removes any whitespace from the output + runtime value ('NY','OH'), this function supports single or multiple locations + provided as a string or a tuple. If the input is a tuple, it converts the tuple + to a comma-separated string. It also removes any whitespace from the output string to prevent errors during further processing. Also validates that all provided location codes are in the TEXT_SEARCH_LOCATIONS_MAPPING and prints the list of acceptable codes if not. If the input string is a location's full name instead of the code (i.e. 'New York' instead of 'NY'), then strings present in - TEXT_SEARCH_LOCATIONS_MAPPING.values() are mapped to an code value instead. + TEXT_SEARCH_LOCATIONS_MAPPING.values() are mapped to an code value instead. Parameters: - location_input (str | tuple | None): The input location(s) to be parsed. - It can be a single location as a string, multiple locations as a tuple + location_input (str | tuple | None): The input location(s) to be parsed. + It can be a single location as a string, multiple locations as a tuple of strings, or None. Returns: str: A string representation of the location(s) with no whitespace. Raises: - ValueError: If the input is not a string, tuple, or None, or if any location + ValueError: If the input is not a string, tuple, or None, or if any location in the input is not in the TEXT_SEARCH_LOCATIONS_MAPPING. """ if not isinstance(location_input, (str, tuple, type(None))): raise ValueError( f'peo_in and inc_in must use format like "NY" or "NY,OH,etc"' - f'and be one of {TEXT_SEARCH_LOCATIONS_MAPPING}' + f"and be one of {TEXT_SEARCH_LOCATIONS_MAPPING}" ) - if isinstance(location_input,tuple): - location_input = ','.join(location_input) - - if isinstance(location_input,str): - location_input = tuple(replace_substrings_in_string(location_input).split(',')) + if isinstance(location_input, tuple): + location_input = ",".join(location_input) + + if isinstance(location_input, str): + location_input = tuple(replace_substrings_in_string(location_input).split(",")) for value in location_input: # Eliminate issues caused by casing and whitespaces - value = value.replace(" ","").upper() + value = value.replace(" ", "").upper() if value not in TEXT_SEARCH_LOCATIONS_MAPPING.keys(): raise ValueError(f"{value} not in {TEXT_SEARCH_LOCATIONS_MAPPING}") - location_input = ','.join(location_input) + location_input = ",".join(location_input) if location_input: location_input = location_input.replace(" ", "") - + return location_input diff --git a/poetry.lock b/poetry.lock index 2d92e5c..6a2d972 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,22 +2,22 @@ [[package]] name = "attrs" -version = "23.2.0" +version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, - {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] -tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "black" @@ -67,24 +67,35 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "cachetools" -version = "5.3.3" +version = "5.5.0" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.3-py3-none-any.whl", hash = "sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945"}, - {file = "cachetools-5.3.3.tar.gz", hash = "sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105"}, + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] name = "certifi" -version = "2024.2.2" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + +[[package]] +name = "cfgv" +version = "3.4.0" +description = "Validate configuration and produce human readable error messages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] [[package]] @@ -249,19 +260,19 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.15.4" +version = "3.16.0" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.15.4-py3-none-any.whl", hash = "sha256:6ca1fffae96225dab4c6eaf1c4f4f28cd2568d3ec2a44e15a08520504de468e7"}, - {file = "filelock-3.15.4.tar.gz", hash = "sha256:2207938cbc1844345cb01a5a95524dae30f0ce089eba5b00378295a17e3e90cb"}, + {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, + {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)", "virtualenv (>=20.26.2)"] -typing = ["typing-extensions (>=4.8)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +typing = ["typing-extensions (>=4.12.2)"] [[package]] name = "fire" @@ -277,15 +288,29 @@ files = [ six = "*" termcolor = "*" +[[package]] +name = "identify" +version = "2.6.0" +description = "File identification library for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, + {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, +] + +[package.extras] +license = ["ukkonen"] + [[package]] name = "idna" -version = "3.6" +version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, - {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, ] [[package]] @@ -299,6 +324,20 @@ files = [ {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] +[[package]] +name = "isort" +version = "5.13.2" +description = "A Python utility / library to sort Python imports." +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, + {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, +] + +[package.extras] +colors = ["colorama (>=0.4.6)"] + [[package]] name = "jsonlines" version = "4.0.0" @@ -324,6 +363,17 @@ files = [ {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, ] +[[package]] +name = "nodeenv" +version = "1.9.1" +description = "Node.js virtual environment builder" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, + {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, +] + [[package]] name = "packaging" version = "24.1" @@ -348,13 +398,13 @@ files = [ [[package]] name = "platformdirs" -version = "4.2.2" +version = "4.3.1" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, + {file = "platformdirs-4.3.1-py3-none-any.whl", hash = "sha256:facaa5a3c57aa1e053e3da7b49e0cc31fe0113ca42a4659d5c2e98e545624afe"}, + {file = "platformdirs-4.3.1.tar.gz", hash = "sha256:63b79589009fa8159973601dd4563143396b35c5f93a58b36f9049ff046949b1"}, ] [package.extras] @@ -377,6 +427,24 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "pre-commit" +version = "3.8.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pre_commit-3.8.0-py2.py3-none-any.whl", hash = "sha256:9a90a53bf82fdd8778d58085faf8d83df56e40dfe18f45b19446e26bf1b3a63f"}, + {file = "pre_commit-3.8.0.tar.gz", hash = "sha256:8bb6494d4a20423842e198980c9ecf9f96607a07ea29549e180eef9ae80fe7af"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + [[package]] name = "pyproject-api" version = "1.7.1" @@ -398,13 +466,13 @@ testing = ["covdefaults (>=2.3)", "pytest (>=8.2.2)", "pytest-cov (>=5)", "pytes [[package]] name = "pytest" -version = "8.2.2" +version = "8.3.2" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, - {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, + {file = "pytest-8.3.2-py3-none-any.whl", hash = "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5"}, + {file = "pytest-8.3.2.tar.gz", hash = "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"}, ] [package.dependencies] @@ -412,21 +480,83 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" -pluggy = ">=1.5,<2.0" +pluggy = ">=1.5,<2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + [[package]] name = "requests" -version = "2.31.0" +version = "2.32.3" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, ] [package.dependencies] @@ -452,17 +582,18 @@ files = [ [[package]] name = "tenacity" -version = "8.2.3" +version = "8.5.0" description = "Retry code until it succeeds" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"}, - {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"}, + {file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"}, + {file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"}, ] [package.extras] -doc = ["reno", "sphinx", "tornado (>=4.5)"] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] [[package]] name = "termcolor" @@ -491,17 +622,17 @@ files = [ [[package]] name = "tox" -version = "4.16.0" +version = "4.18.1" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.8" files = [ - {file = "tox-4.16.0-py3-none-any.whl", hash = "sha256:61e101061b977b46cf00093d4319438055290ad0009f84497a07bf2d2d7a06d0"}, - {file = "tox-4.16.0.tar.gz", hash = "sha256:43499656f9949edb681c0f907f86fbfee98677af9919d8b11ae5ad77cb800748"}, + {file = "tox-4.18.1-py3-none-any.whl", hash = "sha256:35d472032ee1f73fe20c3e0e73d7073a4e85075c86ff02c576f9fc7c6a15a578"}, + {file = "tox-4.18.1.tar.gz", hash = "sha256:3c0c96bc3a568a5c7e66387a4cfcf8c875b52e09f4d47c9f7a277ec82f1a0b11"}, ] [package.dependencies] -cachetools = ">=5.3.3" +cachetools = ">=5.5" chardet = ">=5.2" colorama = ">=0.4.6" filelock = ">=3.15.4" @@ -513,29 +644,29 @@ tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} virtualenv = ">=20.26.3" [package.extras] -docs = ["furo (>=2024.5.6)", "sphinx (>=7.3.7)", "sphinx-argparse-cli (>=1.16)", "sphinx-autodoc-typehints (>=2.2.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"] -testing = ["build[virtualenv] (>=1.2.1)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=9.1)", "distlib (>=0.3.8)", "flaky (>=3.8.1)", "hatch-vcs (>=0.4)", "hatchling (>=1.25)", "psutil (>=6)", "pytest (>=8.2.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-xdist (>=3.6.1)", "re-assert (>=1.1)", "setuptools (>=70.2)", "time-machine (>=2.14.2)", "wheel (>=0.43)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-argparse-cli (>=1.17)", "sphinx-autodoc-typehints (>=2.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=24.8)"] +testing = ["build[virtualenv] (>=1.2.2)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=9.1.1)", "distlib (>=0.3.8)", "flaky (>=3.8.1)", "hatch-vcs (>=0.4)", "hatchling (>=1.25)", "psutil (>=6)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-xdist (>=3.6.1)", "re-assert (>=1.1)", "setuptools (>=74.1.2)", "time-machine (>=2.15)", "wheel (>=0.44)"] [[package]] name = "typing-extensions" -version = "4.10.0" +version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [[package]] name = "urllib3" -version = "2.2.1" +version = "2.2.2" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, + {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, + {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, ] [package.extras] @@ -546,13 +677,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "virtualenv" -version = "20.26.3" +version = "20.26.4" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.3-py3-none-any.whl", hash = "sha256:8cc4a31139e796e9a7de2cd5cf2489de1217193116a8fd42328f1bd65f434589"}, - {file = "virtualenv-20.26.3.tar.gz", hash = "sha256:4c43a2a236279d9ea36a0d76f98d84bd6ca94ac4e0f4a3b9d46d05e10fea542a"}, + {file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"}, + {file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"}, ] [package.dependencies] @@ -578,4 +709,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "3ab406928ad3f1685af32618b71ae1c1bb82432815e097d933748de402a813f1" +content-hash = "cebc53eb0c3700be24a100b720c492b937227b4bdddf6be41bce417f317582b4" diff --git a/pyproject.toml b/pyproject.toml index 79d2950..15a37ae 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,18 +27,27 @@ keywords=["scraper", "edgar", "finance", "sec"] edgar-tool = "edgar_tool.main:main_entrypoint" [tool.poetry.dependencies] -python = "^3.9" -tenacity = "^8.2" fire = "^0.5" jsonlines = "^4.0" +python = "^3.9" requests = "^2.31" +tenacity = "^8.2" xmltodict = "^0.13" [tool.poetry.group.dev.dependencies] -black = "^24.2.0" -tox = "^4.16.0" +black = "24.3.0" +isort = "5.13.2" +pre-commit = "^3.8.0" pytest = "^8.2.2" +tox = "^4.16.0" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" + +[tool.isort] +profile = "black" +line_length = 88 # Should always match tool.black + +[tool.black] +line-length = 88 # black's default