mirror of
https://github.com/soxoj/maigret.git
synced 2026-05-15 10:55:43 +00:00
Compare commits
43 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b59179b4d5 | |||
| a2142634e1 | |||
| ab01dfce92 | |||
| 64cca25b12 | |||
| 7ba2fd31ea | |||
| 4d70f0f7c9 | |||
| 5aae2ee005 | |||
| b145e7b26f | |||
| abd9aa57fe | |||
| 2e430e5039 | |||
| f5786f11ce | |||
| 3e56c95e16 | |||
| 28f35f9a4f | |||
| 79cea49526 | |||
| 2d94269656 | |||
| 829bda885a | |||
| eb541dcf51 | |||
| 4c97025a32 | |||
| 2775181a6a | |||
| b00ef1f5dd | |||
| d3f13ac295 | |||
| 479a614d1d | |||
| e0559e4320 | |||
| 00a9249229 | |||
| 005863c2e0 | |||
| e3aada6aef | |||
| 9b35fc1ab0 | |||
| 146bc0481b | |||
| 5930a3022e | |||
| b4482e0ba4 | |||
| 2c55501bc2 | |||
| 3ba07591a1 | |||
| a2d4373b68 | |||
| b960acec10 | |||
| b1a211c3cd | |||
| 56d0c9f2f1 | |||
| 01049b730d | |||
| 2c2d3409e2 | |||
| e81b50ef61 | |||
| 9ac0a65914 | |||
| 4f397fed1c | |||
| 2def9a2014 | |||
| 0615deab8b |
@@ -4,13 +4,18 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: update-sites-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2.3.2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
|
ref: main
|
||||||
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.
|
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.
|
||||||
|
|
||||||
- name: Install system dependencies
|
- name: Install system dependencies
|
||||||
@@ -22,6 +27,9 @@ jobs:
|
|||||||
pip3 install .
|
pip3 install .
|
||||||
python3 ./utils/update_site_data.py --empty-only
|
python3 ./utils/update_site_data.py --empty-only
|
||||||
|
|
||||||
|
- name: Remove ambiguous main tag
|
||||||
|
run: git tag -d main || true
|
||||||
|
|
||||||
- name: Check for meaningful changes
|
- name: Check for meaningful changes
|
||||||
id: check
|
id: check
|
||||||
run: |
|
run: |
|
||||||
@@ -32,13 +40,18 @@ jobs:
|
|||||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
- name: Delete existing PR branch
|
||||||
|
if: steps.check.outputs.has_changes == 'true'
|
||||||
|
run: git push origin --delete auto/update-sites-list || true
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
if: steps.check.outputs.has_changes == 'true'
|
if: steps.check.outputs.has_changes == 'true'
|
||||||
uses: peter-evans/create-pull-request@v5
|
uses: peter-evans/create-pull-request@v7
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
commit-message: "Updated site list and statistics"
|
commit-message: "Updated site list and statistics"
|
||||||
title: "Automated Sites List Update"
|
title: "Automated Sites List Update"
|
||||||
body: "Automated changes to sites.md based on new Alexa rankings/statistics."
|
body: "Automated changes to sites.md based on new Alexa rankings/statistics."
|
||||||
branch: "auto/update-sites-list"
|
branch: "auto/update-sites-list"
|
||||||
|
base: main
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
@@ -157,6 +157,7 @@ Summary from an earlier false-positive review for: OpenSea, Mercado Livre, Redtu
|
|||||||
- For **Kaggle**, additionally: **`headers`**, **`errors`** for browser-check text.
|
- For **Kaggle**, additionally: **`headers`**, **`errors`** for browser-check text.
|
||||||
- **Redtube** stayed valid on **`status_code`** with a stable **404** for non-existent users.
|
- **Redtube** stayed valid on **`status_code`** with a stable **404** for non-existent users.
|
||||||
- **Picsart**: the web profile URL is a thin SPA shell; use the **JSON API** (`api.picsart.com/users/show/{username}.json`) in **`url`** with **`message`**-style markers (`"status":"success"` vs `user_not_found`), not the browser-only `/posts` vs `/not-found` navigation.
|
- **Picsart**: the web profile URL is a thin SPA shell; use the **JSON API** (`api.picsart.com/users/show/{username}.json`) in **`url`** with **`message`**-style markers (`"status":"success"` vs `user_not_found`), not the browser-only `/posts` vs `/not-found` navigation.
|
||||||
|
- For **Weblate / Anubis Anti-Bot**: Setting `headers` with a basic script User-Agent (e.g. `python-requests/2.25.1`) rather than the default browser UA completely bypassed the Anubis Proof-of-Work challenge HTTP 307 redirect, instantly recovering the native HTTP 404 framework.
|
||||||
|
|
||||||
### What required disabling checks
|
### What required disabling checks
|
||||||
|
|
||||||
|
|||||||
@@ -76,8 +76,11 @@ Practical observations from fixing top-ranked sites. Full details: section **7**
|
|||||||
| **Some sites always generate a page** | Pbase stubs "pbase Artist {name}" for any path; ffm.bio fuzzy-matches to the nearest real entry. No markers can help — `disabled: true`. |
|
| **Some sites always generate a page** | Pbase stubs "pbase Artist {name}" for any path; ffm.bio fuzzy-matches to the nearest real entry. No markers can help — `disabled: true`. |
|
||||||
| **TLS fingerprinting degrades over time** | Kaggle's custom `User-Agent` fix stopped working — aiohttp now gets 404 for both usernames. Accept `disabled: true` when no API exists. |
|
| **TLS fingerprinting degrades over time** | Kaggle's custom `User-Agent` fix stopped working — aiohttp now gets 404 for both usernames. Accept `disabled: true` when no API exists. |
|
||||||
| **API endpoints bypass Cloudflare** | Fandom `api.php` and Substack `/api/v1/` returned clean JSON while main pages were blocked by Cloudflare. Always try API paths on the same domain. |
|
| **API endpoints bypass Cloudflare** | Fandom `api.php` and Substack `/api/v1/` returned clean JSON while main pages were blocked by Cloudflare. Always try API paths on the same domain. |
|
||||||
| **GraphQL supports GET too** | hashnode GraphQL works via `GET ?query=...` (URL-encoded). Don't assume POST-only — Maigret can use GET `urlProbe` for GraphQL. |
|
| **Inspect Network tab for POST APIs** | Many modern platforms (e.g., Discord) heavily protect HTML profiles but expose unauthenticated `POST` endpoints for username checks. Maigret supports this natively: define `"request_method": "POST"` and `"request_payload": {"username": "{username}"}` in `data.json` to query them! |
|
||||||
|
| **Strict JSON markers are bulletproof** | When probing APIs, use `checkType: "message"` with exact JSON substrings (like `"{\"taken\": false}"`). Unlike HTML layout checks, this approach is immune to UI redesigns, A/B testing, and language translations. |
|
||||||
|
| **GraphQL supports GET too** | hashnode GraphQL works via `GET ?query=...` (URL-encoded). You can use either native POST payloads or GET `urlProbe` for GraphQL. |
|
||||||
| **URL-encode braces for template safety** | GraphQL `{...}` conflicts with Maigret's `{username}`. Use `%7B`/`%7D` for literal braces in `urlProbe` — `.format()` ignores percent-encoded chars. |
|
| **URL-encode braces for template safety** | GraphQL `{...}` conflicts with Maigret's `{username}`. Use `%7B`/`%7D` for literal braces in `urlProbe` — `.format()` ignores percent-encoded chars. |
|
||||||
|
| **Anti-bot bypass via simple UA** | "Anubis" anti-bot PoW screens (like on Weblate) intercept modern browser UAs via HTTP 307. Hardcoding `"headers": {"User-Agent": "python-requests/2.25.1"}` circumvents the scraper filter and restores default detection logic. |
|
||||||
|
|
||||||
## 8. Documentation maintenance
|
## 8. Documentation maintenance
|
||||||
|
|
||||||
|
|||||||
@@ -31,19 +31,25 @@ two-letter country codes (**not a language!**). E.g. photo, dating, sport; jp, u
|
|||||||
Multiple tags can be associated with one site. **Warning**: tags markup is
|
Multiple tags can be associated with one site. **Warning**: tags markup is
|
||||||
not stable now. Read more :doc:`in the separate section <tags>`.
|
not stable now. Read more :doc:`in the separate section <tags>`.
|
||||||
|
|
||||||
|
``--exclude-tags`` - Exclude sites with specific tags from the search
|
||||||
|
(blacklist). E.g. ``--exclude-tags porn,dating`` will skip all sites
|
||||||
|
tagged with ``porn`` or ``dating``. Can be combined with ``--tags`` to
|
||||||
|
include certain categories while excluding others. Read more
|
||||||
|
:doc:`in the separate section <tags>`.
|
||||||
|
|
||||||
``-n``, ``--max-connections`` - Allowed number of concurrent connections
|
``-n``, ``--max-connections`` - Allowed number of concurrent connections
|
||||||
**(default: 100)**.
|
**(default: 100)**.
|
||||||
|
|
||||||
``-a``, ``--all-sites`` - Use all sites for scan **(default: top 500)**.
|
``-a``, ``--all-sites`` - Use all sites for scan **(default: top 500)**.
|
||||||
|
|
||||||
``--top-sites`` - Count of sites for scan ranked by Alexa Top
|
``--top-sites`` - Count of sites for scan ranked by Majestic Million
|
||||||
**(default: top 500)**.
|
**(default: top 500)**.
|
||||||
|
|
||||||
**Mirrors:** After the top *N* sites by Alexa rank are chosen (respecting
|
**Mirrors:** After the top *N* sites by Majestic Million rank are chosen (respecting
|
||||||
``--tags``, ``--use-disabled-sites``, etc.), Maigret may add extra sites
|
``--tags``, ``--use-disabled-sites``, etc.), Maigret may add extra sites
|
||||||
whose database field ``source`` names a **parent platform** that itself falls
|
whose database field ``source`` names a **parent platform** that itself falls
|
||||||
in the Alexa top *N* when ranking **including disabled** sites. For example,
|
in the Majestic Million top *N* when ranking **including disabled** sites. For example,
|
||||||
if ``Twitter`` ranks in the first 500 by Alexa, a mirror such as ``memory.lol``
|
if ``Twitter`` ranks in the first 500 by Majestic Million, a mirror such as ``memory.lol``
|
||||||
(with ``source: Twitter``) is included even though it has no rank and would
|
(with ``source: Twitter``) is included even though it has no rank and would
|
||||||
otherwise be cut off. The same applies to Instagram-related mirrors (e.g.
|
otherwise be cut off. The same applies to Instagram-related mirrors (e.g.
|
||||||
Picuki) when ``Instagram`` is in that parent top *N* by rank—even if the
|
Picuki) when ``Instagram`` is in that parent top *N* by rank—even if the
|
||||||
|
|||||||
@@ -22,9 +22,15 @@ The supported methods (``checkType`` values in ``data.json``) are:
|
|||||||
- ``status_code`` - checks that status code of the response is 2XX
|
- ``status_code`` - checks that status code of the response is 2XX
|
||||||
- ``response_url`` - check if there is not redirect and the response is 2XX
|
- ``response_url`` - check if there is not redirect and the response is 2XX
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Maigret natively treats specific anti-bot HTTP status codes (like LinkedIn's ``HTTP 999``) as a standard "Not Found/Available" signal instead of throwing an infrastructure Server Error, gracefully preventing false positives.
|
||||||
|
|
||||||
See the details of check mechanisms in the `checking.py <https://github.com/soxoj/maigret/blob/main/maigret/checking.py#L339>`_ file.
|
See the details of check mechanisms in the `checking.py <https://github.com/soxoj/maigret/blob/main/maigret/checking.py#L339>`_ file.
|
||||||
|
|
||||||
**Mirrors and ``--top-sites``:** When you limit scans with ``--top-sites N``, Maigret also includes *mirror* sites (entries whose ``source`` field points at a parent platform such as Twitter or Instagram) if that parent would appear in the Alexa top *N* when disabled sites are considered for ranking. See the **Mirrors** paragraph under ``--top-sites`` in :doc:`command-line-options`.
|
.. note::
|
||||||
|
Maigret now uses the **Majestic Million** dataset for site popularity sorting instead of the discontinued Alexa Rank API. For backward compatibility with existing configurations and parsers, the ranking field in `data.json` and internal site models remains named ``alexaRank`` and ``alexa_rank``.
|
||||||
|
|
||||||
|
**Mirrors and ``--top-sites``:** When you limit scans with ``--top-sites N``, Maigret also includes *mirror* sites (entries whose ``source`` field points at a parent platform such as Twitter or Instagram) if that parent would appear in the Majestic Million top *N* when disabled sites are considered for ranking. See the **Mirrors** paragraph under ``--top-sites`` in :doc:`command-line-options`.
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
-------
|
-------
|
||||||
@@ -114,6 +120,8 @@ There are few options for sites data.json helpful in various cases:
|
|||||||
- ``headers`` - a dictionary of additional headers to be sent to the site
|
- ``headers`` - a dictionary of additional headers to be sent to the site
|
||||||
- ``requestHeadOnly`` - set to ``true`` if it's enough to make a HEAD request to the site
|
- ``requestHeadOnly`` - set to ``true`` if it's enough to make a HEAD request to the site
|
||||||
- ``regexCheck`` - a regex to check if the username is valid, in case of frequent false-positives
|
- ``regexCheck`` - a regex to check if the username is valid, in case of frequent false-positives
|
||||||
|
- ``requestMethod`` - set the HTTP method to use (e.g., ``POST``). By default, Maigret natively defaults to GET or HEAD.
|
||||||
|
- ``requestPayload`` - a dictionary with the JSON payload to send for POST requests (e.g., ``{"username": "{username}"}``), extremely useful for parsing GraphQL or modern JSON APIs.
|
||||||
|
|
||||||
``urlProbe`` (optional profile probe URL)
|
``urlProbe`` (optional profile probe URL)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|||||||
@@ -23,3 +23,19 @@ Usage
|
|||||||
``--tags coding`` -- search on sites related to software development.
|
``--tags coding`` -- search on sites related to software development.
|
||||||
|
|
||||||
``--tags ucoz`` -- search on uCoz sites only (mostly CIS countries)
|
``--tags ucoz`` -- search on uCoz sites only (mostly CIS countries)
|
||||||
|
|
||||||
|
Blacklisting (excluding) tags
|
||||||
|
------------------------------
|
||||||
|
You can exclude sites with certain tags from the search using ``--exclude-tags``:
|
||||||
|
|
||||||
|
``--exclude-tags porn,dating`` -- skip all sites tagged with ``porn`` or ``dating``.
|
||||||
|
|
||||||
|
``--exclude-tags ru`` -- skip all Russian sites.
|
||||||
|
|
||||||
|
You can combine ``--tags`` and ``--exclude-tags`` to fine-tune your search:
|
||||||
|
|
||||||
|
``--tags forum --exclude-tags ru`` -- search on forum sites, but skip Russian ones.
|
||||||
|
|
||||||
|
In the web interface, the tag cloud supports three states per tag:
|
||||||
|
click once to **include** (green), click again to **exclude** (dark/strikethrough),
|
||||||
|
and click once more to return to **neutral** (red).
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ Use Cases
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
|
|
||||||
1. Search for accounts with username ``machine42`` on top 500 sites (by default, according to Alexa rank) from the Maigret DB.
|
1. Search for accounts with username ``machine42`` on top 500 sites (by default, according to Majestic Million rank) from the Maigret DB.
|
||||||
|
|
||||||
.. code-block:: console
|
.. code-block:: console
|
||||||
|
|
||||||
|
|||||||
+50
-12
@@ -61,30 +61,49 @@ class SimpleAiohttpChecker(CheckerBase):
|
|||||||
self.headers = None
|
self.headers = None
|
||||||
self.allow_redirects = True
|
self.allow_redirects = True
|
||||||
self.timeout = 0
|
self.timeout = 0
|
||||||
|
self.allow_redirects = True
|
||||||
|
self.timeout = 0
|
||||||
self.method = 'get'
|
self.method = 'get'
|
||||||
|
self.payload = None
|
||||||
|
|
||||||
def prepare(self, url, headers=None, allow_redirects=True, timeout=0, method='get'):
|
def prepare(self, url, headers=None, allow_redirects=True, timeout=0, method='get', payload=None):
|
||||||
self.url = url
|
self.url = url
|
||||||
self.headers = headers
|
self.headers = headers
|
||||||
self.allow_redirects = allow_redirects
|
self.allow_redirects = allow_redirects
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
self.method = method
|
self.method = method
|
||||||
|
self.payload = payload
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
async def _make_request(
|
async def _make_request(
|
||||||
self, session, url, headers, allow_redirects, timeout, method, logger
|
self, session, url, headers, allow_redirects, timeout, method, logger, payload=None
|
||||||
) -> Tuple[str, int, Optional[CheckError]]:
|
) -> Tuple[str, int, Optional[CheckError]]:
|
||||||
try:
|
try:
|
||||||
request_method = session.get if method == 'get' else session.head
|
if method.lower() == 'get':
|
||||||
async with request_method(
|
request_method = session.get
|
||||||
url=url,
|
elif method.lower() == 'post':
|
||||||
headers=headers,
|
request_method = session.post
|
||||||
allow_redirects=allow_redirects,
|
elif method.lower() == 'head':
|
||||||
timeout=timeout,
|
request_method = session.head
|
||||||
) as response:
|
else:
|
||||||
|
request_method = session.get
|
||||||
|
|
||||||
|
kwargs = {
|
||||||
|
'url': url,
|
||||||
|
'headers': headers,
|
||||||
|
'allow_redirects': allow_redirects,
|
||||||
|
'timeout': timeout,
|
||||||
|
}
|
||||||
|
if payload and method.lower() == 'post':
|
||||||
|
if headers and headers.get('Content-Type') == 'application/x-www-form-urlencoded':
|
||||||
|
kwargs['data'] = payload
|
||||||
|
else:
|
||||||
|
kwargs['json'] = payload
|
||||||
|
|
||||||
|
async with request_method(**kwargs) as response:
|
||||||
status_code = response.status
|
status_code = response.status
|
||||||
response_content = await response.content.read()
|
response_content = await response.content.read()
|
||||||
charset = response.charset or "utf-8"
|
charset = response.charset or "utf-8"
|
||||||
@@ -141,6 +160,7 @@ class SimpleAiohttpChecker(CheckerBase):
|
|||||||
self.timeout,
|
self.timeout,
|
||||||
self.method,
|
self.method,
|
||||||
self.logger,
|
self.logger,
|
||||||
|
self.payload,
|
||||||
)
|
)
|
||||||
|
|
||||||
if error and str(error) == "Invalid proxy response":
|
if error and str(error) == "Invalid proxy response":
|
||||||
@@ -165,7 +185,7 @@ class AiodnsDomainResolver(CheckerBase):
|
|||||||
self.logger = kwargs.get('logger', Mock())
|
self.logger = kwargs.get('logger', Mock())
|
||||||
self.resolver = aiodns.DNSResolver(loop=loop)
|
self.resolver = aiodns.DNSResolver(loop=loop)
|
||||||
|
|
||||||
def prepare(self, url, headers=None, allow_redirects=True, timeout=0, method='get'):
|
def prepare(self, url, headers=None, allow_redirects=True, timeout=0, method='get', payload=None):
|
||||||
self.url = url
|
self.url = url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -191,7 +211,7 @@ class CheckerMock:
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def prepare(self, url, headers=None, allow_redirects=True, timeout=0, method='get'):
|
def prepare(self, url, headers=None, allow_redirects=True, timeout=0, method='get', payload=None):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def check(self) -> Tuple[str, int, Optional[CheckError]]:
|
async def check(self) -> Tuple[str, int, Optional[CheckError]]:
|
||||||
@@ -220,6 +240,11 @@ def detect_error_page(
|
|||||||
if status_code == 403 and not ignore_403:
|
if status_code == 403 and not ignore_403:
|
||||||
return CheckError("Access denied", "403 status code, use proxy/vpn")
|
return CheckError("Access denied", "403 status code, use proxy/vpn")
|
||||||
|
|
||||||
|
elif status_code == 999:
|
||||||
|
# LinkedIn anti-bot / HTTP 999 workaround. It shouldn't trigger an infrastructure
|
||||||
|
# Server Error because it represents a valid "Not Found / Blocked" state for the username.
|
||||||
|
pass
|
||||||
|
|
||||||
elif status_code >= 500:
|
elif status_code >= 500:
|
||||||
return CheckError("Server", f"{status_code} status code")
|
return CheckError("Server", f"{status_code} status code")
|
||||||
|
|
||||||
@@ -494,7 +519,9 @@ def make_site_result(
|
|||||||
for k, v in site.get_params.items():
|
for k, v in site.get_params.items():
|
||||||
url_probe += f"&{k}={v}"
|
url_probe += f"&{k}={v}"
|
||||||
|
|
||||||
if site.check_type == "status_code" and site.request_head_only:
|
if site.request_method:
|
||||||
|
request_method = site.request_method.lower()
|
||||||
|
elif site.check_type == "status_code" and site.request_head_only:
|
||||||
# In most cases when we are detecting by status code,
|
# In most cases when we are detecting by status code,
|
||||||
# it is not necessary to get the entire body: we can
|
# it is not necessary to get the entire body: we can
|
||||||
# detect fine with just the HEAD response.
|
# detect fine with just the HEAD response.
|
||||||
@@ -505,6 +532,15 @@ def make_site_result(
|
|||||||
# not respond properly unless we request the whole page.
|
# not respond properly unless we request the whole page.
|
||||||
request_method = 'get'
|
request_method = 'get'
|
||||||
|
|
||||||
|
payload = None
|
||||||
|
if site.request_payload:
|
||||||
|
payload = {}
|
||||||
|
for k, v in site.request_payload.items():
|
||||||
|
if isinstance(v, str):
|
||||||
|
payload[k] = v.format(username=username)
|
||||||
|
else:
|
||||||
|
payload[k] = v
|
||||||
|
|
||||||
if site.check_type == "response_url":
|
if site.check_type == "response_url":
|
||||||
# Site forwards request to a different URL if username not
|
# Site forwards request to a different URL if username not
|
||||||
# found. Disallow the redirect so we can capture the
|
# found. Disallow the redirect so we can capture the
|
||||||
@@ -521,6 +557,7 @@ def make_site_result(
|
|||||||
headers=headers,
|
headers=headers,
|
||||||
allow_redirects=allow_redirects,
|
allow_redirects=allow_redirects,
|
||||||
timeout=options['timeout'],
|
timeout=options['timeout'],
|
||||||
|
payload=payload,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Store future request object in the results object
|
# Store future request object in the results object
|
||||||
@@ -577,6 +614,7 @@ async def check_site_for_username(
|
|||||||
allow_redirects=checker.allow_redirects,
|
allow_redirects=checker.allow_redirects,
|
||||||
timeout=checker.timeout,
|
timeout=checker.timeout,
|
||||||
method=checker.method,
|
method=checker.method,
|
||||||
|
payload=getattr(checker, 'payload', None),
|
||||||
)
|
)
|
||||||
response = await checker.check()
|
response = await checker.check()
|
||||||
|
|
||||||
|
|||||||
@@ -277,6 +277,12 @@ def setup_arguments_parser(settings: Settings):
|
|||||||
filter_group.add_argument(
|
filter_group.add_argument(
|
||||||
"--tags", dest="tags", default='', help="Specify tags of sites (see `--stats`)."
|
"--tags", dest="tags", default='', help="Specify tags of sites (see `--stats`)."
|
||||||
)
|
)
|
||||||
|
filter_group.add_argument(
|
||||||
|
"--exclude-tags",
|
||||||
|
dest="exclude_tags",
|
||||||
|
default='',
|
||||||
|
help="Specify tags to exclude from search (blacklist).",
|
||||||
|
)
|
||||||
filter_group.add_argument(
|
filter_group.add_argument(
|
||||||
"--site",
|
"--site",
|
||||||
action="append",
|
action="append",
|
||||||
@@ -532,6 +538,11 @@ async def main():
|
|||||||
if args.tags:
|
if args.tags:
|
||||||
args.tags = list(set(str(args.tags).split(',')))
|
args.tags = list(set(str(args.tags).split(',')))
|
||||||
|
|
||||||
|
if args.exclude_tags:
|
||||||
|
args.exclude_tags = list(set(str(args.exclude_tags).split(',')))
|
||||||
|
else:
|
||||||
|
args.exclude_tags = []
|
||||||
|
|
||||||
db_file = args.db_file \
|
db_file = args.db_file \
|
||||||
if (args.db_file.startswith("http://") or args.db_file.startswith("https://")) \
|
if (args.db_file.startswith("http://") or args.db_file.startswith("https://")) \
|
||||||
else path.join(path.dirname(path.realpath(__file__)), args.db_file)
|
else path.join(path.dirname(path.realpath(__file__)), args.db_file)
|
||||||
@@ -553,6 +564,7 @@ async def main():
|
|||||||
get_top_sites_for_id = lambda x: db.ranked_sites_dict(
|
get_top_sites_for_id = lambda x: db.ranked_sites_dict(
|
||||||
top=args.top_sites,
|
top=args.top_sites,
|
||||||
tags=args.tags,
|
tags=args.tags,
|
||||||
|
excluded_tags=args.exclude_tags,
|
||||||
names=args.site_list,
|
names=args.site_list,
|
||||||
disabled=args.use_disabled_sites,
|
disabled=args.use_disabled_sites,
|
||||||
id_type=x,
|
id_type=x,
|
||||||
|
|||||||
+24606
-24370
File diff suppressed because it is too large
Load Diff
+28
-1
@@ -65,6 +65,10 @@ class MaigretSite:
|
|||||||
url_probe = None
|
url_probe = None
|
||||||
# Type of check to perform
|
# Type of check to perform
|
||||||
check_type = ""
|
check_type = ""
|
||||||
|
# HTTP request method (GET, POST, HEAD, etc.)
|
||||||
|
request_method = ""
|
||||||
|
# HTTP request payload (for POST, PUT, etc.)
|
||||||
|
request_payload: Dict[str, Any] = {}
|
||||||
# Whether to only send HEAD requests (GET by default)
|
# Whether to only send HEAD requests (GET by default)
|
||||||
request_head_only = ""
|
request_head_only = ""
|
||||||
# GET parameters to include in requests
|
# GET parameters to include in requests
|
||||||
@@ -137,6 +141,8 @@ class MaigretSite:
|
|||||||
'regex_check',
|
'regex_check',
|
||||||
'url_probe',
|
'url_probe',
|
||||||
'check_type',
|
'check_type',
|
||||||
|
'request_method',
|
||||||
|
'request_payload',
|
||||||
'request_head_only',
|
'request_head_only',
|
||||||
'get_params',
|
'get_params',
|
||||||
'presense_strs',
|
'presense_strs',
|
||||||
@@ -318,6 +324,7 @@ class MaigretDatabase:
|
|||||||
reverse=False,
|
reverse=False,
|
||||||
top=sys.maxsize,
|
top=sys.maxsize,
|
||||||
tags=[],
|
tags=[],
|
||||||
|
excluded_tags=[],
|
||||||
names=[],
|
names=[],
|
||||||
disabled=True,
|
disabled=True,
|
||||||
id_type="username",
|
id_type="username",
|
||||||
@@ -336,7 +343,8 @@ class MaigretDatabase:
|
|||||||
Args:
|
Args:
|
||||||
reverse (bool, optional): Reverse the sorting order. Defaults to False.
|
reverse (bool, optional): Reverse the sorting order. Defaults to False.
|
||||||
top (int, optional): Maximum number of sites to return. Defaults to sys.maxsize.
|
top (int, optional): Maximum number of sites to return. Defaults to sys.maxsize.
|
||||||
tags (list, optional): List of tags to filter sites by. Defaults to empty list.
|
tags (list, optional): List of tags to filter sites by (whitelist). Defaults to empty list.
|
||||||
|
excluded_tags (list, optional): List of tags to exclude sites by (blacklist). Defaults to empty list.
|
||||||
names (list, optional): List of site names (or urls, see MaigretSite.__eq__) to filter by. Defaults to empty list.
|
names (list, optional): List of site names (or urls, see MaigretSite.__eq__) to filter by. Defaults to empty list.
|
||||||
disabled (bool, optional): Whether to include disabled sites. Defaults to True.
|
disabled (bool, optional): Whether to include disabled sites. Defaults to True.
|
||||||
id_type (str, optional): Type of identifier to filter by. Defaults to "username".
|
id_type (str, optional): Type of identifier to filter by. Defaults to "username".
|
||||||
@@ -347,6 +355,7 @@ class MaigretDatabase:
|
|||||||
"""
|
"""
|
||||||
normalized_names = list(map(str.lower, names))
|
normalized_names = list(map(str.lower, names))
|
||||||
normalized_tags = list(map(str.lower, tags))
|
normalized_tags = list(map(str.lower, tags))
|
||||||
|
normalized_excluded_tags = list(map(str.lower, excluded_tags))
|
||||||
|
|
||||||
is_name_ok = lambda x: x.name.lower() in normalized_names
|
is_name_ok = lambda x: x.name.lower() in normalized_names
|
||||||
is_source_ok = lambda x: x.source and x.source.lower() in normalized_names
|
is_source_ok = lambda x: x.source and x.source.lower() in normalized_names
|
||||||
@@ -360,6 +369,22 @@ class MaigretDatabase:
|
|||||||
)
|
)
|
||||||
is_id_type_ok = lambda x: x.type == id_type
|
is_id_type_ok = lambda x: x.type == id_type
|
||||||
|
|
||||||
|
is_excluded_by_tag = lambda x: set(
|
||||||
|
map(str.lower, x.tags)
|
||||||
|
).intersection(set(normalized_excluded_tags))
|
||||||
|
is_excluded_by_engine = lambda x: (
|
||||||
|
isinstance(x.engine, str)
|
||||||
|
and x.engine.lower() in normalized_excluded_tags
|
||||||
|
)
|
||||||
|
is_excluded_by_protocol = lambda x: (
|
||||||
|
x.protocol and x.protocol in normalized_excluded_tags
|
||||||
|
)
|
||||||
|
is_not_excluded = lambda x: not excluded_tags or not (
|
||||||
|
is_excluded_by_tag(x)
|
||||||
|
or is_excluded_by_engine(x)
|
||||||
|
or is_excluded_by_protocol(x)
|
||||||
|
)
|
||||||
|
|
||||||
filter_tags_engines_fun = (
|
filter_tags_engines_fun = (
|
||||||
lambda x: not tags
|
lambda x: not tags
|
||||||
or is_engine_ok(x)
|
or is_engine_ok(x)
|
||||||
@@ -370,6 +395,7 @@ class MaigretDatabase:
|
|||||||
|
|
||||||
filter_fun = (
|
filter_fun = (
|
||||||
lambda x: filter_tags_engines_fun(x)
|
lambda x: filter_tags_engines_fun(x)
|
||||||
|
and is_not_excluded(x)
|
||||||
and filter_names_fun(x)
|
and filter_names_fun(x)
|
||||||
and is_disabled_needed(x)
|
and is_disabled_needed(x)
|
||||||
and is_id_type_ok(x)
|
and is_id_type_ok(x)
|
||||||
@@ -387,6 +413,7 @@ class MaigretDatabase:
|
|||||||
if top < sys.maxsize and sorted_list:
|
if top < sys.maxsize and sorted_list:
|
||||||
filter_fun_ranking_parents = (
|
filter_fun_ranking_parents = (
|
||||||
lambda x: filter_tags_engines_fun(x)
|
lambda x: filter_tags_engines_fun(x)
|
||||||
|
and is_not_excluded(x)
|
||||||
and filter_names_fun(x)
|
and filter_names_fun(x)
|
||||||
and is_id_type_ok(x)
|
and is_id_type_ok(x)
|
||||||
)
|
)
|
||||||
|
|||||||
+14
-4
@@ -409,8 +409,13 @@ class Submitter:
|
|||||||
self.logger.info('Domain is %s', domain_raw)
|
self.logger.info('Domain is %s', domain_raw)
|
||||||
|
|
||||||
# check for existence
|
# check for existence
|
||||||
|
domain_re = re.compile(
|
||||||
|
r'://(www\.)?' + re.escape(domain_raw) + r'(/|$)'
|
||||||
|
)
|
||||||
matched_sites = list(
|
matched_sites = list(
|
||||||
filter(lambda x: domain_raw in x.url_main + x.url, self.db.sites)
|
filter(
|
||||||
|
lambda x: domain_re.search(x.url_main + x.url), self.db.sites
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if matched_sites:
|
if matched_sites:
|
||||||
@@ -448,9 +453,14 @@ class Submitter:
|
|||||||
old_site = next(
|
old_site = next(
|
||||||
(site for site in matched_sites if site.name == site_name), None
|
(site for site in matched_sites if site.name == site_name), None
|
||||||
)
|
)
|
||||||
print(
|
if old_site is None:
|
||||||
f'{Fore.GREEN}[+] We will update site "{old_site.name}" in case of success.{Style.RESET_ALL}'
|
print(
|
||||||
)
|
f'{Fore.RED}[!] Site "{site_name}" not found in the matched list. Proceeding without updating an existing site.{Style.RESET_ALL}'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f'{Fore.GREEN}[+] We will update site "{old_site.name}" in case of success.{Style.RESET_ALL}'
|
||||||
|
)
|
||||||
|
|
||||||
# Check if the site check is ordinary or not
|
# Check if the site check is ordinary or not
|
||||||
if old_site and (old_site.url_probe or old_site.activation):
|
if old_site and (old_site.url_probe or old_site.activation):
|
||||||
|
|||||||
+7
-3
@@ -49,12 +49,14 @@ async def maigret_search(username, options):
|
|||||||
top_sites = 999999999 # effectively all
|
top_sites = 999999999 # effectively all
|
||||||
|
|
||||||
tags = options.get('tags', [])
|
tags = options.get('tags', [])
|
||||||
|
excluded_tags = options.get('excluded_tags', [])
|
||||||
site_list = options.get('site_list', [])
|
site_list = options.get('site_list', [])
|
||||||
logger.info(f"Filtering sites by tags: {tags}")
|
logger.info(f"Filtering sites by tags: {tags}, excluded: {excluded_tags}")
|
||||||
|
|
||||||
sites = db.ranked_sites_dict(
|
sites = db.ranked_sites_dict(
|
||||||
top=top_sites,
|
top=top_sites,
|
||||||
tags=tags,
|
tags=tags,
|
||||||
|
excluded_tags=excluded_tags,
|
||||||
names=site_list,
|
names=site_list,
|
||||||
disabled=False,
|
disabled=False,
|
||||||
id_type='username',
|
id_type='username',
|
||||||
@@ -225,7 +227,8 @@ def search():
|
|||||||
|
|
||||||
# Get selected tags - ensure it's a list
|
# Get selected tags - ensure it's a list
|
||||||
selected_tags = request.form.getlist('tags')
|
selected_tags = request.form.getlist('tags')
|
||||||
logging.info(f"Selected tags: {selected_tags}")
|
excluded_tags = request.form.getlist('excluded_tags')
|
||||||
|
logging.info(f"Selected tags: {selected_tags}, Excluded tags: {excluded_tags}")
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
'top_sites': request.form.get('top_sites') or '500',
|
'top_sites': request.form.get('top_sites') or '500',
|
||||||
@@ -240,13 +243,14 @@ def search():
|
|||||||
'i2p_proxy': request.form.get('i2p_proxy', None) or None,
|
'i2p_proxy': request.form.get('i2p_proxy', None) or None,
|
||||||
'permute': 'permute' in request.form,
|
'permute': 'permute' in request.form,
|
||||||
'tags': selected_tags, # Pass selected tags as a list
|
'tags': selected_tags, # Pass selected tags as a list
|
||||||
|
'excluded_tags': excluded_tags, # Pass excluded tags as a list
|
||||||
'site_list': [
|
'site_list': [
|
||||||
s.strip() for s in request.form.get('site', '').split(',') if s.strip()
|
s.strip() for s in request.form.get('site', '').split(',') if s.strip()
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
logging.info(
|
logging.info(
|
||||||
f"Starting search for usernames: {usernames} with tags: {selected_tags}"
|
f"Starting search for usernames: {usernames} with tags: {selected_tags}, excluded: {excluded_tags}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Start background job
|
# Start background job
|
||||||
|
|||||||
@@ -28,6 +28,11 @@
|
|||||||
background-color: #28a745;
|
background-color: #28a745;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tag.excluded {
|
||||||
|
background-color: #343a40;
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
.tag:hover {
|
.tag:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||||
@@ -168,7 +173,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Tags (click to select)</label>
|
<label class="form-label">Tags (click to cycle: include → exclude → neutral)</label>
|
||||||
|
<div class="mb-2">
|
||||||
|
<small class="text-muted">
|
||||||
|
<span style="display:inline-block;width:12px;height:12px;background:#28a745;border-radius:50%;"></span> Included (whitelist)
|
||||||
|
|
||||||
|
<span style="display:inline-block;width:12px;height:12px;background:#343a40;border-radius:50%;"></span> Excluded (blacklist)
|
||||||
|
|
||||||
|
<span style="display:inline-block;width:12px;height:12px;background:#dc3545;border-radius:50%;"></span> Neutral
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
<div class="tag-cloud" id="tagCloud"></div>
|
<div class="tag-cloud" id="tagCloud"></div>
|
||||||
<select multiple class="hidden-select" id="tags" name="tags">
|
<select multiple class="hidden-select" id="tags" name="tags">
|
||||||
<option value="gaming">Gaming</option>
|
<option value="gaming">Gaming</option>
|
||||||
@@ -230,6 +244,89 @@
|
|||||||
<option value="q&a">Q&A</option>
|
<option value="q&a">Q&A</option>
|
||||||
<option value="crypto">Crypto</option>
|
<option value="crypto">Crypto</option>
|
||||||
<option value="ai">AI</option>
|
<option value="ai">AI</option>
|
||||||
|
<!-- Country tags -->
|
||||||
|
<option value="ae" data-group="country">AE - United Arab Emirates</option>
|
||||||
|
<option value="ao" data-group="country">AO - Angola</option>
|
||||||
|
<option value="ar" data-group="country">AR - Argentina</option>
|
||||||
|
<option value="at" data-group="country">AT - Austria</option>
|
||||||
|
<option value="au" data-group="country">AU - Australia</option>
|
||||||
|
<option value="az" data-group="country">AZ - Azerbaijan</option>
|
||||||
|
<option value="bd" data-group="country">BD - Bangladesh</option>
|
||||||
|
<option value="be" data-group="country">BE - Belgium</option>
|
||||||
|
<option value="bg" data-group="country">BG - Bulgaria</option>
|
||||||
|
<option value="br" data-group="country">BR - Brazil</option>
|
||||||
|
<option value="by" data-group="country">BY - Belarus</option>
|
||||||
|
<option value="ca" data-group="country">CA - Canada</option>
|
||||||
|
<option value="ch" data-group="country">CH - Switzerland</option>
|
||||||
|
<option value="cl" data-group="country">CL - Chile</option>
|
||||||
|
<option value="cn" data-group="country">CN - China</option>
|
||||||
|
<option value="co" data-group="country">CO - Colombia</option>
|
||||||
|
<option value="cr" data-group="country">CR - Costa Rica</option>
|
||||||
|
<option value="cz" data-group="country">CZ - Czechia</option>
|
||||||
|
<option value="de" data-group="country">DE - Germany</option>
|
||||||
|
<option value="dk" data-group="country">DK - Denmark</option>
|
||||||
|
<option value="dz" data-group="country">DZ - Algeria</option>
|
||||||
|
<option value="ee" data-group="country">EE - Estonia</option>
|
||||||
|
<option value="eg" data-group="country">EG - Egypt</option>
|
||||||
|
<option value="es" data-group="country">ES - Spain</option>
|
||||||
|
<option value="eu" data-group="country">EU - European Union</option>
|
||||||
|
<option value="fi" data-group="country">FI - Finland</option>
|
||||||
|
<option value="fr" data-group="country">FR - France</option>
|
||||||
|
<option value="gb" data-group="country">GB - United Kingdom</option>
|
||||||
|
<option value="global" data-group="country">🌍 Global</option>
|
||||||
|
<option value="gr" data-group="country">GR - Greece</option>
|
||||||
|
<option value="hk" data-group="country">HK - Hong Kong</option>
|
||||||
|
<option value="hr" data-group="country">HR - Croatia</option>
|
||||||
|
<option value="hu" data-group="country">HU - Hungary</option>
|
||||||
|
<option value="id" data-group="country">ID - Indonesia</option>
|
||||||
|
<option value="ie" data-group="country">IE - Ireland</option>
|
||||||
|
<option value="il" data-group="country">IL - Israel</option>
|
||||||
|
<option value="in" data-group="country">IN - India</option>
|
||||||
|
<option value="ir" data-group="country">IR - Iran</option>
|
||||||
|
<option value="it" data-group="country">IT - Italy</option>
|
||||||
|
<option value="jp" data-group="country">JP - Japan</option>
|
||||||
|
<option value="kg" data-group="country">KG - Kyrgyzstan</option>
|
||||||
|
<option value="kr" data-group="country">KR - Korea</option>
|
||||||
|
<option value="kz" data-group="country">KZ - Kazakhstan</option>
|
||||||
|
<option value="la" data-group="country">LA - Laos</option>
|
||||||
|
<option value="lk" data-group="country">LK - Sri Lanka</option>
|
||||||
|
<option value="lt" data-group="country">LT - Lithuania</option>
|
||||||
|
<option value="ma" data-group="country">MA - Morocco</option>
|
||||||
|
<option value="md" data-group="country">MD - Moldova</option>
|
||||||
|
<option value="mg" data-group="country">MG - Madagascar</option>
|
||||||
|
<option value="mk" data-group="country">MK - North Macedonia</option>
|
||||||
|
<option value="mx" data-group="country">MX - Mexico</option>
|
||||||
|
<option value="ng" data-group="country">NG - Nigeria</option>
|
||||||
|
<option value="nl" data-group="country">NL - Netherlands</option>
|
||||||
|
<option value="no" data-group="country">NO - Norway</option>
|
||||||
|
<option value="ph" data-group="country">PH - Philippines</option>
|
||||||
|
<option value="pk" data-group="country">PK - Pakistan</option>
|
||||||
|
<option value="pl" data-group="country">PL - Poland</option>
|
||||||
|
<option value="pt" data-group="country">PT - Portugal</option>
|
||||||
|
<option value="re" data-group="country">RE - Réunion</option>
|
||||||
|
<option value="ro" data-group="country">RO - Romania</option>
|
||||||
|
<option value="rs" data-group="country">RS - Serbia</option>
|
||||||
|
<option value="ru" data-group="country">RU - Russia</option>
|
||||||
|
<option value="sa" data-group="country">SA - Saudi Arabia</option>
|
||||||
|
<option value="sd" data-group="country">SD - Sudan</option>
|
||||||
|
<option value="se" data-group="country">SE - Sweden</option>
|
||||||
|
<option value="sg" data-group="country">SG - Singapore</option>
|
||||||
|
<option value="sk" data-group="country">SK - Slovakia</option>
|
||||||
|
<option value="sv" data-group="country">SV - El Salvador</option>
|
||||||
|
<option value="th" data-group="country">TH - Thailand</option>
|
||||||
|
<option value="tn" data-group="country">TN - Tunisia</option>
|
||||||
|
<option value="tr" data-group="country">TR - Türkiye</option>
|
||||||
|
<option value="tw" data-group="country">TW - Taiwan</option>
|
||||||
|
<option value="ua" data-group="country">UA - Ukraine</option>
|
||||||
|
<option value="uk" data-group="country">UK - United Kingdom</option>
|
||||||
|
<option value="us" data-group="country">US - United States</option>
|
||||||
|
<option value="uz" data-group="country">UZ - Uzbekistan</option>
|
||||||
|
<option value="ve" data-group="country">VE - Venezuela</option>
|
||||||
|
<option value="vi" data-group="country">VI - Virgin Islands</option>
|
||||||
|
<option value="vn" data-group="country">VN - Viet Nam</option>
|
||||||
|
<option value="za" data-group="country">ZA - South Africa</option>
|
||||||
|
</select>
|
||||||
|
<select multiple class="hidden-select" id="excludedTags" name="excluded_tags">
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -292,26 +389,66 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
// Tag cloud functionality
|
// Tag cloud functionality with include/exclude (whitelist/blacklist) support
|
||||||
const tagCloud = document.getElementById('tagCloud');
|
const tagCloud = document.getElementById('tagCloud');
|
||||||
const hiddenSelect = document.getElementById('tags');
|
const hiddenSelect = document.getElementById('tags');
|
||||||
|
const excludedSelect = document.getElementById('excludedTags');
|
||||||
const allTags = Array.from(hiddenSelect.options).map(opt => ({
|
const allTags = Array.from(hiddenSelect.options).map(opt => ({
|
||||||
value: opt.value,
|
value: opt.value,
|
||||||
label: opt.text
|
label: opt.text,
|
||||||
|
group: opt.dataset.group || 'category'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
function updateTagSelects() {
|
||||||
|
// Clear and repopulate hidden selects based on tag states
|
||||||
|
Array.from(hiddenSelect.options).forEach(opt => opt.selected = false);
|
||||||
|
// Clear excluded select
|
||||||
|
excludedSelect.innerHTML = '';
|
||||||
|
|
||||||
|
document.querySelectorAll('#tagCloud .tag').forEach(tagEl => {
|
||||||
|
const val = tagEl.dataset.value;
|
||||||
|
if (tagEl.classList.contains('selected')) {
|
||||||
|
const option = Array.from(hiddenSelect.options).find(opt => opt.value === val);
|
||||||
|
if (option) option.selected = true;
|
||||||
|
} else if (tagEl.classList.contains('excluded')) {
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.value = val;
|
||||||
|
opt.selected = true;
|
||||||
|
excludedSelect.appendChild(opt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastGroup = '';
|
||||||
allTags.forEach(tag => {
|
allTags.forEach(tag => {
|
||||||
|
if (tag.group !== lastGroup && tag.group === 'country') {
|
||||||
|
const separator = document.createElement('div');
|
||||||
|
separator.style.cssText = 'width:100%;margin:8px 0 4px;padding:4px 0;border-top:1px solid rgba(0,0,0,0.15);font-size:13px;color:#666;';
|
||||||
|
separator.textContent = 'Countries';
|
||||||
|
tagCloud.appendChild(separator);
|
||||||
|
}
|
||||||
|
lastGroup = tag.group;
|
||||||
|
|
||||||
const tagElement = document.createElement('span');
|
const tagElement = document.createElement('span');
|
||||||
tagElement.className = 'tag';
|
tagElement.className = 'tag';
|
||||||
tagElement.textContent = tag.label;
|
tagElement.textContent = tag.label;
|
||||||
tagElement.dataset.value = tag.value;
|
tagElement.dataset.value = tag.value;
|
||||||
|
|
||||||
tagElement.addEventListener('click', function () {
|
// Single click cycles: neutral -> included -> excluded -> neutral
|
||||||
const isSelected = this.classList.toggle('selected');
|
tagElement.addEventListener('click', function (e) {
|
||||||
const option = Array.from(hiddenSelect.options).find(opt => opt.value === tag.value);
|
e.preventDefault();
|
||||||
if (option) {
|
if (this.classList.contains('selected')) {
|
||||||
option.selected = isSelected;
|
// included -> excluded
|
||||||
|
this.classList.remove('selected');
|
||||||
|
this.classList.add('excluded');
|
||||||
|
} else if (this.classList.contains('excluded')) {
|
||||||
|
// excluded -> neutral
|
||||||
|
this.classList.remove('excluded');
|
||||||
|
} else {
|
||||||
|
// neutral -> included
|
||||||
|
this.classList.add('selected');
|
||||||
}
|
}
|
||||||
|
updateTagSelects();
|
||||||
});
|
});
|
||||||
|
|
||||||
tagCloud.appendChild(tagElement);
|
tagCloud.appendChild(tagElement);
|
||||||
|
|||||||
Generated
+259
-232
@@ -14,18 +14,18 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiodns"
|
name = "aiodns"
|
||||||
version = "3.5.0"
|
version = "4.0.0"
|
||||||
description = "Simple DNS resolver for asyncio"
|
description = "Simple DNS resolver for asyncio"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.10"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "aiodns-3.5.0-py3-none-any.whl", hash = "sha256:6d0404f7d5215849233f6ee44854f2bb2481adf71b336b2279016ea5990ca5c5"},
|
{file = "aiodns-4.0.0-py3-none-any.whl", hash = "sha256:a188a75fb8b2b7862ac8f84811a231402fb74f5b4e6f10766dc8a4544b0cf989"},
|
||||||
{file = "aiodns-3.5.0.tar.gz", hash = "sha256:11264edbab51896ecf546c18eb0dd56dff0428c6aa6d2cd87e643e07300eb310"},
|
{file = "aiodns-4.0.0.tar.gz", hash = "sha256:17be26a936ba788c849ba5fd20e0ba69d8c46e6273e846eb5430eae2630ce5b1"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pycares = ">=4.9.0"
|
pycares = ">=5.0.0,<6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohappyeyeballs"
|
name = "aiohappyeyeballs"
|
||||||
@@ -184,14 +184,14 @@ speedups = ["Brotli (>=1.2) ; platform_python_implementation == \"CPython\"", "a
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aiohttp-socks"
|
name = "aiohttp-socks"
|
||||||
version = "0.10.1"
|
version = "0.11.0"
|
||||||
description = "Proxy connector for aiohttp"
|
description = "Proxy connector for aiohttp"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.8.0"
|
python-versions = ">=3.8.0"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "aiohttp_socks-0.10.1-py3-none-any.whl", hash = "sha256:6fd4d46c09f952f971a011ff446170daab8d539cf5310c0627f8423df2fb15ea"},
|
{file = "aiohttp_socks-0.11.0-py3-none-any.whl", hash = "sha256:9aacce57c931b8fbf8f6d333cf3cafe4c35b971b35430309e167a35a8aab9ec1"},
|
||||||
{file = "aiohttp_socks-0.10.1.tar.gz", hash = "sha256:49f2e1f8051f2885719beb1b77e312b5a27c3e4b60f0b045a388f194d995e068"},
|
{file = "aiohttp_socks-0.11.0.tar.gz", hash = "sha256:0afe51638527c79077e4bd6e57052c87c4824233d6e20bb061c53766421b10f0"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -305,14 +305,14 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "attrs"
|
name = "attrs"
|
||||||
version = "25.4.0"
|
version = "26.1.0"
|
||||||
description = "Classes Without Boilerplate"
|
description = "Classes Without Boilerplate"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"},
|
{file = "attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309"},
|
||||||
{file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"},
|
{file = "attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -417,14 +417,14 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2025.11.12"
|
version = "2026.2.25"
|
||||||
description = "Python package for providing Mozilla's CA Bundle."
|
description = "Python package for providing Mozilla's CA Bundle."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "certifi-2025.11.12-py3-none-any.whl", hash = "sha256:97de8790030bbd5c2d96b7ec782fc2f7820ef8dba6db909ccf95449f2d062d4b"},
|
{file = "certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa"},
|
||||||
{file = "certifi-2025.11.12.tar.gz", hash = "sha256:d8ab5478f2ecd78af242878415affce761ca6bc54a22a27e026d7c25357c3316"},
|
{file = "certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -697,104 +697,118 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "coverage"
|
name = "coverage"
|
||||||
version = "7.12.0"
|
version = "7.13.5"
|
||||||
description = "Code coverage measurement for Python"
|
description = "Code coverage measurement for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.10"
|
python-versions = ">=3.10"
|
||||||
groups = ["dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "coverage-7.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:32b75c2ba3f324ee37af3ccee5b30458038c50b349ad9b88cee85096132a575b"},
|
{file = "coverage-7.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0723d2c96324561b9aa76fb982406e11d93cdb388a7a7da2b16e04719cf7ca5"},
|
||||||
{file = "coverage-7.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cb2a1b6ab9fe833714a483a915de350abc624a37149649297624c8d57add089c"},
|
{file = "coverage-7.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52f444e86475992506b32d4e5ca55c24fc88d73bcbda0e9745095b28ef4dc0cf"},
|
||||||
{file = "coverage-7.12.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5734b5d913c3755e72f70bf6cc37a0518d4f4745cde760c5d8e12005e62f9832"},
|
{file = "coverage-7.13.5-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:704de6328e3d612a8f6c07000a878ff38181ec3263d5a11da1db294fa6a9bdf8"},
|
||||||
{file = "coverage-7.12.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b527a08cdf15753279b7afb2339a12073620b761d79b81cbe2cdebdb43d90daa"},
|
{file = "coverage-7.13.5-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a1a6d79a14e1ec1832cabc833898636ad5f3754a678ef8bb4908515208bf84f4"},
|
||||||
{file = "coverage-7.12.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9bb44c889fb68004e94cab71f6a021ec83eac9aeabdbb5a5a88821ec46e1da73"},
|
{file = "coverage-7.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79060214983769c7ba3f0cee10b54c97609dca4d478fa1aa32b914480fd5738d"},
|
||||||
{file = "coverage-7.12.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4b59b501455535e2e5dde5881739897967b272ba25988c89145c12d772810ccb"},
|
{file = "coverage-7.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:356e76b46783a98c2a2fe81ec79df4883a1e62895ea952968fb253c114e7f930"},
|
||||||
{file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d8842f17095b9868a05837b7b1b73495293091bed870e099521ada176aa3e00e"},
|
{file = "coverage-7.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cef0cdec915d11254a7f549c1170afecce708d30610c6abdded1f74e581666d"},
|
||||||
{file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c5a6f20bf48b8866095c6820641e7ffbe23f2ac84a2efc218d91235e404c7777"},
|
{file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dc022073d063b25a402454e5712ef9e007113e3a676b96c5f29b2bda29352f40"},
|
||||||
{file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:5f3738279524e988d9da2893f307c2093815c623f8d05a8f79e3eff3a7a9e553"},
|
{file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9b74db26dfea4f4e50d48a4602207cd1e78be33182bc9cbf22da94f332f99878"},
|
||||||
{file = "coverage-7.12.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0d68c1f7eabbc8abe582d11fa393ea483caf4f44b0af86881174769f185c94d"},
|
{file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ad146744ca4fd09b50c482650e3c1b1f4dfa1d4792e0a04a369c7f23336f0400"},
|
||||||
{file = "coverage-7.12.0-cp310-cp310-win32.whl", hash = "sha256:7670d860e18b1e3ee5930b17a7d55ae6287ec6e55d9799982aa103a2cc1fa2ef"},
|
{file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c555b48be1853fe3997c11c4bd521cdd9a9612352de01fa4508f16ec341e6fe0"},
|
||||||
{file = "coverage-7.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:f999813dddeb2a56aab5841e687b68169da0d3f6fc78ccf50952fa2463746022"},
|
{file = "coverage-7.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7034b5c56a58ae5e85f23949d52c14aca2cfc6848a31764995b7de88f13a1ea0"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa124a3683d2af98bd9d9c2bfa7a5076ca7e5ab09fdb96b81fa7d89376ae928f"},
|
{file = "coverage-7.13.5-cp310-cp310-win32.whl", hash = "sha256:eb7fdf1ef130660e7415e0253a01a7d5a88c9c4d158bcf75cbbd922fd65a5b58"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d93fbf446c31c0140208dcd07c5d882029832e8ed7891a39d6d44bd65f2316c3"},
|
{file = "coverage-7.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:3e1bb5f6c78feeb1be3475789b14a0f0a5b47d505bfc7267126ccbd50289999e"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:52ca620260bd8cd6027317bdd8b8ba929be1d741764ee765b42c4d79a408601e"},
|
{file = "coverage-7.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66a80c616f80181f4d643b0f9e709d97bcea413ecd9631e1dedc7401c8e6695d"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f3433ffd541380f3a0e423cff0f4926d55b0cc8c1d160fdc3be24a4c03aa65f7"},
|
{file = "coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7bbb321d4adc9f65e402c677cd1c8e4c2d0105d3ce285b51b4d87f1d5db5245"},
|
{file = "coverage-7.13.5-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0672854dc733c342fa3e957e0605256d2bf5934feeac328da9e0b5449634a642"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22a7aade354a72dff3b59c577bfd18d6945c61f97393bc5fb7bd293a4237024b"},
|
{file = "coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3ff651dcd36d2fea66877cd4a82de478004c59b849945446acb5baf9379a1b64"},
|
{file = "coverage-7.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be3d4bbad9d4b037791794ddeedd7d64a56f5933a2c1373e18e9e568b9141686"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:31b8b2e38391a56e3cea39d22a23faaa7c3fc911751756ef6d2621d2a9daf742"},
|
{file = "coverage-7.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d2afbc5cc54d286bfb54541aa50b64cdb07a718227168c87b9e2fb8f25e1743"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:297bc2da28440f5ae51c845a47c8175a4db0553a53827886e4fb25c66633000c"},
|
{file = "coverage-7.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3ad050321264c49c2fa67bb599100456fc51d004b82534f379d16445da40fb75"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6ff7651cc01a246908eac162a6a86fc0dbab6de1ad165dfb9a1e2ec660b44984"},
|
{file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7300c8a6d13335b29bb76d7651c66af6bd8658517c43499f110ddc6717bfc209"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-win32.whl", hash = "sha256:313672140638b6ddb2c6455ddeda41c6a0b208298034544cfca138978c6baed6"},
|
{file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:eb07647a5738b89baab047f14edd18ded523de60f3b30e75c2acc826f79c839a"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1783ed5bd0d5938d4435014626568dc7f93e3cb99bc59188cc18857c47aa3c4"},
|
{file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:9adb6688e3b53adffefd4a52d72cbd8b02602bfb8f74dcd862337182fd4d1a4e"},
|
||||||
{file = "coverage-7.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:4648158fd8dd9381b5847622df1c90ff314efbfc1df4550092ab6013c238a5fc"},
|
{file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7c8d4bc913dd70b93488d6c496c77f3aff5ea99a07e36a18f865bca55adef8bd"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:29644c928772c78512b48e14156b81255000dcfd4817574ff69def189bcb3647"},
|
{file = "coverage-7.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0e3c426ffc4cd952f54ee9ffbdd10345709ecc78a3ecfd796a57236bfad0b9b8"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8638cbb002eaa5d7c8d04da667813ce1067080b9a91099801a0053086e52b736"},
|
{file = "coverage-7.13.5-cp311-cp311-win32.whl", hash = "sha256:259b69bb83ad9894c4b25be2528139eecba9a82646ebdda2d9db1ba28424a6bf"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:083631eeff5eb9992c923e14b810a179798bb598e6a0dd60586819fc23be6e60"},
|
{file = "coverage-7.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99d5415c73ca12d558e07776bd957c4222c687b9f1d26fa0e1b57e3598bdcde8"},
|
{file = "coverage-7.13.5-cp311-cp311-win_arm64.whl", hash = "sha256:bff95879c33ec8da99fc9b6fe345ddb5be6414b41d6d1ad1c8f188d26f36e028"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e949ebf60c717c3df63adb4a1a366c096c8d7fd8472608cd09359e1bd48ef59f"},
|
{file = "coverage-7.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:460cf0114c5016fa841214ff5564aa4864f11948da9440bc97e21ad1f4ba1e01"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6d907ddccbca819afa2cd014bc69983b146cca2735a0b1e6259b2a6c10be1e70"},
|
{file = "coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b1518ecbad4e6173f4c6e6c4a46e49555ea5679bf3feda5edb1b935c7c44e8a0"},
|
{file = "coverage-7.13.5-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6e3370441f4513c6252bf042b9c36d22491142385049243253c7e48398a15a9f"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:51777647a749abdf6f6fd8c7cffab12de68ab93aab15efc72fbbb83036c2a068"},
|
{file = "coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:42435d46d6461a3b305cdfcad7cdd3248787771f53fe18305548cba474e6523b"},
|
{file = "coverage-7.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f4818d065964db3c1c66dc0fbdac5ac692ecbc875555e13374fdbe7eedb4376"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5bcead88c8423e1855e64b8057d0544e33e4080b95b240c2a355334bb7ced937"},
|
{file = "coverage-7.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:012d5319e66e9d5a218834642d6c35d265515a62f01157a45bcc036ecf947256"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-win32.whl", hash = "sha256:dcbb630ab034e86d2a0f79aefd2be07e583202f41e037602d438c80044957baa"},
|
{file = "coverage-7.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8dd02af98971bdb956363e4827d34425cb3df19ee550ef92855b0acb9c7ce51c"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:2fd8354ed5d69775ac42986a691fbf68b4084278710cee9d7c3eaa0c28fa982a"},
|
{file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f08fd75c50a760c7eb068ae823777268daaf16a80b918fa58eea888f8e3919f5"},
|
||||||
{file = "coverage-7.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:737c3814903be30695b2de20d22bcc5428fdae305c61ba44cdc8b3252984c49c"},
|
{file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:843ea8643cf967d1ac7e8ecd4bb00c99135adf4816c0c0593fdcc47b597fcf09"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:47324fffca8d8eae7e185b5bb20c14645f23350f870c1649003618ea91a78941"},
|
{file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9d44d7aa963820b1b971dbecd90bfe5fe8f81cff79787eb6cca15750bd2f79b9"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ccf3b2ede91decd2fb53ec73c1f949c3e034129d1e0b07798ff1d02ea0c8fa4a"},
|
{file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:7132bed4bd7b836200c591410ae7d97bf7ae8be6fc87d160b2bd881df929e7bf"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:b365adc70a6936c6b0582dc38746b33b2454148c02349345412c6e743efb646d"},
|
{file = "coverage-7.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a698e363641b98843c517817db75373c83254781426e94ada3197cabbc2c919c"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bc13baf85cd8a4cfcf4a35c7bc9d795837ad809775f782f697bf630b7e200211"},
|
{file = "coverage-7.13.5-cp312-cp312-win32.whl", hash = "sha256:bdba0a6b8812e8c7df002d908a9a2ea3c36e92611b5708633c50869e6d922fdf"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:099d11698385d572ceafb3288a5b80fe1fc58bf665b3f9d362389de488361d3d"},
|
{file = "coverage-7.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:473dc45d69694069adb7680c405fb1e81f60b2aff42c81e2f2c3feaf544d878c"},
|
{file = "coverage-7.13.5-cp312-cp312-win_arm64.whl", hash = "sha256:bf69236a9a81bdca3bff53796237aab096cdbf8d78a66ad61e992d9dac7eb2de"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:583f9adbefd278e9de33c33d6846aa8f5d164fa49b47144180a0e037f0688bb9"},
|
{file = "coverage-7.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5ec4af212df513e399cf11610cc27063f1586419e814755ab362e50a85ea69c1"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2089cc445f2dc0af6f801f0d1355c025b76c24481935303cf1af28f636688f0"},
|
{file = "coverage-7.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:941617e518602e2d64942c88ec8499f7fbd49d3f6c4327d3a71d43a1973032f3"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:950411f1eb5d579999c5f66c62a40961f126fc71e5e14419f004471957b51508"},
|
{file = "coverage-7.13.5-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:da305e9937617ee95c2e39d8ff9f040e0487cbf1ac174f777ed5eddd7a7c1f26"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b1aab7302a87bafebfe76b12af681b56ff446dc6f32ed178ff9c092ca776e6bc"},
|
{file = "coverage-7.13.5-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:78e696e1cc714e57e8b25760b33a8b1026b7048d270140d25dafe1b0a1ee05a3"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-win32.whl", hash = "sha256:d7e0d0303c13b54db495eb636bc2465b2fb8475d4c8bcec8fe4b5ca454dfbae8"},
|
{file = "coverage-7.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02ca0eed225b2ff301c474aeeeae27d26e2537942aa0f87491d3e147e784a82b"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:ce61969812d6a98a981d147d9ac583a36ac7db7766f2e64a9d4d059c2fe29d07"},
|
{file = "coverage-7.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:04690832cbea4e4663d9149e05dba142546ca05cb1848816760e7f58285c970a"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bcec6f47e4cb8a4c2dc91ce507f6eefc6a1b10f58df32cdc61dff65455031dfc"},
|
{file = "coverage-7.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0590e44dd2745c696a778f7bab6aa95256de2cbc8b8cff4f7db8ff09813d6969"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:459443346509476170d553035e4a3eed7b860f4fe5242f02de1010501956ce87"},
|
{file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d7cfad2d6d81dd298ab6b89fe72c3b7b05ec7544bdda3b707ddaecff8d25c161"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04a79245ab2b7a61688958f7a855275997134bc84f4a03bc240cf64ff132abf6"},
|
{file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e092b9499de38ae0fbfbc603a74660eb6ff3e869e507b50d85a13b6db9863e15"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:09a86acaaa8455f13d6a99221d9654df249b33937b4e212b4e5a822065f12aa7"},
|
{file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:48c39bc4a04d983a54a705a6389512883d4a3b9862991b3617d547940e9f52b1"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:907e0df1b71ba77463687a74149c6122c3f6aac56c2510a5d906b2f368208560"},
|
{file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2d3807015f138ffea1ed9afeeb8624fd781703f2858b62a8dd8da5a0994c57b6"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b57e2d0ddd5f0582bae5437c04ee71c46cd908e7bc5d4d0391f9a41e812dd12"},
|
{file = "coverage-7.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ee2aa19e03161671ec964004fb74b2257805d9710bf14a5c704558b9d8dbaf17"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:58c1c6aa677f3a1411fe6fb28ec3a942e4f665df036a3608816e0847fad23296"},
|
{file = "coverage-7.13.5-cp313-cp313-win32.whl", hash = "sha256:ce1998c0483007608c8382f4ff50164bfc5bd07a2246dd272aa4043b75e61e85"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4c589361263ab2953e3c4cd2a94db94c4ad4a8e572776ecfbad2389c626e4507"},
|
{file = "coverage-7.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:631efb83f01569670a5e866ceb80fe483e7c159fac6f167e6571522636104a0b"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:91b810a163ccad2e43b1faa11d70d3cf4b6f3d83f9fd5f2df82a32d47b648e0d"},
|
{file = "coverage-7.13.5-cp313-cp313-win_arm64.whl", hash = "sha256:f4cd16206ad171cbc2470dbea9103cf9a7607d5fe8c242fdf1edf36174020664"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:40c867af715f22592e0d0fb533a33a71ec9e0f73a6945f722a0c85c8c1cbe3a2"},
|
{file = "coverage-7.13.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0428cbef5783ad91fe240f673cc1f76b25e74bbfe1a13115e4aa30d3f538162d"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:68b0d0a2d84f333de875666259dadf28cc67858bc8fd8b3f1eae84d3c2bec455"},
|
{file = "coverage-7.13.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e0b216a19534b2427cc201a26c25da4a48633f29a487c61258643e89d28200c0"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-win32.whl", hash = "sha256:73f9e7fbd51a221818fd11b7090eaa835a353ddd59c236c57b2199486b116c6d"},
|
{file = "coverage-7.13.5-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:972a9cd27894afe4bc2b1480107054e062df08e671df7c2f18c205e805ccd806"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:24cff9d1f5743f67db7ba46ff284018a6e9aeb649b67aa1e70c396aa1b7cb23c"},
|
{file = "coverage-7.13.5-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b59148601efcd2bac8c4dbf1f0ad6391693ccf7a74b8205781751637076aee3"},
|
||||||
{file = "coverage-7.12.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c87395744f5c77c866d0f5a43d97cc39e17c7f1cb0115e54a2fe67ca75c5d14d"},
|
{file = "coverage-7.13.5-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:505d7083c8b0c87a8fa8c07370c285847c1f77739b22e299ad75a6af6c32c5c9"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a1c59b7dc169809a88b21a936eccf71c3895a78f5592051b1af8f4d59c2b4f92"},
|
{file = "coverage-7.13.5-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:60365289c3741e4db327e7baff2a4aaacf22f788e80fa4683393891b70a89fbd"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8787b0f982e020adb732b9f051f3e49dd5054cebbc3f3432061278512a2b1360"},
|
{file = "coverage-7.13.5-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1b88c69c8ef5d4b6fe7dea66d6636056a0f6a7527c440e890cf9259011f5e606"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5ea5a9f7dc8877455b13dd1effd3202e0bca72f6f3ab09f9036b1bcf728f69ac"},
|
{file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5b13955d31d1633cf9376908089b7cebe7d15ddad7aeaabcbe969a595a97e95e"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fdba9f15849534594f60b47c9a30bc70409b54947319a7c4fd0e8e3d8d2f355d"},
|
{file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:f70c9ab2595c56f81a89620e22899eea8b212a4041bd728ac6f4a28bf5d3ddd0"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a00594770eb715854fb1c57e0dea08cce6720cfbc531accdb9850d7c7770396c"},
|
{file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:084b84a8c63e8d6fc7e3931b316a9bcafca1458d753c539db82d31ed20091a87"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5560c7e0d82b42eb1951e4f68f071f8017c824ebfd5a6ebe42c60ac16c6c2434"},
|
{file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:ad14385487393e386e2ea988b09d62dd42c397662ac2dabc3832d71253eee479"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d6c2e26b481c9159c2773a37947a9718cfdc58893029cdfb177531793e375cfc"},
|
{file = "coverage-7.13.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:7f2c47b36fe7709a6e83bfadf4eefb90bd25fbe4014d715224c4316f808e59a2"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6e1a8c066dabcde56d5d9fed6a66bc19a2883a3fe051f0c397a41fc42aedd4cc"},
|
{file = "coverage-7.13.5-cp313-cp313t-win32.whl", hash = "sha256:67e9bc5449801fad0e5dff329499fb090ba4c5800b86805c80617b4e29809b2a"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f7ba9da4726e446d8dd8aae5a6cd872511184a5d861de80a86ef970b5dacce3e"},
|
{file = "coverage-7.13.5-cp313-cp313t-win_amd64.whl", hash = "sha256:da86cdcf10d2519e10cabb8ac2de03da1bcb6e4853790b7fbd48523332e3a819"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e0f483ab4f749039894abaf80c2f9e7ed77bbf3c737517fb88c8e8e305896a17"},
|
{file = "coverage-7.13.5-cp313-cp313t-win_arm64.whl", hash = "sha256:0ecf12ecb326fe2c339d93fc131816f3a7367d223db37817208905c89bded911"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-win32.whl", hash = "sha256:76336c19a9ef4a94b2f8dc79f8ac2da3f193f625bb5d6f51a328cd19bfc19933"},
|
{file = "coverage-7.13.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fbabfaceaeb587e16f7008f7795cd80d20ec548dc7f94fbb0d4ec2e038ce563f"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7c1059b600aec6ef090721f8f633f60ed70afaffe8ecab85b59df748f24b31fe"},
|
{file = "coverage-7.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9bb2a28101a443669a423b665939381084412b81c3f8c0fcfbac57f4e30b5b8e"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314-win_arm64.whl", hash = "sha256:172cf3a34bfef42611963e2b661302a8931f44df31629e5b1050567d6b90287d"},
|
{file = "coverage-7.13.5-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:bd3a2fbc1c6cccb3c5106140d87cc6a8715110373ef42b63cf5aea29df8c217a"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:aa7d48520a32cb21c7a9b31f81799e8eaec7239db36c3b670be0fa2403828d1d"},
|
{file = "coverage-7.13.5-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6c36ddb64ed9d7e496028d1d00dfec3e428e0aabf4006583bb1839958d280510"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:90d58ac63bc85e0fb919f14d09d6caa63f35a5512a2205284b7816cafd21bb03"},
|
{file = "coverage-7.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380e8e9084d8eb38db3a9176a1a4f3c0082c3806fa0dc882d1d87abc3c789247"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca8ecfa283764fdda3eae1bdb6afe58bf78c2c3ec2b2edcb05a671f0bba7b3f9"},
|
{file = "coverage-7.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e808af52a0513762df4d945ea164a24b37f2f518cbe97e03deaa0ee66139b4d6"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:874fe69a0785d96bd066059cd4368022cebbec1a8958f224f0016979183916e6"},
|
{file = "coverage-7.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e301d30dd7e95ae068671d746ba8c34e945a82682e62918e41b2679acd2051a0"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3c889c0b8b283a24d721a9eabc8ccafcfc3aebf167e4cd0d0e23bf8ec4e339"},
|
{file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:800bc829053c80d240a687ceeb927a94fd108bbdc68dfbe505d0d75ab578a882"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8bb5b894b3ec09dcd6d3743229dc7f2c42ef7787dc40596ae04c0edda487371e"},
|
{file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:0b67af5492adb31940ee418a5a655c28e48165da5afab8c7fa6fd72a142f8740"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:79a44421cd5fba96aa57b5e3b5a4d3274c449d4c622e8f76882d76635501fd13"},
|
{file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c9136ff29c3a91e25b1d1552b5308e53a1e0653a23e53b6366d7c2dcbbaf8a16"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:33baadc0efd5c7294f436a632566ccc1f72c867f82833eb59820ee37dc811c6f"},
|
{file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:cff784eef7f0b8f6cb28804fbddcfa99f89efe4cc35fb5627e3ac58f91ed3ac0"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c406a71f544800ef7e9e0000af706b88465f3573ae8b8de37e5f96c59f689ad1"},
|
{file = "coverage-7.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:68a4953be99b17ac3c23b6efbc8a38330d99680c9458927491d18700ef23ded0"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e71bba6a40883b00c6d571599b4627f50c360b3d0d02bfc658168936be74027b"},
|
{file = "coverage-7.13.5-cp314-cp314-win32.whl", hash = "sha256:35a31f2b1578185fbe6aa2e74cea1b1d0bbf4c552774247d9160d29b80ed56cc"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-win32.whl", hash = "sha256:9157a5e233c40ce6613dead4c131a006adfda70e557b6856b97aceed01b0e27a"},
|
{file = "coverage-7.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e84da3a0fd233aeec797b981c51af1cabac74f9bd67be42458365b30d11b5291"},
|
{file = "coverage-7.13.5-cp314-cp314-win_arm64.whl", hash = "sha256:1b11eef33edeae9d142f9b4358edb76273b3bfd30bc3df9a4f95d0e49caf94e8"},
|
||||||
{file = "coverage-7.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:01d24af36fedda51c2b1aca56e4330a3710f83b02a5ff3743a6b015ffa7c9384"},
|
{file = "coverage-7.13.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10a0c37f0b646eaff7cce1874c31d1f1ccb297688d4c747291f4f4c70741cc8b"},
|
||||||
{file = "coverage-7.12.0-py3-none-any.whl", hash = "sha256:159d50c0b12e060b15ed3d39f87ed43d4f7f7ad40b8a534f4dd331adbb51104a"},
|
{file = "coverage-7.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b5db73ba3c41c7008037fa731ad5459fc3944cb7452fc0aa9f822ad3533c583c"},
|
||||||
{file = "coverage-7.12.0.tar.gz", hash = "sha256:fc11e0a4e372cb5f282f16ef90d4a585034050ccda536451901abfb19a57f40c"},
|
{file = "coverage-7.13.5-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:750db93a81e3e5a9831b534be7b1229df848b2e125a604fe6651e48aa070e5f9"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9ddb4f4a5479f2539644be484da179b653273bca1a323947d48ab107b3ed1f29"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8a7a2049c14f413163e2bdabd37e41179b1d1ccb10ffc6ccc4b7a718429c607"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1c85e0b6c05c592ea6d8768a66a254bfb3874b53774b12d4c89c481eb78cb90"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:777c4d1eff1b67876139d24288aaf1817f6c03d6bae9c5cc8d27b83bcfe38fe3"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6697e29b93707167687543480a40f0db8f356e86d9f67ddf2e37e2dfd91a9dab"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8fdf453a942c3e4d99bd80088141c4c6960bb232c409d9c3558e2dbaa3998562"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:32ca0c0114c9834a43f045a87dcebd69d108d8ffb666957ea65aa132f50332e2"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:8769751c10f339021e2638cd354e13adeac54004d1941119b2c96fe5276d45ea"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cec2d83125531bd153175354055cdb7a09987af08a9430bd173c937c6d0fba2a"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-win32.whl", hash = "sha256:0cd9ed7a8b181775459296e402ca4fb27db1279740a24e93b3b41942ebe4b215"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:301e3b7dfefecaca37c9f1aa6f0049b7d4ab8dd933742b607765d757aca77d43"},
|
||||||
|
{file = "coverage-7.13.5-cp314-cp314t-win_arm64.whl", hash = "sha256:9dacc2ad679b292709e0f5fc1ac74a6d4d5562e424058962c7bb0c658ad25e45"},
|
||||||
|
{file = "coverage-7.13.5-py3-none-any.whl", hash = "sha256:34b02417cf070e173989b3db962f7ed56d2f644307b2cf9d5a0f258e13084a61"},
|
||||||
|
{file = "coverage-7.13.5.tar.gz", hash = "sha256:c81f6515c4c40141f83f502b07bbfa5c240ba25bbe73da7b33f1e5b6120ff179"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -1310,6 +1324,7 @@ description = "Mypyc runtime library"
|
|||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["dev"]
|
groups = ["dev"]
|
||||||
|
markers = "platform_python_implementation != \"PyPy\""
|
||||||
files = [
|
files = [
|
||||||
{file = "librt-0.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:45660d26569cc22ed30adf583389d8a0d1b468f8b5e518fcf9bfe2cd298f9dd1"},
|
{file = "librt-0.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:45660d26569cc22ed30adf583389d8a0d1b468f8b5e518fcf9bfe2cd298f9dd1"},
|
||||||
{file = "librt-0.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:54f3b2177fb892d47f8016f1087d21654b44f7fc4cf6571c1c6b3ea531ab0fcf"},
|
{file = "librt-0.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:54f3b2177fb892d47f8016f1087d21654b44f7fc4cf6571c1c6b3ea531ab0fcf"},
|
||||||
@@ -1849,54 +1864,54 @@ typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""}
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy"
|
name = "mypy"
|
||||||
version = "1.19.0"
|
version = "1.19.1"
|
||||||
description = "Optional static typing for Python"
|
description = "Optional static typing for Python"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "mypy-1.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6148ede033982a8c5ca1143de34c71836a09f105068aaa8b7d5edab2b053e6c8"},
|
{file = "mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec"},
|
||||||
{file = "mypy-1.19.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a9ac09e52bb0f7fb912f5d2a783345c72441a08ef56ce3e17c1752af36340a39"},
|
{file = "mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b"},
|
||||||
{file = "mypy-1.19.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f7254c15ab3f8ed68f8e8f5cbe88757848df793e31c36aaa4d4f9783fd08ab"},
|
{file = "mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6"},
|
||||||
{file = "mypy-1.19.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318ba74f75899b0e78b847d8c50821e4c9637c79d9a59680fc1259f29338cb3e"},
|
{file = "mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74"},
|
||||||
{file = "mypy-1.19.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf7d84f497f78b682edd407f14a7b6e1a2212b433eedb054e2081380b7395aa3"},
|
{file = "mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1"},
|
||||||
{file = "mypy-1.19.0-cp310-cp310-win_amd64.whl", hash = "sha256:c3385246593ac2b97f155a0e9639be906e73534630f663747c71908dfbf26134"},
|
{file = "mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac"},
|
||||||
{file = "mypy-1.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a31e4c28e8ddb042c84c5e977e28a21195d086aaffaf08b016b78e19c9ef8106"},
|
{file = "mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288"},
|
||||||
{file = "mypy-1.19.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34ec1ac66d31644f194b7c163d7f8b8434f1b49719d403a5d26c87fff7e913f7"},
|
{file = "mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab"},
|
||||||
{file = "mypy-1.19.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cb64b0ba5980466a0f3f9990d1c582bcab8db12e29815ecb57f1408d99b4bff7"},
|
{file = "mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6"},
|
||||||
{file = "mypy-1.19.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:120cffe120cca5c23c03c77f84abc0c14c5d2e03736f6c312480020082f1994b"},
|
{file = "mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331"},
|
||||||
{file = "mypy-1.19.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a500ab5c444268a70565e374fc803972bfd1f09545b13418a5174e29883dab7"},
|
{file = "mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925"},
|
||||||
{file = "mypy-1.19.0-cp311-cp311-win_amd64.whl", hash = "sha256:c14a98bc63fd867530e8ec82f217dae29d0550c86e70debc9667fff1ec83284e"},
|
{file = "mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042"},
|
||||||
{file = "mypy-1.19.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:0fb3115cb8fa7c5f887c8a8d81ccdcb94cff334684980d847e5a62e926910e1d"},
|
{file = "mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1"},
|
||||||
{file = "mypy-1.19.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3e19e3b897562276bb331074d64c076dbdd3e79213f36eed4e592272dabd760"},
|
{file = "mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e"},
|
||||||
{file = "mypy-1.19.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b9d491295825182fba01b6ffe2c6fe4e5a49dbf4e2bb4d1217b6ced3b4797bc6"},
|
{file = "mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2"},
|
||||||
{file = "mypy-1.19.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6016c52ab209919b46169651b362068f632efcd5eb8ef9d1735f6f86da7853b2"},
|
{file = "mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8"},
|
||||||
{file = "mypy-1.19.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f188dcf16483b3e59f9278c4ed939ec0254aa8a60e8fc100648d9ab5ee95a431"},
|
{file = "mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a"},
|
||||||
{file = "mypy-1.19.0-cp312-cp312-win_amd64.whl", hash = "sha256:0e3c3d1e1d62e678c339e7ade72746a9e0325de42cd2cccc51616c7b2ed1a018"},
|
{file = "mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13"},
|
||||||
{file = "mypy-1.19.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7686ed65dbabd24d20066f3115018d2dce030d8fa9db01aa9f0a59b6813e9f9e"},
|
{file = "mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250"},
|
||||||
{file = "mypy-1.19.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fd4a985b2e32f23bead72e2fb4bbe5d6aceee176be471243bd831d5b2644672d"},
|
{file = "mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b"},
|
||||||
{file = "mypy-1.19.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fc51a5b864f73a3a182584b1ac75c404396a17eced54341629d8bdcb644a5bba"},
|
{file = "mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e"},
|
||||||
{file = "mypy-1.19.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:37af5166f9475872034b56c5efdcf65ee25394e9e1d172907b84577120714364"},
|
{file = "mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef"},
|
||||||
{file = "mypy-1.19.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:510c014b722308c9bd377993bcbf9a07d7e0692e5fa8fc70e639c1eb19fc6bee"},
|
{file = "mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75"},
|
||||||
{file = "mypy-1.19.0-cp313-cp313-win_amd64.whl", hash = "sha256:cabbee74f29aa9cd3b444ec2f1e4fa5a9d0d746ce7567a6a609e224429781f53"},
|
{file = "mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd"},
|
||||||
{file = "mypy-1.19.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f2e36bed3c6d9b5f35d28b63ca4b727cb0228e480826ffc8953d1892ddc8999d"},
|
{file = "mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1"},
|
||||||
{file = "mypy-1.19.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a18d8abdda14035c5718acb748faec09571432811af129bf0d9e7b2d6699bf18"},
|
{file = "mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718"},
|
||||||
{file = "mypy-1.19.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75e60aca3723a23511948539b0d7ed514dda194bc3755eae0bfc7a6b4887aa7"},
|
{file = "mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b"},
|
||||||
{file = "mypy-1.19.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8f44f2ae3c58421ee05fe609160343c25f70e3967f6e32792b5a78006a9d850f"},
|
{file = "mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045"},
|
||||||
{file = "mypy-1.19.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:63ea6a00e4bd6822adbfc75b02ab3653a17c02c4347f5bb0cf1d5b9df3a05835"},
|
{file = "mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957"},
|
||||||
{file = "mypy-1.19.0-cp314-cp314-win_amd64.whl", hash = "sha256:3ad925b14a0bb99821ff6f734553294aa6a3440a8cb082fe1f5b84dfb662afb1"},
|
{file = "mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f"},
|
||||||
{file = "mypy-1.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0dde5cb375cb94deff0d4b548b993bec52859d1651e073d63a1386d392a95495"},
|
{file = "mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3"},
|
||||||
{file = "mypy-1.19.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1cf9c59398db1c68a134b0b5354a09a1e124523f00bacd68e553b8bd16ff3299"},
|
{file = "mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a"},
|
||||||
{file = "mypy-1.19.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3210d87b30e6af9c8faed61be2642fcbe60ef77cec64fa1ef810a630a4cf671c"},
|
{file = "mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67"},
|
||||||
{file = "mypy-1.19.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2c1101ab41d01303103ab6ef82cbbfedb81c1a060c868fa7cc013d573d37ab5"},
|
{file = "mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e"},
|
||||||
{file = "mypy-1.19.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ea4fd21bb48f0da49e6d3b37ef6bd7e8228b9fe41bbf4d80d9364d11adbd43c"},
|
{file = "mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376"},
|
||||||
{file = "mypy-1.19.0-cp39-cp39-win_amd64.whl", hash = "sha256:16f76ff3f3fd8137aadf593cb4607d82634fca675e8211ad75c43d86033ee6c6"},
|
{file = "mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24"},
|
||||||
{file = "mypy-1.19.0-py3-none-any.whl", hash = "sha256:0c01c99d626380752e527d5ce8e69ffbba2046eb8a060db0329690849cf9b6f9"},
|
{file = "mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247"},
|
||||||
{file = "mypy-1.19.0.tar.gz", hash = "sha256:f6b874ca77f733222641e5c46e4711648c4037ea13646fd0cdc814c2eaec2528"},
|
{file = "mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
librt = ">=0.6.2"
|
librt = {version = ">=0.6.2", markers = "platform_python_implementation != \"PyPy\""}
|
||||||
mypy_extensions = ">=1.0.0"
|
mypy_extensions = ">=1.0.0"
|
||||||
pathspec = ">=0.9.0"
|
pathspec = ">=0.9.0"
|
||||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||||
@@ -2324,92 +2339,104 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pycares"
|
name = "pycares"
|
||||||
version = "4.9.0"
|
version = "5.0.1"
|
||||||
description = "Python interface for c-ares"
|
description = "Python interface for c-ares"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["main"]
|
groups = ["main"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pycares-4.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b8bd9a3ee6e9bc990e1933dc7e7e2f44d4184f49a90fa444297ac12ab6c0c84"},
|
{file = "pycares-5.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:adc592534a10fe24fd1a801173c46769f75b97c440c9162f5d402ee1ba3eaf51"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:417a5c20861f35977240ad4961479a6778125bcac21eb2ad1c3aad47e2ff7fab"},
|
{file = "pycares-5.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8848bbea6b5c2a0f7c9d0231ee455c3ce976c5c85904e014b2e9aa636a34140e"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ab290faa4ea53ce53e3ceea1b3a42822daffce2d260005533293a52525076750"},
|
{file = "pycares-5.0.1-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5003cbbae0a943f49089cc149996c3d078cef482971d834535032d53558f4d48"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1df81193084c9717734e4615e8c5074b9852478c9007d1a8bb242f7f580e67"},
|
{file = "pycares-5.0.1-cp310-cp310-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc0cdeadb2892e7f0ab30b6a288a357441c21dcff2ce16e91fccbc9fae9d1e2a"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:20c7a6af0c2ccd17cc5a70d76e299a90e7ebd6c4d8a3d7fff5ae533339f61431"},
|
{file = "pycares-5.0.1-cp310-cp310-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:faa093af3bea365947325ec23ed24efe81dcb0efc1be7e19a36ba37108945237"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:370f41442a5b034aebdb2719b04ee04d3e805454a20d3f64f688c1c49f9137c3"},
|
{file = "pycares-5.0.1-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dedd6d41bd09dbed7eeea84a30b4b6fd1cacf9523a3047e088b5e692cff13d84"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:340e4a3bbfd14d73c01ec0793a321b8a4a93f64c508225883291078b7ee17ac8"},
|
{file = "pycares-5.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d3eb5e6ba290efd8b543a2cb77ad938c3494250e6e0302ee2aa55c06bbe153cd"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f0ec94785856ea4f5556aa18f4c027361ba4b26cb36c4ad97d2105ef4eec68ba"},
|
{file = "pycares-5.0.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:58634f83992c81f438987b572d371825dae187d3a09d6e213edbe71fbb4ba18c"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6b7e23a4a9e2039b5d67dfa0499d2d5f114667dc13fb5d7d03eed230c7ac4f"},
|
{file = "pycares-5.0.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fe9ce4361809903261c4b055284ba91d94adedfd2202e0257920b9085d505e37"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:490c978b0be9d35a253a5e31dd598f6d66b453625f0eb7dc2d81b22b8c3bb3f4"},
|
{file = "pycares-5.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:965ec648814829788233155ef3f6d4d7e7d6183460d10f9c71859c504f8f488b"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e433faaf07f44e44f1a1b839fee847480fe3db9431509dafc9f16d618d491d0f"},
|
{file = "pycares-5.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:171182baa32951fffd1568ba9b934a76f36ed86c6248855ec6f82bbb3954d604"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cf6d8851a06b79d10089962c9dadcb34dad00bf027af000f7102297a54aaff2e"},
|
{file = "pycares-5.0.1-cp310-cp310-win_arm64.whl", hash = "sha256:48ac858124728b8bac0591aa8361c683064fefe35794c29b3a954818c59f1e9b"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-win32.whl", hash = "sha256:4f803e7d66ac7d8342998b8b07393788991353a46b05bbaad0b253d6f3484ea8"},
|
{file = "pycares-5.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c29ca77ff9712e20787201ca8e76ad89384771c0e058a0a4f3dc05afbc4b32de"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:8e17bd32267e3870855de3baed7d0efa6337344d68f44853fd9195c919f39400"},
|
{file = "pycares-5.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f11424bf5cf6226d0b136ed47daa58434e377c61b62d0100d1de7793f8e34a72"},
|
||||||
{file = "pycares-4.9.0-cp310-cp310-win_arm64.whl", hash = "sha256:6b74f75d8e430f9bb11a1cc99b2e328eed74b17d8d4b476de09126f38d419eb9"},
|
{file = "pycares-5.0.1-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d765afb52d579879f5c4f005763827d3b1eb86b23139e9614e6089c9f98db017"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:16a97ee83ec60d35c7f716f117719932c27d428b1bb56b242ba1c4aa55521747"},
|
{file = "pycares-5.0.1-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ea0d57ba5add4bfbcc40cbdfa92bbb8a5ef0c4c21881e26c7229d9bdc92a4533"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:78748521423a211ce699a50c27cc5c19e98b7db610ccea98daad652ace373990"},
|
{file = "pycares-5.0.1-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ae9ec2aa3553d33e6220aeb1a05f4853fb83fce4cec3e0dea2dc970338ea47dc"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8818b2c7a57d9d6d41e8b64d9ff87992b8ea2522fc0799686725228bc3cff6c5"},
|
{file = "pycares-5.0.1-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5c63fb2498b05e9f5670a1bf3b900c5d09343b3b6d5001a9714d593f9eb54de1"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96df8990f16013ca5194d6ece19dddb4ef9cd7c3efaab9f196ec3ccd44b40f8d"},
|
{file = "pycares-5.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:71316f7a87c15a8d32127ff01374dc2c969c37410693cc0cf6532590b7f18e7a"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61af86fd58b8326e723b0d20fb96b56acaec2261c3a7c9a1c29d0a79659d613a"},
|
{file = "pycares-5.0.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a2117dffbb78615bfdb41ad77b17038689e4e01c66f153649e80d268c6228b4f"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec72edb276bda559813cc807bc47b423d409ffab2402417a5381077e9c2c6be1"},
|
{file = "pycares-5.0.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7d7c4f5d8b88b586ef2288142b806250020e6490b9f2bd8fd5f634a78fd20fcf"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:832fb122c7376c76cab62f8862fa5e398b9575fb7c9ff6bc9811086441ee64ca"},
|
{file = "pycares-5.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433b9a4b5a7e10ef8aef0b957e6cd0bfc1bb5bc730d2729f04e93c91c25979c0"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cdcfaef24f771a471671470ccfd676c0366ab6b0616fd8217b8f356c40a02b83"},
|
{file = "pycares-5.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:cf2699883b88713670d3f9c0a1e44ac24c70aeace9f8c6aa7f0b9f222d5b08a5"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:52cb056d06ff55d78a8665b97ae948abaaba2ca200ca59b10346d4526bce1e7d"},
|
{file = "pycares-5.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:9528dc11749e5e098c996475b60f879e1db5a6cb3dd0cdc747530620bb1a8941"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:54985ed3f2e8a87315269f24cb73441622857a7830adfc3a27c675a94c3261c1"},
|
{file = "pycares-5.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ee551be4f3f3ac814ac8547586c464c9035e914f5122a534d25de147fa745e1"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:08048e223615d4aef3dac81fe0ea18fb18d6fc97881f1eb5be95bb1379969b8d"},
|
{file = "pycares-5.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:252d4e5a52a68f825eaa90e16b595f9baee22c760f51e286ab612c6829b96de3"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cc60037421ce05a409484287b2cd428e1363cca73c999b5f119936bb8f255208"},
|
{file = "pycares-5.0.1-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c1aa549b8c2f2e224215c793d660270778dcba9abc3b85abbc7c41eabe4f1e5"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-win32.whl", hash = "sha256:62b86895b60cfb91befb3086caa0792b53f949231c6c0c3053c7dfee3f1386ab"},
|
{file = "pycares-5.0.1-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:db7c9c9f16e8311998667a7488e817f8cbeedec2447bac827c71804663f1437e"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:7046b3c80954beaabf2db52b09c3d6fe85f6c4646af973e61be79d1c51589932"},
|
{file = "pycares-5.0.1-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4b9c4c8bb69bab863f677fa166653bb872bfa5d5a742f1f30bebc2d53b6e71db"},
|
||||||
{file = "pycares-4.9.0-cp311-cp311-win_arm64.whl", hash = "sha256:fcbda3fdf44e94d3962ca74e6ba3dc18c0d7029106f030d61c04c0876f319403"},
|
{file = "pycares-5.0.1-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09ef90da8da3026fcba4ed223bd71e8057608d5b3fec4f5990b52ae1e8c855cc"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d68ca2da1001aeccdc81c4a2fb1f1f6cfdafd3d00e44e7c1ed93e3e05437f666"},
|
{file = "pycares-5.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ce193ebd54f4c74538b751ebb0923a9208c234ff180589d4d3cec134c001840e"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4f0c8fa5a384d79551a27eafa39eed29529e66ba8fa795ee432ab88d050432a3"},
|
{file = "pycares-5.0.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:36b9ff18ef231277f99a846feade50b417187a96f742689a9d08b9594e386de4"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0eb8c428cf3b9c6ff9c641ba50ab6357b4480cd737498733e6169b0ac8a1a89b"},
|
{file = "pycares-5.0.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5e40ea4a0ef0c01a02ef7f7390a58c62d237d5ad48d36bc3245e9c2ac181cc22"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6845bd4a43abf6dab7fedbf024ef458ac3750a25b25076ea9913e5ac5fec4548"},
|
{file = "pycares-5.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3f323b0ddfd2c7896af6fba4f8851d34d3d13387566aa573d93330fb01cb1038"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e28f4acc3b97e46610cf164665ebf914f709daea6ced0ca4358ce55bc1c3d6b"},
|
{file = "pycares-5.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:bdc6bcafb72a97b3cdd529fc87210e59e67feb647a7e138110656023599b84da"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9464a39861840ce35a79352c34d653a9db44f9333af7c9feddb97998d3e00c07"},
|
{file = "pycares-5.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:f8ef4c70c1edaf022875a8f9ff6c0c064f82831225acc91aa1b4f4d389e2e03a"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0611c1bd46d1fc6bdd9305b8850eb84c77df485769f72c574ed7b8389dfbee2"},
|
{file = "pycares-5.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7d1b2c6b152c65f14d0e12d741fabb78a487f0f0d22773eede8d8cfc97af612b"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4fb5a38a51d03b75ac4320357e632c2e72e03fdeb13263ee333a40621415fdc"},
|
{file = "pycares-5.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8c8ffcc9a48cfc296fe1aefc07d2c8e29a7f97e4bb366ce17effea6a38825f70"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:df5edae05fb3e1370ab7639e67e8891fdaa9026cb10f05dbd57893713f7a9cfe"},
|
{file = "pycares-5.0.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8efc38c2703e3530b823a4165a7b28d7ce0fdcf41960fb7a4ca834a0f8cfe79"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:397123ea53d261007bb0aa7e767ef238778f45026db40bed8196436da2cc73de"},
|
{file = "pycares-5.0.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e380bf6eff42c260f829a0a14547e13375e949053a966c23ca204a13647ef265"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bb0d874d0b131b29894fd8a0f842be91ac21d50f90ec04cff4bb3f598464b523"},
|
{file = "pycares-5.0.1-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:35dd5858ee1246bd092a212b5e85a8ef70853f7cfaf16b99569bf4af3ae4695d"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:497cc03a61ec1585eb17d2cb086a29a6a67d24babf1e9be519b47222916a3b06"},
|
{file = "pycares-5.0.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c257c6e7bf310cdb5823aa9d9a28f1e370fed8c653a968d38a954a8f8e0375ce"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-win32.whl", hash = "sha256:b46e46313fdb5e82da15478652aac0fd15e1c9f33e08153bad845aa4007d6f84"},
|
{file = "pycares-5.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:07711acb0ef75758f081fb7436acaccc91e8afd5ae34fd35d4edc44297e81f27"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:12547a06445777091605a7581da15a0da158058beb8a05a3ebbf7301fd1f58d4"},
|
{file = "pycares-5.0.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:30e5db1ae85cffb031dd8bc1b37903cd74c6d37eb737643bbca3ff2cd4bc6ae2"},
|
||||||
{file = "pycares-4.9.0-cp312-cp312-win_arm64.whl", hash = "sha256:f1e10bf1e8eb80b08e5c828627dba1ebc4acd54803bd0a27d92b9063b6aa99d8"},
|
{file = "pycares-5.0.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:efbe7f89425a14edbc94787042309be77cb3674415eb6079b356e1f9552ba747"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:574d815112a95ab09d75d0a9dc7dea737c06985e3125cf31c32ba6a3ed6ca006"},
|
{file = "pycares-5.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5de9e7ce52d638d78723c24704eb032e60b96fbb6fe90c6b3110882987251377"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50e5ab06361d59625a27a7ad93d27e067dc7c9f6aa529a07d691eb17f3b43605"},
|
{file = "pycares-5.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:0e99af0a1ce015ab6cc6bd85ce158d95ed89fb3b654515f1d0989d1afcf11026"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:785f5fd11ff40237d9bc8afa441551bb449e2812c74334d1d10859569e07515c"},
|
{file = "pycares-5.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:2a511c9f3b11b7ce9f159c956ea1b8f2de7f419d7ca9fa24528d582cb015dbf9"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e194a500e403eba89b91fb863c917495c5b3dfcd1ce0ee8dc3a6f99a1360e2fc"},
|
{file = "pycares-5.0.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e330e3561be259ad7a1b7b0ce282c872938625f76587fae7ac8d6bc5af1d0c3d"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:112dd49cdec4e6150a8d95b197e8b6b7b4468a3170b30738ed9b248cb2240c04"},
|
{file = "pycares-5.0.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82bd37fec2a3fa62add30d4a3854720f7b051386e2f18e6e8f4ee94b89b5a7b0"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94aa3c2f3eb0aa69160137134775501f06c901188e722aac63d2a210d4084f99"},
|
{file = "pycares-5.0.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:258c38aaa82ad1d565b4591cdb93d2c191be8e0a2c70926999c8e0b717a01f2a"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b510d71255cf5a92ccc2643a553548fcb0623d6ed11c8c633b421d99d7fa4167"},
|
{file = "pycares-5.0.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ccc1b2df8a09ca20eefbe20b9f7a484d376525c0fb173cfadd692320013c6bc5"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5c6aa30b1492b8130f7832bf95178642c710ce6b7ba610c2b17377f77177e3cd"},
|
{file = "pycares-5.0.1-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3c4dfc80cc8b43dc79e02a15486c58eead5cae0a40906d6be64e2522285b5b39"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:e5767988e044faffe2aff6a76aa08df99a8b6ef2641be8b00ea16334ce5dea93"},
|
{file = "pycares-5.0.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f498a6606247bfe896c2a4d837db711eb7b0ba23e409e16e4b23def4bada4b9d"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b9928a942820a82daa3207509eaba9e0fa9660756ac56667ec2e062815331fcb"},
|
{file = "pycares-5.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a7d197835cdb4b202a3b12562b32799e27bb132262d4aa1ac3ee9d440e8ec22c"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:556c854174da76d544714cdfab10745ed5d4b99eec5899f7b13988cd26ff4763"},
|
{file = "pycares-5.0.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f78ab823732b050d658eb735d553726663c9bccdeeee0653247533a23eb2e255"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d42e2202ca9aa9a0a9a6e43a4a4408bbe0311aaa44800fa27b8fd7f82b20152a"},
|
{file = "pycares-5.0.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f444ab7f318e9b2c209b45496fb07bff5e7ada606e15d5253a162964aa078527"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-win32.whl", hash = "sha256:cce8ef72c9ed4982c84114e6148a4e42e989d745de7862a0ad8b3f1cdc05def2"},
|
{file = "pycares-5.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9de80997de7538619b7dd28ec4371e5172e3f9480e4fc648726d3d5ba661ca05"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:318cdf24f826f1d2f0c5a988730bd597e1683296628c8f1be1a5b96643c284fe"},
|
{file = "pycares-5.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:206ce9f3cb9d51f5065c81b23c22996230fbc2cf58ae22834c623631b2b473aa"},
|
||||||
{file = "pycares-4.9.0-cp313-cp313-win_arm64.whl", hash = "sha256:faa9de8e647ed06757a2c117b70a7645a755561def814da6aca0d766cf71a402"},
|
{file = "pycares-5.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:45fb3b07231120e8cb5b75be7f15f16115003e9251991dc37a3e5c63733d63b5"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8310d27d68fa25be9781ce04d330f4860634a2ac34dd9265774b5f404679b41f"},
|
{file = "pycares-5.0.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:602f3eac4b880a2527d21f52b2319cb10fde9225d103d338c4d0b2b07f136849"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:99cf98452d3285307eec123049f2c9c50b109e06751b0727c6acefb6da30c6a0"},
|
{file = "pycares-5.0.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a1c3736deef003f0c57bc4e7f94d54270d0824350a8f5ceaba3a20b2ce8fb427"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ffd6e8c8250655504602b076f106653e085e6b1e15318013442558101aa4777"},
|
{file = "pycares-5.0.1-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e63328df86d37150ce697fb5d9313d1d468dd4dddee1d09342cb2ed241ce6ad9"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4065858d8c812159c9a55601fda73760d9e5e3300f7868d9e546eab1084f36c"},
|
{file = "pycares-5.0.1-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:57f6fd696213329d9a69b9664a68b1ff2a71ccbdc1fc928a42c9a92858c1ec5d"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91ee6818113faf9013945c2b54bcd6b123d0ac192ae3099cf4288cedaf2dbb25"},
|
{file = "pycares-5.0.1-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d0878edabfbecb48a29e8769284003d8dbc05936122fe361849cd5fa52722e0"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21f0602059ec11857ab7ad608c7ec8bc6f7a302c04559ec06d33e82f040585f8"},
|
{file = "pycares-5.0.1-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50e21f27a91be122e066ddd78c2d0d2769e547561481d8342a9d652a345b89f7"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e22e5b46ed9b12183091da56e4a5a20813b5436c4f13135d7a1c20a84027ca8a"},
|
{file = "pycares-5.0.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:97ceda969f5a5d5c6b15558b658c29e4301b3a2c4615523797b5f9d4ac74772e"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9eded8649867bfd7aea7589c5755eae4d37686272f6ed7a995da40890d02de71"},
|
{file = "pycares-5.0.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4d1713e602ab09882c3e65499b2cc763bff0371117327cad704cf524268c2604"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f71d31cbbe066657a2536c98aad850724a9ab7b1cd2624f491832ae9667ea8e7"},
|
{file = "pycares-5.0.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:954a379055d6c66b2e878b52235b382168d1a3230793ff44454019394aecac5e"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2b30945982ab4741f097efc5b0853051afc3c11df26996ed53a700c7575175af"},
|
{file = "pycares-5.0.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:145d8a20f7fd1d58a2e49b7ef4309ec9bdcab479ac65c2e49480e20d3f890c23"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54a8f1f067d64810426491d33033f5353b54f35e5339126440ad4e6afbf3f149"},
|
{file = "pycares-5.0.1-cp314-cp314t-win_amd64.whl", hash = "sha256:ebc9daba03c7ff3f62616c84c6cb37517445d15df00e1754852d6006039eb4a4"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:41556a269a192349e92eee953f62eddd867e9eddb27f444b261e2c1c4a4a9eff"},
|
{file = "pycares-5.0.1-cp314-cp314t-win_arm64.whl", hash = "sha256:e0a86eff6bf9e91d5dd8876b1b82ee45704f46b1104c24291d3dea2c1fc8ebcb"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-win32.whl", hash = "sha256:524d6c14eaa167ed098a4fe54856d1248fa20c296cdd6976f9c1b838ba32d014"},
|
{file = "pycares-5.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:89fbb801bd7328d38025ab3576eee697cf9eca1f45774a0353b6a68a867e5516"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:15f930c733d36aa487b4ad60413013bd811281b5ea4ca620070fa38505d84df4"},
|
{file = "pycares-5.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f760ed82ad8b7311ada58f9f68673e135ece3b1beb06d3ec8723a4f3d5dd824e"},
|
||||||
{file = "pycares-4.9.0-cp39-cp39-win_arm64.whl", hash = "sha256:79b7addb2a41267d46650ac0d9c4f3b3233b036f186b85606f7586881dfb4b69"},
|
{file = "pycares-5.0.1-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94cb140b78bde232f6eb64c95cdac08dac9ae1829bfee1c436932eea10aabd39"},
|
||||||
{file = "pycares-4.9.0.tar.gz", hash = "sha256:8ee484ddb23dbec4d88d14ed5b6d592c1960d2e93c385d5e52b6fad564d82395"},
|
{file = "pycares-5.0.1-cp39-cp39-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:83da4b2e30bb80a424337376af0bce1216d787821b71c74d2f2bf3d40ea0bcf9"},
|
||||||
|
{file = "pycares-5.0.1-cp39-cp39-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:07260c6c0eff8aa809d6cd64010303098c7d0fe79176aba207d747c9ffc7a95a"},
|
||||||
|
{file = "pycares-5.0.1-cp39-cp39-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4e1630844c695fc41e760d653d775d03c61bf8c5ac259e90784f7f270e8c440c"},
|
||||||
|
{file = "pycares-5.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8dc84c0bce595c572971c1a9c7a3b417465572382968faac9bfddebd60e946b4"},
|
||||||
|
{file = "pycares-5.0.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:83115177cc0f1c8e6fbeb4e483d676f91d0ce90aad2933d5f0c87feccdc05688"},
|
||||||
|
{file = "pycares-5.0.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:eb93ea76094c46fd4a1294eb49affcf849d36d9b939322009d2bee7d507fcb20"},
|
||||||
|
{file = "pycares-5.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:534dd25083e7ba4c65fedbc94126bada53fe8de4466d9ca29b7aa2ab4eec36b4"},
|
||||||
|
{file = "pycares-5.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:52901b7a15a3b99631021a90fa3d1451d42b47b977208928012bf8238f70ba13"},
|
||||||
|
{file = "pycares-5.0.1-cp39-cp39-win_arm64.whl", hash = "sha256:153239d8c51f9e051d37867287ee1b283a201076e4cd9f4624ead30c86dfd5c9"},
|
||||||
|
{file = "pycares-5.0.1.tar.gz", hash = "sha256:5a3c249c830432631439815f9a818463416f2a8cbdb1e988e78757de9ae75081"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
cffi = ">=1.5.0"
|
cffi = [
|
||||||
|
{version = ">=1.5.0", markers = "python_version < \"3.14\""},
|
||||||
|
{version = ">=2.0.0b1", markers = "python_version >= \"3.14\""},
|
||||||
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
idna = ["idna (>=2.1)"]
|
idna = ["idna (>=2.1)"]
|
||||||
@@ -2655,14 +2682,14 @@ testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest-cov"
|
name = "pytest-cov"
|
||||||
version = "7.0.0"
|
version = "7.1.0"
|
||||||
description = "Pytest plugin for measuring coverage."
|
description = "Pytest plugin for measuring coverage."
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.9"
|
python-versions = ">=3.9"
|
||||||
groups = ["dev"]
|
groups = ["dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861"},
|
{file = "pytest_cov-7.1.0-py3-none-any.whl", hash = "sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678"},
|
||||||
{file = "pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1"},
|
{file = "pytest_cov-7.1.0.tar.gz", hash = "sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -3015,14 +3042,14 @@ png = ["pypng"]
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reportlab"
|
name = "reportlab"
|
||||||
version = "4.4.5"
|
version = "4.4.10"
|
||||||
description = "The Reportlab Toolkit"
|
description = "The Reportlab Toolkit"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "<4,>=3.9"
|
python-versions = "<4,>=3.9"
|
||||||
groups = ["main", "dev"]
|
groups = ["main", "dev"]
|
||||||
files = [
|
files = [
|
||||||
{file = "reportlab-4.4.5-py3-none-any.whl", hash = "sha256:849773d7cd5dde2072fedbac18c8bc909506c8befba8f088ba7b09243c6684cc"},
|
{file = "reportlab-4.4.10-py3-none-any.whl", hash = "sha256:5abc815746ae2bc44e7ff25db96814f921349ca814c992c7eac3c26029bf7c24"},
|
||||||
{file = "reportlab-4.4.5.tar.gz", hash = "sha256:0457d642aa76df7b36b0235349904c58d8f9c606a872456ed04436aafadc1510"},
|
{file = "reportlab-4.4.10.tar.gz", hash = "sha256:5cbbb34ac3546039d0086deb2938cdec06b12da3cdb836e813258eb33cd28487"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
@@ -3624,4 +3651,4 @@ propcache = ">=0.2.1"
|
|||||||
[metadata]
|
[metadata]
|
||||||
lock-version = "2.1"
|
lock-version = "2.1"
|
||||||
python-versions = "^3.10"
|
python-versions = "^3.10"
|
||||||
content-hash = "f301651d69bc3fceacce509ba49c288d421991ba35847bc6968cf6d66af62d12"
|
content-hash = "9aebf9e26d7bc39efeb4ff199a90199112954ca3face15b1813041cf9a88490e"
|
||||||
|
|||||||
+5
-4
@@ -31,13 +31,13 @@ classifiers = [
|
|||||||
# Install with dev dependencies:
|
# Install with dev dependencies:
|
||||||
# poetry install --with dev
|
# poetry install --with dev
|
||||||
python = "^3.10"
|
python = "^3.10"
|
||||||
aiodns = "^3.0.0"
|
aiodns = ">=3,<5"
|
||||||
aiohttp = "^3.12.14"
|
aiohttp = "^3.12.14"
|
||||||
aiohttp-socks = "^0.10.1"
|
aiohttp-socks = ">=0.10.1,<0.12.0"
|
||||||
arabic-reshaper = "^3.0.0"
|
arabic-reshaper = "^3.0.0"
|
||||||
async-timeout = "^5.0.1"
|
async-timeout = "^5.0.1"
|
||||||
attrs = "^25.3.0"
|
attrs = ">=25.3,<27.0"
|
||||||
certifi = "^2025.6.15"
|
certifi = ">=2025.6.15,<2027.0.0"
|
||||||
chardet = "^5.0.0"
|
chardet = "^5.0.0"
|
||||||
colorama = "^0.4.6"
|
colorama = "^0.4.6"
|
||||||
future = "^1.0.0"
|
future = "^1.0.0"
|
||||||
@@ -55,6 +55,7 @@ PySocks = "^1.7.1"
|
|||||||
python-bidi = "^0.6.3"
|
python-bidi = "^0.6.3"
|
||||||
requests = "^2.32.4"
|
requests = "^2.32.4"
|
||||||
requests-futures = "^1.0.2"
|
requests-futures = "^1.0.2"
|
||||||
|
requests-toolbelt = "^1.0.0"
|
||||||
six = "^1.17.0"
|
six = "^1.17.0"
|
||||||
socid-extractor = "^0.0.27"
|
socid-extractor = "^0.0.27"
|
||||||
soupsieve = "^2.6"
|
soupsieve = "^2.6"
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ DEFAULT_ARGS: Dict[str, Any] = {
|
|||||||
'site_list': [],
|
'site_list': [],
|
||||||
'stats': False,
|
'stats': False,
|
||||||
'tags': '',
|
'tags': '',
|
||||||
|
'exclude_tags': '',
|
||||||
'timeout': 30,
|
'timeout': 30,
|
||||||
'tor_proxy': 'socks5://127.0.0.1:9050',
|
'tor_proxy': 'socks5://127.0.0.1:9050',
|
||||||
'i2p_proxy': 'http://127.0.0.1:4444',
|
'i2p_proxy': 'http://127.0.0.1:4444',
|
||||||
@@ -105,3 +106,34 @@ def test_args_multiple_sites(argparser):
|
|||||||
|
|
||||||
for arg in vars(args):
|
for arg in vars(args):
|
||||||
assert getattr(args, arg) == want_args[arg]
|
assert getattr(args, arg) == want_args[arg]
|
||||||
|
|
||||||
|
|
||||||
|
def test_args_exclude_tags(argparser):
|
||||||
|
args = argparser.parse_args('--exclude-tags porn,dating username'.split())
|
||||||
|
|
||||||
|
want_args = dict(DEFAULT_ARGS)
|
||||||
|
want_args.update(
|
||||||
|
{
|
||||||
|
'exclude_tags': 'porn,dating',
|
||||||
|
'username': ['username'],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for arg in vars(args):
|
||||||
|
assert getattr(args, arg) == want_args[arg]
|
||||||
|
|
||||||
|
|
||||||
|
def test_args_tags_with_exclude_tags(argparser):
|
||||||
|
args = argparser.parse_args('--tags coding --exclude-tags porn username'.split())
|
||||||
|
|
||||||
|
want_args = dict(DEFAULT_ARGS)
|
||||||
|
want_args.update(
|
||||||
|
{
|
||||||
|
'tags': 'coding',
|
||||||
|
'exclude_tags': 'porn',
|
||||||
|
'username': ['username'],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for arg in vars(args):
|
||||||
|
assert getattr(args, arg) == want_args[arg]
|
||||||
|
|||||||
@@ -182,6 +182,54 @@ def test_ranked_sites_dict_id_type():
|
|||||||
assert len(db.ranked_sites_dict(id_type='gaia_id')) == 1
|
assert len(db.ranked_sites_dict(id_type='gaia_id')) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_ranked_sites_dict_excluded_tags():
|
||||||
|
db = MaigretDatabase()
|
||||||
|
db.update_site(MaigretSite('3', {'alexaRank': 1000, 'engine': 'ucoz'}))
|
||||||
|
db.update_site(MaigretSite('1', {'alexaRank': 2, 'tags': ['forum']}))
|
||||||
|
db.update_site(MaigretSite('2', {'alexaRank': 10, 'tags': ['ru', 'forum']}))
|
||||||
|
|
||||||
|
# excluding by tag
|
||||||
|
assert list(db.ranked_sites_dict(excluded_tags=['ru']).keys()) == ['1', '3']
|
||||||
|
assert list(db.ranked_sites_dict(excluded_tags=['forum']).keys()) == ['3']
|
||||||
|
|
||||||
|
# excluding by engine
|
||||||
|
assert list(db.ranked_sites_dict(excluded_tags=['ucoz']).keys()) == ['1', '2']
|
||||||
|
|
||||||
|
# combining include and exclude tags
|
||||||
|
assert list(db.ranked_sites_dict(tags=['forum'], excluded_tags=['ru']).keys()) == ['1']
|
||||||
|
|
||||||
|
# excluding non-existent tag has no effect
|
||||||
|
assert list(db.ranked_sites_dict(excluded_tags=['nonexistent']).keys()) == ['1', '2', '3']
|
||||||
|
|
||||||
|
# exclude all
|
||||||
|
assert list(db.ranked_sites_dict(excluded_tags=['forum', 'ucoz']).keys()) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_ranked_sites_dict_excluded_tags_with_top():
|
||||||
|
"""Excluded tags should also prevent mirrors from being included."""
|
||||||
|
db = MaigretDatabase()
|
||||||
|
db.update_site(
|
||||||
|
MaigretSite('Parent', {'alexaRank': 1, 'tags': ['forum'], 'type': 'username'})
|
||||||
|
)
|
||||||
|
db.update_site(
|
||||||
|
MaigretSite('Mirror', {'alexaRank': 999999, 'source': 'Parent', 'tags': ['forum'], 'type': 'username'})
|
||||||
|
)
|
||||||
|
db.update_site(
|
||||||
|
MaigretSite('Other', {'alexaRank': 2, 'tags': ['coding'], 'type': 'username'})
|
||||||
|
)
|
||||||
|
|
||||||
|
# Without exclusion, mirror should be included
|
||||||
|
result = db.ranked_sites_dict(top=1, id_type='username')
|
||||||
|
assert 'Parent' in result
|
||||||
|
assert 'Mirror' in result
|
||||||
|
|
||||||
|
# With exclusion of 'forum', both Parent and Mirror should be excluded
|
||||||
|
result = db.ranked_sites_dict(top=2, excluded_tags=['forum'], id_type='username')
|
||||||
|
assert 'Parent' not in result
|
||||||
|
assert 'Mirror' not in result
|
||||||
|
assert 'Other' in result
|
||||||
|
|
||||||
|
|
||||||
def test_ranked_sites_dict_mirrors_disabled_parent():
|
def test_ranked_sites_dict_mirrors_disabled_parent():
|
||||||
"""Mirror is included when parent ranks in top N but parent is disabled."""
|
"""Mirror is included when parent ranks in top N but parent is disabled."""
|
||||||
db = MaigretDatabase()
|
db = MaigretDatabase()
|
||||||
|
|||||||
+84
-1
@@ -1,8 +1,10 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
from maigret.submit import Submitter
|
from maigret.submit import Submitter
|
||||||
from aiohttp import ClientSession
|
from aiohttp import ClientSession
|
||||||
from maigret.sites import MaigretDatabase
|
from maigret.sites import MaigretDatabase, MaigretSite
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
@@ -275,3 +277,84 @@ async def test_dialog_adds_site_negative(settings):
|
|||||||
await submitter.close()
|
await submitter.close()
|
||||||
|
|
||||||
assert result is False
|
assert result is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_domain_matching_exact():
|
||||||
|
"""Test that domain matching uses proper boundary checks, not substring matching.
|
||||||
|
|
||||||
|
x.com should NOT match sites like 500px.com, mix.com, etc.
|
||||||
|
"""
|
||||||
|
domain_raw = "x.com"
|
||||||
|
domain_re = re.compile(
|
||||||
|
r'://(www\.)?' + re.escape(domain_raw) + r'(/|$)'
|
||||||
|
)
|
||||||
|
|
||||||
|
# These should NOT match x.com
|
||||||
|
non_matching = [
|
||||||
|
MaigretSite("500px", {"url": "https://500px.com/p/{username}", "urlMain": "https://500px.com/"}),
|
||||||
|
MaigretSite("Mix", {"url": "https://mix.com/{username}", "urlMain": "https://mix.com"}),
|
||||||
|
MaigretSite("Screwfix", {"url": "{urlMain}{urlSubpath}/members/?username={username}", "urlMain": "https://community.screwfix.com"}),
|
||||||
|
MaigretSite("Wix", {"url": "https://{username}.wix.com", "urlMain": "https://wix.com/"}),
|
||||||
|
MaigretSite("1x", {"url": "https://1x.com/{username}", "urlMain": "https://1x.com"}),
|
||||||
|
MaigretSite("Roblox", {"url": "https://www.roblox.com/user.aspx?username={username}", "urlMain": "https://www.roblox.com/"}),
|
||||||
|
]
|
||||||
|
|
||||||
|
for site in non_matching:
|
||||||
|
assert not domain_re.search(site.url_main + site.url), \
|
||||||
|
f"x.com should NOT match site {site.name} ({site.url_main})"
|
||||||
|
|
||||||
|
|
||||||
|
def test_domain_matching_positive():
|
||||||
|
"""Test that domain matching correctly matches the exact domain."""
|
||||||
|
domain_raw = "x.com"
|
||||||
|
domain_re = re.compile(
|
||||||
|
r'://(www\.)?' + re.escape(domain_raw) + r'(/|$)'
|
||||||
|
)
|
||||||
|
|
||||||
|
# These SHOULD match x.com
|
||||||
|
matching = [
|
||||||
|
MaigretSite("X", {"url": "https://x.com/{username}", "urlMain": "https://x.com"}),
|
||||||
|
MaigretSite("X-www", {"url": "https://www.x.com/{username}", "urlMain": "https://www.x.com"}),
|
||||||
|
]
|
||||||
|
|
||||||
|
for site in matching:
|
||||||
|
assert domain_re.search(site.url_main + site.url), \
|
||||||
|
f"x.com SHOULD match site {site.name} ({site.url_main})"
|
||||||
|
|
||||||
|
|
||||||
|
def test_dialog_nonexistent_site_name_no_crash():
|
||||||
|
"""Test that entering a site name not in the matched list doesn't crash.
|
||||||
|
|
||||||
|
This tests the fix for: AttributeError: 'NoneType' object has no attribute 'name'
|
||||||
|
The old_site should be None when user enters a name not in matched_sites,
|
||||||
|
and the code should handle it gracefully.
|
||||||
|
"""
|
||||||
|
# Simulate the logic that was crashing
|
||||||
|
matched_sites = [
|
||||||
|
MaigretSite("ValidActive", {"url": "https://example.com/{username}", "urlMain": "https://example.com"}),
|
||||||
|
MaigretSite("InvalidActive", {"url": "https://example.com/alt/{username}", "urlMain": "https://example.com"}),
|
||||||
|
]
|
||||||
|
site_name = "NonExistentSite"
|
||||||
|
|
||||||
|
old_site = next(
|
||||||
|
(site for site in matched_sites if site.name == site_name), None
|
||||||
|
)
|
||||||
|
|
||||||
|
# This is what the old code did - it would crash here
|
||||||
|
assert old_site is None
|
||||||
|
|
||||||
|
# The fix: check before accessing .name
|
||||||
|
if old_site is None:
|
||||||
|
result = "not found"
|
||||||
|
else:
|
||||||
|
result = old_site.name
|
||||||
|
|
||||||
|
assert result == "not found"
|
||||||
|
|
||||||
|
# And when site_name IS in matched_sites, it should work
|
||||||
|
site_name = "ValidActive"
|
||||||
|
old_site = next(
|
||||||
|
(site for site in matched_sites if site.name == site_name), None
|
||||||
|
)
|
||||||
|
assert old_site is not None
|
||||||
|
assert old_site.name == "ValidActive"
|
||||||
|
|||||||
+50
-39
@@ -24,36 +24,44 @@ RANKS.update({
|
|||||||
'100000000': '100M',
|
'100000000': '100M',
|
||||||
})
|
})
|
||||||
|
|
||||||
SEMAPHORE = threading.Semaphore(20)
|
|
||||||
|
|
||||||
|
|
||||||
def get_rank(domain_to_query, site, print_errors=True):
|
import csv
|
||||||
with SEMAPHORE:
|
import io
|
||||||
# Retrieve ranking data via alexa API
|
from urllib.parse import urlparse
|
||||||
url = f"http://data.alexa.com/data?cli=10&url={domain_to_query}"
|
|
||||||
xml_data = requests.get(url).text
|
|
||||||
root = ET.fromstring(xml_data)
|
|
||||||
|
|
||||||
try:
|
def fetch_majestic_million():
|
||||||
#Get ranking for this site.
|
print("Fetching Majestic Million CSV (this may take a few seconds)...")
|
||||||
site.alexa_rank = int(root.find('.//REACH').attrib['RANK'])
|
ranks = {}
|
||||||
# country = root.find('.//COUNTRY')
|
url = "https://downloads.majestic.com/majestic_million.csv"
|
||||||
# if not country is None and country.attrib:
|
try:
|
||||||
# country_code = country.attrib['CODE']
|
response = requests.get(url, stream=True)
|
||||||
# tags = set(site.tags)
|
response.raise_for_status()
|
||||||
# if country_code:
|
|
||||||
# tags.add(country_code.lower())
|
csv_file = io.StringIO(response.text)
|
||||||
# site.tags = sorted(list(tags))
|
reader = csv.reader(csv_file)
|
||||||
# if site.type != 'username':
|
next(reader) # skip headers
|
||||||
# site.disabled = False
|
|
||||||
except Exception as e:
|
for row in reader:
|
||||||
if print_errors:
|
if not row or len(row) < 3:
|
||||||
logging.error(e)
|
continue
|
||||||
# We did not find the rank for some reason.
|
rank = int(row[0])
|
||||||
print(f"Error retrieving rank information for '{domain_to_query}'")
|
domain = row[2].lower()
|
||||||
print(f" Returned XML is |{xml_data}|")
|
ranks[domain] = rank
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error fetching Majestic Million: {e}")
|
||||||
|
|
||||||
|
print(f"Loaded {len(ranks)} domains from Majestic Million.")
|
||||||
|
return ranks
|
||||||
|
|
||||||
return
|
def get_base_domain(url):
|
||||||
|
try:
|
||||||
|
netloc = urlparse(url).netloc
|
||||||
|
if netloc.startswith('www.'):
|
||||||
|
netloc = netloc[4:]
|
||||||
|
return netloc.lower()
|
||||||
|
except Exception:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def get_step_rank(rank):
|
def get_step_rank(rank):
|
||||||
@@ -91,30 +99,33 @@ def main():
|
|||||||
with open("sites.md", "w") as site_file:
|
with open("sites.md", "w") as site_file:
|
||||||
site_file.write(f"""
|
site_file.write(f"""
|
||||||
## List of supported sites (search methods): total {len(sites_subset)}\n
|
## List of supported sites (search methods): total {len(sites_subset)}\n
|
||||||
Rank data fetched from Alexa by domains.
|
Rank data fetched from Majestic Million by domains.
|
||||||
|
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
majestic_ranks = {}
|
||||||
|
if args.with_rank:
|
||||||
|
majestic_ranks = fetch_majestic_million()
|
||||||
|
|
||||||
for site in sites_subset:
|
for site in sites_subset:
|
||||||
if not args.with_rank:
|
if not args.with_rank:
|
||||||
break
|
break
|
||||||
url_main = site.url_main
|
|
||||||
if site.alexa_rank < sys.maxsize and args.empty_only:
|
if site.alexa_rank < sys.maxsize and args.empty_only:
|
||||||
continue
|
continue
|
||||||
if args.exclude_engine_list and site.engine in args.exclude_engine_list:
|
if args.exclude_engine_list and site.engine in args.exclude_engine_list:
|
||||||
continue
|
continue
|
||||||
site.alexa_rank = 0
|
|
||||||
th = threading.Thread(target=get_rank, args=(url_main, site,))
|
domain = get_base_domain(site.url_main)
|
||||||
pool.append((site.name, url_main, th))
|
|
||||||
th.start()
|
if domain in majestic_ranks:
|
||||||
|
site.alexa_rank = majestic_ranks[domain]
|
||||||
|
else:
|
||||||
|
site.alexa_rank = sys.maxsize
|
||||||
|
|
||||||
|
# In memory matching complete, no threads to join
|
||||||
if args.with_rank:
|
if args.with_rank:
|
||||||
index = 1
|
print("Successfully updated ranks matching Majestic Million dataset.")
|
||||||
for site_name, url_main, th in pool:
|
|
||||||
th.join()
|
|
||||||
sys.stdout.write("\r{0}".format(f"Updated {index} out of {len(sites_subset)} entries"))
|
|
||||||
sys.stdout.flush()
|
|
||||||
index = index + 1
|
|
||||||
|
|
||||||
sites_full_list = [(s, int(s.alexa_rank)) for s in sites_subset]
|
sites_full_list = [(s, int(s.alexa_rank)) for s in sites_subset]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user