Merge pull request #2560 from NotoriousRebel/codex/stdlib-json-release

Replace UltraJSON with the standard library
This commit is contained in:
Matt
2026-08-16 20:24:30 -04:00
committed by GitHub
11 changed files with 85 additions and 112 deletions
+2 -1
View File
@@ -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.
-2
View File
@@ -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",
]
+49
View File
@@ -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
+2 -3
View File
@@ -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 -12
View File
@@ -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 -12
View File
@@ -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 -12
View File
@@ -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."""
+2 -2
View File
@@ -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):
+25 -16
View File
@@ -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 -5
View File
@@ -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)
Generated
-47
View File
@@ -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"