phihung commited on
Commit
b5bffcc
·
1 Parent(s): ebec85b

Add more examples

Browse files
Dockerfile CHANGED
@@ -11,6 +11,7 @@ ADD pyproject.toml /app/pyproject.toml
11
 
12
  RUN uv sync --frozen --no-install-project
13
 
 
14
  COPY --chown=user src ./src
15
  RUN touch README.md
16
 
 
11
 
12
  RUN uv sync --frozen --no-install-project
13
 
14
+ COPY --chown=user public ./public
15
  COPY --chown=user src ./src
16
  RUN touch README.md
17
 
public/data/contacts.csv ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Venus,Grimes,lectus.rutrum@Duisa.edu
2
+ Fletcher,Owen,metus@Aenean.org
3
+ William,Hale,eu.dolor@risusodio.edu
4
+ TaShya,Cash,tincidunt.orci.quis@nuncnullavulputate.co.uk
5
+ Kevyn,Hoover,tristique.pellentesque.tellus@Cumsociis.co.uk
6
+ Jakeem,Walker,Morbi.vehicula.Pellentesque@faucibusorci.org
7
+ Malcolm,Trujillo,sagittis@velit.edu
8
+ Wynne,Rice,augue.id@felisorciadipiscing.edu
9
+ Evangeline,Klein,adipiscing.lobortis@sem.org
10
+ Jennifer,Russell,sapien.Aenean.massa@risus.com
11
+ Rama,Freeman,Proin@quamPellentesquehabitant.net
12
+ Jena,Mathis,non.cursus.non@Phaselluselit.com
13
+ Alexandra,Maynard,porta.elit.a@anequeNullam.ca
14
+ Tallulah,Haley,ligula@id.net
15
+ Timon,Small,velit.Quisque.varius@gravidaPraesent.org
16
+ Randall,Pena,facilisis@Donecconsectetuer.edu
17
+ Conan,Vaughan,luctus.sit@Classaptenttaciti.edu
18
+ Dora,Allen,est.arcu.ac@Vestibulumante.co.uk
19
+ Aiko,Little,quam.dignissim@convallisest.net
20
+ Jessamine,Bauer,taciti.sociosqu@nibhvulputatemauris.co.uk
21
+ Gillian,Livingston,justo@atiaculisquis.com
22
+ Laith,Nicholson,elit.pellentesque.a@diam.org
23
+ Paloma,Alston,cursus@metus.org
24
+ Freya,Dunn,Vestibulum.accumsan@metus.co.uk
25
+ Griffin,Rice,justo@tortordictumeu.net
26
+ Catherine,West,malesuada.augue@elementum.com
27
+ Jena,Chambers,erat.Etiam.vestibulum@quamelementumat.net
28
+ Neil,Rodriguez,enim@facilisis.com
29
+ Freya,Charles,metus@nec.net
30
+ Anastasia,Strong,sit@vitae.edu
31
+ Bell,Simon,mollis.nec.cursus@disparturientmontes.ca
32
+ Minerva,Allison,Donec@nequeIn.edu
33
+ Yoko,Dawson,neque.sed@semper.net
34
+ Nadine,Justice,netus@et.edu
35
+ Hoyt,Rosa,Nullam.ut.nisi@Aliquam.co.uk
36
+ Shafira,Noel,tincidunt.nunc@non.edu
37
+ Jin,Nunez,porttitor.tellus.non@venenatisamagna.net
38
+ Barbara,Gay,est.congue.a@elit.com
39
+ Riley,Hammond,tempor.diam@sodalesnisi.net
40
+ Molly,Fulton,semper@Naminterdumenim.net
41
+ Dexter,Owen,non.ante@odiosagittissemper.ca
42
+ Kuame,Merritt,ornare.placerat.orci@nisinibh.ca
43
+ Maggie,Delgado,Nam.ligula.elit@Cum.org
44
+ Hanae,Washington,nec.euismod@adipiscingelit.org
45
+ Jonah,Cherry,ridiculus.mus.Proin@quispede.edu
46
+ Cheyenne,Munoz,at@molestiesodalesMauris.edu
47
+ India,Mack,sem.mollis@Inmi.co.uk
48
+ Lael,Mcneil,porttitor@risusDonecegestas.com
49
+ Jillian,Mckay,vulputate.eu.odio@amagnaLorem.co.uk
50
+ Shaine,Wright,malesuada@pharetraQuisqueac.org
51
+ Keane,Richmond,nostra.per.inceptos@euismodurna.org
52
+ Samuel,Davis,felis@euenim.com
53
+ Zelenia,Sheppard,Quisque.nonummy@antelectusconvallis.org
54
+ Giacomo,Cole,aliquet.libero@urnaUttincidunt.ca
55
+ Mason,Hinton,est@Nunc.co.uk
56
+ Katelyn,Koch,velit.Aliquam@Suspendisse.edu
57
+ Olga,Spencer,faucibus@Praesenteudui.net
58
+ Erasmus,Strong,dignissim.lacus@euarcu.net
59
+ Regan,Cline,vitae.erat.vel@lacusEtiambibendum.co.uk
60
+ Stone,Holt,eget.mollis.lectus@Aeneanegestas.ca
61
+ Deanna,Branch,turpis@estMauris.net
62
+ Rana,Green,metus@conguea.edu
63
+ Caryn,Henson,Donec.sollicitudin.adipiscing@sed.net
64
+ Clarke,Stein,nec@mollis.co.uk
65
+ Kelsie,Porter,Cum@gravidaAliquam.com
66
+ Cooper,Pugh,Quisque.ornare.tortor@dictum.co.uk
67
+ Paul,Spencer,ac@InfaucibusMorbi.com
68
+ Cassady,Farrell,Suspendisse.non@venenatisa.net
69
+ Sydnee,Velazquez,mollis@loremfringillaornare.com
70
+ Felix,Boyle,id.libero.Donec@aauctor.org
71
+ Ryder,House,molestie@natoquepenatibus.org
72
+ Hadley,Holcomb,penatibus@nisi.ca
73
+ Marsden,Nunez,Nulla.eget.metus@facilisisvitaeorci.org
74
+ Alana,Powell,non.lobortis.quis@interdumfeugiatSed.net
75
+ Dennis,Wyatt,Morbi.non@nibhQuisquenonummy.ca
76
+ Karleigh,Walton,nascetur.ridiculus@quamdignissimpharetra.com
77
+ Brielle,Donovan,placerat@at.edu
78
+ Donna,Dickerson,lacus.pede.sagittis@lacusvestibulum.com
79
+ Eagan,Pate,est.Nunc@cursusNunc.ca
80
+ Carlos,Ramsey,est.ac.facilisis@duinec.co.uk
81
+ Regan,Murphy,lectus.Cum@aptent.com
82
+ Claudia,Spence,Nunc.lectus.pede@aceleifend.co.uk
83
+ Genevieve,Parker,ultrices@inaliquetlobortis.net
84
+ Marshall,Allison,erat.semper.rutrum@odio.org
85
+ Reuben,Davis,Donec@auctorodio.edu
86
+ Ralph,Doyle,pede.Suspendisse.dui@Curabitur.org
87
+ Constance,Gilliam,mollis@Nulla.edu
88
+ Serina,Jacobson,dictum.augue@ipsum.net
89
+ Charity,Byrd,convallis.ante.lectus@scelerisquemollisPhasellus.co.uk
90
+ Hyatt,Bird,enim.Nunc.ut@nonmagnaNam.com
91
+ Brent,Dunn,ac.sem@nuncid.com
92
+ Casey,Bonner,id@ornareelitelit.edu
93
+ Hakeem,Gill,dis@nonummyipsumnon.org
94
+ Stewart,Meadows,Nunc.pulvinar.arcu@convallisdolorQuisque.net
95
+ Nomlanga,Wooten,inceptos@turpisegestas.ca
96
+ Sebastian,Watts,Sed.diam.lorem@lorem.co.uk
97
+ Chelsea,Larsen,ligula@Nam.net
98
+ Cameron,Humphrey,placerat@id.org
99
+ Juliet,Bush,consectetuer.euismod@vitaeeratVivamus.co.uk
100
+ Caryn,Hooper,eu.enim.Etiam@ridiculus.org
pyproject.toml CHANGED
@@ -18,6 +18,8 @@ build-backend = "hatchling.build"
18
  [tool.uv]
19
  dev-dependencies = [
20
  "ipykernel>=6.29.5",
 
 
21
  "pytest-cov>=5.0.0",
22
  "pytest>=8.3.2",
23
  "ruff>=0.6.3",
 
18
  [tool.uv]
19
  dev-dependencies = [
20
  "ipykernel>=6.29.5",
21
+ "lxml>=5.3.0",
22
+ "pandas>=2.2.2",
23
  "pytest-cov>=5.0.0",
24
  "pytest>=8.3.2",
25
  "ruff>=0.6.3",
src/tutorial/_03_click_to_load.py CHANGED
@@ -18,7 +18,7 @@ def contact_table():
18
 
19
  @app.get("/contacts")
20
  def load_contacts(page: int, limit: int = 5):
21
- rows = [Tr(Td("Smith"), Td(page * limit + i)) for i in range(limit)]
22
  return *rows, make_last_row(page)
23
 
24
 
 
18
 
19
  @app.get("/contacts")
20
  def load_contacts(page: int, limit: int = 5):
21
+ rows = [Tr(Td("Smith"), Td((page - 1) * limit + i)) for i in range(limit)]
22
  return *rows, make_last_row(page)
23
 
24
 
src/tutorial/_08_infinite_scroll.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fasthtml.common import Div, Table, Tbody, Td, Th, Thead, Tr, fast_app
2
+
3
+ app, rt = fast_app()
4
+
5
+
6
+ # fmt: off
7
+ @app.get("/table")
8
+ def contact_table():
9
+ return Div(
10
+ Table(
11
+ Thead(Tr(Th("Name"), Th("ID"))),
12
+ Tbody(load_contacts(page=1)),
13
+ ),
14
+ cls="container",
15
+ )
16
+ # fmt: on
17
+
18
+
19
+ @app.get("/contacts")
20
+ def load_contacts(page: int, limit: int = 5):
21
+ rows = [Tr(Td("Smith"), Td((page - 1) * limit + i)) for i in range(1, limit)]
22
+ return *rows, make_last_row(page, limit)
23
+
24
+
25
+ def make_last_row(page, limit):
26
+ return Tr(
27
+ Td("Smith"),
28
+ Td(page * limit),
29
+ hx_trigger="revealed",
30
+ hx_swap="afterend",
31
+ hx_get=f"/contacts?page={page + 1}",
32
+ )
33
+
34
+
35
+ DESC = "Demonstrates infinite scrolling of a page"
36
+ DOC = """
37
+ The infinite scroll pattern provides a way to load content dynamically on user scrolling action.
38
+
39
+ Let’s focus on the final row (or the last element of your content):
40
+ ::make_last_row load_contacts::
41
+ This last element contains a listener which, when scrolled into view, will trigger a request. The result is then appended after it. The last element of the results will itself contain the listener to load the next page of results, and so on.
42
+
43
+ <blockquote><ins>revealed</ins> triggered when an element is scrolled into the viewport (also useful for lazy-loading). If you are using overflow in css like overflow-y: scroll you should use intersect once instead of revealed.</blockquote>
44
+ """
src/tutorial/_09_active_search.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ from pathlib import Path
3
+
4
+ from fasthtml.common import H3, Div, Img, Input, Span, Table, Tbody, Td, Th, Thead, Tr, fast_app
5
+
6
+ app, rt = fast_app()
7
+
8
+
9
+ # fmt: off
10
+ @app.get("/page")
11
+ def main_page():
12
+ return Div(
13
+ H3("Search Contacts"),
14
+ Input(
15
+ type="search",
16
+ name="query",
17
+ placeholder="Begin Typing To Search Users...",
18
+ hx_post="/search",
19
+ hx_trigger="input changed delay:500ms, search",
20
+ hx_target="#search-results",
21
+ hx_indicator=".htmx-indicator",
22
+ cls="form-control",
23
+ ),
24
+ Span(Img(src="/img/bars.svg"), "Searching...", cls="htmx-indicator"),
25
+ Table(
26
+ Thead(Tr(Th("First Name"), Th("Last Name"), Th("Email"))),
27
+ Tbody(id="search-results"),
28
+ ),
29
+ )
30
+ # fmt: on
31
+
32
+
33
+ @app.post("/search")
34
+ def search(query: str, limit: int = 10):
35
+ time.sleep(0.5)
36
+ data = [x.split(",") for x in LINES if query.lower() in x.lower()]
37
+ return [Tr(Td(x[0]), Td(x[1]), Td(x[2])) for x in data[:limit]]
38
+
39
+
40
+ LINES = Path("public/data/contacts.csv").read_text().splitlines()
41
+
42
+
43
+ DESC = "Demonstrates the active search box pattern"
44
+ DOC = """
45
+ This example actively searches a contacts database as the user enters text.
46
+
47
+ We start with a search input and an empty table:
48
+ ::main_page::
49
+ The input issues a POST to /search on the input event and sets the body of the table to be the resulting content. Note that the keyup event could be used as well, but would not fire if the user pasted text with their mouse (or any other non-keyboard method).
50
+
51
+ We add the delay:500ms modifier to the trigger to delay sending the query until the user stops typing. Additionally, we add the changed modifier to the trigger to ensure we don’t send new queries when the user doesn’t change the value of the input (e.g. they hit an arrow key, or pasted the same value).
52
+
53
+ Since we use a search type input we will get an x in the input field to clear the input. To make this trigger a new POST we have to specify another trigger. We specify another trigger by using a comma to separate them. The search trigger will be run when the field is cleared but it also makes it possible to override the 500 ms input event delay by just pressing enter.
54
+
55
+ Finally, we show an indicator when the search is in flight with the hx-indicator attribute.
56
+ """
src/tutorial/_10_progress_bar.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fasthtml.common import H3, Button, Div, HttpHeader, Style, fast_app
2
+
3
+ css = """
4
+ .progress {
5
+ height: 20px;
6
+ margin-bottom: 20px;
7
+ overflow: hidden;
8
+ background-color: #f5f5f5;
9
+ border-radius: 4px;
10
+ box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
11
+ }
12
+ .progress-bar {
13
+ float: left;
14
+ width: 0%;
15
+ height: 100%;
16
+ font-size: 12px;
17
+ line-height: 20px;
18
+ color: #fff;
19
+ text-align: center;
20
+ background-color: #337ab7;
21
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
22
+ box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);
23
+ -webkit-transition: width .6s ease;
24
+ -o-transition: width .6s ease;
25
+ transition: width .6s ease;
26
+ }
27
+ """
28
+
29
+ app, rt = fast_app(hdrs=[Style(css)])
30
+
31
+ current = 1
32
+
33
+
34
+ @app.get("/page")
35
+ def main_page():
36
+ return Div(
37
+ H3("Start Progress"),
38
+ Button("Start Job", hx_post="/start", cls="btn primary"),
39
+ hx_target="this",
40
+ hx_swap="outerHTML",
41
+ )
42
+
43
+
44
+ @app.post("/start")
45
+ def start_job():
46
+ global current
47
+ current = 1
48
+ return Div(
49
+ H3("Running", role="status", id="pblabel", tabindex="-1", autofocus=""),
50
+ Div(
51
+ get_progress(),
52
+ hx_get="/job/progress",
53
+ hx_trigger="every 600ms",
54
+ hx_target="this",
55
+ hx_swap="innerHTML",
56
+ ),
57
+ hx_trigger="done",
58
+ hx_get="/job",
59
+ hx_swap="outerHTML",
60
+ hx_target="this",
61
+ )
62
+
63
+
64
+ @app.get("/job/progress")
65
+ def get_progress():
66
+ global current
67
+ if current <= 100:
68
+ current += 10
69
+ return Div(Div(style=f"width:{current - 10}%", cls="progress-bar"), cls="progress")
70
+ return HttpHeader("HX-Trigger", "done")
71
+
72
+
73
+ @app.get("/job")
74
+ def view_completed():
75
+ return Div(
76
+ H3("Complete", role="status", id="pblabel", tabindex="-1", autofocus=""),
77
+ Div(Div(style="width:100%", cls="progress-bar"), cls="progress"),
78
+ Button("Restart Job", hx_post="/start", cls="btn primary show"),
79
+ hx_swap="outerHTML",
80
+ hx_target="this",
81
+ )
82
+
83
+
84
+ DESC = "Demonstrates a job-runner like progress bar"
85
+ DOC = """
86
+ This example shows how to implement a smoothly scrolling progress bar.
87
+
88
+ We start with an initial state with a button that issues a POST to /start to begin the job:
89
+ ::main_page::
90
+ This progress bar is updated every 600 milliseconds, with the “width” style attribute and aria-valuenow attributed set to current progress value. Because there is an id on the progress bar div, htmx will smoothly transition between requests by settling the style attribute into its new value. This, when coupled with CSS transitions, makes the visual transition continuous rather than jumpy.
91
+ ::start_job get_progress::
92
+ Finally, when the process is complete, a server returns HX-Trigger: done header, which triggers an update of the UI to “Complete” state with a restart button added to the UI:
93
+ ::view_completed::
94
+ """
src/tutorial/_11_cascading_select.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+
3
+ from fasthtml.common import H3, Div, Form, Img, Label, Option, Select, fast_app
4
+
5
+ app, rt = fast_app()
6
+
7
+
8
+ @app.get("/page")
9
+ def main_page():
10
+ return Div(
11
+ H3("Pick A Make/Model"),
12
+ Form(
13
+ Div(
14
+ Label("Make"),
15
+ Select(
16
+ Option("Audi", value="audi"),
17
+ Option("Toyota", value="toyota"),
18
+ Option("BMW", value="bmw"),
19
+ name="make",
20
+ hx_get="/models?sleep=1",
21
+ hx_target="#models",
22
+ hx_indicator=".htmx-indicator",
23
+ ),
24
+ ),
25
+ Div(
26
+ Label("Model"),
27
+ Select(load_models("audi"), id="models"),
28
+ Img(width="20", src="/img/bars.svg", cls="htmx-indicator"),
29
+ ),
30
+ ),
31
+ cls="container",
32
+ )
33
+
34
+
35
+ @app.get("/models")
36
+ def load_models(make: str, sleep: int = 0):
37
+ time.sleep(sleep)
38
+ cars = {
39
+ "audi": ["A1", "A4", "A6"],
40
+ "toyota": ["Landcruiser", "Tacoma", "Yaris"],
41
+ "bmw": ["325i", "325ix", "X5"],
42
+ }
43
+ return tuple(Option(v, value=v) for v in cars[make])
44
+
45
+
46
+ DESC = "Demonstrates making the values of a select dependent on another select"
47
+ HTMX_URL = "https://htmx.org/examples/value-select/"
48
+ DOC = """
49
+ In this example we show how to make the values in one select depend on the value selected in another select.
50
+
51
+ To begin we start with a default value for the make select: Audi. We render the model select for this make. We then have the make select trigger a GET to /models to retrieve the models options and target the models select.
52
+
53
+ Here is the code:
54
+ ::main_page::
55
+
56
+ When a request is made to the /models end point, we return the models for that make:
57
+ ::load_models::
58
+
59
+ And they become available in the model select.
60
+ """
src/tutorial/__init__.py CHANGED
@@ -37,10 +37,18 @@ app, rt = fast_app(hdrs=hdrs, static_path="public")
37
  examples = sorted([f.stem for f in Path(__file__).parent.glob("*.py") if f.stem not in ["__init__"]])
38
 
39
 
 
 
 
 
 
 
 
40
  @app.get("/")
41
  def homepage():
42
  ls = [get_example(name) for name in examples]
43
  return Main(
 
44
  Table(
45
  Thead(Tr(Th("Pattern"), Th("Description"))),
46
  Tbody(tuple(Tr(Td(A(ex.title, href="/" + ex.slug)), Td(ex.desc)) for ex in ls)),
 
37
  examples = sorted([f.stem for f in Path(__file__).parent.glob("*.py") if f.stem not in ["__init__"]])
38
 
39
 
40
+ INTRO = """
41
+ # HTMX examples with FastHTML
42
+
43
+ Reproduction of HTMX official [examples](https://htmx.org/examples/) in Python with [FastHTML](https://docs.fastht.ml/)
44
+ """
45
+
46
+
47
  @app.get("/")
48
  def homepage():
49
  ls = [get_example(name) for name in examples]
50
  return Main(
51
+ Div(INTRO, cls="marked"),
52
  Table(
53
  Thead(Tr(Th("Pattern"), Th("Description"))),
54
  Tbody(tuple(Tr(Td(A(ex.title, href="/" + ex.slug)), Td(ex.desc)) for ex in ls)),
uv.lock CHANGED
@@ -1,5 +1,9 @@
1
  version = 1
2
  requires-python = ">=3.11"
 
 
 
 
3
 
4
  [[package]]
5
  name = "anyio"
@@ -421,6 +425,65 @@ wheels = [
421
  { url = "https://files.pythonhosted.org/packages/c9/fb/108ecd1fe961941959ad0ee4e12ee7b8b1477247f30b1fdfd83ceaf017f0/jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409", size = 28965 },
422
  ]
423
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
  [[package]]
425
  name = "matplotlib-inline"
426
  version = "0.1.7"
@@ -442,6 +505,52 @@ wheels = [
442
  { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 },
443
  ]
444
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
445
  [[package]]
446
  name = "oauthlib"
447
  version = "3.2.2"
@@ -460,6 +569,34 @@ wheels = [
460
  { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 },
461
  ]
462
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
  [[package]]
464
  name = "parso"
465
  version = "0.8.4"
@@ -643,6 +780,15 @@ wheels = [
643
  { url = "https://files.pythonhosted.org/packages/3d/47/444768600d9e0ebc82f8e347775d24aef8f6348cf00e9fa0e81910814e6d/python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215", size = 22299 },
644
  ]
645
 
 
 
 
 
 
 
 
 
 
646
  [[package]]
647
  name = "pywin32"
648
  version = "306"
@@ -884,6 +1030,8 @@ dependencies = [
884
  [package.dev-dependencies]
885
  dev = [
886
  { name = "ipykernel" },
 
 
887
  { name = "pytest" },
888
  { name = "pytest-cov" },
889
  { name = "ruff" },
@@ -895,6 +1043,8 @@ requires-dist = [{ name = "python-fasthtml", specifier = ">=0.5.1" }]
895
  [package.metadata.requires-dev]
896
  dev = [
897
  { name = "ipykernel", specifier = ">=6.29.5" },
 
 
898
  { name = "pytest", specifier = ">=8.3.2" },
899
  { name = "pytest-cov", specifier = ">=5.0.0" },
900
  { name = "ruff", specifier = ">=0.6.3" },
@@ -909,6 +1059,15 @@ wheels = [
909
  { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
910
  ]
911
 
 
 
 
 
 
 
 
 
 
912
  [[package]]
913
  name = "uvicorn"
914
  version = "0.30.6"
 
1
  version = 1
2
  requires-python = ">=3.11"
3
+ resolution-markers = [
4
+ "python_full_version < '3.12'",
5
+ "python_full_version >= '3.12'",
6
+ ]
7
 
8
  [[package]]
9
  name = "anyio"
 
425
  { url = "https://files.pythonhosted.org/packages/c9/fb/108ecd1fe961941959ad0ee4e12ee7b8b1477247f30b1fdfd83ceaf017f0/jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409", size = 28965 },
426
  ]
427
 
428
+ [[package]]
429
+ name = "lxml"
430
+ version = "5.3.0"
431
+ source = { registry = "https://pypi.org/simple" }
432
+ sdist = { url = "https://files.pythonhosted.org/packages/e7/6b/20c3a4b24751377aaa6307eb230b66701024012c29dd374999cc92983269/lxml-5.3.0.tar.gz", hash = "sha256:4e109ca30d1edec1ac60cdbe341905dc3b8f55b16855e03a54aaf59e51ec8c6f", size = 3679318 }
433
+ wheels = [
434
+ { url = "https://files.pythonhosted.org/packages/5c/a8/449faa2a3cbe6a99f8d38dcd51a3ee8844c17862841a6f769ea7c2a9cd0f/lxml-5.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74bcb423462233bc5d6066e4e98b0264e7c1bed7541fff2f4e34fe6b21563c8b", size = 8141056 },
435
+ { url = "https://files.pythonhosted.org/packages/ac/8a/ae6325e994e2052de92f894363b038351c50ee38749d30cc6b6d96aaf90f/lxml-5.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a3d819eb6f9b8677f57f9664265d0a10dd6551d227afb4af2b9cd7bdc2ccbf18", size = 4425238 },
436
+ { url = "https://files.pythonhosted.org/packages/f8/fb/128dddb7f9086236bce0eeae2bfb316d138b49b159f50bc681d56c1bdd19/lxml-5.3.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b8f5db71b28b8c404956ddf79575ea77aa8b1538e8b2ef9ec877945b3f46442", size = 5095197 },
437
+ { url = "https://files.pythonhosted.org/packages/b4/f9/a181a8ef106e41e3086629c8bdb2d21a942f14c84a0e77452c22d6b22091/lxml-5.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3406b63232fc7e9b8783ab0b765d7c59e7c59ff96759d8ef9632fca27c7ee4", size = 4809809 },
438
+ { url = "https://files.pythonhosted.org/packages/25/2f/b20565e808f7f6868aacea48ddcdd7e9e9fb4c799287f21f1a6c7c2e8b71/lxml-5.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ecdd78ab768f844c7a1d4a03595038c166b609f6395e25af9b0f3f26ae1230f", size = 5407593 },
439
+ { url = "https://files.pythonhosted.org/packages/23/0e/caac672ec246d3189a16c4d364ed4f7d6bf856c080215382c06764058c08/lxml-5.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168f2dfcfdedf611eb285efac1516c8454c8c99caf271dccda8943576b67552e", size = 4866657 },
440
+ { url = "https://files.pythonhosted.org/packages/67/a4/1f5fbd3f58d4069000522196b0b776a014f3feec1796da03e495cf23532d/lxml-5.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa617107a410245b8660028a7483b68e7914304a6d4882b5ff3d2d3eb5948d8c", size = 4967017 },
441
+ { url = "https://files.pythonhosted.org/packages/ee/73/623ecea6ca3c530dd0a4ed0d00d9702e0e85cd5624e2d5b93b005fe00abd/lxml-5.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:69959bd3167b993e6e710b99051265654133a98f20cec1d9b493b931942e9c16", size = 4810730 },
442
+ { url = "https://files.pythonhosted.org/packages/1d/ce/fb84fb8e3c298f3a245ae3ea6221c2426f1bbaa82d10a88787412a498145/lxml-5.3.0-cp311-cp311-manylinux_2_28_ppc64le.whl", hash = "sha256:bd96517ef76c8654446fc3db9242d019a1bb5fe8b751ba414765d59f99210b79", size = 5455154 },
443
+ { url = "https://files.pythonhosted.org/packages/b1/72/4d1ad363748a72c7c0411c28be2b0dc7150d91e823eadad3b91a4514cbea/lxml-5.3.0-cp311-cp311-manylinux_2_28_s390x.whl", hash = "sha256:ab6dd83b970dc97c2d10bc71aa925b84788c7c05de30241b9e96f9b6d9ea3080", size = 4969416 },
444
+ { url = "https://files.pythonhosted.org/packages/42/07/b29571a58a3a80681722ea8ed0ba569211d9bb8531ad49b5cacf6d409185/lxml-5.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:eec1bb8cdbba2925bedc887bc0609a80e599c75b12d87ae42ac23fd199445654", size = 5013672 },
445
+ { url = "https://files.pythonhosted.org/packages/b9/93/bde740d5a58cf04cbd38e3dd93ad1e36c2f95553bbf7d57807bc6815d926/lxml-5.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6a7095eeec6f89111d03dabfe5883a1fd54da319c94e0fb104ee8f23616b572d", size = 4878644 },
446
+ { url = "https://files.pythonhosted.org/packages/56/b5/645c8c02721d49927c93181de4017164ec0e141413577687c3df8ff0800f/lxml-5.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6f651ebd0b21ec65dfca93aa629610a0dbc13dbc13554f19b0113da2e61a4763", size = 5511531 },
447
+ { url = "https://files.pythonhosted.org/packages/85/3f/6a99a12d9438316f4fc86ef88c5d4c8fb674247b17f3173ecadd8346b671/lxml-5.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:f422a209d2455c56849442ae42f25dbaaba1c6c3f501d58761c619c7836642ec", size = 5402065 },
448
+ { url = "https://files.pythonhosted.org/packages/80/8a/df47bff6ad5ac57335bf552babfb2408f9eb680c074ec1ba412a1a6af2c5/lxml-5.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:62f7fdb0d1ed2065451f086519865b4c90aa19aed51081979ecd05a21eb4d1be", size = 5069775 },
449
+ { url = "https://files.pythonhosted.org/packages/08/ae/e7ad0f0fbe4b6368c5ee1e3ef0c3365098d806d42379c46c1ba2802a52f7/lxml-5.3.0-cp311-cp311-win32.whl", hash = "sha256:c6379f35350b655fd817cd0d6cbeef7f265f3ae5fedb1caae2eb442bbeae9ab9", size = 3474226 },
450
+ { url = "https://files.pythonhosted.org/packages/c3/b5/91c2249bfac02ee514ab135e9304b89d55967be7e53e94a879b74eec7a5c/lxml-5.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c52100e2c2dbb0649b90467935c4b0de5528833c76a35ea1a2691ec9f1ee7a1", size = 3814971 },
451
+ { url = "https://files.pythonhosted.org/packages/eb/6d/d1f1c5e40c64bf62afd7a3f9b34ce18a586a1cccbf71e783cd0a6d8e8971/lxml-5.3.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e99f5507401436fdcc85036a2e7dc2e28d962550afe1cbfc07c40e454256a859", size = 8171753 },
452
+ { url = "https://files.pythonhosted.org/packages/bd/83/26b1864921869784355459f374896dcf8b44d4af3b15d7697e9156cb2de9/lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:384aacddf2e5813a36495233b64cb96b1949da72bef933918ba5c84e06af8f0e", size = 4441955 },
453
+ { url = "https://files.pythonhosted.org/packages/e0/d2/e9bff9fb359226c25cda3538f664f54f2804f4b37b0d7c944639e1a51f69/lxml-5.3.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:874a216bf6afaf97c263b56371434e47e2c652d215788396f60477540298218f", size = 5050778 },
454
+ { url = "https://files.pythonhosted.org/packages/88/69/6972bfafa8cd3ddc8562b126dd607011e218e17be313a8b1b9cc5a0ee876/lxml-5.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65ab5685d56914b9a2a34d67dd5488b83213d680b0c5d10b47f81da5a16b0b0e", size = 4748628 },
455
+ { url = "https://files.pythonhosted.org/packages/5d/ea/a6523c7c7f6dc755a6eed3d2f6d6646617cad4d3d6d8ce4ed71bfd2362c8/lxml-5.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aac0bbd3e8dd2d9c45ceb82249e8bdd3ac99131a32b4d35c8af3cc9db1657179", size = 5322215 },
456
+ { url = "https://files.pythonhosted.org/packages/99/37/396fbd24a70f62b31d988e4500f2068c7f3fd399d2fd45257d13eab51a6f/lxml-5.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b369d3db3c22ed14c75ccd5af429086f166a19627e84a8fdade3f8f31426e52a", size = 4813963 },
457
+ { url = "https://files.pythonhosted.org/packages/09/91/e6136f17459a11ce1757df864b213efbeab7adcb2efa63efb1b846ab6723/lxml-5.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c24037349665434f375645fa9d1f5304800cec574d0310f618490c871fd902b3", size = 4923353 },
458
+ { url = "https://files.pythonhosted.org/packages/1d/7c/2eeecf87c9a1fca4f84f991067c693e67340f2b7127fc3eca8fa29d75ee3/lxml-5.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:62d172f358f33a26d6b41b28c170c63886742f5b6772a42b59b4f0fa10526cb1", size = 4740541 },
459
+ { url = "https://files.pythonhosted.org/packages/3b/ed/4c38ba58defca84f5f0d0ac2480fdcd99fc7ae4b28fc417c93640a6949ae/lxml-5.3.0-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:c1f794c02903c2824fccce5b20c339a1a14b114e83b306ff11b597c5f71a1c8d", size = 5346504 },
460
+ { url = "https://files.pythonhosted.org/packages/a5/22/bbd3995437e5745cb4c2b5d89088d70ab19d4feabf8a27a24cecb9745464/lxml-5.3.0-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:5d6a6972b93c426ace71e0be9a6f4b2cfae9b1baed2eed2006076a746692288c", size = 4898077 },
461
+ { url = "https://files.pythonhosted.org/packages/0a/6e/94537acfb5b8f18235d13186d247bca478fea5e87d224644e0fe907df976/lxml-5.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3879cc6ce938ff4eb4900d901ed63555c778731a96365e53fadb36437a131a99", size = 4946543 },
462
+ { url = "https://files.pythonhosted.org/packages/8d/e8/4b15df533fe8e8d53363b23a41df9be907330e1fa28c7ca36893fad338ee/lxml-5.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:74068c601baff6ff021c70f0935b0c7bc528baa8ea210c202e03757c68c5a4ff", size = 4816841 },
463
+ { url = "https://files.pythonhosted.org/packages/1a/e7/03f390ea37d1acda50bc538feb5b2bda6745b25731e4e76ab48fae7106bf/lxml-5.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ecd4ad8453ac17bc7ba3868371bffb46f628161ad0eefbd0a855d2c8c32dd81a", size = 5417341 },
464
+ { url = "https://files.pythonhosted.org/packages/ea/99/d1133ab4c250da85a883c3b60249d3d3e7c64f24faff494cf0fd23f91e80/lxml-5.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:7e2f58095acc211eb9d8b5771bf04df9ff37d6b87618d1cbf85f92399c98dae8", size = 5327539 },
465
+ { url = "https://files.pythonhosted.org/packages/7d/ed/e6276c8d9668028213df01f598f385b05b55a4e1b4662ee12ef05dab35aa/lxml-5.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e63601ad5cd8f860aa99d109889b5ac34de571c7ee902d6812d5d9ddcc77fa7d", size = 5012542 },
466
+ { url = "https://files.pythonhosted.org/packages/36/88/684d4e800f5aa28df2a991a6a622783fb73cf0e46235cfa690f9776f032e/lxml-5.3.0-cp312-cp312-win32.whl", hash = "sha256:17e8d968d04a37c50ad9c456a286b525d78c4a1c15dd53aa46c1d8e06bf6fa30", size = 3486454 },
467
+ { url = "https://files.pythonhosted.org/packages/fc/82/ace5a5676051e60355bd8fb945df7b1ba4f4fb8447f2010fb816bfd57724/lxml-5.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:c1a69e58a6bb2de65902051d57fde951febad631a20a64572677a1052690482f", size = 3816857 },
468
+ { url = "https://files.pythonhosted.org/packages/94/6a/42141e4d373903bfea6f8e94b2f554d05506dfda522ada5343c651410dc8/lxml-5.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c72e9563347c7395910de6a3100a4840a75a6f60e05af5e58566868d5eb2d6a", size = 8156284 },
469
+ { url = "https://files.pythonhosted.org/packages/91/5e/fa097f0f7d8b3d113fb7312c6308af702f2667f22644441715be961f2c7e/lxml-5.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e92ce66cd919d18d14b3856906a61d3f6b6a8500e0794142338da644260595cd", size = 4432407 },
470
+ { url = "https://files.pythonhosted.org/packages/2d/a1/b901988aa6d4ff937f2e5cfc114e4ec561901ff00660c3e56713642728da/lxml-5.3.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d04f064bebdfef9240478f7a779e8c5dc32b8b7b0b2fc6a62e39b928d428e51", size = 5048331 },
471
+ { url = "https://files.pythonhosted.org/packages/30/0f/b2a54f48e52de578b71bbe2a2f8160672a8a5e103df3a78da53907e8c7ed/lxml-5.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c2fb570d7823c2bbaf8b419ba6e5662137f8166e364a8b2b91051a1fb40ab8b", size = 4744835 },
472
+ { url = "https://files.pythonhosted.org/packages/82/9d/b000c15538b60934589e83826ecbc437a1586488d7c13f8ee5ff1f79a9b8/lxml-5.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c120f43553ec759f8de1fee2f4794452b0946773299d44c36bfe18e83caf002", size = 5316649 },
473
+ { url = "https://files.pythonhosted.org/packages/e3/ee/ffbb9eaff5e541922611d2c56b175c45893d1c0b8b11e5a497708a6a3b3b/lxml-5.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:562e7494778a69086f0312ec9689f6b6ac1c6b65670ed7d0267e49f57ffa08c4", size = 4812046 },
474
+ { url = "https://files.pythonhosted.org/packages/15/ff/7ff89d567485c7b943cdac316087f16b2399a8b997007ed352a1248397e5/lxml-5.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:423b121f7e6fa514ba0c7918e56955a1d4470ed35faa03e3d9f0e3baa4c7e492", size = 4918597 },
475
+ { url = "https://files.pythonhosted.org/packages/c6/a3/535b6ed8c048412ff51268bdf4bf1cf052a37aa7e31d2e6518038a883b29/lxml-5.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c00f323cc00576df6165cc9d21a4c21285fa6b9989c5c39830c3903dc4303ef3", size = 4738071 },
476
+ { url = "https://files.pythonhosted.org/packages/7a/8f/cbbfa59cb4d4fd677fe183725a76d8c956495d7a3c7f111ab8f5e13d2e83/lxml-5.3.0-cp313-cp313-manylinux_2_28_ppc64le.whl", hash = "sha256:1fdc9fae8dd4c763e8a31e7630afef517eab9f5d5d31a278df087f307bf601f4", size = 5342213 },
477
+ { url = "https://files.pythonhosted.org/packages/5c/fb/db4c10dd9958d4b52e34d1d1f7c1f434422aeaf6ae2bbaaff2264351d944/lxml-5.3.0-cp313-cp313-manylinux_2_28_s390x.whl", hash = "sha256:658f2aa69d31e09699705949b5fc4719cbecbd4a97f9656a232e7d6c7be1a367", size = 4893749 },
478
+ { url = "https://files.pythonhosted.org/packages/f2/38/bb4581c143957c47740de18a3281a0cab7722390a77cc6e610e8ebf2d736/lxml-5.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1473427aff3d66a3fa2199004c3e601e6c4500ab86696edffdbc84954c72d832", size = 4945901 },
479
+ { url = "https://files.pythonhosted.org/packages/fc/d5/18b7de4960c731e98037bd48fa9f8e6e8f2558e6fbca4303d9b14d21ef3b/lxml-5.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a87de7dd873bf9a792bf1e58b1c3887b9264036629a5bf2d2e6579fe8e73edff", size = 4815447 },
480
+ { url = "https://files.pythonhosted.org/packages/97/a8/cd51ceaad6eb849246559a8ef60ae55065a3df550fc5fcd27014361c1bab/lxml-5.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:0d7b36afa46c97875303a94e8f3ad932bf78bace9e18e603f2085b652422edcd", size = 5411186 },
481
+ { url = "https://files.pythonhosted.org/packages/89/c3/1e3dabab519481ed7b1fdcba21dcfb8832f57000733ef0e71cf6d09a5e03/lxml-5.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:cf120cce539453ae086eacc0130a324e7026113510efa83ab42ef3fcfccac7fb", size = 5324481 },
482
+ { url = "https://files.pythonhosted.org/packages/b6/17/71e9984cf0570cd202ac0a1c9ed5c1b8889b0fc8dc736f5ef0ffb181c284/lxml-5.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:df5c7333167b9674aa8ae1d4008fa4bc17a313cc490b2cca27838bbdcc6bb15b", size = 5011053 },
483
+ { url = "https://files.pythonhosted.org/packages/69/68/9f7e6d3312a91e30829368c2b3217e750adef12a6f8eb10498249f4e8d72/lxml-5.3.0-cp313-cp313-win32.whl", hash = "sha256:c802e1c2ed9f0c06a65bc4ed0189d000ada8049312cfeab6ca635e39c9608957", size = 3485634 },
484
+ { url = "https://files.pythonhosted.org/packages/7d/db/214290d58ad68c587bd5d6af3d34e56830438733d0d0856c0275fde43652/lxml-5.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:406246b96d552e0503e17a1006fd27edac678b3fcc9f1be71a2f94b4ff61528d", size = 3814417 },
485
+ ]
486
+
487
  [[package]]
488
  name = "matplotlib-inline"
489
  version = "0.1.7"
 
505
  { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 },
506
  ]
507
 
508
+ [[package]]
509
+ name = "numpy"
510
+ version = "2.1.1"
511
+ source = { registry = "https://pypi.org/simple" }
512
+ sdist = { url = "https://files.pythonhosted.org/packages/59/5f/9003bb3e632f2b58f5e3a3378902dcc73c5518070736c6740fe52454e8e1/numpy-2.1.1.tar.gz", hash = "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd", size = 18874860 }
513
+ wheels = [
514
+ { url = "https://files.pythonhosted.org/packages/f7/86/2c01070424a42b286ea0271203682c3d3e81e10ce695545b35768307b383/numpy-2.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550", size = 21154850 },
515
+ { url = "https://files.pythonhosted.org/packages/ef/4e/d3426d9e620a18bbb979f28e4dc7f9a2c35eb7cf726ffcb33545ebdd3e6a/numpy-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f", size = 13789477 },
516
+ { url = "https://files.pythonhosted.org/packages/c6/6e/fb6b1b2da9f4c757f55b202f10b6af0fe4fee87ace6e830228a12ab8ae5d/numpy-2.1.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0", size = 5351769 },
517
+ { url = "https://files.pythonhosted.org/packages/58/9a/07c8a9dc7254f3265ae014e33768d1cfd8eb73ee6cf215f4ec3b497e4255/numpy-2.1.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95", size = 6890872 },
518
+ { url = "https://files.pythonhosted.org/packages/08/4e/3b50fa3b1e045793056ed5a1fc6f89dd897ff9cb00900ca6377fe552d442/numpy-2.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca", size = 13984256 },
519
+ { url = "https://files.pythonhosted.org/packages/d9/37/108d692f7e2544b9ae972c7bfa06c26717871c273ccec86470bc3132b04d/numpy-2.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf", size = 16337778 },
520
+ { url = "https://files.pythonhosted.org/packages/95/2d/df81a1be3be6d3a92fd12dfd6c26a0dc026b276136ec1056562342a484a2/numpy-2.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e", size = 16710448 },
521
+ { url = "https://files.pythonhosted.org/packages/8f/34/4b2e604c5c44bd64b6c85e89d88871b41e60233b3ddf97419b37ae5b0c72/numpy-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2", size = 14489002 },
522
+ { url = "https://files.pythonhosted.org/packages/9f/0d/67c04b6bfefd0abbe7f60f7e4f11e3aca15d688faec1d1df089966105a9a/numpy-2.1.1-cp311-cp311-win32.whl", hash = "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d", size = 6533215 },
523
+ { url = "https://files.pythonhosted.org/packages/94/7a/4c00332a3ca79702bbc86228afd0e84e6f91b47222ec8cdf00677dd16481/numpy-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e", size = 12870550 },
524
+ { url = "https://files.pythonhosted.org/packages/36/11/c573ef66c004f991989c2c6218229d9003164525549409aec5ec9afc0285/numpy-2.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e", size = 20884403 },
525
+ { url = "https://files.pythonhosted.org/packages/6b/6c/a9fbef5fd2f9685212af2a9e47485cde9357c3e303e079ccf85127516f2d/numpy-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe", size = 13493375 },
526
+ { url = "https://files.pythonhosted.org/packages/34/f2/1316a6b08ad4c161d793abe81ff7181e9ae2e357a5b06352a383b9f8e800/numpy-2.1.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f", size = 5088823 },
527
+ { url = "https://files.pythonhosted.org/packages/be/15/fabf78a6d4a10c250e87daf1cd901af05e71501380532ac508879cc46a7e/numpy-2.1.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521", size = 6619825 },
528
+ { url = "https://files.pythonhosted.org/packages/9f/8a/76ddef3e621541ddd6984bc24d256a4e3422d036790cbbe449e6cad439ee/numpy-2.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b", size = 13696705 },
529
+ { url = "https://files.pythonhosted.org/packages/cb/22/2b840d297183916a95847c11f82ae11e248fa98113490b2357f774651e1d/numpy-2.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201", size = 16041649 },
530
+ { url = "https://files.pythonhosted.org/packages/c7/e8/6f4825d8f576cfd5e4d6515b9eec22bd618868bdafc8a8c08b446dcb65f0/numpy-2.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a", size = 16409358 },
531
+ { url = "https://files.pythonhosted.org/packages/bf/f8/5edf1105b0dc24fd66fc3e9e7f3bca3d920cde571caaa4375ec1566073c3/numpy-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313", size = 14172488 },
532
+ { url = "https://files.pythonhosted.org/packages/f4/c2/dddca3e69a024d2f249a5b68698328163cbdafb7e65fbf6d36373bbabf12/numpy-2.1.1-cp312-cp312-win32.whl", hash = "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed", size = 6237195 },
533
+ { url = "https://files.pythonhosted.org/packages/b7/98/5640a09daa3abf0caeaefa6e7bf0d10c0aa28a77c84e507d6a716e0e23df/numpy-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270", size = 12568082 },
534
+ { url = "https://files.pythonhosted.org/packages/6b/9e/8bc6f133bc6d359ccc9ec051853aded45504d217685191f31f46d36b7065/numpy-2.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5", size = 20834810 },
535
+ { url = "https://files.pythonhosted.org/packages/32/1b/429519a2fa28681814c511574017d35f3aab7136d554cc65f4c1526dfbf5/numpy-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5", size = 13507739 },
536
+ { url = "https://files.pythonhosted.org/packages/25/18/c732d7dd9896d11e4afcd487ac65e62f9fa0495563b7614eb850765361fa/numpy-2.1.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136", size = 5074465 },
537
+ { url = "https://files.pythonhosted.org/packages/3e/37/838b7ae9262c370ab25312bab365492016f11810ffc03ebebbd54670b669/numpy-2.1.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0", size = 6606418 },
538
+ { url = "https://files.pythonhosted.org/packages/8b/b9/7ff3bfb71e316a5b43a124c4b7a5881ab12f3c32636014bef1f757f19dbd/numpy-2.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb", size = 13692464 },
539
+ { url = "https://files.pythonhosted.org/packages/42/78/75bcf16e6737cd196ff7ecf0e1fd3f953293a34dff4fd93fb488e8308536/numpy-2.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df", size = 16037763 },
540
+ { url = "https://files.pythonhosted.org/packages/23/99/36bf5ffe034d06df307bc783e25cf164775863166dcd878879559fe0379f/numpy-2.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78", size = 16410374 },
541
+ { url = "https://files.pythonhosted.org/packages/7f/16/04c5dab564887d4cd31a9ed30e51467fa70d52a4425f5a9bd1eed5b3d34c/numpy-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556", size = 14169873 },
542
+ { url = "https://files.pythonhosted.org/packages/09/e0/d1b5adbf1731886c4186c59a9fa208585df9452a43a2b60e79af7c649717/numpy-2.1.1-cp313-cp313-win32.whl", hash = "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b", size = 6234118 },
543
+ { url = "https://files.pythonhosted.org/packages/d0/9c/2391ee6e9ebe77232ddcab29d92662b545e99d78c3eb3b4e26d59b9ca1ca/numpy-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0", size = 12561742 },
544
+ { url = "https://files.pythonhosted.org/packages/38/0e/c4f754f9e73f9bb520e8bf418c646f2c4f70c5d5f2bc561e90f884593193/numpy-2.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553", size = 20858403 },
545
+ { url = "https://files.pythonhosted.org/packages/32/fc/d69092b9171efa0cb8079577e71ce0cac0e08f917d33f6e99c916ed51d44/numpy-2.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480", size = 13519851 },
546
+ { url = "https://files.pythonhosted.org/packages/14/2a/d7cf2cd9f15b23f623075546ea64a2c367cab703338ca22aaaecf7e704df/numpy-2.1.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f", size = 5115444 },
547
+ { url = "https://files.pythonhosted.org/packages/8e/00/e87b2cb4afcecca3b678deefb8fa53005d7054f3b5c39596e5554e5d98f8/numpy-2.1.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468", size = 6628903 },
548
+ { url = "https://files.pythonhosted.org/packages/ab/9d/337ae8721b3beec48c3413d71f2d44b2defbf3c6f7a85184fc18b7b61f4a/numpy-2.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef", size = 13665945 },
549
+ { url = "https://files.pythonhosted.org/packages/c0/90/ee8668e84c5d5cc080ef3beb622c016adf19ca3aa51afe9dbdcc6a9baf59/numpy-2.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f", size = 16023473 },
550
+ { url = "https://files.pythonhosted.org/packages/38/a0/57c24b2131879183051dc698fbb53fd43b77c3fa85b6e6311014f2bc2973/numpy-2.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c", size = 16400624 },
551
+ { url = "https://files.pythonhosted.org/packages/bb/4c/14a41eb5c9548c6cee6af0936eabfd985c69230ffa2f2598321431a9aa0a/numpy-2.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec", size = 14155072 },
552
+ ]
553
+
554
  [[package]]
555
  name = "oauthlib"
556
  version = "3.2.2"
 
569
  { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 },
570
  ]
571
 
572
+ [[package]]
573
+ name = "pandas"
574
+ version = "2.2.2"
575
+ source = { registry = "https://pypi.org/simple" }
576
+ dependencies = [
577
+ { name = "numpy" },
578
+ { name = "python-dateutil" },
579
+ { name = "pytz" },
580
+ { name = "tzdata" },
581
+ ]
582
+ sdist = { url = "https://files.pythonhosted.org/packages/88/d9/ecf715f34c73ccb1d8ceb82fc01cd1028a65a5f6dbc57bfa6ea155119058/pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54", size = 4398391 }
583
+ wheels = [
584
+ { url = "https://files.pythonhosted.org/packages/1b/70/61704497903d43043e288017cb2b82155c0d41e15f5c17807920877b45c2/pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288", size = 12574808 },
585
+ { url = "https://files.pythonhosted.org/packages/16/c6/75231fd47afd6b3f89011e7077f1a3958441264aca7ae9ff596e3276a5d0/pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151", size = 11304876 },
586
+ { url = "https://files.pythonhosted.org/packages/97/2d/7b54f80b93379ff94afb3bd9b0cd1d17b48183a0d6f98045bc01ce1e06a7/pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b", size = 15602548 },
587
+ { url = "https://files.pythonhosted.org/packages/fc/a5/4d82be566f069d7a9a702dcdf6f9106df0e0b042e738043c0cc7ddd7e3f6/pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee", size = 13031332 },
588
+ { url = "https://files.pythonhosted.org/packages/92/a2/b79c48f530673567805e607712b29814b47dcaf0d167e87145eb4b0118c6/pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db", size = 16286054 },
589
+ { url = "https://files.pythonhosted.org/packages/40/c7/47e94907f1d8fdb4868d61bd6c93d57b3784a964d52691b77ebfdb062842/pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1", size = 13879507 },
590
+ { url = "https://files.pythonhosted.org/packages/ab/63/966db1321a0ad55df1d1fe51505d2cdae191b84c907974873817b0a6e849/pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24", size = 11634249 },
591
+ { url = "https://files.pythonhosted.org/packages/dd/49/de869130028fb8d90e25da3b7d8fb13e40f5afa4c4af1781583eb1ff3839/pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef", size = 12500886 },
592
+ { url = "https://files.pythonhosted.org/packages/db/7c/9a60add21b96140e22465d9adf09832feade45235cd22f4cb1668a25e443/pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce", size = 11340320 },
593
+ { url = "https://files.pythonhosted.org/packages/b0/85/f95b5f322e1ae13b7ed7e97bd999160fa003424711ab4dc8344b8772c270/pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad", size = 15204346 },
594
+ { url = "https://files.pythonhosted.org/packages/40/10/79e52ef01dfeb1c1ca47a109a01a248754ebe990e159a844ece12914de83/pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad", size = 12733396 },
595
+ { url = "https://files.pythonhosted.org/packages/35/9d/208febf8c4eb5c1d9ea3314d52d8bd415fd0ef0dd66bb24cc5bdbc8fa71a/pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76", size = 15858913 },
596
+ { url = "https://files.pythonhosted.org/packages/99/d1/2d9bd05def7a9e08a92ec929b5a4c8d5556ec76fae22b0fa486cbf33ea63/pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32", size = 13417786 },
597
+ { url = "https://files.pythonhosted.org/packages/22/a5/a0b255295406ed54269814bc93723cfd1a0da63fb9aaf99e1364f07923e5/pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23", size = 11498828 },
598
+ ]
599
+
600
  [[package]]
601
  name = "parso"
602
  version = "0.8.4"
 
780
  { url = "https://files.pythonhosted.org/packages/3d/47/444768600d9e0ebc82f8e347775d24aef8f6348cf00e9fa0e81910814e6d/python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215", size = 22299 },
781
  ]
782
 
783
+ [[package]]
784
+ name = "pytz"
785
+ version = "2024.1"
786
+ source = { registry = "https://pypi.org/simple" }
787
+ sdist = { url = "https://files.pythonhosted.org/packages/90/26/9f1f00a5d021fff16dee3de13d43e5e978f3d58928e129c3a62cf7eb9738/pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812", size = 316214 }
788
+ wheels = [
789
+ { url = "https://files.pythonhosted.org/packages/9c/3d/a121f284241f08268b21359bd425f7d4825cffc5ac5cd0e1b3d82ffd2b10/pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319", size = 505474 },
790
+ ]
791
+
792
  [[package]]
793
  name = "pywin32"
794
  version = "306"
 
1030
  [package.dev-dependencies]
1031
  dev = [
1032
  { name = "ipykernel" },
1033
+ { name = "lxml" },
1034
+ { name = "pandas" },
1035
  { name = "pytest" },
1036
  { name = "pytest-cov" },
1037
  { name = "ruff" },
 
1043
  [package.metadata.requires-dev]
1044
  dev = [
1045
  { name = "ipykernel", specifier = ">=6.29.5" },
1046
+ { name = "lxml", specifier = ">=5.3.0" },
1047
+ { name = "pandas", specifier = ">=2.2.2" },
1048
  { name = "pytest", specifier = ">=8.3.2" },
1049
  { name = "pytest-cov", specifier = ">=5.0.0" },
1050
  { name = "ruff", specifier = ">=0.6.3" },
 
1059
  { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
1060
  ]
1061
 
1062
+ [[package]]
1063
+ name = "tzdata"
1064
+ version = "2024.1"
1065
+ source = { registry = "https://pypi.org/simple" }
1066
+ sdist = { url = "https://files.pythonhosted.org/packages/74/5b/e025d02cb3b66b7b76093404392d4b44343c69101cc85f4d180dd5784717/tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd", size = 190559 }
1067
+ wheels = [
1068
+ { url = "https://files.pythonhosted.org/packages/65/58/f9c9e6be752e9fcb8b6a0ee9fb87e6e7a1f6bcab2cdc73f02bb7ba91ada0/tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252", size = 345370 },
1069
+ ]
1070
+
1071
  [[package]]
1072
  name = "uvicorn"
1073
  version = "0.30.6"