Skip to main content

Python interface to generate executable Typst code.

Project description

typstpy

Introduction

typstpy is a python package for generating executable typst codes. This package is written primarily in functional programming paradigm with some OOP contents. Each module in this package has greater than 90% unit test coverage.

This package provides interfaces which are as close as possible to typst's native functions. Through typstpy and other data processing packages, you can generate data report instantly.

Repository on GitHub: python-typst. Homepage on PyPI: python-typst. Any contributions are welcome.

Installation

pip install typstpy

Current Supports

Package's function name Typst's function name Documentation on typst
align align https://typst.app/docs/reference/layout/align/
bibliography bibliography https://typst.app/docs/reference/model/bibliography/
block block https://typst.app/docs/reference/layout/block/
box box https://typst.app/docs/reference/layout/box/
bullet_list list https://typst.app/docs/reference/model/list/
circle circle https://typst.app/docs/reference/visualize/circle/
cite cite https://typst.app/docs/reference/model/cite/
cmyk cmyk https://typst.app/docs/reference/visualize/color/#definitions-cmyk
colbreak colbreak https://typst.app/docs/reference/layout/colbreak/
color color https://typst.app/docs/reference/visualize/color/
columns columns https://typst.app/docs/reference/layout/columns/
document document https://typst.app/docs/reference/model/document/
ellipse ellipse https://typst.app/docs/reference/visualize/ellipse/
emph emph https://typst.app/docs/reference/model/emph/
figure figure https://typst.app/docs/reference/model/figure/
footnote footnote https://typst.app/docs/reference/model/footnote/
gradient gradient https://typst.app/docs/reference/visualize/gradient/
grid grid https://typst.app/docs/reference/layout/grid/
heading heading https://typst.app/docs/reference/model/heading/
hide hide https://typst.app/docs/reference/layout/hide/
highlight highlight https://typst.app/docs/reference/text/highlight/
hspace h https://typst.app/docs/reference/layout/h/
image image https://typst.app/docs/reference/visualize/image/
layout layout https://typst.app/docs/reference/layout/layout/
line line https://typst.app/docs/reference/visualize/line/
linebreak linebreak https://typst.app/docs/reference/text/linebreak/
link link https://typst.app/docs/reference/model/link/
lorem lorem https://typst.app/docs/reference/text/lorem/
lower lower https://typst.app/docs/reference/text/lower/
luma luma https://typst.app/docs/reference/visualize/color/#definitions-luma
measure measure https://typst.app/docs/reference/layout/measure/
move move https://typst.app/docs/reference/layout/move/
numbered_list enum https://typst.app/docs/reference/model/enum/
numbering numbering https://typst.app/docs/reference/model/numbering/
oklab oklab https://typst.app/docs/reference/visualize/color/#definitions-oklab
oklch oklch https://typst.app/docs/reference/visualize/color/#definitions-oklch
outline outline https://typst.app/docs/reference/model/outline/
overline overline https://typst.app/docs/reference/text/overline/
padding pad https://typst.app/docs/reference/layout/pad/
page page https://typst.app/docs/reference/layout/page/
pagebreak pagebreak https://typst.app/docs/reference/layout/pagebreak/
par par https://typst.app/docs/reference/model/par/
parbreak parbreak https://typst.app/docs/reference/model/parbreak/
path path https://typst.app/docs/reference/visualize/path/
pattern pattern https://typst.app/docs/reference/visualize/pattern/
place place https://typst.app/docs/reference/layout/place/
polygon polygon https://typst.app/docs/reference/visualize/polygon/
quote quote https://typst.app/docs/reference/model/quote/
raw raw https://typst.app/docs/reference/text/raw/
rect rect https://typst.app/docs/reference/visualize/rect/
ref ref https://typst.app/docs/reference/model/ref/
repeat repeat https://typst.app/docs/reference/layout/repeat/
rgb rgb https://typst.app/docs/reference/visualize/color/#definitions-rgb
rotate rotate https://typst.app/docs/reference/layout/rotate/
scale scale https://typst.app/docs/reference/layout/scale/
skew skew https://typst.app/docs/reference/layout/skew/
smallcaps smallcaps https://typst.app/docs/reference/text/smallcaps/
smartquote smartquote https://typst.app/docs/reference/text/smartquote/
square square https://typst.app/docs/reference/visualize/square/
stack stack https://typst.app/docs/reference/layout/stack/
strike strike https://typst.app/docs/reference/text/strike/
strong strong https://typst.app/docs/reference/model/strong/
subscript sub https://typst.app/docs/reference/text/sub/
superscript super https://typst.app/docs/reference/text/super/
table table https://typst.app/docs/reference/model/table/
terms terms https://typst.app/docs/reference/model/terms/
text text https://typst.app/docs/reference/text/text/
underline underline https://typst.app/docs/reference/text/underline/
upper upper https://typst.app/docs/reference/text/upper/
vspace v https://typst.app/docs/reference/layout/v/

Design philosophy

What do typstpy do is just string concatenating. So the first problem occurred is how to render python object to valid typst parameter representation. It is obvious that to render None to none, True to true and False to false.

And how about other typst's basic types like Relative, Color and so on? In the early versions, I had defined a series of classes corresponding to these basic types, but not for long I found this solution was very awkward. You would import some useless classes before using, most of them is just restricting whether to add double quotes.

Change logs

  • 1.0.1: Implement set, show, and import.
  • 1.0.0: Completed documentation and test cases in layout, model, text and visualize modules. Improved functionality.
  • 1.0.0-beta.2: Improved the implementation and documentation of functions in the layout module.
  • 1.0.0-beta.1: Completely reconstructed the underlying implementation.

Examples

from typstpy.std import *

align:

>>> align('"Hello, World!"', 'center')
'#align("Hello, World!", center)'
>>> align('[Hello, World!]', 'center')
'#align([Hello, World!], center)'
>>> align(lorem(20), 'center')
'#align(lorem(20), center)'

bibliography:

>>> bibliography('"bibliography.bib"', style='"cell"')
'#bibliography("bibliography.bib", style: "cell")'

block:

>>> block('"Hello, World!"')
'#block("Hello, World!")'
>>> block('[Hello, World!]')
'#block([Hello, World!])'
>>> block(lorem(20))
'#block(lorem(20))'
>>> block(lorem(20), width='100%')
'#block(lorem(20), width: 100%)'

box:

>>> box('"Hello, World!"')
'#box("Hello, World!")'
>>> box('[Hello, World!]')
'#box([Hello, World!])'
>>> box(lorem(20))
'#box(lorem(20))'
>>> box(lorem(20), width='100%')
'#box(lorem(20), width: 100%)'

bullet_list:

>>> bullet_list(lorem(20), lorem(20), lorem(20))
'#list(lorem(20), lorem(20), lorem(20))'
>>> bullet_list(lorem(20), lorem(20), lorem(20), tight=False)
'#list(tight: false, lorem(20), lorem(20), lorem(20))'

circle:

>>> circle('[Hello, world!]')
'#circle([Hello, world!])'
>>> circle('[Hello, world!]', radius='10pt')
'#circle([Hello, world!], radius: 10pt)'
>>> circle('[Hello, world!]', width='100%', height='100%')
'#circle([Hello, world!], width: 100%, height: 100%)'

cite:

>>> cite('<label>')
'#cite(<label>)'
>>> cite('<label>', supplement='[Hello, World!]')
'#cite(<label>, supplement: [Hello, World!])'
>>> cite('<label>', form='"prose"')
'#cite(<label>, form: "prose")'
>>> cite('<label>', style='"annual-reviews"')
'#cite(<label>, style: "annual-reviews")'

cmyk:

>>> cmyk('0%', '0%', '0%', '0%')
'#cmyk(0%, 0%, 0%, 0%)'
>>> cmyk('50%', '50%', '50%', '50%')
'#cmyk(50%, 50%, 50%, 50%)'

colbreak:

>>> colbreak()
'#colbreak()'
>>> colbreak(weak=True)
'#colbreak(weak: true)'

color:

>>> color()
'#color'

columns:

>>> columns(lorem(20))
'#columns(lorem(20))'
>>> columns(lorem(20), 3)
'#columns(lorem(20), 3)'
>>> columns(lorem(20), 3, gutter='8% + 0pt')
'#columns(lorem(20), 3, gutter: 8% + 0pt)'

ellipse:

>>> ellipse('[Hello, World!]')
'#ellipse([Hello, World!])'
>>> ellipse('[Hello, World!]', width='100%')
'#ellipse([Hello, World!], width: 100%)'

emph:

>>> emph('"Hello, World!"')
'#emph("Hello, World!")'
>>> emph('[Hello, World!]')
'#emph([Hello, World!])'

figure:

>>> figure(image('"image.png"'))
'#figure(image("image.png"))'
>>> figure(image('"image.png"'), caption='[Hello, World!]')
'#figure(image("image.png"), caption: [Hello, World!])'

footnote:

>>> footnote('[Hello, World!]')
'#footnote([Hello, World!])'
>>> footnote('[Hello, World!]', numbering='"a"')
'#footnote([Hello, World!], numbering: "a")'

gradient:

>>> gradient()
'#gradient'

grid:

>>> grid(lorem(20), lorem(20), lorem(20), align=('center',) * 3)
'#grid(align: (center, center, center), lorem(20), lorem(20), lorem(20))'

heading:

>>> heading('[Hello, World!]')
'#heading([Hello, World!])'
>>> heading('[Hello, World!]', level=1)
'#heading([Hello, World!], level: 1)'
>>> heading('[Hello, World!]', level=1, depth=2)
'#heading([Hello, World!], level: 1, depth: 2)'
>>> heading('[Hello, World!]', level=1, depth=2, offset=10)
'#heading([Hello, World!], level: 1, depth: 2, offset: 10)'
>>> heading('[Hello, World!]', level=1, depth=2, offset=10, numbering='"a"')
'#heading([Hello, World!], level: 1, depth: 2, offset: 10, numbering: "a")'
>>> heading(
...     '[Hello, World!]',
...     level=1,
...     depth=2,
...     offset=10,
...     numbering='"a"',
...     supplement='"Supplement"',
... )
'#heading([Hello, World!], level: 1, depth: 2, offset: 10, numbering: "a", supplement: "Supplement")'

hide:

>>> hide(lorem(20))
'#hide(lorem(20))'

highlight:

>>> highlight('"Hello, world!"', fill=rgb('"#ffffff"'))
'#highlight("Hello, world!", fill: rgb("#ffffff"))'
>>> highlight('"Hello, world!"', fill=rgb('"#ffffff"'), stroke=rgb('"#000000"'))
'#highlight("Hello, world!", fill: rgb("#ffffff"), stroke: rgb("#000000"))'
>>> highlight(
...     '"Hello, world!"',
...     fill=rgb('"#ffffff"'),
...     stroke=rgb('"#000000"'),
...     top_edge='"bounds"',
...     bottom_edge='"bounds"',
... )
'#highlight("Hello, world!", fill: rgb("#ffffff"), stroke: rgb("#000000"), top-edge: "bounds", bottom-edge: "bounds")'

hspace:

>>> hspace('1em')
'#h(1em)'
>>> hspace('1em', weak=True)
'#h(1em, weak: true)'

image:

>>> image('"image.png"')
'#image("image.png")'
>>> image('"image.png"', fit='"contain"')
'#image("image.png", fit: "contain")'

line:

>>> line()
'#line()'
>>> line(end=('100% + 0pt', '100% + 0pt'))
'#line(end: (100% + 0pt, 100% + 0pt))'
>>> line(angle='90deg')
'#line(angle: 90deg)'
>>> line(stroke='1pt + red')
'#line(stroke: 1pt + red)'

linebreak:

>>> linebreak()
'#linebreak()'
>>> linebreak(justify=True)
'#linebreak(justify: true)'

link:

>>> link('"https://typst.app"')
'#link("https://typst.app")'
>>> link('"https://typst.app"', '"Typst"')
'#link("https://typst.app", "Typst")'

lorem:

>>> lorem(10)
'#lorem(10)'

lower:

>>> lower('"Hello, World!"')
'#lower("Hello, World!")'
>>> lower('[Hello, World!]')
'#lower([Hello, World!])'
>>> lower(upper('"Hello, World!"'))
'#lower(upper("Hello, World!"))'

luma:

>>> luma('50%')
'#luma(50%)'
>>> luma('50%', '50%')
'#luma(50%, 50%)'

move:

>>> move(lorem(20), dx='50% + 10pt', dy='10% + 5pt')
'#move(lorem(20), dx: 50% + 10pt, dy: 10% + 5pt)'

numbered_list:

>>> numbered_list(lorem(20), lorem(20), lorem(20))
'#enum(lorem(20), lorem(20), lorem(20))'
>>> numbered_list(lorem(20), lorem(20), lorem(20), tight=False)
'#enum(tight: false, lorem(20), lorem(20), lorem(20))'

numbering:

>>> numbering('"1.1)"', 1, 2)
'#numbering("1.1)", 1, 2)'

oklab:

>>> oklab('50%', '0%', '0%')
'#oklab(50%, 0%, 0%)'
>>> oklab('50%', '0%', '0%', '50%')
'#oklab(50%, 0%, 0%, 50%)'

oklch:

>>> oklch('50%', '0%', '0deg')
'#oklch(50%, 0%, 0deg)'
>>> oklch('50%', '0%', '0deg', '50%')
'#oklch(50%, 0%, 0deg, 50%)'

outline:

>>> outline()
'#outline()'
>>> outline(title='"Hello, World!"', target=heading.where(outlined=False))
'#outline(title: "Hello, World!", target: heading.where(outlined: false))'

overline:

>>> overline('"Hello, World!"')
'#overline("Hello, World!")'
>>> overline('[Hello, World!]')
'#overline([Hello, World!])'
>>> overline(
...     upper('"Hello, World!"'),
...     stroke='red',
...     offset='0pt',
...     extent='0pt',
...     evade=False,
...     background=True,
... )
'#overline(upper("Hello, World!"), stroke: red, offset: 0pt, evade: false, background: true)'

padding:

>>> padding(
...     lorem(20),
...     left='4% + 0pt',
...     top='4% + 0pt',
...     right='4% + 0pt',
...     bottom='4% + 0pt',
... )
'#pad(lorem(20), left: 4% + 0pt, top: 4% + 0pt, right: 4% + 0pt, bottom: 4% + 0pt)'

page:

>>> page(lorem(20))
'#page(lorem(20))'
>>> page(lorem(20), paper='"a0"', width='8.5in', height='11in')
'#page(lorem(20), paper: "a0", width: 8.5in, height: 11in)'

pagebreak:

>>> pagebreak()
'#pagebreak()'
>>> pagebreak(weak=True)
'#pagebreak(weak: true)'
>>> pagebreak(to='"even"')
'#pagebreak(to: "even")'

par:

>>> par('"Hello, World!"')
'#par("Hello, World!")'
>>> par('[Hello, World!]')
'#par([Hello, World!])'
>>> par(
...     '[Hello, World!]',
...     leading='0.1em',
...     spacing='0.5em',
...     justify=True,
...     linebreaks='"simple"',
...     first_line_indent='0.2em',
...     hanging_indent='0.3em',
... )
'#par([Hello, World!], leading: 0.1em, spacing: 0.5em, justify: true, linebreaks: "simple", first-line-indent: 0.2em, hanging-indent: 0.3em)'

parbreak:

>>> parbreak()
'#parbreak()'

path:

>>> path(('0%', '0%'), ('100%', '0%'), ('100%', '100%'), ('0%', '100%'))
'#path((0%, 0%), (100%, 0%), (100%, 100%), (0%, 100%))'
>>> path(('0%', '0%'), ('100%', '0%'), ('100%', '100%'), ('0%', '100%'), fill='red')
'#path(fill: red, (0%, 0%), (100%, 0%), (100%, 100%), (0%, 100%))'
>>> path(
...     ('0%', '0%'),
...     ('100%', '0%'),
...     ('100%', '100%'),
...     ('0%', '100%'),
...     fill='red',
...     stroke='blue',
... )
'#path(fill: red, stroke: blue, (0%, 0%), (100%, 0%), (100%, 100%), (0%, 100%))'

place:

>>> place(lorem(20))
'#place(lorem(20))'
>>> place(lorem(20), 'top')
'#place(lorem(20), top)'

quote:

>>> quote('"Hello, World!"')
'#quote("Hello, World!")'
>>> quote('"Hello, World!"', block=True)
'#quote("Hello, World!", block: true)'
>>> quote('"Hello, World!"', quotes=False)
'#quote("Hello, World!", quotes: false)'
>>> quote('"Hello, World!"', attribution='"John Doe"')
'#quote("Hello, World!", attribution: "John Doe")'

raw:

>>> raw('"Hello, World!"')
'#raw("Hello, World!")'
>>> raw('"Hello, World!"', block=True, align='center')
'#raw("Hello, World!", block: true, align: center)'
>>> raw('"Hello, World!"', lang='"rust"')
'#raw("Hello, World!", lang: "rust")'
>>> raw('"Hello, World!"', tab_size=4)
'#raw("Hello, World!", tab-size: 4)'

ref:

>>> ref('<label>')
'#ref(<label>)'
>>> ref('<label>', supplement='[Hello, World!]')
'#ref(<label>, supplement: [Hello, World!])'

repeat:

>>> repeat(lorem(20), gap='0.5em')
'#repeat(lorem(20), gap: 0.5em)'
>>> repeat(lorem(20), gap='0.5em', justify=False)
'#repeat(lorem(20), gap: 0.5em, justify: false)'

rgb:

>>> rgb(255, 255, 255)
'#rgb(255, 255, 255)'
>>> rgb('50%', '50%', '50%', '50%')
'#rgb(50%, 50%, 50%, 50%)'
>>> rgb('"#ffffff"')
'#rgb("#ffffff")'

rotate:

>>> rotate(lorem(20), '20deg')
'#rotate(lorem(20), 20deg)'
>>> rotate(lorem(20), '20deg', origin='left + horizon')
'#rotate(lorem(20), 20deg, origin: left + horizon)'

scale:

>>> scale(lorem(20), '50%')
'#scale(lorem(20), 50%)'
>>> scale(lorem(20), x='50%', y='50%')
'#scale(lorem(20), x: 50%, y: 50%)'
>>> scale(lorem(20), '50%', x='50%', y='50%')
'#scale(lorem(20), 50%, x: 50%, y: 50%)'

skew:

>>> skew(lorem(20), ax='10deg', ay='20deg')
'#skew(lorem(20), ax: 10deg, ay: 20deg)'

smallcaps:

>>> smallcaps('"Hello, World!"')
'#smallcaps("Hello, World!")'
>>> smallcaps('[Hello, World!]')
'#smallcaps([Hello, World!])'

smartquote:

>>> smartquote(double=False, enabled=False, alternative=True, quotes='"()"')
'#smartquote(double: false, enabled: false, alternative: true, quotes: "()")'
>>> smartquote(quotes=('"()"', '"{}"'))
'#smartquote(quotes: ("()", "{}"))'

stack:

>>> stack(rect(width='40pt'), rect(width='120pt'), rect(width='90pt'), dir='btt')
'#stack(dir: btt, rect(width: 40pt), rect(width: 120pt), rect(width: 90pt))'
>>> stack((rect(width='40pt'), rect(width='120pt'), rect(width='90pt')), dir='btt')
'#stack(dir: btt, ..(rect(width: 40pt), rect(width: 120pt), rect(width: 90pt)))'

strike:

>>> strike('"Hello, World!"')
'#strike("Hello, World!")'
>>> strike('[Hello, World!]')
'#strike([Hello, World!])'
>>> strike(
...     upper('"Hello, World!"'),
...     stroke='red',
...     offset='0.1em',
...     extent='0.2em',
...     background=True,
... )
'#strike(upper("Hello, World!"), stroke: red, offset: 0.1em, extent: 0.2em, background: true)'

strong:

>>> strong('"Hello, World!"')
'#strong("Hello, World!")'
>>> strong('[Hello, World!]', delta=400)
'#strong([Hello, World!], delta: 400)'

subscript:

>>> subscript('"Hello, World!"')
'#sub("Hello, World!")'
>>> subscript('[Hello, World!]')
'#sub([Hello, World!])'
>>> subscript('[Hello, World!]', typographic=False, baseline='0.3em', size='0.7em')
'#sub([Hello, World!], typographic: false, baseline: 0.3em, size: 0.7em)'

superscript:

>>> superscript('"Hello, World!"')
'#super("Hello, World!")'
>>> superscript('[Hello, World!]')
'#super([Hello, World!])'
>>> superscript(
...     '[Hello, World!]', typographic=False, baseline='-0.4em', size='0.7em'
... )
'#super([Hello, World!], typographic: false, baseline: -0.4em, size: 0.7em)'

table:

>>> table('[1]', '[2]', '[3]')
'#table([1], [2], [3])'
>>> table(
...     '[1]',
...     '[2]',
...     '[3]',
...     columns=['1fr', '2fr', '3fr'],
...     rows=['1fr', '2fr', '3fr'],
...     gutter=['1fr', '2fr', '3fr'],
...     column_gutter=['1fr', '2fr', '3fr'],
...     row_gutter=['1fr', '2fr', '3fr'],
...     fill='red',
...     align=['center', 'center', 'center'],
... )
'#table(columns: (1fr, 2fr, 3fr), rows: (1fr, 2fr, 3fr), gutter: (1fr, 2fr, 3fr), column-gutter: (1fr, 2fr, 3fr), row-gutter: (1fr, 2fr, 3fr), fill: red, align: (center, center, center), [1], [2], [3])'

terms:

>>> terms(('[1]', lorem(20)), ('[1]', lorem(20)))
'#terms(([1], lorem(20)), ([1], lorem(20)))'
>>> terms(('[1]', lorem(20)), ('[1]', lorem(20)), tight=False)
'#terms(tight: false, ([1], lorem(20)), ([1], lorem(20)))'
>>> terms(terms.item('[1]', lorem(20)), terms.item('[1]', lorem(20)))
'#terms(terms.item([1], lorem(20)), terms.item([1], lorem(20)))'

text:

>>> text('"Hello, World!"')
'#text("Hello, World!")'
>>> text('[Hello, World!]')
'#text([Hello, World!])'
>>> text('[Hello, World!]', font='"Times New Roman"')
'#text([Hello, World!], font: "Times New Roman")'

underline:

>>> underline('"Hello, World!"')
'#underline("Hello, World!")'
>>> underline('[Hello, World!]')
'#underline([Hello, World!])'
>>> underline(
...     '[Hello, World!]',
...     stroke='1pt + red',
...     offset='0pt',
...     extent='1pt',
...     evade=False,
...     background=True,
... )
'#underline([Hello, World!], stroke: 1pt + red, offset: 0pt, extent: 1pt, evade: false, background: true)'

upper:

>>> upper('"Hello, World!"')
'#upper("Hello, World!")'
>>> upper('[Hello, World!]')
'#upper([Hello, World!])'
>>> upper(lower('"Hello, World!"'))
'#upper(lower("Hello, World!"))'

vspace:

>>> vspace('1em')
'#v(1em)'
>>> vspace('1em', weak=True)
'#v(1em, weak: true)'

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

typstpy-1.0.1.tar.gz (38.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

typstpy-1.0.1-py3-none-any.whl (37.1 kB view details)

Uploaded Python 3

File details

Details for the file typstpy-1.0.1.tar.gz.

File metadata

  • Download URL: typstpy-1.0.1.tar.gz
  • Upload date:
  • Size: 38.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.12.5 Windows/11

File hashes

Hashes for typstpy-1.0.1.tar.gz
Algorithm Hash digest
SHA256 b60b21bd45f1337b9862bb376414bd7eb4c7ff13dcfeff2b15de46ac21b3c0ff
MD5 02a5c365702facb7cefeb127297605b5
BLAKE2b-256 93ac2d1c1477d3b65207014fdaed5863112f1093269e0b422f8219f2551aa9d8

See more details on using hashes here.

File details

Details for the file typstpy-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: typstpy-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 37.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.12.5 Windows/11

File hashes

Hashes for typstpy-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4eb8f23be150fe80f35f16fb6329dee25282ae63a84586155c91ed8321afb93b
MD5 3859b027b6d330698e6032251185a8a9
BLAKE2b-256 e3e6a0ea6cee75e42db196f45a4174974842592cdae7be32905936aa98f25cd6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page