mirror of
https://github.com/laramies/theHarvester.git
synced 2026-08-17 19:35:40 +02:00
Merge pull request #2560 from NotoriousRebel/codex/stdlib-json-release
Replace UltraJSON with the standard library
This commit is contained in:
+2
-1
@@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Changed
|
||||
- Raised the minimum supported Python version to 3.14, selected it for source checkouts, and limited release validation to that runtime.
|
||||
- Replaced the maintenance-only UltraJSON dependency with Python's standard `json` module for provider payloads and legacy reports, removing the native runtime extension without changing JSONL or API serialization.
|
||||
- Centralized passive-source completion reporting in an immutable `SourceExecutionReport` returned by adapters, with the source runner alone deriving partial and no-result outcomes from retained evidence; removed mutable per-adapter `execution_status` and `stop_reason` state and made the runner reject adapters that still expose either field.
|
||||
- Updated BeVigil, Dymo, FOFA, FullHunt, Hunter.how, Netlas, ONYPHE, SecurityScorecard, SecurityTrails, SherlockEye, SubdomainFinder C99, VirusTotal, WhoisXML, and ZoomEye provider contracts to retain scoped partial evidence and report authentication, quota, transport, HTTP, and malformed-response outcomes truthfully. FOFA and ONYPHE now honor the operator result limit across documented pagination, while ZoomEye uses the current `POST /v2/search` API and no longer stops after five empty result pages.
|
||||
- Replaced runtime takeover fingerprint downloads and global body-substring matches with pinned provider-gated DNS, wildcard controls, and compound HTTP rules. Every checked hostname is now stored as an indicator, no-indicator, or inconclusive outcome with typed DNS, HTTP, rule, and error details in JSONL, SQLite, the API, and HarvestView. Direct checks share one cookie-free HTTP session, keep bounded response bodies, and rely on the whole-run deadline instead of silently inheriting aiohttp's default timeout.
|
||||
@@ -69,7 +70,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Replaced deprecated hostname resolution with `getaddrinfo`-based handling ([6a847435](https://github.com/laramies/theHarvester/commit/6a847435)).
|
||||
- Reworked routine CI to use read-only permissions, non-mutating Ruff checks, offline tests, and explicit opt-in live provider checks ([72e5820f](https://github.com/laramies/theHarvester/commit/72e5820f)).
|
||||
- Grouped GitHub Actions, Python, and Docker Dependabot updates with a seven-day cooldown, and added a seven-day `uv` dependency freshness window ([7a947b66](https://github.com/laramies/theHarvester/commit/7a947b66), [52a79cdb](https://github.com/laramies/theHarvester/commit/52a79cdb)).
|
||||
- Updated runtime dependencies: `aiohttp` to `3.14.1`, `beautifulsoup4` to `4.15.0`, `certifi` to `2026.6.17`, `fastapi` to `0.138.1`, `ujson` to `5.13.0`, and `uvicorn` to `0.49.0`.
|
||||
- Updated runtime dependencies: `aiohttp` to `3.14.1`, `beautifulsoup4` to `4.15.0`, `certifi` to `2026.6.17`, `fastapi` to `0.138.1`, and `uvicorn` to `0.49.0`.
|
||||
- Updated development dependencies: `pytest` to `9.1.1`, `ruff` to `0.15.20`, and `ty` to `0.0.54`.
|
||||
- Updated CI and container maintenance pins, including `actions/checkout`, `astral-sh/setup-uv`, `astral-sh/ruff-action`, `github/codeql-action`, StepSecurity Harden-Runner, Docker actions, and the Python base image.
|
||||
- Expanded offline regression coverage for discovery providers, configuration contracts, logging, output, documentation, workflow policy, and scope boundaries.
|
||||
|
||||
@@ -34,7 +34,6 @@ dependencies = [
|
||||
"httpx==0.28.1",
|
||||
"retrying==1.4.2",
|
||||
"sqlalchemy==2.0.51",
|
||||
"ujson==5.13.0",
|
||||
"uvicorn==0.52.1",
|
||||
"uvloop==0.22.1; platform_system != 'Windows'",
|
||||
"winloop==0.6.3; platform_system == 'Windows'",
|
||||
@@ -52,7 +51,6 @@ dev = [
|
||||
"types-python-dateutil==2.9.0.20260518",
|
||||
"types-PyYAML==6.0.12.20250915",
|
||||
"ruff==0.16.1",
|
||||
"types-ujson==5.10.0.20250822",
|
||||
"wheel==0.47.0",
|
||||
"ty==0.0.69",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_json_extensions_are_not_installation_requirements() -> None:
|
||||
project = tomllib.loads(Path('pyproject.toml').read_text(encoding='utf-8'))
|
||||
|
||||
assert all(not dependency.startswith('ujson') for dependency in project['project']['dependencies'])
|
||||
assert all(not dependency.startswith('types-ujson') for dependency in project['dependency-groups']['dev'])
|
||||
|
||||
|
||||
def test_runtime_imports_without_optional_json_extensions() -> None:
|
||||
script = textwrap.dedent(
|
||||
"""
|
||||
import builtins
|
||||
import importlib
|
||||
|
||||
real_import = builtins.__import__
|
||||
|
||||
def import_without_ujson(name, *args, **kwargs):
|
||||
if name == 'ujson':
|
||||
raise ModuleNotFoundError('ujson is unavailable')
|
||||
return real_import(name, *args, **kwargs)
|
||||
|
||||
builtins.__import__ = import_without_ujson
|
||||
for module in (
|
||||
'theHarvester.__main__',
|
||||
'theHarvester.discovery.commoncrawl',
|
||||
'theHarvester.discovery.gitlabsearch',
|
||||
'theHarvester.discovery.robtex',
|
||||
'theHarvester.discovery.subdomainfinderc99',
|
||||
'theHarvester.discovery.windvane',
|
||||
'theHarvester.lib.core',
|
||||
):
|
||||
importlib.import_module(module)
|
||||
"""
|
||||
)
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, '-c', script],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
assert result.returncode == 0, result.stderr
|
||||
@@ -20,7 +20,6 @@ from uuid import UUID, uuid4
|
||||
|
||||
import anyio
|
||||
import netaddr
|
||||
import ujson
|
||||
|
||||
from theHarvester.discovery import (
|
||||
api_endpoints,
|
||||
@@ -589,7 +588,7 @@ async def start(
|
||||
for host in canonical_hosts:
|
||||
if host.ip not in shodan_hosts:
|
||||
output_logger.info(
|
||||
ujson.dumps(
|
||||
json.dumps(
|
||||
{'type': 'shodan-host', 'value': host.ip, 'details': host.to_details()},
|
||||
indent=4,
|
||||
sort_keys=True,
|
||||
@@ -2144,7 +2143,7 @@ async def start(
|
||||
|
||||
json_dict['shodan'] = shodanres
|
||||
async with await anyio.open_file(filename, 'w+') as fp:
|
||||
dumped_json = ujson.dumps(json_dict, sort_keys=True)
|
||||
dumped_json = json.dumps(json_dict, separators=(',', ':'), sort_keys=True)
|
||||
await fp.write(dumped_json)
|
||||
output_logger.info('[*] JSON File saved.')
|
||||
except (OSError, ValueError, TypeError, UnicodeEncodeError) as er:
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import asyncio
|
||||
import json as _stdlib_json
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from types import ModuleType
|
||||
from urllib.parse import urlencode, urlsplit
|
||||
|
||||
from theHarvester.lib.core import AsyncFetcher, Core
|
||||
@@ -10,16 +9,6 @@ from theHarvester.lib.source_execution import SourceExecutionReport
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
json: ModuleType = _stdlib_json
|
||||
try:
|
||||
import ujson as _ujson
|
||||
|
||||
json = _ujson
|
||||
except ImportError:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
class SearchCommoncrawl:
|
||||
"""Gather subdomains from every crawl ending within one year of the newest catalog entry.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import json as _stdlib_json
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
from types import ModuleType
|
||||
from urllib.parse import quote
|
||||
|
||||
from theHarvester.lib.core import AsyncFetcher, Core
|
||||
@@ -9,16 +8,6 @@ from theHarvester.lib.hostnames import normalize_scoped_hostname
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
json: ModuleType = _stdlib_json
|
||||
try:
|
||||
import ujson as _ujson
|
||||
|
||||
json = _ujson
|
||||
except ImportError:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
class SearchGitlab:
|
||||
"""Search public GitLab project metadata, README files, and user profiles."""
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import json as _stdlib_json
|
||||
import json
|
||||
import logging
|
||||
from ipaddress import ip_address
|
||||
from types import ModuleType
|
||||
|
||||
import aiohttp
|
||||
|
||||
@@ -10,16 +9,6 @@ from theHarvester.lib.source_execution import SourceExecutionReport
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
json: ModuleType = _stdlib_json
|
||||
try:
|
||||
import ujson as _ujson
|
||||
|
||||
json = _ujson
|
||||
except ImportError as e:
|
||||
logger.info(f"'ujson' not available. Falling back to standard 'json' module. Reason: {e}")
|
||||
except (AttributeError, OSError, RuntimeError, SystemError, ValueError) as e:
|
||||
logger.info(f"Unexpected error while importing 'ujson'. Falling back to standard 'json'. Reason: {e}")
|
||||
|
||||
|
||||
class SearchRobtex:
|
||||
"""Gather IP addresses for a hostname from the Robtex passive DNS API."""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import asyncio
|
||||
import json
|
||||
|
||||
import ujson
|
||||
from bs4 import BeautifulSoup
|
||||
from bs4.element import Tag
|
||||
|
||||
@@ -45,7 +45,7 @@ class SearchSubdomainfinderc99:
|
||||
second_resp = await AsyncFetcher.post_fetch(
|
||||
self.server,
|
||||
session=session,
|
||||
data=ujson.dumps(data),
|
||||
data=json.dumps(data, separators=(',', ':')),
|
||||
include_metadata=True,
|
||||
)
|
||||
if error := provider_http_error(second_resp):
|
||||
|
||||
@@ -1,22 +1,11 @@
|
||||
import json as _stdlib_json
|
||||
import json
|
||||
import logging
|
||||
from types import ModuleType
|
||||
|
||||
from theHarvester.lib.core import AsyncFetcher, Core
|
||||
from theHarvester.lib.hostnames import normalize_scoped_hostname
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
json: ModuleType = _stdlib_json
|
||||
try:
|
||||
import ujson as _ujson
|
||||
|
||||
json = _ujson
|
||||
except ImportError:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
class SearchWindvane:
|
||||
"""Class uses the Windvane API to gather subdomains and domain intelligence
|
||||
@@ -109,7 +98,12 @@ class SearchWindvane:
|
||||
data = {'domain': self.word, 'page_request': {'page': page, 'count': 30}}
|
||||
|
||||
try:
|
||||
response = await AsyncFetcher.post_fetch(url, headers=headers, data=json.dumps(data), proxy=self.proxy)
|
||||
response = await AsyncFetcher.post_fetch(
|
||||
url,
|
||||
headers=headers,
|
||||
data=json.dumps(data, separators=(',', ':')),
|
||||
proxy=self.proxy,
|
||||
)
|
||||
if response:
|
||||
response_data = self._safe_parse_json(response)
|
||||
|
||||
@@ -147,7 +141,12 @@ class SearchWindvane:
|
||||
data = {'domain': self.word, 'page_request': {'page': page, 'count': 30}}
|
||||
|
||||
try:
|
||||
response = await AsyncFetcher.post_fetch(url, headers=headers, data=json.dumps(data), proxy=self.proxy)
|
||||
response = await AsyncFetcher.post_fetch(
|
||||
url,
|
||||
headers=headers,
|
||||
data=json.dumps(data, separators=(',', ':')),
|
||||
proxy=self.proxy,
|
||||
)
|
||||
if response:
|
||||
response_data = self._safe_parse_json(response)
|
||||
|
||||
@@ -186,7 +185,12 @@ class SearchWindvane:
|
||||
data = {'email': self.word, 'page_request': {'page': 1, 'count': 50}}
|
||||
|
||||
try:
|
||||
response = await AsyncFetcher.post_fetch(url, headers=headers, data=json.dumps(data), proxy=self.proxy)
|
||||
response = await AsyncFetcher.post_fetch(
|
||||
url,
|
||||
headers=headers,
|
||||
data=json.dumps(data, separators=(',', ':')),
|
||||
proxy=self.proxy,
|
||||
)
|
||||
if response:
|
||||
response_data = self._safe_parse_json(response)
|
||||
|
||||
@@ -221,7 +225,12 @@ class SearchWindvane:
|
||||
}
|
||||
|
||||
try:
|
||||
response = await AsyncFetcher.post_fetch(url, headers=headers, data=json.dumps(data), proxy=self.proxy)
|
||||
response = await AsyncFetcher.post_fetch(
|
||||
url,
|
||||
headers=headers,
|
||||
data=json.dumps(data, separators=(',', ':')),
|
||||
proxy=self.proxy,
|
||||
)
|
||||
if response:
|
||||
response_data = self._safe_parse_json(response)
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import contextlib
|
||||
import json as stdlib_json
|
||||
import json as json_loader
|
||||
import logging
|
||||
import random
|
||||
import re
|
||||
@@ -13,9 +13,6 @@ from typing import TYPE_CHECKING, Any, ClassVar, Literal
|
||||
|
||||
import aiohttp
|
||||
import certifi
|
||||
|
||||
# need to import as different name as to not shadow already existing json var in post_fetch
|
||||
import ujson as json_loader
|
||||
import yaml
|
||||
from aiohttp_socks import ProxyConnector
|
||||
|
||||
@@ -857,7 +854,7 @@ class AsyncFetcher:
|
||||
text = body.decode('utf-8')
|
||||
if not text.strip():
|
||||
raise ValueError('empty JSON response')
|
||||
parsed = stdlib_json.loads(text, parse_constant=_reject_json_constant)
|
||||
parsed = json_loader.loads(text, parse_constant=_reject_json_constant)
|
||||
except (UnicodeDecodeError, ValueError, RecursionError) as error:
|
||||
raise ResponseStreamError('invalid-response') from error
|
||||
return FetcherResponse(body=parsed, status=response.status, headers=response_headers)
|
||||
|
||||
@@ -1131,7 +1131,6 @@ dependencies = [
|
||||
{ name = "pyyaml" },
|
||||
{ name = "retrying" },
|
||||
{ name = "sqlalchemy" },
|
||||
{ name = "ujson" },
|
||||
{ name = "uvicorn" },
|
||||
{ name = "uvloop", marker = "sys_platform != 'win32'" },
|
||||
{ name = "winloop", marker = "sys_platform == 'win32'" },
|
||||
@@ -1150,7 +1149,6 @@ dev = [
|
||||
{ name = "types-chardet" },
|
||||
{ name = "types-python-dateutil" },
|
||||
{ name = "types-pyyaml" },
|
||||
{ name = "types-ujson" },
|
||||
{ name = "wheel" },
|
||||
]
|
||||
|
||||
@@ -1173,7 +1171,6 @@ requires-dist = [
|
||||
{ name = "pyyaml", specifier = "==6.0.3" },
|
||||
{ name = "retrying", specifier = "==1.4.2" },
|
||||
{ name = "sqlalchemy", specifier = "==2.0.51" },
|
||||
{ name = "ujson", specifier = "==5.13.0" },
|
||||
{ name = "uvicorn", specifier = "==0.52.1" },
|
||||
{ name = "uvloop", marker = "sys_platform != 'win32'", specifier = "==0.22.1" },
|
||||
{ name = "winloop", marker = "sys_platform == 'win32'", specifier = "==0.6.3" },
|
||||
@@ -1192,7 +1189,6 @@ dev = [
|
||||
{ name = "types-chardet", specifier = "==5.0.4.6" },
|
||||
{ name = "types-python-dateutil", specifier = "==2.9.0.20260518" },
|
||||
{ name = "types-pyyaml", specifier = "==6.0.12.20250915" },
|
||||
{ name = "types-ujson", specifier = "==5.10.0.20250822" },
|
||||
{ name = "wheel", specifier = "==0.47.0" },
|
||||
]
|
||||
|
||||
@@ -1257,15 +1253,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/e0/1eed384f02555dde685fff1a1ac805c1c7dcb6dd019c916fe659b1c1f9ec/types_pyyaml-6.0.12.20250915-py3-none-any.whl", hash = "sha256:e7d4d9e064e89a3b3cae120b4990cd370874d2bf12fa5f46c97018dd5d3c9ab6", size = 20338, upload-time = "2025-09-15T03:00:59.218Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-ujson"
|
||||
version = "5.10.0.20250822"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5c/bd/d372d44534f84864a96c19a7059d9b4d29db8541828b8b9dc3040f7a46d0/types_ujson-5.10.0.20250822.tar.gz", hash = "sha256:0a795558e1f78532373cf3f03f35b1f08bc60d52d924187b97995ee3597ba006", size = 8437, upload-time = "2025-08-22T03:02:19.433Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/f2/d812543c350674d8b3f6e17c8922248ee3bb752c2a76f64beb8c538b40cf/types_ujson-5.10.0.20250822-py3-none-any.whl", hash = "sha256:3e9e73a6dc62ccc03449d9ac2c580cd1b7a8e4873220db498f7dd056754be080", size = 7657, upload-time = "2025-08-22T03:02:18.699Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
@@ -1287,40 +1274,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ujson"
|
||||
version = "5.13.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/89/7a/c8bb37c8f6f3623d60c33d15d18cd6d6655d0f9c3eb31a9969f76361b199/ujson-5.13.0.tar.gz", hash = "sha256:d62e3d7625384c08082abad81a077af587fdef2761bb14c3822f4234b8d07d75", size = 7166784, upload-time = "2026-06-14T22:36:50.209Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1c/9a/b5139d696f5328f3cab70b9ec046f15e3f49497a4de6280974640602f539/ujson-5.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:cc9dfd41fed397ab03bb9d9fe1cbd83301211c772a17536033ce7d68877ac82b", size = 56897, upload-time = "2026-06-14T22:35:57.974Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/55/477183aeddfdf0f88ae039ffee0ed866cfb993da0c0c9aa915807554aef8/ujson-5.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ca7ef2fa6c408a7c0f558e4d33d93b32ddc35ed6d3cfc505747931a64b7465d5", size = 54451, upload-time = "2026-06-14T22:35:58.932Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/63/55e5f23e156b4c8bca095d828b4cd3180c0b42aa3501ef88836d79606fea/ujson-5.13.0-cp314-cp314-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a554b2e5bee85030369514cef8b0b913cebe1a4c2c0c13541966d50bcba22b1a", size = 60053, upload-time = "2026-06-14T22:35:59.969Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/26/b6/08c6cf5548bd6f4bb557c9fa7e8edf87324bb04c17249d1966028d61dde0/ujson-5.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ea939ff629ab03ae970d03eca6d1febd8ed55ba38ca44aec64ce997537cd3fa0", size = 53481, upload-time = "2026-06-14T22:36:01.007Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/b3/0ac9a03551467784067f505df1bb875c639ba32f1da79ce467ab15911ada/ujson-5.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b98bf2faa5e37ecfe752226ea08290031e375a0c43d425a0b955fb3e702a2a71", size = 55058, upload-time = "2026-06-14T22:36:02.297Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ba/be/ec91029aec067174473d022fa0f6c3c1431a173f888d7599739f05c668eb/ujson-5.13.0-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a4b92344b16e414aeb609e57f62c466500e53c94f1698f5b149dc0b7223ec3e", size = 58225, upload-time = "2026-06-14T22:36:03.321Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/33/a948f329252ece3f9c93d177243de6e677927ebc6ac44256742dbbef3c39/ujson-5.13.0-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7df805aad707507a1fa165fb716218ca3a89f142125dc4b23c9fcc08fa402d97", size = 57930, upload-time = "2026-06-14T22:36:04.385Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/0c/c33655218b8e0a8adbf066de0b999cae5c324061f3eaa4dda17423145d9e/ujson-5.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7576bdbef327c3528f011002a2d74486f6fe4e33289bdb7a042b7f1a6e9d8285", size = 1037728, upload-time = "2026-06-14T22:36:05.467Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2c/bd/d286947525ea7ce3f2d8dc55c15b9ffbe425bc455c96af7b8f8a402599a9/ujson-5.13.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:6eee5d7cce3f32a468905f9ff61807a60287a90258d849460f6fa826e810870d", size = 1197146, upload-time = "2026-06-14T22:36:06.839Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/3c/9eb916377050b0785f048a34588c1c390ddd41ae00b78db68ee1ad022356/ujson-5.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:144e9d8a454cfa727e0f755e1863738ed68068583bda5463052cb446835bd56c", size = 1090223, upload-time = "2026-06-14T22:36:08.329Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/5f/242fd97a2628b842d4bfaa9b18e1f68187f934d67503291ebbaab1254637/ujson-5.13.0-cp314-cp314-win32.whl", hash = "sha256:576f35c35b918d67d41b933878062ec0a5c9f4d1e9e14e04aeef35384963feae", size = 41223, upload-time = "2026-06-14T22:36:09.644Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/f3/7f2bd9ca0c507142d0c22347b3d6f8803be1d8851c31707e57f5923fdbea/ujson-5.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:d5e206e9f849ead27e51ef8da44e52b38da7c6dbd929a7340ab44533edcda8d7", size = 42265, upload-time = "2026-06-14T22:36:11.043Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/29/3e9a8fba321c031315f6d263510969a5d01f41fc471b5be107e413c1b2f8/ujson-5.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:dc470179775468f9a007d3a6a2734624248c94bf47c6645e808c7e50a5070d1a", size = 40205, upload-time = "2026-06-14T22:36:12.286Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/e9/1c543837c6a3c6672361882a0fa269bd02daf9cc4c0ca88a9dccd9df98d9/ujson-5.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:69b4e36bb7d5f413ba8c00c8006b2ec627cc5ace97301462f6aadb66ec9d2979", size = 57402, upload-time = "2026-06-14T22:36:13.238Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/e4/39862f0f7174ff07cfd1e2d0c9065ded34aeebdb7db8daf2f0e5bf89b46f/ujson-5.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:8b644d50f66de5490c1823c7176618cead5e8e8a88cba9f40a6308ca52e79267", size = 54973, upload-time = "2026-06-14T22:36:14.432Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/66/f53d3b32c3f177f846ca6b624e832f29000d8a213a2d8768e254bd470ced/ujson-5.13.0-cp314-cp314t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:15107aaa4f559d55201165ec32abb35c283a861be1fa67229578cb7d93fcd93a", size = 60683, upload-time = "2026-06-14T22:36:15.806Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/d4/dddc4646d2633c85c938c2ded7d5a9711cdad5be1e13b31b7dad76f61c83/ujson-5.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6e343c5f0c058523f1edbf6ae4eceb4e0d934205a53bbdd8d9a945c83324662a", size = 54167, upload-time = "2026-06-14T22:36:16.952Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5f/c0/d8608c3f4d3f05e6441364b63fde1d279700135c1a6577a773662c07fbcc/ujson-5.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:02200035bc80e830f076ffc1b329a94c295aee6d9de8c9043647cb9a7bd4f76f", size = 55568, upload-time = "2026-06-14T22:36:17.975Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/8e/dd12b735aaba0806c3d70c18184d50e1f9712e0757c7c0a4f376450cfe28/ujson-5.13.0-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d7f19b81b73ff28f5c5022ee794f94122bfcda07a76423078e349465d71223a1", size = 59086, upload-time = "2026-06-14T22:36:19.071Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/43/ad41e8752d5ec3a590a5e7b426a54e36b7aab911d9b5a4f7384dc62507ab/ujson-5.13.0-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82e1393e6dbe3c95fdfc95c6c528890e191351a1f024ef51126cf1f22543af52", size = 58667, upload-time = "2026-06-14T22:36:20.171Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/8e/b44a6afb77b94118655c029081b7932d64bb4c5b1c8ba2b7f5808b5d0bc2/ujson-5.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:38afcf994b28ed85ea2420e2a8d79a37d0a77348b3daf53850c16edda66f942d", size = 1038553, upload-time = "2026-06-14T22:36:21.245Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/93/fab1d786174c8780eb3e386c73f1925a435e97fbf77c957fea4fca83994d/ujson-5.13.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:1bdf2518971586f2b413156c49d9dd8b56cc990a8647081e1bd00af60564d469", size = 1197938, upload-time = "2026-06-14T22:36:22.585Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/bc/2f073bb708f9d128f5d1cb39063a5f6421b1ce94c61be8661c55a189f407/ujson-5.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:751ad01042472f1c7c02f5c597c7aee79834e82a6cc384ca302173bbc8e8deb8", size = 1090938, upload-time = "2026-06-14T22:36:23.947Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c4/67/cdaa50bba29d7dc9eb19212755b09bb96f56596e75957c3717c6b85454de/ujson-5.13.0-cp314-cp314t-win32.whl", hash = "sha256:74f3dd61aeb01b7b2a6754e400224e819279041b3867935a55ccf57fb86a43b2", size = 41802, upload-time = "2026-06-14T22:36:25.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/66/a6e669e90083febdf6c0600d3807f6017fd4d3962d5bd6ddc605c73a06e5/ujson-5.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5c31317d5e4504dae8f98795358b6082fc0ef96e7394806db0a76a4a8717f446", size = 42790, upload-time = "2026-06-14T22:36:26.614Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3d/5f/fcc6c6a9d711fd8b020ca8ff65148212f0a712c809d173cd949e58de68c6/ujson-5.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:aefd3c9c95f9b62348956396ff7b31818476f8f54dc4a4e64cbd4f0491db6fca", size = 40708, upload-time = "2026-06-14T22:36:27.721Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "2.7.0"
|
||||
|
||||
Reference in New Issue
Block a user