fix(pdf): render shaped Arabic reports without clipping across pages
Signed-off-by: Haozhe Wu <haozhe_wu@connect.hku.hk>
This commit is contained in:
1 parent
8ec3b80f10
commit
2ce2165160
6 files changed
+574
-4
No files matched your search
@@ -70,3 +70,6 @@ nh3>=0.2.18
|
||||
weasyprint>=60.0
|
||||
# Pure-Python PDF fallback where weasyprint's system pango/cairo libs are missing
|
||||
reportlab>=4.0
|
||||
# Portable Arabic shaping and bidirectional layout for ReportLab fallback
|
||||
arabic-reshaper>=3.0.1
|
||||
python-bidi>=0.6.11
|
||||
@@ -5,10 +5,173 @@ from __future__ import annotations
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
import unicodedata
|
||||
from functools import lru_cache
|
||||
from itertools import groupby
|
||||
from pathlib import Path
|
||||
from xml.sax.saxutils import escape
|
||||
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _arabic_font() -> str:
|
||||
"""Register the Arabic-capable font already shipped by Matplotlib."""
|
||||
import matplotlib
|
||||
from reportlab.pdfbase import pdfmetrics
|
||||
from reportlab.pdfbase.ttfonts import TTFont
|
||||
|
||||
family = "VibeReportArabic"
|
||||
path = Path(matplotlib.get_data_path()) / "fonts" / "ttf" / "DejaVuSans.ttf"
|
||||
pdfmetrics.registerFont(TTFont(family, str(path)))
|
||||
pdfmetrics.registerFontFamily(family, normal=family, bold=family, italic=family, boldItalic=family)
|
||||
return family
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def _reshaper():
|
||||
"""Keep Arabic vowel marks when shaping for visual RTL order."""
|
||||
import arabic_reshaper
|
||||
|
||||
return arabic_reshaper.ArabicReshaper(configuration={
|
||||
"delete_harakat": False, "shift_harakat_position": True,
|
||||
})
|
||||
|
||||
|
||||
def _visual_line(text: str, direction: str) -> str:
|
||||
"""Shape Arabic and apply the Unicode bidi algorithm to one logical line."""
|
||||
from bidi.algorithm import get_display
|
||||
|
||||
return get_display(_reshaper().reshape(text), base_dir=direction)
|
||||
|
||||
|
||||
def _font_runs(text: str, cjk: str, arabic: str) -> list[tuple[str, str]]:
|
||||
"""Select embedded fonts by real glyph coverage; never emit missing boxes."""
|
||||
from reportlab.pdfbase import pdfmetrics
|
||||
|
||||
coverage = {family: pdfmetrics.getFont(family).face.charToGlyph for family in (arabic, cjk)}
|
||||
|
||||
def select(char: str) -> str:
|
||||
for family, cmap in coverage.items():
|
||||
if cmap.get(ord(char), 0):
|
||||
return family
|
||||
raise ValueError(f"PDF fonts do not cover U+{ord(char):04X}")
|
||||
|
||||
return [(family, "".join(chars)) for family, chars in groupby(text, key=select)]
|
||||
|
||||
|
||||
def _rtl_lines(text: str, width: float, size: float, cjk: str, arabic: str) -> tuple[list[tuple[str, str]], str]:
|
||||
"""Wrap logical text before shaping/reordering each visual line.
|
||||
|
||||
Reordering the whole paragraph before wrapping reverses its line order.
|
||||
Widths use the actual shaped glyphs and the font selected for each run.
|
||||
"""
|
||||
from bidi.algorithm import get_base_level
|
||||
from reportlab.pdfbase.pdfmetrics import stringWidth
|
||||
|
||||
direction = "R" if get_base_level(text) else "L"
|
||||
|
||||
def fits(value: str) -> bool:
|
||||
visual = _visual_line(value, direction)
|
||||
return sum(stringWidth(run, family, size) for family, run in _font_runs(visual, cjk, arabic)) <= width
|
||||
|
||||
lines: list[str] = []
|
||||
current = ""
|
||||
for word in text.split():
|
||||
candidate = f"{current} {word}" if current else word
|
||||
if fits(candidate):
|
||||
current = candidate
|
||||
continue
|
||||
if current:
|
||||
lines.append(current)
|
||||
current = ""
|
||||
if fits(word):
|
||||
current = word
|
||||
continue
|
||||
# A long URL or CJK run has no word separator. Split in logical order,
|
||||
# keeping combining marks attached to their base character.
|
||||
clusters: list[str] = []
|
||||
for char in word:
|
||||
if clusters and unicodedata.combining(char):
|
||||
clusters[-1] += char
|
||||
else:
|
||||
clusters.append(char)
|
||||
for cluster in clusters:
|
||||
if current and not fits(current + cluster):
|
||||
lines.append(current)
|
||||
current = ""
|
||||
if not fits(cluster):
|
||||
raise ValueError("A PDF glyph is wider than the report column")
|
||||
current += cluster
|
||||
if current:
|
||||
lines.append(current)
|
||||
return [(line, _visual_line(line, direction)) for line in lines], direction
|
||||
|
||||
|
||||
def _paragraph(markup: str, style, cjk: str):
|
||||
"""Keep the standard layout for LTR/CJK and reorder RTL after line breaks."""
|
||||
from reportlab.platypus import Flowable, Paragraph
|
||||
|
||||
logical = Paragraph(markup, style)
|
||||
text = logical.getPlainText()
|
||||
if not any(unicodedata.bidirectional(char) in {"R", "AL"} for char in text):
|
||||
return logical
|
||||
|
||||
class BidirectionalParagraph(Flowable):
|
||||
def __init__(self, prepared=None, direction=None):
|
||||
super().__init__()
|
||||
self.style = style
|
||||
self.lines = prepared
|
||||
self.direction = direction
|
||||
|
||||
def wrap(self, availWidth, availHeight):
|
||||
if self.lines is None:
|
||||
self.lines, self.direction = _rtl_lines(
|
||||
text, max(1, availWidth - style.leftIndent - style.rightIndent),
|
||||
style.fontSize, cjk, _arabic_font(),
|
||||
)
|
||||
self.width = availWidth
|
||||
self.height = len(self.lines) * style.leading
|
||||
return self.width, self.height
|
||||
|
||||
def split(self, availWidth, availHeight):
|
||||
self.wrap(availWidth, availHeight)
|
||||
count = int(availHeight // style.leading)
|
||||
if count <= 0:
|
||||
return []
|
||||
first = BidirectionalParagraph(self.lines[:count], self.direction)
|
||||
rest = BidirectionalParagraph(self.lines[count:], self.direction)
|
||||
first.spaceAfter = 0
|
||||
rest.spaceBefore = 0
|
||||
return [first, rest] if rest.lines else [first]
|
||||
|
||||
def draw(self):
|
||||
from reportlab.pdfbase.pdfmetrics import stringWidth
|
||||
|
||||
canvas = self.canv
|
||||
canvas.saveState()
|
||||
if style.backColor:
|
||||
canvas.setFillColor(style.backColor)
|
||||
canvas.rect(0, 0, self.width, self.height, stroke=0, fill=1)
|
||||
canvas.setFillColor(style.textColor)
|
||||
for index, (logical_line, visual) in enumerate(self.lines):
|
||||
runs = _font_runs(visual, cjk, _arabic_font())
|
||||
width = sum(stringWidth(run, family, style.fontSize) for family, run in runs)
|
||||
x = self.width - style.rightIndent - width if self.direction == "R" else style.leftIndent
|
||||
y = self.height - (index + 1) * style.leading + max(0, (style.leading - style.fontSize) / 2)
|
||||
# PDF ActualText supplies canonical logical text for copy/search;
|
||||
# glyph positions remain the shaped visual order. Hex UTF-16BE
|
||||
# keeps user text out of PDF operator syntax.
|
||||
actual = ("\ufeff" + logical_line).encode("utf-16-be").hex()
|
||||
canvas._code.append(f"/Span << /ActualText <{actual}> >> BDC")
|
||||
for family, run in runs:
|
||||
canvas.setFont(family, style.fontSize)
|
||||
canvas.drawString(x, y, run)
|
||||
x += stringWidth(run, family, style.fontSize)
|
||||
canvas._code.append("EMC")
|
||||
canvas.restoreState()
|
||||
|
||||
return BidirectionalParagraph()
|
||||
|
||||
def render_markdown_pdf(content: str, target: Path) -> None:
|
||||
"""Render a paginated report atomically, retaining an old file on failure.
|
||||
|
||||
@@ -54,7 +217,7 @@ def render_markdown_pdf(content: str, target: Path) -> None:
|
||||
in_code = not in_code
|
||||
continue
|
||||
if in_code:
|
||||
story.append(Paragraph(escape(lines[position - 1]).replace(" ", " "), code))
|
||||
story.append(_paragraph(escape(lines[position - 1]).replace(" ", " "), code, family))
|
||||
continue
|
||||
if not line:
|
||||
continue
|
||||
@@ -65,7 +228,7 @@ def render_markdown_pdf(content: str, target: Path) -> None:
|
||||
rows.append([part.strip() for part in lines[position].strip().strip("|").split("|")])
|
||||
position += 1
|
||||
columns = max(map(len, rows))
|
||||
table = Table([[Paragraph(inline(value), cell) for value in row + [""] * (columns - len(row))]
|
||||
table = Table([[_paragraph(inline(value), cell, family) for value in row + [""] * (columns - len(row))]
|
||||
for row in rows], colWidths=[width / columns] * columns, repeatRows=1,
|
||||
splitInRow=1)
|
||||
table.setStyle(TableStyle([
|
||||
@@ -79,11 +242,11 @@ def render_markdown_pdf(content: str, target: Path) -> None:
|
||||
continue
|
||||
heading = re.match(r"^(#{1,4})\s+(.+)", line)
|
||||
if heading:
|
||||
story.append(Paragraph(inline(heading[2]), headings[len(heading[1])]))
|
||||
story.append(_paragraph(inline(heading[2]), headings[len(heading[1])], family))
|
||||
else:
|
||||
line = re.sub(r"^[-*+]\s+", "• ", line)
|
||||
line = re.sub(r"^>\s?", "", line)
|
||||
story.append(Paragraph(inline(line), body))
|
||||
story.append(_paragraph(inline(line), body, family))
|
||||
if not story:
|
||||
story.append(Paragraph(" ", body))
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
"""Portable Arabic report rendering through real fonts, layout, and PDF bytes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import unicodedata
|
||||
import base64
|
||||
import re
|
||||
import zlib
|
||||
|
||||
import pypdfium2 as pdfium
|
||||
import pytest
|
||||
from reportlab.pdfbase import pdfmetrics
|
||||
|
||||
from src.shadow_account.pdf_fallback import _ensure_font
|
||||
from src.tools import pdf_report
|
||||
|
||||
|
||||
def _text(path):
|
||||
with pdfium.PdfDocument(path) as document:
|
||||
return "\n".join(document[index].get_textpage().get_text_range() for index in range(len(document)))
|
||||
|
||||
|
||||
def test_arabic_shapes_and_preserves_latin_numbers_and_diacritics():
|
||||
logical = "تقرير مُهِمّ: AAPL 123.45 USD و BTC-USDT 67,890.12"
|
||||
visual = pdf_report._visual_line(logical, "R")
|
||||
assert "AAPL 123.45 USD" in visual
|
||||
assert "BTC-USDT 67,890.12" in visual
|
||||
assert any(0xFE70 <= ord(char) <= 0xFEFF for char in visual)
|
||||
assert {char for char in logical if unicodedata.combining(char)} <= set(visual)
|
||||
assert visual != logical and visual != logical[::-1]
|
||||
|
||||
|
||||
def test_every_mixed_script_glyph_has_a_real_embedded_font():
|
||||
cjk, arabic = _ensure_font(), pdf_report._arabic_font()
|
||||
assert pdfmetrics.getFont(cjk).face.charToGlyph.get(ord("ت"), 0) == 0
|
||||
visual = pdf_report._visual_line("تقرير عربي 中国股票 AAPL 123.45 ١٢٣", "R")
|
||||
runs = pdf_report._font_runs(visual, cjk, arabic)
|
||||
assert {font for font, _ in runs} == {cjk, arabic}
|
||||
for font, text in runs:
|
||||
cmap = pdfmetrics.getFont(font).face.charToGlyph
|
||||
assert all(cmap.get(ord(char), 0) > 0 for char in text)
|
||||
|
||||
|
||||
def test_wrapping_keeps_logical_paragraph_order_and_column_width():
|
||||
from bidi.algorithm import get_display
|
||||
|
||||
cjk, arabic = _ensure_font(), pdf_report._arabic_font()
|
||||
source = "البداية " + "تقرير الأسواق العربية والمخاطر " * 8 + "النهاية"
|
||||
lines, direction = pdf_report._rtl_lines(source, 140, 10, cjk, arabic)
|
||||
assert direction == "R" and len(lines) > 3
|
||||
restored = [unicodedata.normalize("NFKC", get_display(visual, base_dir="R")) for _, visual in lines]
|
||||
assert " ".join(restored) == source
|
||||
assert restored[0].startswith("البداية")
|
||||
assert restored[-1].endswith("النهاية")
|
||||
for _, line in lines:
|
||||
width = sum(pdfmetrics.stringWidth(text, font, 10) for font, text in pdf_report._font_runs(line, cjk, arabic))
|
||||
assert width <= 140
|
||||
|
||||
|
||||
def test_real_mixed_arabic_markdown_table_pdf(tmp_path):
|
||||
target = tmp_path / "report.pdf"
|
||||
pdf_report.render_markdown_pdf(
|
||||
"# تقرير الأسواق\n\nتحليل AAPL 123.45 USD و ١٢٣\n\n"
|
||||
"| السوق | السعر | Notes |\n| --- | --- | --- |\n"
|
||||
"| الصين 中国 | 600519.SH | تقرير عربي |\n"
|
||||
"| AAPL | 123.45 USD | 中文报告 |\n\n"
|
||||
"## English / 中文\n\nA **bold** report with < & > and [source](https://example.com).\n"
|
||||
"```\nprice = 123.45\n```\n",
|
||||
target,
|
||||
)
|
||||
data = target.read_bytes()
|
||||
assert data.startswith(b"%PDF-") and data.rstrip().endswith(b"%%EOF")
|
||||
assert data.count(b"/FontFile2") >= 2 and b"/ToUnicode" in data
|
||||
text = _text(target)
|
||||
for value in ["AAPL", "123.45 USD", "600519", "SH", "中国", "中文报告", "English", "price = 123.45"]:
|
||||
assert value in text
|
||||
assert "\x00" not in text and "\ufffd" not in text
|
||||
# PDFium applies its own bidi heuristic across an entire table row; it
|
||||
# may reorder 600519.SH into SH600519. ActualText is checked independently.
|
||||
decoded = []
|
||||
for stream in re.findall(rb"stream\r?\n(.*?)endstream", data, re.S):
|
||||
try:
|
||||
decoded.append(zlib.decompress(base64.a85decode(stream.strip(), adobe=True)))
|
||||
except (ValueError, zlib.error):
|
||||
continue
|
||||
canonical = ("\ufeff" + "تحليل AAPL 123.45 USD و ١٢٣").encode("utf-16-be").hex().encode()
|
||||
assert b"/ActualText <" + canonical + b">" in b"\n".join(decoded)
|
||||
normalized = unicodedata.normalize("NFKC", text)
|
||||
assert any(unicodedata.bidirectional(char) == "AL" for char in normalized)
|
||||
assert not list(tmp_path.glob(".report-*.pdf"))
|
||||
|
||||
|
||||
def test_arabic_paragraph_and_tall_table_cell_paginate(tmp_path):
|
||||
target = tmp_path / "long.pdf"
|
||||
text = "البداية " + "تقرير الأسواق العربية AAPL 123.45 والمخاطر. " * 160 + " النهاية"
|
||||
pdf_report.render_markdown_pdf(
|
||||
"# تقرير طويل\n\n" + text + "\n\n| الوصف | القيمة |\n| --- | --- |\n| " + text + " | 123.45 |\n",
|
||||
target,
|
||||
)
|
||||
with pdfium.PdfDocument(target) as document:
|
||||
assert len(document) >= 3
|
||||
extracted = _text(target)
|
||||
assert extracted.count("123.45") == 321
|
||||
|
||||
|
||||
@pytest.mark.parametrize("failure", ["missing_glyph", "replace"])
|
||||
def test_arabic_render_failure_retains_old_target_and_cleans_temporary(tmp_path, monkeypatch, failure):
|
||||
target = tmp_path / "report.pdf"
|
||||
target.write_bytes(b"previous PDF")
|
||||
content = "تقرير عربي"
|
||||
if failure == "missing_glyph":
|
||||
content += " \U0001fae0" # Absent from both packaged report fonts.
|
||||
else:
|
||||
def denied(*args):
|
||||
raise OSError("replace denied")
|
||||
monkeypatch.setattr(pdf_report.os, "replace", denied)
|
||||
with pytest.raises((ValueError, OSError)):
|
||||
pdf_report.render_markdown_pdf(content, target)
|
||||
assert target.read_bytes() == b"previous PDF"
|
||||
assert not list(tmp_path.glob(".report-*.pdf"))
|
||||
|
||||
|
||||
|
||||
def test_tall_mixed_table_keeps_every_glyph_on_its_page(tmp_path):
|
||||
target = tmp_path / "mixed-pages.pdf"
|
||||
content = ("هذا تقرير للأسواق مع AAPL 123.45 USD و 中文报告 والعائد +5.25%. " * 90)
|
||||
content += "\n\n| وصف | قيمة |\n| --- | --- |\n| "
|
||||
content += "تقرير طويل 中文 123.45 USD. " * 160
|
||||
content += " | 123.45 |\n"
|
||||
pdf_report.render_markdown_pdf(content, target)
|
||||
with pdfium.PdfDocument(target) as document:
|
||||
assert len(document) >= 3
|
||||
for page_number, page in enumerate(document):
|
||||
width, height = page.get_size()
|
||||
text = page.get_textpage()
|
||||
for index in range(text.count_chars()):
|
||||
left, bottom, right, top = text.get_charbox(index)
|
||||
assert left >= -1 and bottom >= -1 and right <= width + 1 and top <= height + 1, (
|
||||
page_number, index, (left, bottom, right, top), (width, height)
|
||||
)
|
||||
@@ -172,6 +172,10 @@ anyio==4.14.2 \
|
||||
# sse-starlette
|
||||
# starlette
|
||||
# watchfiles
|
||||
arabic-reshaper==3.0.1 \
|
||||
--hash=sha256:41c5adc2420f85758eada7e880251c4b6a2adbd83377bd27e5d4eba71f648bc7 \
|
||||
--hash=sha256:a0d9b2a9fa29b5f2c1d705f407adf6ca4242405b9cac0e5cc09e6c4f3f8fb68c
|
||||
# via -r agent/requirements.txt
|
||||
attrs==26.1.0 \
|
||||
--hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \
|
||||
--hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32
|
||||
@@ -2921,6 +2925,133 @@ pyphen==0.17.2 \
|
||||
--hash=sha256:3a07fb017cb2341e1d9ff31b8634efb1ae4dc4b130468c7c39dd3d32e7c3affd \
|
||||
--hash=sha256:f60647a9c9b30ec6c59910097af82bc5dd2d36576b918e44148d8b07ef3b4aa3
|
||||
# via weasyprint
|
||||
python-bidi==0.6.11 \
|
||||
--hash=sha256:034090c597af250d699299d7e7f1e83eb016f9e47b3b707bd89ab2bdec77bce0 \
|
||||
--hash=sha256:0608bddcc1c53dfa5293499de13ca9935b31aa46d1c722c404a88c703d1a4e47 \
|
||||
--hash=sha256:0d339c4ebf3c2501d7569971a22e762fb45d8af7e19a0573afe260d69b35fb0d \
|
||||
--hash=sha256:0d496f9fc21b7457e12395e54088ab99776966c663b4cd4a74770c7a6418ab59 \
|
||||
--hash=sha256:19bae96ff1ee76b3a7dc962598b69426538e3021460cf85a4017437548ab6947 \
|
||||
--hash=sha256:19d13c70b1f7bb5dc69866a11624f3b78123807b4ede18f4f99f656ed86babec \
|
||||
--hash=sha256:1a639a2c62f825e72eb42901fd1c69296d09c192ae003195e6cf773f92d6bb42 \
|
||||
--hash=sha256:1ad5f712a32be30d28eb96e119e85818747a43cea253de6c3160b464b62a5619 \
|
||||
--hash=sha256:1b41cc6bc9ad78a12da5f987da15e931c771f18ceca58f2fe8ed50f253490a97 \
|
||||
--hash=sha256:1ffd728f1f7866ff7399906bbc17ef5cf010b90ce56a1e94937b28a1a4cf5a7d \
|
||||
--hash=sha256:20ab47a4098577fc9a82816c330c89ff597c31ba69c98bc6a1b6a5737b03de05 \
|
||||
--hash=sha256:239a00b2adb5f897d11d7b7a491d759fb9883e71a71cfd90c4147a733b4df4d3 \
|
||||
--hash=sha256:2b208fc26ab2c7adfd2c4d84753a5045f664e0bec19ca462af2406132e62f92e \
|
||||
--hash=sha256:2d6970b09f5a3102c0aa192f5258c585742ab4ebd94f637a635ad3448ccba567 \
|
||||
--hash=sha256:2ff75d1befc335cbe85f834e81554a024f94d9b5d1dd75a5bd99af81a1cb783c \
|
||||
--hash=sha256:302f4ca7dabfe447e707d40e98520551181036c15750bdd1e73292ad8b3d8e75 \
|
||||
--hash=sha256:30d543b5baf9fca5ef5ec95647aa07c5e38fc7fa0f18be0c61d1d6c0a1032c6c \
|
||||
--hash=sha256:337ec315f8b286399e411f5bfc339fc2124db07757409b0595ceb46de087c4cd \
|
||||
--hash=sha256:3611d13b53d4c899c4f2a7cd8eb897064e8b5546c3c5d1037dd6209c82858a27 \
|
||||
--hash=sha256:36f23f12d9b1c56ed8d82e11f56c6cba7bc3f614ee73374bc7772bb2270e0966 \
|
||||
--hash=sha256:380b70d615647646dbe06f7d95dc30b8fdc9b596dbfa1cd3814feb9805c0c8f2 \
|
||||
--hash=sha256:397f7f289eba6ce25d99dbea99f873d699bf9aa030074e7fb746d8f93c2fb6f9 \
|
||||
--hash=sha256:3bdb64ee0a74951465cd4a761e1029e73806ff43b2fe5be98643e52da5cabb66 \
|
||||
--hash=sha256:3da9e536546f7c62c0da595c8f71a096e0b9a80e94cfd0f329b7b200ef81e7d5 \
|
||||
--hash=sha256:41c7d1914c1f33954aa2ab0e2c309be5d1d4afc75ce524762eaab4dd825c5ab8 \
|
||||
--hash=sha256:43f3e81bdd36f49171b7de6cf471086df503c29555f6f7f035ebe8f8ec1da779 \
|
||||
--hash=sha256:490118f4af5a3535923c76be07e937ef1cb4cf84c489b37471aa95455fe923b3 \
|
||||
--hash=sha256:493655043ebea6fec0edb76053bdad46f1f7352a759ca4deb733f13b3c09839c \
|
||||
--hash=sha256:495a76c881d78ab87b57c5270679c9bc3c1de36d8c6596d5e3a5a1b5f9c57471 \
|
||||
--hash=sha256:4ac819cac1abb15486c48af3399a5c726e89f0977a3aff205ac162533186e756 \
|
||||
--hash=sha256:4ccf1fe9ecb3b02a1a11b103cd2557e2653d82c141b6e2dccec8177e6af5c4bb \
|
||||
--hash=sha256:4d00757ef7bbbf14d8628f9bdb6b0e168d5e7b03fec20da3226624f11bffce89 \
|
||||
--hash=sha256:51915502898c45e9cb36636e974aca068fc5cdb9f06b794f49abea5b12f02016 \
|
||||
--hash=sha256:519eb90bedb04b5cbba0bfc8062984a5ba054a019d81ada34053dc67397d61cb \
|
||||
--hash=sha256:539d99efe02f4981171ed57bbc085094ef780405ca14663550a56c6c3e265c34 \
|
||||
--hash=sha256:555cdf9303c40bae1ab512ca427f1f0316a574bc0a48db22eec76ec0fd1213cf \
|
||||
--hash=sha256:55f27bd2cdeca96f46448a741d393575fbedfc70e38e09dbb030ed98dba8cf2a \
|
||||
--hash=sha256:55fa5b22cc8c220a6daf077570a8e29d3b607bd978bd3dbc04b445d01a17de57 \
|
||||
--hash=sha256:565d819fddb2bbc58c42ca5c97d73da7567f181115011e8f04c76b9d08378dcd \
|
||||
--hash=sha256:56807e0dde88d5ab96880c9d668bda7bca1df83cd30d20cbff5d6b6e6c1e9276 \
|
||||
--hash=sha256:56dfa3c1c13cb89da39a4e8f2f9442e4291de01877511a13f830125d36e9ce5b \
|
||||
--hash=sha256:56f27c1edfd15c12c9c348378ccd79166930d720cf316b1181a0a0ade2146253 \
|
||||
--hash=sha256:57aa56b1eca5f63ebdd25fe8fad02eab7797fc45ad1efc5eed2a830e2bd2038d \
|
||||
--hash=sha256:5dd49c11ae87ee6ced68594faa78b34a89a1a3c43647fa6157765f9726f9daf3 \
|
||||
--hash=sha256:5f3e1743b2d43377c4da5d687a03430a27f5769e158a9f75a50b05e7e82f4d21 \
|
||||
--hash=sha256:5f9ed312d445a691c0afec995ca4f726abe357d8da500e389f77fb874fa7ef76 \
|
||||
--hash=sha256:60150bffbe43bfc1e356c0c2900ba643a02ce09c37bc0c553c4d83e5c3de63ad \
|
||||
--hash=sha256:60d9bf6c60c022657637f64897e63dc3f5b1c07cb7f0e1ead6150aff5150c5ab \
|
||||
--hash=sha256:619ee3fe03daec8d3ce12239f0c22455676a063b2bcde361caecd788fae5b8d5 \
|
||||
--hash=sha256:62a6b700f3d7a2c4d52a5dccca765711a2414e734360b00365e155698a26e461 \
|
||||
--hash=sha256:63d9dcc714d549a5118ebc93b7a7c903a0c1feec8e57f5fcacf98d984b76325b \
|
||||
--hash=sha256:6623683fe39b9fbf508e3069f17e8e9cab26143f9d9f89c8a8f45424c052df4f \
|
||||
--hash=sha256:68c3e570f2908ac39db0b269f5648e257fab5204344b9bf6dae80852ae4cbe1c \
|
||||
--hash=sha256:69d1f4ee17644e8aeac93a7238ee2f28d79b0180815441eb511b77e6585aa971 \
|
||||
--hash=sha256:6c92d1cad16f9ec2f2a3ae439a0bc3a8e4189ec227987bed03d5b4056d5eb9c5 \
|
||||
--hash=sha256:6ca92a4e460f7e25e434a6d7982d94a4765ca242f527995da70abb5a32003b8a \
|
||||
--hash=sha256:6d9ef69c108b31f38e1f281a55fdedad7774bc1e952a45c8c14a18e891eee397 \
|
||||
--hash=sha256:6dc112e2239f69913273cbb0c050ca816b145b32037d4266c430159c5ddcdab2 \
|
||||
--hash=sha256:721697187f4da67dafc26f63488f463fa35ff2de8668d959fda773cccdbef0eb \
|
||||
--hash=sha256:73c38c604bfc01647c69ce38e5cf8b4206e2ede91ca3eb8e5d79b7409f17e0b3 \
|
||||
--hash=sha256:74457b43db34f984252e915828b5d1a4042a771f44e853a5643506d01562eccb \
|
||||
--hash=sha256:7738cfc7ee9fcdff3fef76b40007982ce7704010a51df307c4a51d378f5ff1ba \
|
||||
--hash=sha256:777fb44a6f1e91d6adde36e815024c210b0208f35d00762880257b8fb04dc849 \
|
||||
--hash=sha256:788c11c84b520ea973992cc95751d56b783ff5857df504c1347d99cacd2fcfe7 \
|
||||
--hash=sha256:79df1099a08e53edb678236d4d76d8de4e3901bafc84ce1788b71f9b96547325 \
|
||||
--hash=sha256:7c4dc0d1328321aeb08054be654e1d39f171d5aae8dca931c9332860f18f57d7 \
|
||||
--hash=sha256:7cb34dd8d22ca1847653c0e1cbba8fd96de09c24b8c83b7805eab270c81e863a \
|
||||
--hash=sha256:8013c1e6267a929be98d10c3a617171709c8226bd33c6180ec65e560c35b6df8 \
|
||||
--hash=sha256:80cfe97e2d65981be877ae5bd2338c6919a7cc1171fc308c8b8c7c306ba6ffd8 \
|
||||
--hash=sha256:83c780f7e4c3dd3f020db75dc425567981e65c6d5571b3c0372203d0df92c834 \
|
||||
--hash=sha256:83ee87feb5eafc0442e1db0014dad20d52a2a7a140b6cddc8f7bc65918f0a7b4 \
|
||||
--hash=sha256:879c3fba3e7511c7d01020449d970ca7d6a593f20cfc47c014d3d229a38930b2 \
|
||||
--hash=sha256:8b6b7fce8f47578be9aebf5a0b6b2d6c157b4e97af7586ecf13bfca5d128deea \
|
||||
--hash=sha256:8eb09af209cd660fa9689f6ce9e61e73c8afa4829ae61801deea7f6e32263800 \
|
||||
--hash=sha256:8fbb6d222b50324fb9d49b6ff0f8566fa97b907a68c00e6622fcf34463104f4a \
|
||||
--hash=sha256:9435acc52438c3c8f5142b9a17a622927618e80a32eed707343bc375cb51cebe \
|
||||
--hash=sha256:946b7dbec4e64017680f1a66b3a8534659d889018ac83bd2abf958278e6f62b0 \
|
||||
--hash=sha256:94f7f66bd7680d3f2085adaeea91c01b3281a79bb5041db1a51cbe8af36a9d88 \
|
||||
--hash=sha256:959335cd3814cb767fb832c5c71cbc838ccd9231a812ef2cb43092a216a91d5b \
|
||||
--hash=sha256:969ee7db3e169fcc0b2d2d094826e03cc5798dfd6b3571a340ea883672396cb1 \
|
||||
--hash=sha256:9af2b5c26a3eb960699dff040535a86dc2c0f708087b2d63bcfd6452fe9d0664 \
|
||||
--hash=sha256:9d18916c97855d68515ca2b3581948b6d864c304cd386210ab5aff5e0fb9be2c \
|
||||
--hash=sha256:a1115f3f02eb836b39e0c986a6b9e92c4377a1e5a680409650b02ec6b1a795e6 \
|
||||
--hash=sha256:a1b5ee069001bf7f4fff109598a9396caa96bb35e1b906b2c6d1bab9f9b2c4bd \
|
||||
--hash=sha256:a4f4a0cd24c09df748bfdc207b089c00e7af19f3151063f4cd74ac658290186b \
|
||||
--hash=sha256:a52f7ad9ef9091e81869e5d255e796755ccf542ade14dda17647cb7d7ffe1b9c \
|
||||
--hash=sha256:a873e6f8ab28b5a56201d5cdb98f4c7d2ff88e636a58659c5ac271e287001658 \
|
||||
--hash=sha256:a9aa2890661b238730e680ccd6eb06f4da625b2dbc1730052dae6f2d88957192 \
|
||||
--hash=sha256:ab8af1cc29c3f400a0f781d50f8ab52a8c63adf2fcef16f14641cd97e06a80e0 \
|
||||
--hash=sha256:acccb6d90e694684db2d314db2e2b0d3b8949bf1cfaec6d9808a8889f548add7 \
|
||||
--hash=sha256:ad5d9b8e8a6c330208eba413db506de58f21dbf88a1f1d5d75ef5f9e0e714adf \
|
||||
--hash=sha256:af7711cc6eeeadcb1aae877e0736a68e111c73a29562da6106c5e2fbb4dd83b2 \
|
||||
--hash=sha256:b2c759e13ebb81edaac3041697328bb1ca8433b55281723879f6b54e74881240 \
|
||||
--hash=sha256:b433840a924c8788f0abbda15d22c71dc636e2078d7d3ba39369cebe4bef74b8 \
|
||||
--hash=sha256:bde89739a979d9eb3ac48c4882c8a42dd528a7708b0faa99b90b551588bd5f8d \
|
||||
--hash=sha256:c17393753ddb77377f754ba2a88cacfe3056040a8a5dff8d43f9d5e51bbe1edc \
|
||||
--hash=sha256:c26cd9d81f820159026b1c99905ca12bf13892e3f6a9303359fef42fe6f39e50 \
|
||||
--hash=sha256:c6aca20472d7040bc1f0aadb510cb20d40b3d6caa4c8bf82d075ba2650a41652 \
|
||||
--hash=sha256:c6b3c853f99172ef22e5a16c8114cf243e351c8e70f72a894164088c2c99d9cb \
|
||||
--hash=sha256:caee7ee3662eab1411b44fef8571b87273cb5235061d7463ebd10e412ac07986 \
|
||||
--hash=sha256:cd564b8c583eba2d230d02d0467fd840045f08856b6524555cfff7f32af63c72 \
|
||||
--hash=sha256:ce2ef68dce82ae4067cf28910224d77b45eb2c2b3904db6eb670d1cddb8d0eb2 \
|
||||
--hash=sha256:cf4e88a6fec81b7155a487cbbea7753a3d9a76dc4d391b4f8958b37227ef2c12 \
|
||||
--hash=sha256:d14f2d400c75e07d1154299463a3d4d14aa5565b05088b2d9b314ccd9fd6dc3a \
|
||||
--hash=sha256:d52ec8ccdc2fd5c61749d876a9d1eb0ea6543c1676722e1e3fb9d7800852131c \
|
||||
--hash=sha256:d5695d87969fed5b3799b8a98848cb04fe2873fded46efe9f3f2f341efd1b829 \
|
||||
--hash=sha256:d7291e13496cb74fc1b71f7f1e3628586afefa531102bb4fa7af9c2d543efc3c \
|
||||
--hash=sha256:d73a821873c52635321196cfb8d3a231d7917ca284cf2bcd9422f6deb19db7ca \
|
||||
--hash=sha256:da51a3a2940478219f19249fcf7cd45e8ddc197982be22efa43c4763e9d2eb57 \
|
||||
--hash=sha256:dc69d594682fbedec7d0cc81cd4d5f14fb40b6b365587238fafb91509cfea020 \
|
||||
--hash=sha256:dfbb9ba8343a60daf4ced67c11d551dafe9a3c94892c326e4c216fa2e6eca802 \
|
||||
--hash=sha256:e142820537e08d567c2c017e26df05f88856a03f0f3948209cfcdc39617df363 \
|
||||
--hash=sha256:e4ebc24ac38e50676f65daf7ba6c568789660cb60d6dcf2606d4310dba826721 \
|
||||
--hash=sha256:e6c474618a7b6a50c10007f8a9edb50def6d98d297a07a34dc2fb82c344f2b8b \
|
||||
--hash=sha256:f0ee4e80dd3b2a46868dcc73e00aa59f99c0e1de54a2ba783faf0fddba2ccce2 \
|
||||
--hash=sha256:f237ebb570fd8bbe479b6967374d82b7f0b26f9452c276bdd5f793d83e7062bd \
|
||||
--hash=sha256:f400a30573774a1e90c0d50d43c35d9c004afcf53de801bbfd259f84ea80f31c \
|
||||
--hash=sha256:f563d20481f7d316adf605bb94d5b7182acecdbc4d431d60473e9b1d526d0210 \
|
||||
--hash=sha256:f7a8429d0d65232e314b4f494825c1205abcc3039f42bf7da80424a15b731709 \
|
||||
--hash=sha256:f8655ac559dcfce7179b150ebcc207982d4d60e67b3089b2032eed3a0a78c07a \
|
||||
--hash=sha256:f99162d6c6c9522c46eb213f1bc932829c2602131676c92f081cb865b8ef6784 \
|
||||
--hash=sha256:fa2848c3116d619870d114471fcd4a9f1aa43587a002111d7af1e6582f1b57e9 \
|
||||
--hash=sha256:fc3b0a6e2460f68de9ab98f71e3098bb21bb984563417ad104dec7ab08ebcadc \
|
||||
--hash=sha256:fccd1808bb427d6a6d34168461bef551faa93ce3dda489fcef9073bcd9b34a5e \
|
||||
--hash=sha256:ff675d036823bff05a2e0f8cdb13f5414274ff517d13b0d57c15527015eb6acb
|
||||
# via -r agent/requirements.txt
|
||||
python-dateutil==2.9.0.post0 \
|
||||
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
|
||||
--hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
|
||||
|
||||
@@ -79,6 +79,8 @@ dependencies = [
|
||||
# ships binary wheels everywhere and backs the pure-Python PDF fallback
|
||||
# (shadow_account/pdf_fallback.py) so the PDF renders on any host.
|
||||
"reportlab>=4.0",
|
||||
"arabic-reshaper>=3.0.1",
|
||||
"python-bidi>=0.6.11",
|
||||
"prompt_toolkit>=3.0.0",
|
||||
]
|
||||
|
||||
|
||||
@@ -172,6 +172,10 @@ anyio==4.14.2 \
|
||||
# sse-starlette
|
||||
# starlette
|
||||
# watchfiles
|
||||
arabic-reshaper==3.0.1 \
|
||||
--hash=sha256:41c5adc2420f85758eada7e880251c4b6a2adbd83377bd27e5d4eba71f648bc7 \
|
||||
--hash=sha256:a0d9b2a9fa29b5f2c1d705f407adf6ca4242405b9cac0e5cc09e6c4f3f8fb68c
|
||||
# via -r agent/requirements.txt
|
||||
attrs==26.1.0 \
|
||||
--hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \
|
||||
--hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32
|
||||
@@ -2974,6 +2978,133 @@ pyphen==0.18.1 \
|
||||
--hash=sha256:0aa9051e15928cecadd4c632cea0258ba57215b2a197a39baa46abcdb0f47e84 \
|
||||
--hash=sha256:dbae6fbbe4f01cb206108b43573d857c67107be9d0e38eb1b08d6fa2210634a7
|
||||
# via weasyprint
|
||||
python-bidi==0.6.11 \
|
||||
--hash=sha256:034090c597af250d699299d7e7f1e83eb016f9e47b3b707bd89ab2bdec77bce0 \
|
||||
--hash=sha256:0608bddcc1c53dfa5293499de13ca9935b31aa46d1c722c404a88c703d1a4e47 \
|
||||
--hash=sha256:0d339c4ebf3c2501d7569971a22e762fb45d8af7e19a0573afe260d69b35fb0d \
|
||||
--hash=sha256:0d496f9fc21b7457e12395e54088ab99776966c663b4cd4a74770c7a6418ab59 \
|
||||
--hash=sha256:19bae96ff1ee76b3a7dc962598b69426538e3021460cf85a4017437548ab6947 \
|
||||
--hash=sha256:19d13c70b1f7bb5dc69866a11624f3b78123807b4ede18f4f99f656ed86babec \
|
||||
--hash=sha256:1a639a2c62f825e72eb42901fd1c69296d09c192ae003195e6cf773f92d6bb42 \
|
||||
--hash=sha256:1ad5f712a32be30d28eb96e119e85818747a43cea253de6c3160b464b62a5619 \
|
||||
--hash=sha256:1b41cc6bc9ad78a12da5f987da15e931c771f18ceca58f2fe8ed50f253490a97 \
|
||||
--hash=sha256:1ffd728f1f7866ff7399906bbc17ef5cf010b90ce56a1e94937b28a1a4cf5a7d \
|
||||
--hash=sha256:20ab47a4098577fc9a82816c330c89ff597c31ba69c98bc6a1b6a5737b03de05 \
|
||||
--hash=sha256:239a00b2adb5f897d11d7b7a491d759fb9883e71a71cfd90c4147a733b4df4d3 \
|
||||
--hash=sha256:2b208fc26ab2c7adfd2c4d84753a5045f664e0bec19ca462af2406132e62f92e \
|
||||
--hash=sha256:2d6970b09f5a3102c0aa192f5258c585742ab4ebd94f637a635ad3448ccba567 \
|
||||
--hash=sha256:2ff75d1befc335cbe85f834e81554a024f94d9b5d1dd75a5bd99af81a1cb783c \
|
||||
--hash=sha256:302f4ca7dabfe447e707d40e98520551181036c15750bdd1e73292ad8b3d8e75 \
|
||||
--hash=sha256:30d543b5baf9fca5ef5ec95647aa07c5e38fc7fa0f18be0c61d1d6c0a1032c6c \
|
||||
--hash=sha256:337ec315f8b286399e411f5bfc339fc2124db07757409b0595ceb46de087c4cd \
|
||||
--hash=sha256:3611d13b53d4c899c4f2a7cd8eb897064e8b5546c3c5d1037dd6209c82858a27 \
|
||||
--hash=sha256:36f23f12d9b1c56ed8d82e11f56c6cba7bc3f614ee73374bc7772bb2270e0966 \
|
||||
--hash=sha256:380b70d615647646dbe06f7d95dc30b8fdc9b596dbfa1cd3814feb9805c0c8f2 \
|
||||
--hash=sha256:397f7f289eba6ce25d99dbea99f873d699bf9aa030074e7fb746d8f93c2fb6f9 \
|
||||
--hash=sha256:3bdb64ee0a74951465cd4a761e1029e73806ff43b2fe5be98643e52da5cabb66 \
|
||||
--hash=sha256:3da9e536546f7c62c0da595c8f71a096e0b9a80e94cfd0f329b7b200ef81e7d5 \
|
||||
--hash=sha256:41c7d1914c1f33954aa2ab0e2c309be5d1d4afc75ce524762eaab4dd825c5ab8 \
|
||||
--hash=sha256:43f3e81bdd36f49171b7de6cf471086df503c29555f6f7f035ebe8f8ec1da779 \
|
||||
--hash=sha256:490118f4af5a3535923c76be07e937ef1cb4cf84c489b37471aa95455fe923b3 \
|
||||
--hash=sha256:493655043ebea6fec0edb76053bdad46f1f7352a759ca4deb733f13b3c09839c \
|
||||
--hash=sha256:495a76c881d78ab87b57c5270679c9bc3c1de36d8c6596d5e3a5a1b5f9c57471 \
|
||||
--hash=sha256:4ac819cac1abb15486c48af3399a5c726e89f0977a3aff205ac162533186e756 \
|
||||
--hash=sha256:4ccf1fe9ecb3b02a1a11b103cd2557e2653d82c141b6e2dccec8177e6af5c4bb \
|
||||
--hash=sha256:4d00757ef7bbbf14d8628f9bdb6b0e168d5e7b03fec20da3226624f11bffce89 \
|
||||
--hash=sha256:51915502898c45e9cb36636e974aca068fc5cdb9f06b794f49abea5b12f02016 \
|
||||
--hash=sha256:519eb90bedb04b5cbba0bfc8062984a5ba054a019d81ada34053dc67397d61cb \
|
||||
--hash=sha256:539d99efe02f4981171ed57bbc085094ef780405ca14663550a56c6c3e265c34 \
|
||||
--hash=sha256:555cdf9303c40bae1ab512ca427f1f0316a574bc0a48db22eec76ec0fd1213cf \
|
||||
--hash=sha256:55f27bd2cdeca96f46448a741d393575fbedfc70e38e09dbb030ed98dba8cf2a \
|
||||
--hash=sha256:55fa5b22cc8c220a6daf077570a8e29d3b607bd978bd3dbc04b445d01a17de57 \
|
||||
--hash=sha256:565d819fddb2bbc58c42ca5c97d73da7567f181115011e8f04c76b9d08378dcd \
|
||||
--hash=sha256:56807e0dde88d5ab96880c9d668bda7bca1df83cd30d20cbff5d6b6e6c1e9276 \
|
||||
--hash=sha256:56dfa3c1c13cb89da39a4e8f2f9442e4291de01877511a13f830125d36e9ce5b \
|
||||
--hash=sha256:56f27c1edfd15c12c9c348378ccd79166930d720cf316b1181a0a0ade2146253 \
|
||||
--hash=sha256:57aa56b1eca5f63ebdd25fe8fad02eab7797fc45ad1efc5eed2a830e2bd2038d \
|
||||
--hash=sha256:5dd49c11ae87ee6ced68594faa78b34a89a1a3c43647fa6157765f9726f9daf3 \
|
||||
--hash=sha256:5f3e1743b2d43377c4da5d687a03430a27f5769e158a9f75a50b05e7e82f4d21 \
|
||||
--hash=sha256:5f9ed312d445a691c0afec995ca4f726abe357d8da500e389f77fb874fa7ef76 \
|
||||
--hash=sha256:60150bffbe43bfc1e356c0c2900ba643a02ce09c37bc0c553c4d83e5c3de63ad \
|
||||
--hash=sha256:60d9bf6c60c022657637f64897e63dc3f5b1c07cb7f0e1ead6150aff5150c5ab \
|
||||
--hash=sha256:619ee3fe03daec8d3ce12239f0c22455676a063b2bcde361caecd788fae5b8d5 \
|
||||
--hash=sha256:62a6b700f3d7a2c4d52a5dccca765711a2414e734360b00365e155698a26e461 \
|
||||
--hash=sha256:63d9dcc714d549a5118ebc93b7a7c903a0c1feec8e57f5fcacf98d984b76325b \
|
||||
--hash=sha256:6623683fe39b9fbf508e3069f17e8e9cab26143f9d9f89c8a8f45424c052df4f \
|
||||
--hash=sha256:68c3e570f2908ac39db0b269f5648e257fab5204344b9bf6dae80852ae4cbe1c \
|
||||
--hash=sha256:69d1f4ee17644e8aeac93a7238ee2f28d79b0180815441eb511b77e6585aa971 \
|
||||
--hash=sha256:6c92d1cad16f9ec2f2a3ae439a0bc3a8e4189ec227987bed03d5b4056d5eb9c5 \
|
||||
--hash=sha256:6ca92a4e460f7e25e434a6d7982d94a4765ca242f527995da70abb5a32003b8a \
|
||||
--hash=sha256:6d9ef69c108b31f38e1f281a55fdedad7774bc1e952a45c8c14a18e891eee397 \
|
||||
--hash=sha256:6dc112e2239f69913273cbb0c050ca816b145b32037d4266c430159c5ddcdab2 \
|
||||
--hash=sha256:721697187f4da67dafc26f63488f463fa35ff2de8668d959fda773cccdbef0eb \
|
||||
--hash=sha256:73c38c604bfc01647c69ce38e5cf8b4206e2ede91ca3eb8e5d79b7409f17e0b3 \
|
||||
--hash=sha256:74457b43db34f984252e915828b5d1a4042a771f44e853a5643506d01562eccb \
|
||||
--hash=sha256:7738cfc7ee9fcdff3fef76b40007982ce7704010a51df307c4a51d378f5ff1ba \
|
||||
--hash=sha256:777fb44a6f1e91d6adde36e815024c210b0208f35d00762880257b8fb04dc849 \
|
||||
--hash=sha256:788c11c84b520ea973992cc95751d56b783ff5857df504c1347d99cacd2fcfe7 \
|
||||
--hash=sha256:79df1099a08e53edb678236d4d76d8de4e3901bafc84ce1788b71f9b96547325 \
|
||||
--hash=sha256:7c4dc0d1328321aeb08054be654e1d39f171d5aae8dca931c9332860f18f57d7 \
|
||||
--hash=sha256:7cb34dd8d22ca1847653c0e1cbba8fd96de09c24b8c83b7805eab270c81e863a \
|
||||
--hash=sha256:8013c1e6267a929be98d10c3a617171709c8226bd33c6180ec65e560c35b6df8 \
|
||||
--hash=sha256:80cfe97e2d65981be877ae5bd2338c6919a7cc1171fc308c8b8c7c306ba6ffd8 \
|
||||
--hash=sha256:83c780f7e4c3dd3f020db75dc425567981e65c6d5571b3c0372203d0df92c834 \
|
||||
--hash=sha256:83ee87feb5eafc0442e1db0014dad20d52a2a7a140b6cddc8f7bc65918f0a7b4 \
|
||||
--hash=sha256:879c3fba3e7511c7d01020449d970ca7d6a593f20cfc47c014d3d229a38930b2 \
|
||||
--hash=sha256:8b6b7fce8f47578be9aebf5a0b6b2d6c157b4e97af7586ecf13bfca5d128deea \
|
||||
--hash=sha256:8eb09af209cd660fa9689f6ce9e61e73c8afa4829ae61801deea7f6e32263800 \
|
||||
--hash=sha256:8fbb6d222b50324fb9d49b6ff0f8566fa97b907a68c00e6622fcf34463104f4a \
|
||||
--hash=sha256:9435acc52438c3c8f5142b9a17a622927618e80a32eed707343bc375cb51cebe \
|
||||
--hash=sha256:946b7dbec4e64017680f1a66b3a8534659d889018ac83bd2abf958278e6f62b0 \
|
||||
--hash=sha256:94f7f66bd7680d3f2085adaeea91c01b3281a79bb5041db1a51cbe8af36a9d88 \
|
||||
--hash=sha256:959335cd3814cb767fb832c5c71cbc838ccd9231a812ef2cb43092a216a91d5b \
|
||||
--hash=sha256:969ee7db3e169fcc0b2d2d094826e03cc5798dfd6b3571a340ea883672396cb1 \
|
||||
--hash=sha256:9af2b5c26a3eb960699dff040535a86dc2c0f708087b2d63bcfd6452fe9d0664 \
|
||||
--hash=sha256:9d18916c97855d68515ca2b3581948b6d864c304cd386210ab5aff5e0fb9be2c \
|
||||
--hash=sha256:a1115f3f02eb836b39e0c986a6b9e92c4377a1e5a680409650b02ec6b1a795e6 \
|
||||
--hash=sha256:a1b5ee069001bf7f4fff109598a9396caa96bb35e1b906b2c6d1bab9f9b2c4bd \
|
||||
--hash=sha256:a4f4a0cd24c09df748bfdc207b089c00e7af19f3151063f4cd74ac658290186b \
|
||||
--hash=sha256:a52f7ad9ef9091e81869e5d255e796755ccf542ade14dda17647cb7d7ffe1b9c \
|
||||
--hash=sha256:a873e6f8ab28b5a56201d5cdb98f4c7d2ff88e636a58659c5ac271e287001658 \
|
||||
--hash=sha256:a9aa2890661b238730e680ccd6eb06f4da625b2dbc1730052dae6f2d88957192 \
|
||||
--hash=sha256:ab8af1cc29c3f400a0f781d50f8ab52a8c63adf2fcef16f14641cd97e06a80e0 \
|
||||
--hash=sha256:acccb6d90e694684db2d314db2e2b0d3b8949bf1cfaec6d9808a8889f548add7 \
|
||||
--hash=sha256:ad5d9b8e8a6c330208eba413db506de58f21dbf88a1f1d5d75ef5f9e0e714adf \
|
||||
--hash=sha256:af7711cc6eeeadcb1aae877e0736a68e111c73a29562da6106c5e2fbb4dd83b2 \
|
||||
--hash=sha256:b2c759e13ebb81edaac3041697328bb1ca8433b55281723879f6b54e74881240 \
|
||||
--hash=sha256:b433840a924c8788f0abbda15d22c71dc636e2078d7d3ba39369cebe4bef74b8 \
|
||||
--hash=sha256:bde89739a979d9eb3ac48c4882c8a42dd528a7708b0faa99b90b551588bd5f8d \
|
||||
--hash=sha256:c17393753ddb77377f754ba2a88cacfe3056040a8a5dff8d43f9d5e51bbe1edc \
|
||||
--hash=sha256:c26cd9d81f820159026b1c99905ca12bf13892e3f6a9303359fef42fe6f39e50 \
|
||||
--hash=sha256:c6aca20472d7040bc1f0aadb510cb20d40b3d6caa4c8bf82d075ba2650a41652 \
|
||||
--hash=sha256:c6b3c853f99172ef22e5a16c8114cf243e351c8e70f72a894164088c2c99d9cb \
|
||||
--hash=sha256:caee7ee3662eab1411b44fef8571b87273cb5235061d7463ebd10e412ac07986 \
|
||||
--hash=sha256:cd564b8c583eba2d230d02d0467fd840045f08856b6524555cfff7f32af63c72 \
|
||||
--hash=sha256:ce2ef68dce82ae4067cf28910224d77b45eb2c2b3904db6eb670d1cddb8d0eb2 \
|
||||
--hash=sha256:cf4e88a6fec81b7155a487cbbea7753a3d9a76dc4d391b4f8958b37227ef2c12 \
|
||||
--hash=sha256:d14f2d400c75e07d1154299463a3d4d14aa5565b05088b2d9b314ccd9fd6dc3a \
|
||||
--hash=sha256:d52ec8ccdc2fd5c61749d876a9d1eb0ea6543c1676722e1e3fb9d7800852131c \
|
||||
--hash=sha256:d5695d87969fed5b3799b8a98848cb04fe2873fded46efe9f3f2f341efd1b829 \
|
||||
--hash=sha256:d7291e13496cb74fc1b71f7f1e3628586afefa531102bb4fa7af9c2d543efc3c \
|
||||
--hash=sha256:d73a821873c52635321196cfb8d3a231d7917ca284cf2bcd9422f6deb19db7ca \
|
||||
--hash=sha256:da51a3a2940478219f19249fcf7cd45e8ddc197982be22efa43c4763e9d2eb57 \
|
||||
--hash=sha256:dc69d594682fbedec7d0cc81cd4d5f14fb40b6b365587238fafb91509cfea020 \
|
||||
--hash=sha256:dfbb9ba8343a60daf4ced67c11d551dafe9a3c94892c326e4c216fa2e6eca802 \
|
||||
--hash=sha256:e142820537e08d567c2c017e26df05f88856a03f0f3948209cfcdc39617df363 \
|
||||
--hash=sha256:e4ebc24ac38e50676f65daf7ba6c568789660cb60d6dcf2606d4310dba826721 \
|
||||
--hash=sha256:e6c474618a7b6a50c10007f8a9edb50def6d98d297a07a34dc2fb82c344f2b8b \
|
||||
--hash=sha256:f0ee4e80dd3b2a46868dcc73e00aa59f99c0e1de54a2ba783faf0fddba2ccce2 \
|
||||
--hash=sha256:f237ebb570fd8bbe479b6967374d82b7f0b26f9452c276bdd5f793d83e7062bd \
|
||||
--hash=sha256:f400a30573774a1e90c0d50d43c35d9c004afcf53de801bbfd259f84ea80f31c \
|
||||
--hash=sha256:f563d20481f7d316adf605bb94d5b7182acecdbc4d431d60473e9b1d526d0210 \
|
||||
--hash=sha256:f7a8429d0d65232e314b4f494825c1205abcc3039f42bf7da80424a15b731709 \
|
||||
--hash=sha256:f8655ac559dcfce7179b150ebcc207982d4d60e67b3089b2032eed3a0a78c07a \
|
||||
--hash=sha256:f99162d6c6c9522c46eb213f1bc932829c2602131676c92f081cb865b8ef6784 \
|
||||
--hash=sha256:fa2848c3116d619870d114471fcd4a9f1aa43587a002111d7af1e6582f1b57e9 \
|
||||
--hash=sha256:fc3b0a6e2460f68de9ab98f71e3098bb21bb984563417ad104dec7ab08ebcadc \
|
||||
--hash=sha256:fccd1808bb427d6a6d34168461bef551faa93ce3dda489fcef9073bcd9b34a5e \
|
||||
--hash=sha256:ff675d036823bff05a2e0f8cdb13f5414274ff517d13b0d57c15527015eb6acb
|
||||
# via -r agent/requirements.txt
|
||||
python-dateutil==2.9.0.post0 \
|
||||
--hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \
|
||||
--hash=sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427
|
||||
|
||||
Reference in new issue
Block a user