diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml index 1ac4929..f75fdb3 100644 --- a/.github/workflows/build_docs.yml +++ b/.github/workflows/build_docs.yml @@ -1,44 +1,56 @@ name: Build Docs + on: push: branches: - master - main - docs + pull_request: + branches: + - master + - main + permissions: contents: write + jobs: - deploy: + render: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + + - uses: quarto-dev/quarto-actions/setup@v2 + + - uses: actions/setup-python@v5 with: python-version: "3.12" - - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV - - uses: actions/cache@v3 - with: - key: mkdocs-material-${{ env.cache_id }} - path: .cache - restore-keys: | - mkdocs-material- + - name: Install OS dependencies - shell: bash -el {0} run: | - sudo apt update - sudo apt install -y build-essential zlib1g-dev libcurl4-openssl-dev - - name: Install the package - shell: bash -el {0} + sudo apt-get update + sudo apt-get remove -y libcurl4-openssl-dev || true + sudo apt-get install -y \ + build-essential \ + zlib1g-dev \ + libcurl4-gnutls-dev \ + bedtools \ + libhts-dev \ + samtools + + - name: Install PlotNado docs environment run: | - pip install numpy cython - git clone https://github.com/hiclib/iced.git - cd iced - for fn in $(find . -iname "*.pyx"); do cython $fn; done - pip install . -v - cd .. - rm -rf iced - pip install . -v - - name: Install Mkdocs plugins - run: pip install mkdocs-material mkdocstrings-python mkdocs-click pygments mkdocs-gen-files mkdocs-jupyter mkdocs-autorefs mkdocs-literate-nav mkdocs-section-index - - name: Build the documentation - run: mkdocs gh-deploy --force + python -m pip install --upgrade pip uv + uv pip install --system -e ".[docs]" + + - name: Render Quarto documentation + run: quarto render + + - name: Publish Quarto site + if: github.event_name == 'push' + uses: quarto-dev/quarto-actions/publish@v2 + with: + target: gh-pages + render: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 7f21b62..155c3a4 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ demo_genome_browser.py pr_docs.txt test_parser.py examples/output/* + +/.quarto/ +**/*.quarto_ipynb diff --git a/README.md b/README.md index f8f1bc2..f8c6490 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,10 @@ fig.bigwig(signal, title="Synthetic signal", style="fill", color="#1f77b4") fig.save("quickstart.png", "chr1:1,010,000-1,080,000") ``` +Output: + +![Quickstart output](docs/images/quickstart.png) + `GenomicFigure()` uses publication-style defaults automatically. Use `GenomicFigure(theme=None)` to opt out. ## CLI Quick Start @@ -129,6 +133,10 @@ fig = GenomicFigure.from_template("template.yaml") fig.save("browser_view.png", region="chr1:1,000,000-1,100,000") ``` +Output: + +![Template-rendered output](docs/images/examples/quickstart_first_plot.png) + ## What The Template Looks Like The generated YAML is meant to be human-editable: @@ -188,8 +196,8 @@ BigWigTrack.options_markdown() ```bash uv sync --extra docs -uv run mkdocs build --strict -uv run mkdocs serve +quarto render +quarto preview ``` ## Development diff --git a/_quarto.yml b/_quarto.yml new file mode 100644 index 0000000..bb9e825 --- /dev/null +++ b/_quarto.yml @@ -0,0 +1,98 @@ +project: + type: website + output-dir: site + execute-dir: project + render: + - index.qmd + - docs/*.qmd + - docs/reference/*.qmd + +website: + title: "PlotNado Documentation" + description: "Publication-ready genome browser figures in Python." + site-url: "https://alsmith151.github.io/plotnado" + repo-url: "https://github.com/alsmith151/plotnado" + repo-actions: [edit, issue] + favicon: docs/images/Logo.jpeg + search: + location: navbar + type: overlay + navbar: + logo: docs/images/Logo.jpeg + left: + - text: Home + href: index.qmd + - text: Getting Started + menu: + - text: Installation + href: docs/installation.qmd + - text: Quick Start + href: docs/quickstart.qmd + - text: Track Construction + href: docs/quickstart_tracks.qmd + - text: Guides + menu: + - text: Track Catalog + href: docs/track_catalog.qmd + - text: Data Inputs + href: docs/data_inputs.qmd + - text: Figure Workflows + href: docs/figure_workflows.qmd + - text: Aesthetics + href: docs/aesthetics.qmd + - text: Recipes + href: docs/recipes.qmd + - text: Best Practices + href: docs/best_practices.qmd + - text: Reference + menu: + - text: Overview + href: docs/reference.qmd + - text: API Reference + href: docs/api_reference.qmd + - text: Track Aliases + href: docs/track_aliases.qmd + - text: Living Gallery + href: docs/example_coverage.qmd + - text: Reference Summary + href: docs/reference/SUMMARY.qmd + - text: CLI + href: docs/cli.qmd + - text: Examples + menu: + - text: Living Gallery + href: docs/example_coverage.qmd + - text: Basic Example + href: docs/basic_example.qmd + - text: Simple Overlays + href: docs/simple_overlays.qmd + - text: Worked Examples + href: docs/worked_examples.qmd + - text: Help + menu: + - text: FAQ + href: docs/faq.qmd + - text: Troubleshooting + href: docs/troubleshooting.qmd + - text: Changelog + href: docs/changelog.qmd + +format: + html: + theme: + light: [cosmo, docs/stylesheets/plotnado.scss] + dark: [darkly, docs/stylesheets/plotnado.scss] + toc: true + toc-depth: 4 + code-copy: true + code-overflow: wrap + page-layout: full + +execute: + enabled: true + warning: false + message: false + error: false + freeze: false + +metadata-files: [] diff --git a/docs/aesthetics.md b/docs/aesthetics.qmd similarity index 89% rename from docs/aesthetics.md rename to docs/aesthetics.qmd index 428eae1..250a70e 100644 --- a/docs/aesthetics.md +++ b/docs/aesthetics.qmd @@ -1,8 +1,8 @@ # Aesthetics -This guide is about the visual layer: how tracks scale, label, color, and share space. For exact field names, pair it with [Aesthetics Reference](aesthetics_reference.md). +This guide is about the visual layer: how tracks scale, label, color, and share space. For exact field names, pair it with [Aesthetics Reference](aesthetics_reference.qmd). -The rendered panels on this page are generated by [scripts/gen_docs_images.py](https://github.com/alsmith151/plotnado/blob/main/scripts/gen_docs_images.py). Runnable source scripts for the overlapping track combinations are collected on [Example Coverage](example_coverage.md). +The rendered panels on this page are paired with live Quarto examples in the [Living Gallery](example_coverage.qmd), where the code cells and output are rendered together from the current package. Every track kwarg is routed automatically — aesthetics fields go into the style model, label fields go into the label model. You do not need to nest anything manually: @@ -17,13 +17,17 @@ fig.bigwig( ) ``` +Output: + +![Automatic kwarg routing output](images/quickstart.png) + --- ## BigWig styles `style` controls how the signal is drawn. The four options: -Runnable example: [BigWig styles](example_coverage.md#bigwig-styles) +Runnable example: [BigWig styles](example_coverage.qmd#bigwig-styles) ```python fig.bigwig(signal, title="fill", style="fill", color="#1f77b4") @@ -114,7 +118,7 @@ fig.bigwig( BED tracks support `display="expanded"` for stacked intervals and `show_labels=True` for name text. narrowPeak adds score-based coloring via `color_by` and optional summit lines. -Runnable example: [BED and narrowPeak](example_coverage.md#bed-and-narrowpeak) +Runnable example: [BED and narrowPeak](example_coverage.qmd#bed-and-narrowpeak) ```python fig.bed( @@ -142,7 +146,7 @@ fig.narrowpeak( `overlay()` draws multiple signals in one panel. `autoscale(True)` and `autoscale_group` now treat overlays as first-class signal panels rather than leaving them on an isolated scale. -Runnable example: [Overlay, autoscale, and highlight](example_coverage.md#overlay-autoscale-and-highlight) +Runnable example: [Overlay, autoscale, and highlight](example_coverage.qmd#overlay-autoscale-and-highlight) Key rules: @@ -171,8 +175,9 @@ fig.overlay( ![Overlay, autoscale, highlight](images/aesthetics/overlay_autoscale.png) -!!! tip "When overlay scaling looks wrong" - If the overlay panel should match nearby signal tracks, put `autoscale_group` on the overlay itself. Putting it only on the nested component tracks does not synchronize the panel with sibling axes. +::: {.callout-tip title="When overlay scaling looks wrong"} +If the overlay panel should match nearby signal tracks, put `autoscale_group` on the overlay itself. Putting it only on the nested component tracks does not synchronize the panel with sibling axes. +::: --- @@ -180,7 +185,7 @@ fig.overlay( `display="collapsed"` fits all genes on one row. `display="expanded"` stacks overlapping genes. -Related runnable example: [Gene label strategies](example_coverage.md#gene-label-strategies) +Related runnable example: [Gene label strategies](example_coverage.qmd#gene-label-strategies) ```python fig.genes("hg38", title="display='collapsed'", display="collapsed") @@ -195,7 +200,7 @@ fig.genes("hg38", title="display='expanded'", display="expanded") Arc width and color can be driven by a `score` column using `color_by_score=True` and any matplotlib colormap. -Runnable example: [Links, hline, and vline](example_coverage.md#links-hline-and-vline) +Runnable example: [Links, hline, and vline](example_coverage.qmd#links-hline-and-vline) ```python fig.links(links_df, title="Loops", color_by_score=True, cmap="viridis", alpha=0.8) diff --git a/docs/aesthetics_reference.md b/docs/aesthetics_reference.md deleted file mode 100644 index 68ac1a3..0000000 --- a/docs/aesthetics_reference.md +++ /dev/null @@ -1,41 +0,0 @@ -# Aesthetics Reference - -Use this page when you want the fastest route to track fields and styling options without reading source. - -Every track exposes three groups of fields: - -- `track`: constructor-level fields such as `title`, `height`, `autoscale_group`, and `color_group` -- `aesthetics`: visual controls such as `style`, `color`, `alpha`, `min_value`, and `max_value` -- `label`: title and scale annotation controls such as `plot_scale`, `title_location`, and `scale_location` - -## Discover fields at runtime - -```python -from plotnado import GenomicFigure, BigWigTrack, list_options - -GenomicFigure.track_options("bigwig") -GenomicFigure.track_options("overlay") -GenomicFigure.track_options_markdown("genes") - -BigWigTrack.options() -BigWigTrack.options_markdown() -list_options(BigWigTrack) -``` - -## High-value fields by category - -| Category | Common fields | Notes | -| --- | --- | --- | -| Track | `title`, `height`, `autoscale_group`, `color_group` | These apply to the whole track panel | -| Aesthetics | `color`, `alpha`, `style`, `min_value`, `max_value` | These affect how the track is drawn | -| Label | `plot_title`, `plot_scale`, `title_location`, `scale_location` | These control title and scale text | - -## Overlay-specific guidance - -Overlay tracks are scaled as a single panel. - -- `autoscale_group` belongs on the overlay itself when the whole panel should match sibling signal tracks. -- Explicit overlay `min_value` / `max_value` overrides grouped or global autoscale on that edge. -- `show_labels=False` is useful when the overlay is dense and the panel title carries enough meaning. - -For behavior details, see [Aesthetics](aesthetics.md) and [Reference](reference.md). \ No newline at end of file diff --git a/docs/aesthetics_reference.qmd b/docs/aesthetics_reference.qmd new file mode 100644 index 0000000..932ffe7 --- /dev/null +++ b/docs/aesthetics_reference.qmd @@ -0,0 +1,349 @@ +# Aesthetics Reference + +Use this page when you want the fastest route to track fields and styling options without reading source. + +Every track exposes three groups of fields: + +- `track`: constructor-level fields such as `title`, `height`, `autoscale_group`, and `color_group` +- `aesthetics`: visual controls such as `style`, `color`, `alpha`, `min_value`, and `max_value` +- `label`: title and scale annotation controls such as `plot_scale`, `title_location`, and `scale_location` + +## Discover fields at runtime + +```python +from plotnado import GenomicFigure, BigWigTrack, list_options + +GenomicFigure.track_options("bigwig") +GenomicFigure.track_options("overlay") +GenomicFigure.track_options_markdown("genes") + +BigWigTrack.options() +BigWigTrack.options_markdown() +list_options(BigWigTrack) +``` + +## High-value fields by category + +| Category | Common fields | Notes | +| --- | --- | --- | +| Track | `title`, `height`, `autoscale_group`, `color_group` | These apply to the whole track panel | +| Aesthetics | `color`, `alpha`, `style`, `min_value`, `max_value` | These affect how the track is drawn | +| Label | `plot_title`, `plot_scale`, `title_location`, `scale_location` | These control title and scale text | + +## Track Option Index + +These anchors are stable targets for the [Track Catalog](track_catalog.qmd). Use the runtime calls shown in each section for the complete field table in your installed version. + +### BigWig / bedGraph {#options-bigwig} + +Aliases: `bigwig`, `bw`, `signal`, `bedgraph` + +Runtime reference: + +```python +GenomicFigure.track_options("bigwig") +GenomicFigure.track_options_markdown("bigwig") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Signal drawing | `style`, `color`, `alpha`, `linewidth`, `scatter_point_size` | +| Signal limits | `min_value`, `max_value`, `smoothing_window`, `smoothing_method` | +| Figure coordination | `autoscale_group`, `color_group`, `height` | +| Labels | `title`, `plot_scale`, `scale_location`, `label_on_track`, `label_box_enabled` | + +### Overlay / BigWig overlay {#options-overlay} + +Aliases: `overlay`, `bigwig_overlay` + +Runtime reference: + +```python +GenomicFigure.track_options("overlay") +GenomicFigure.track_options_markdown("overlay") +``` + +Overlay tracks are scaled as a single panel. + +- `autoscale_group` belongs on the overlay itself when the whole panel should match sibling signal tracks. +- Explicit overlay `min_value` / `max_value` overrides grouped or global autoscale on that edge. +- `show_labels=False` is useful when the overlay is dense and the panel title carries enough meaning. + +High-value controls: + +| Area | Fields | +| --- | --- | +| Components | `tracks`, `colors`, `alpha`, `show_labels` | +| Shared scale | `min_value`, `max_value`, `autoscale_group` | +| Labels | `title`, `plot_scale`, `scale_location`, `label_on_track` | + +### BigWig collection {#options-bigwig-collection} + +Alias: `bigwig_collection` + +Runtime reference: + +```python +GenomicFigure.track_options("bigwig_collection") +GenomicFigure.track_options_markdown("bigwig_collection") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Inputs | `files`, `labels` | +| Styling | `colors`, `alpha`, `height` | +| Coordination | `title`, `color_group` | + +### BigWig diff {#options-bigwig-diff} + +Alias: `bigwig_diff` + +Runtime reference: + +```python +GenomicFigure.track_options("bigwig_diff") +GenomicFigure.track_options_markdown("bigwig_diff") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Inputs | `data_a`, `data_b`, `method` | +| Positive/negative signal | `positive_color`, `negative_color`, `bar_alpha` | +| Scale | `min_value`, `max_value`, `height` | + +### BED / annotation / unknown fallback {#options-bed} + +Aliases: `bed`, `annotation`, `unknown` + +Runtime reference: + +```python +GenomicFigure.track_options("bed") +GenomicFigure.track_options_markdown("bed") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Interval layout | `display`, `interval_height`, `max_rows`, `draw_edges` | +| Labels | `show_labels`, `label_field`, `font_size`, `title` | +| Styling | `color`, `alpha`, `height`, `color_group` | + +### NarrowPeak {#options-narrowpeak} + +Alias: `narrowpeak` + +Runtime reference: + +```python +GenomicFigure.track_options("narrowpeak") +GenomicFigure.track_options_markdown("narrowpeak") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Peak layout | `display`, `show_summit`, `show_labels` | +| Score styling | `color_by`, `cmap`, `color`, `alpha` | +| Labels | `label_field`, `font_size`, `title` | + +### Genes / gene {#options-genes} + +Aliases: `genes`, `gene` + +Runtime reference: + +```python +GenomicFigure.track_options("genes") +GenomicFigure.track_options_markdown("genes") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Annotation source | `genome`, `data` | +| Layout | `display`, `minimum_gene_length`, `max_number_of_rows`, `height` | +| Gene labels | `show_labels`, `gene_label_font_size`, `color` | + +### Links {#options-links} + +Alias: `links` + +Runtime reference: + +```python +GenomicFigure.track_options("links") +GenomicFigure.track_options_markdown("links") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Input | BEDPE-like `data`, optional `score` | +| Arc styling | `color_by_score`, `cmap`, `alpha`, `linewidth` | +| Labels/layout | `title`, `height` | + +### Highlight {#options-highlight} + +Alias: `highlight` + +Runtime reference: + +```python +GenomicFigure.track_options("highlight") +GenomicFigure.track_options_markdown("highlight") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Input | BED-like file/table or inline `fig.highlight(...)` region | +| Styling | `color`, `alpha` | +| Behavior | Drawn behind tracks; does not change y-axis limits | + +### HLine / VLine {#options-lines} + +Aliases: `hline`, `vline` + +Runtime reference: + +```python +GenomicFigure.track_options("hline") +GenomicFigure.track_options("vline") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Position | y-value for `hline`, genomic x-position for `vline` | +| Styling | `color`, `linewidth`, `linestyle`, `alpha` | +| Behavior | Drawn as reference marks over existing panels | + +### Cooler / Cooler average {#options-cooler} + +Aliases: `cooler`, `cooler_average` + +Runtime reference: + +```python +GenomicFigure.track_options("cooler") +GenomicFigure.track_options("cooler_average") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Input | cooler-compatible path(s) | +| Matrix rendering | `balance`, `cmap`, `min_value`, `max_value` | +| Layout | `height`, `title` | + +### CapCruncher {#options-capcruncher} + +Alias: `capcruncher` + +Runtime reference: + +```python +GenomicFigure.track_options("capcruncher") +GenomicFigure.track_options_markdown("capcruncher") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Input | CapCruncher HDF5 reporters store | +| Viewpoint | `viewpoint` | +| Matrix rendering | `balance`, `cmap`, `min_value`, `max_value` | + +### QuantNado coverage {#options-quantnado-coverage} + +Aliases: `quantnado_coverage`, `quantnado_stranded_coverage` + +Runtime reference: + +```python +GenomicFigure.track_options("quantnado_coverage") +GenomicFigure.track_options("quantnado_stranded_coverage") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Input | QuantNado object name plus `dataset_path` | +| Coverage styling | `color`, `fill`, `reverse_color` | +| Scale/layout | `min_value`, `max_value`, `height`, `title` | + +### QuantNado methylation {#options-quantnado-methylation} + +Alias: `quantnado_methylation` + +Runtime reference: + +```python +GenomicFigure.track_options("quantnado_methylation") +GenomicFigure.track_options_markdown("quantnado_methylation") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Input | QuantNado methylation dataset | +| Point styling | `color`, `point_size`, `alpha` | +| Scale/layout | methylation percentage scale, `height`, `title` | + +### QuantNado variant {#options-quantnado-variant} + +Alias: `quantnado_variant` + +Runtime reference: + +```python +GenomicFigure.track_options("quantnado_variant") +GenomicFigure.track_options_markdown("quantnado_variant") +``` + +High-value controls: + +| Area | Fields | +| --- | --- | +| Input | QuantNado variant dataset | +| Genotype styling | `het_color`, `hom_alt_color`, `marker_size` | +| Scale/layout | allele-balance scale, `height`, `title` | + +### Structural tracks {#options-structural} + +Aliases: `scalebar`, `scale`, `axis`, `spacer` + +Runtime reference: + +```python +GenomicFigure.track_options("scalebar") +GenomicFigure.track_options("axis") +GenomicFigure.track_options("spacer") +``` + +High-value controls: + +| Track | Fields | +| --- | --- | +| `scalebar` / `scale` | `plot_scale`, `scale_location`, `scale_size`, `height` | +| `axis` | `num_ticks`, tick/label styling, `height` | +| `spacer` | `height` | + +For behavior details, see [Reference](reference.qmd) and [Track Catalog](track_catalog.qmd). diff --git a/docs/api_reference.md b/docs/api_reference.qmd similarity index 89% rename from docs/api_reference.md rename to docs/api_reference.qmd index 5135b1f..8796d85 100644 --- a/docs/api_reference.md +++ b/docs/api_reference.qmd @@ -2,8 +2,8 @@ Use this section when you need API-level details beyond the task-oriented guides. -- Generated module/class reference: [Reference Summary](reference/SUMMARY.md) -- Auto-generated options reference for every track: [Aesthetics Reference](aesthetics_reference.md) +- Generated module/class reference: [Reference Summary](reference/SUMMARY.qmd) +- Auto-generated options reference for every track: [Aesthetics Reference](aesthetics_reference.qmd) ## Discover options at runtime @@ -32,7 +32,7 @@ Each option payload is split into: - `aesthetics`: style model fields (`aesthetics={...}` or shorthand kwargs). - `label`: label controls (`label={...}` or shorthand kwargs). -If you want a browsable summary rather than runtime output, start with [Aesthetics Reference](aesthetics_reference.md). +If you want a browsable summary rather than runtime output, start with [Aesthetics Reference](aesthetics_reference.qmd). ## `GenomicFigure` helper methods: automatic kwargs @@ -56,6 +56,10 @@ gf.bigwig( ) ``` +Output: + +![Helper method output](images/quickstart.png) + `color_group` is a track-level kwarg and works well with `gf.autocolor()` for consistent sample coloring: ```python @@ -65,6 +69,10 @@ gf.bed("sampleA.bigBed", title="A peaks", color_group="sampleA") gf.bigwig("sampleA.bw", title="A signal", color_group="sampleA") ``` +Output: + +![Color group helper output](images/aesthetics/autocolor.png) + ## Common entry points - `plotnado.GenomicFigure`: high-level composition (`add_track`, `plot`, `plot_regions`, `plot_gene`, `from_template`, `from_igv_session`, `to_toml`, `from_toml`). @@ -91,4 +99,4 @@ After a figure is built, tracks can be edited in-place. All editing methods return `self` for chaining. -For practical usage, prefer [Quick Start](quickstart.md), [Track Catalog](track_catalog.md), and [Recipes](recipes.md). +For practical usage, prefer [Quick Start](quickstart.qmd), [Track Catalog](track_catalog.qmd), and [Recipes](recipes.qmd). diff --git a/docs/basic_example.ipynb b/docs/basic_example.ipynb deleted file mode 100644 index 03d7777..0000000 --- a/docs/basic_example.ipynb +++ /dev/null @@ -1,447 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Basic Example (New API)\n", - "\n", - "A quick walkthrough for plotting multiple BigWig tracks with the current PlotNado API." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Imports" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from plotnado import GenomicFigure, LabelConfig" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Define the BigWig files that we want to plot\n", - "\n", - "**Note**: The BigWig files are not included in the dataset, as it is possible to just use remote URLs to plot the data I'm going to use data stored on the CCB Oxford HPC.\n", - "\n", - "To make life easier I'm going to make a dictionary of the BigWig files that I want to plot. You could just add these one by one to the plot if you wanted to." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "bigwigs = {\n", - " 'MV4;11 0nM EPZ H3K79me2': 'https://userweb.molbiol.ox.ac.uk/public/project/milne_group/cchahrou/orlando_chip/hg38_dm6/MV4110nMH3K79me2_bigWig.bigWig',\n", - " 'MV4;11 0.5nM EPZ H3K79me2': 'https://userweb.molbiol.ox.ac.uk/public/project/milne_group/cchahrou/orlando_chip/hg38_dm6/MV41105nMH3K79me2_bigWig.bigWig',\n", - " 'MV4;11 2nM EPZ H3K79me2': 'https://userweb.molbiol.ox.ac.uk/public/project/milne_group/cchahrou/orlando_chip/hg38_dm6/MV4112nMH3K79me2_bigWig.bigWig',\n", - " 'MV4;11 5nM EPZ H3K79me2': 'https://userweb.molbiol.ox.ac.uk/public/project/milne_group/cchahrou/orlando_chip/hg38_dm6/MV4115nMH3K79me2_bigWig.bigWig',\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Generate the plot\n", - "\n", - "This example builds a figure with:\n", - "\n", - "- scale bar\n", - "- gene annotations\n", - "- 4 BigWig tracks\n", - "- genomic coordinate axis" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "figure = Figure().autocolor()\n", - "figure.add_track(\"scalebar\")\n", - "figure.add_track(\n", - " \"genes\",\n", - " genome=\"hg38\",\n", - " minimum_gene_length=100_000,\n", - " height=0.5,\n", - " display=\"collapsed\",\n", - ")\n", - "\n", - "for name, url in bigwigs.items():\n", - " figure.add_track(\n", - " \"bigwig\",\n", - " data=url,\n", - " title=name,\n", - " style=\"fill\",\n", - " autoscale_group=\"H3K79me2\",\n", - " min_value=0,\n", - " label=LabelConfig(plot_title=True, plot_scale=True),\n", - " )\n", - "\n", - "figure.add_track(\"axis\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Display the plot\n", - "\n", - "You've got multiple options for displaying the plot:\n", - "\n", - "* Display it in a Jupyter notebook\n", - "* Save it to a file\n", - "* Save it as a template for later use or plotting using the CLI" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Define the region to plot\n", - "\n", - "There are two ways to define the region to plot:\n", - "\n", - "1. Use a set of genomic coordinates\n", - "1. Use a gene name (this must be from a genome that is supported by the package)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "region_to_plot = 'chr9:77,620,642-78,119,542' # Define the region to plot, this is a region around the GNAQ gene\n", - "gene_to_plot = 'GNAQ' # Define the gene to plot, this is the GNAQ gene" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Perform the plotting" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### By Region" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "figure.plot(region_to_plot) # Plot the region" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### By Gene" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "figure.plot_gene(gene_to_plot)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Save the plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "figure.save(\n", - " path=\"GNAQ_H3K79me2.png\",\n", - " region=region_to_plot,\n", - " dpi=300,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Examine the saved plot\n", - "\n", - "![Basic Example](./GNAQ_H3K79me2.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Save the plot as a template\n", - "\n", - "This is useful if you want to use the same plot settings for multiple plots, it also allows you to edit the plot settings with standard text editors and then you can either use the PlotNado CLI to generate the plot or you can read in the template and plot it again using the API." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " import tomli_w # noqa: F401\n", - "except ImportError:\n", - " import subprocess\n", - " import sys\n", - " subprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"tomli-w\"])\n", - "figure.to_toml(\"H3K79me2.toml\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Examine the template\n", - "\n", - "The TOML template stores figure and track settings that can be reloaded with `Figure.from_toml(...)`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Re-load the template and plot it\n", - "\n", - "`Figure.from_toml(...)` reconstructs the figure and tracks.\n", - "If you want auto colors again, call `.autocolor()` on the loaded figure." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "new_figure = Figure.from_toml(\"H3K79me2.toml\").autocolor()\n", - "new_figure.plot(region_to_plot)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## CLI replotting\n", - "\n", - "You can regenerate a figure from a template with the CLI:\n", - "\n", - "```bash\n", - "plotnado plot --template H3K79me2.toml --region chr9:77620642-78119542 --output GNAQ_H3K79me2_cli.png\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Customizing the plot\n", - "\n", - "A few common customizations with the new API:\n", - "\n", - "1. Changing track colors\n", - "2. Changing label/scale presentation\n", - "3. Switching BigWig rendering styles" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "figure = Figure()\n", - "figure.add_track(\"scalebar\")\n", - "figure.add_track(\n", - " \"genes\",\n", - " genome=\"hg38\",\n", - " minimum_gene_length=100_000,\n", - " height=0.5,\n", - " display=\"collapsed\",\n", - ")\n", - "\n", - "bigwig_colours = {\n", - " \"MV4;11 0nM EPZ H3K79me2\": \"red\",\n", - " \"MV4;11 0.5nM EPZ H3K79me2\": \"blue\",\n", - " \"MV4;11 2nM EPZ H3K79me2\": \"green\",\n", - " \"MV4;11 5nM EPZ H3K79me2\": \"purple\",\n", - "}\n", - "\n", - "for name, url in bigwigs.items():\n", - " figure.add_track(\n", - " \"bigwig\",\n", - " data=url,\n", - " title=name,\n", - " style=\"fill\",\n", - " color=bigwig_colours[name],\n", - " autoscale_group=\"H3K79me2\",\n", - " min_value=0,\n", - " label=LabelConfig(\n", - " plot_title=True,\n", - " plot_scale=True,\n", - " data_range_style=\"text\",\n", - " label_box_enabled=True,\n", - " label_box_alpha=0.8,\n", - " ),\n", - " )\n", - "\n", - "figure.add_track(\"axis\")\n", - "figure.plot_gene(\"GNAQ\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Changing BigWig styles\n", - "\n", - "Supported styles include `line`, `fill`, `fragment`, and `scatter`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "figure = Figure()\n", - "figure.add_track(\"scalebar\")\n", - "figure.add_track(\n", - " \"genes\",\n", - " genome=\"hg38\",\n", - " minimum_gene_length=100_000,\n", - " height=0.5,\n", - " display=\"collapsed\",\n", - ")\n", - "\n", - "bigwig_colours = {\n", - " \"MV4;11 0nM EPZ H3K79me2\": \"red\",\n", - " \"MV4;11 0.5nM EPZ H3K79me2\": \"blue\",\n", - " \"MV4;11 2nM EPZ H3K79me2\": \"green\",\n", - " \"MV4;11 5nM EPZ H3K79me2\": \"purple\",\n", - "}\n", - "\n", - "styles = [\"line\", \"fill\", \"fragment\", \"scatter\"]\n", - "\n", - "for i, (name, url) in enumerate(bigwigs.items()):\n", - " style = styles[i]\n", - " figure.add_track(\n", - " \"bigwig\",\n", - " data=url,\n", - " title=f\"{name} ({style})\",\n", - " style=style,\n", - " color=bigwig_colours[name],\n", - " autoscale_group=\"H3K79me2\",\n", - " min_value=0,\n", - " label=LabelConfig(plot_title=True, plot_scale=True, data_range_style=\"text\"),\n", - " )\n", - "\n", - "figure.add_track(\"axis\")\n", - "figure.plot_gene(\"GNAQ\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Changing line width and alpha\n", - "\n", - "Instead of changing unsupported bin settings, adjust visual emphasis with `linewidth` and `alpha`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "figure = Figure()\n", - "figure.add_track(\"scalebar\")\n", - "figure.add_track(\n", - " \"genes\",\n", - " genome=\"hg38\",\n", - " minimum_gene_length=100_000,\n", - " height=0.5,\n", - " display=\"collapsed\",\n", - ")\n", - "\n", - "settings = [\n", - " (0.4, 0.8),\n", - " (0.8, 0.8),\n", - " (1.2, 0.7),\n", - " (1.8, 0.6),\n", - "]\n", - "\n", - "for linewidth, alpha in settings:\n", - " figure.add_track(\n", - " \"bigwig\",\n", - " data=\"https://userweb.molbiol.ox.ac.uk/public/project/milne_group/cchahrou/orlando_chip/hg38_dm6/MV4110nMH3K79me2_bigWig.bigWig\",\n", - " title=f\"linewidth={linewidth}, alpha={alpha}\",\n", - " style=\"line\",\n", - " color=\"red\",\n", - " autoscale_group=\"H3K79me2\",\n", - " min_value=0,\n", - " linewidth=linewidth,\n", - " alpha=alpha,\n", - " label=LabelConfig(plot_title=True, plot_scale=True, data_range_style=\"text\"),\n", - " )\n", - "\n", - "figure.add_track(\"axis\")\n", - "figure.plot_gene(\"GNAQ\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.6" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/basic_example.qmd b/docs/basic_example.qmd new file mode 100644 index 0000000..a0dd4ef --- /dev/null +++ b/docs/basic_example.qmd @@ -0,0 +1,80 @@ +--- +title: Basic Example +jupyter: python3 +execute: + echo: true + warning: false + message: false +--- + +This page is an executable PlotNado walkthrough. Each plotting cell builds the figure with the public API and displays the plot it creates. + +## Setup + +```{python} +import numpy as np +import pandas as pd + +from plotnado import GenomicFigure +``` + +```{python} +#| echo: false +REGION = "chr1:1,010,000-1,080,000" + + +def signal(phase=0.0, scale=1.0, step=1_000): + bins = np.arange(1_000_000, 1_100_000, step) + values = scale * (4 + 2 * np.sin(np.linspace(phase, 6 + phase, bins.shape[0]))) + return pd.DataFrame( + {"chrom": "chr1", "start": bins, "end": bins + step, "value": values} + ) + + +def bed_intervals(): + return pd.DataFrame( + { + "chrom": ["chr1"] * 5, + "start": [1_012_000, 1_022_000, 1_038_000, 1_055_000, 1_068_000], + "end": [1_016_000, 1_028_000, 1_046_000, 1_062_000, 1_074_000], + "name": ["a", "b", "c", "d", "e"], + } + ) +``` + +## Build a first figure + +```{python} +fig = GenomicFigure(width=11, track_height=1.2) +fig.scalebar() +fig.axis() +fig.genes("hg38", title="Genes") +fig.bigwig(signal(), title="Synthetic signal", style="fill", color="#1f77b4") + +fig.plot(REGION) +``` + +## Add intervals and semantic colors + +```{python} +fig = GenomicFigure(width=11, track_height=1.1) +fig.autocolor() +fig.scalebar() +fig.axis() +fig.bigwig(signal(0.0), title="Sample A signal", color_group="A", style="fill") +fig.bed(bed_intervals(), title="Sample A peaks", color_group="A", display="expanded") +fig.bigwig(signal(1.5), title="Sample B signal", color_group="B", style="fill") +fig.bed(bed_intervals(), title="Sample B peaks", color_group="B", display="expanded") + +fig.plot(REGION) +``` + +## Save the figure + +```{python} +fig.save("docs/GNAQ_H3K79me2.png", region=REGION, dpi=180) +``` + +The saved file is the same figure definition rendered to disk. + +![Saved basic example output](GNAQ_H3K79me2.png) diff --git a/docs/best_practices.md b/docs/best_practices.qmd similarity index 95% rename from docs/best_practices.md rename to docs/best_practices.qmd index 869782a..94f3f43 100644 --- a/docs/best_practices.md +++ b/docs/best_practices.qmd @@ -30,6 +30,10 @@ gf.bed("B.bigBed", title="B peaks", color_group="B") gf.bigwig("B.bw", title="B signal", color_group="B") ``` +Output: + +![Semantically consistent colors output](images/aesthetics/autocolor.png) + ## Use aliases for speed, models for strictness - Use alias helpers (`fig.bigwig(...)`, `fig.bed(...)`) for exploratory work. @@ -61,7 +65,7 @@ GenomicFigure.track_options_markdown("genes") ```bash uv run pytest tests/ -v -uv run mkdocs build --strict +quarto render ``` - Prefer example scripts in `examples/` as regression fixtures for plotting behavior. diff --git a/docs/changelog.md b/docs/changelog.qmd similarity index 100% rename from docs/changelog.md rename to docs/changelog.qmd diff --git a/docs/cli.md b/docs/cli.qmd similarity index 92% rename from docs/cli.md rename to docs/cli.qmd index ef58435..8505fe0 100644 --- a/docs/cli.md +++ b/docs/cli.qmd @@ -71,6 +71,10 @@ Render a template for one or more regions: plotnado plot template.yaml --region chr1:1,000,000-1,100,000 --output output.png ``` +Output: + +![CLI plot output](images/examples/quickstart_first_plot.png) + Useful options: - `--region` / `-r`: genomic region or gene name; repeat for multiple outputs @@ -88,6 +92,10 @@ plotnado plot template.yaml --region chr1:1,000,000-2,000,000 --region chr2:5,00 plotnado plot template.yaml --region chr1:1,000,000-2,000,000 --output plot.pdf --dpi 300 ``` +Output: + +![Additional CLI plot output](images/examples/quickstart_first_plot.png) + Gene-name resolution requires `genome` to be defined in the template. ## Python bridge @@ -100,3 +108,7 @@ from plotnado import GenomicFigure fig = GenomicFigure.from_template("template.yaml") fig.save("output.png", region="chr1:1,000,000-1,100,000") ``` + +Output: + +![Python bridge output](images/examples/quickstart_first_plot.png) diff --git a/docs/data_inputs.md b/docs/data_inputs.qmd similarity index 100% rename from docs/data_inputs.md rename to docs/data_inputs.qmd diff --git a/docs/example_coverage.md b/docs/example_coverage.md deleted file mode 100644 index 6d2aa43..0000000 --- a/docs/example_coverage.md +++ /dev/null @@ -1,174 +0,0 @@ -# Example Coverage - -This page maps track types to runnable examples and rendered outputs. - -The default run of [examples/run_examples.py](https://github.com/alsmith151/plotnado/blob/main/examples/run_examples.py) replays the local example set. Remote Blueprint `bigwig_collection` / `bigwig_diff` coverage is opt-in with `python examples/run_examples.py --include-remote` because those scripts stream public BigWig files instead of checking large fixtures into the repo. - -Rendered PNG outputs are written into `examples/output/` and synced into `docs/images/examples/`, so the gallery below shows the same results produced by the current code. - -## Runnable script coverage - -| Track / alias | Coverage level | Example path | -|---|---|---| -| `scalebar` | Full runnable | [examples/quickstart/01_first_plot.py](https://github.com/alsmith151/plotnado/blob/main/examples/quickstart/01_first_plot.py) | -| `axis` | Full runnable | [examples/quickstart/01_first_plot.py](https://github.com/alsmith151/plotnado/blob/main/examples/quickstart/01_first_plot.py) | -| `genes` | Full runnable | [examples/recipes/03_gene_label_strategies.py](https://github.com/alsmith151/plotnado/blob/main/examples/recipes/03_gene_label_strategies.py) | -| `bigwig` | Full runnable | [examples/tracks/01_bigwig_styles.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/01_bigwig_styles.py) | -| `bed` | Full runnable | [examples/tracks/02_bed_and_narrowpeak.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/02_bed_and_narrowpeak.py) | -| `narrowpeak` | Full runnable | [examples/tracks/02_bed_and_narrowpeak.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/02_bed_and_narrowpeak.py) | -| `links` | Full runnable | [examples/tracks/03_links_annotations.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/03_links_annotations.py) | -| `hline` | Full runnable | [examples/tracks/03_links_annotations.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/03_links_annotations.py) | -| `vline` | Full runnable | [examples/tracks/03_links_annotations.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/03_links_annotations.py) | -| `bigwig_collection` | Runnable from remote Blueprint data | [examples/tracks/04_bigwig_collection_and_diff.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/04_bigwig_collection_and_diff.py) | -| `bigwig_diff` | Runnable from remote Blueprint data | [examples/tracks/04_bigwig_collection_and_diff.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/04_bigwig_collection_and_diff.py) | -| `cooler` | Full runnable | [examples/tracks/05_matrix_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/05_matrix_tracks.py) | -| `capcruncher` | Full runnable | [examples/tracks/05_matrix_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/05_matrix_tracks.py) | -| `cooler_average` | Full runnable | [examples/tracks/05_matrix_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/05_matrix_tracks.py) | -| `quantnado_coverage` | Full runnable | [examples/tracks/06_quantnado_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/06_quantnado_tracks.py) | -| `quantnado_stranded_coverage` | Full runnable | [examples/tracks/06_quantnado_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/06_quantnado_tracks.py) | -| `quantnado_methylation` | Full runnable | [examples/tracks/06_quantnado_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/06_quantnado_tracks.py) | -| `quantnado_variant` | Full runnable | [examples/tracks/06_quantnado_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/06_quantnado_tracks.py) | -| `highlight` | Full runnable | [examples/recipes/01_autoscale_overlay_highlight.py](https://github.com/alsmith151/plotnado/blob/main/examples/recipes/01_autoscale_overlay_highlight.py) | -| `overlay` | Full runnable | [examples/recipes/01_autoscale_overlay_highlight.py](https://github.com/alsmith151/plotnado/blob/main/examples/recipes/01_autoscale_overlay_highlight.py) | - -## Runnable Example Gallery - -### Basic figure - -Minimal scale bar, axis, genes, and one in-memory signal track. - -Source: [examples/basic_figure.py](https://github.com/alsmith151/plotnado/blob/main/examples/basic_figure.py) - -![Basic figure](images/examples/basic_figure.png) - -### Advanced features - -Autocolor, highlight overlays, interval tracks, grouped autoscaling, and reference lines in one figure. - -Source: [examples/advanced_features.py](https://github.com/alsmith151/plotnado/blob/main/examples/advanced_features.py) - -![Advanced features](images/examples/advanced_features.png) - -### Quickstart first plot - -The minimal quickstart script used from the installation guide. - -Source: [examples/quickstart/01_first_plot.py](https://github.com/alsmith151/plotnado/blob/main/examples/quickstart/01_first_plot.py) - -![Quickstart first plot](images/examples/quickstart_first_plot.png) - -### Alias and options discovery - -Demonstrates `available_track_aliases()` and `track_options(...)` and saves a small multi-track plot. - -Source: [examples/quickstart/02_aliases_and_options.py](https://github.com/alsmith151/plotnado/blob/main/examples/quickstart/02_aliases_and_options.py) - -![Alias and options discovery](images/examples/quickstart_aliases_and_options.png) - -### BigWig styles - -Compares `fill`, `fragment`, `scatter`, and `std` BigWig render modes. - -Source: [examples/tracks/01_bigwig_styles.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/01_bigwig_styles.py) - -![BigWig styles](images/examples/track_bigwig_styles.png) - -### BED and narrowPeak - -Compares stacked BED intervals with score-colored narrowPeak rendering. - -Source: [examples/tracks/02_bed_and_narrowpeak.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/02_bed_and_narrowpeak.py) - -![BED and narrowPeak](images/examples/track_bed_and_narrowpeak.png) - -### Links, hline, and vline - -Shows BEDPE-style links together with horizontal and vertical annotation lines. - -Source: [examples/tracks/03_links_annotations.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/03_links_annotations.py) - -![Links, hline, and vline](images/examples/track_links_annotations.png) - -### BigWig collection and diff - -Fetches two public Blueprint plasma-cell RNA BigWigs once, stages tiny local crops for the target window in a temporary directory, then renders a `bigwig_collection` panel and a matching `bigwig_diff` panel from those local files. - -Source: [examples/tracks/04_bigwig_collection_and_diff.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/04_bigwig_collection_and_diff.py) - -![BigWig collection and diff](images/examples/track_bigwig_collection_diff.png) - -### Cooler and cooler_average - -Uses the checked-in GM12878 cooler fixture for a direct matrix view plus a simple averaged view built from the same file twice. - -Source: [examples/tracks/05_matrix_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/05_matrix_tracks.py) - -![Cooler and cooler_average](images/examples/track_matrix_cooler_average.png) - -### CapCruncher viewpoint matrix - -Uses the checked-in reporters-store fixture and resolves the `Slc25A37` viewpoint to the cooler group inside the HDF5 container. - -Source: [examples/tracks/05_matrix_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/05_matrix_tracks.py) - -![CapCruncher viewpoint matrix](images/examples/track_capcruncher.png) - -### QuantNado coverage and stranded coverage - -Uses local on-disk QuantNado fixtures to render real ATAC coverage and stranded RNA signal. - -Source: [examples/tracks/06_quantnado_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/06_quantnado_tracks.py) - -![QuantNado coverage and stranded coverage](images/examples/track_quantnado_signal.png) - -### QuantNado methylation and variant tracks - -The same script also renders file-backed methylation and variant panels from the compact test fixtures. - -Source: [examples/tracks/06_quantnado_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/06_quantnado_tracks.py) - -![QuantNado methylation](images/examples/track_quantnado_methylation.png) - -![QuantNado variant](images/examples/track_quantnado_variant.png) - -### Overlay, autoscale, and highlight - -Demonstrates a shared overlay panel, grouped autoscaling, and region highlighting. - -Source: [examples/recipes/01_autoscale_overlay_highlight.py](https://github.com/alsmith151/plotnado/blob/main/examples/recipes/01_autoscale_overlay_highlight.py) - -![Overlay, autoscale, and highlight](images/examples/recipe_autoscale_overlay_highlight.png) - -### Theme, labels, and TOML round-trip - -Applies the publication theme, saves a TOML figure definition, and reloads it to render again. - -Source: [examples/recipes/02_theme_labels_toml.py](https://github.com/alsmith151/plotnado/blob/main/examples/recipes/02_theme_labels_toml.py) - -![Theme, labels, and TOML](images/examples/recipe_theme_labels.png) - -Reloaded from TOML: - -![Theme, labels, and TOML reloaded](images/examples/recipe_theme_labels_from_toml.png) - -### Gene label strategies - -Compares staggered, suppressed, and auto-expanded gene label placement strategies. - -Source: [examples/recipes/03_gene_label_strategies.py](https://github.com/alsmith151/plotnado/blob/main/examples/recipes/03_gene_label_strategies.py) - -![Gene label strategies](images/examples/recipe_gene_label_strategies.png) - -## Validate option coverage quickly - -Use runtime metadata to inspect every field (track, aesthetics, label): - -```python -from plotnado import GenomicFigure - -for alias in sorted(GenomicFigure.available_track_aliases()): - print(alias) - GenomicFigure.track_options(alias) -``` - -For full generated tables, see [Aesthetics Reference](aesthetics_reference.md). diff --git a/docs/example_coverage.qmd b/docs/example_coverage.qmd new file mode 100644 index 0000000..cfe023d --- /dev/null +++ b/docs/example_coverage.qmd @@ -0,0 +1,609 @@ +--- +title: Living Example Gallery +jupyter: python3 +execute: + echo: true + warning: false + message: false +--- + +This gallery is rendered by Quarto from the code shown on this page. The helper data below keeps the examples deterministic; each gallery entry then shows the key PlotNado calls followed by the figure produced by the current package version. + +## Shared setup + +```{python} +#| code-fold: true +#| code-summary: "Imports and deterministic example data" +from __future__ import annotations + +import importlib.util +import os +import tempfile +from pathlib import Path + +import numpy as np +import pandas as pd + +from plotnado import GenomicFigure, Theme +``` + +```{python} +#| code-fold: true +#| code-summary: "Synthetic signals, intervals, links, genes, and fixtures" +REGION = "chr1:1,010,000-1,080,000" +REPO_ROOT = Path.cwd() +TEST_DATA_DIR = REPO_ROOT / "tests" / "data" +QUANTNADO_FIXTURE_DIR = TEST_DATA_DIR / "quantnado" + + +def has_module(module_name: str) -> bool: + return importlib.util.find_spec(module_name) is not None + + +def unavailable_example_figure(example_name: str, requirement: str): + import matplotlib.pyplot as plt + + fig, ax = plt.subplots(figsize=(9, 1.8)) + ax.axis("off") + ax.text( + 0.5, + 0.5, + f"{example_name} requires: {requirement}", + ha="center", + va="center", + fontsize=11, + ) + plt.close(fig) + return fig + + +BLUEPRINT_BIGWIGS = { + "T12-15 plasma cell RNA": "http://ftp.ebi.ac.uk/pub/databases/blueprint/data/homo_sapiens/GRCh38/tonsil/T12-15/plasma_cell/RNA-Seq/IDIBAPS/T12-15-PC.signal.star_grape2_crg.GRCh38.20150815.bw", + "T12-16 plasma cell RNA": "http://ftp.ebi.ac.uk/pub/databases/blueprint/data/homo_sapiens/GRCh38/tonsil/T12-16/plasma_cell/RNA-Seq/IDIBAPS/T12-16-PC.signal.star_grape2_crg.GRCh38.20150815.bw", +} +BLUEPRINT_REGION = "chr1:155,190,000-155,220,000" + + +def cache_bigwig_crop(label: str, url: str, cache_dir: Path) -> Path: + import pybigtools + + from plotnado.tracks import GenomicRegion + from plotnado.tracks.bigwig import BigWigTrack + + cache_dir.mkdir(parents=True, exist_ok=True) + cache_path = cache_dir / f"{label.lower().replace(' ', '_').replace('-', '_')}.bw" + if cache_path.exists(): + return cache_path + + region = GenomicRegion(chromosome="chr1", start=155_190_000, end=155_220_000) + data = BigWigTrack(data=url).fetch_data(region) + if data.empty: + raise ValueError(f"No Blueprint signal fetched for {label} in {BLUEPRINT_REGION}") + + records = [ + ("chr1", int(row.start), int(row.end), float(row.value)) + for row in data.itertuples(index=False) + ] + pybigtools.open(str(cache_path), "w").write({"chr1": 155_220_000}, records) + return cache_path + + +def synthetic_signal( + start: int = 1_000_000, + end: int = 1_100_000, + step: int = 1_000, + phase: float = 0.0, + scale: float = 1.0, +) -> pd.DataFrame: + bins = np.arange(start, end, step) + values = scale * (5.0 + 2.0 * np.sin(np.linspace(phase, 6 * np.pi + phase, bins.shape[0]))) + return pd.DataFrame({"chrom": "chr1", "start": bins, "end": bins + step, "value": values}) + + +def review_signal(scale: float, phase: float = 0.0) -> pd.DataFrame: + bins = np.arange(1_000_000, 1_120_000, 1_000) + values = scale * (1.2 + np.sin(np.linspace(phase, 6 + phase, bins.shape[0]))) + return pd.DataFrame({"chrom": "chr1", "start": bins, "end": bins + 1_000, "value": values}) + + +def style_signal(style_shift: float) -> pd.DataFrame: + bins = np.arange(1_000_000, 1_090_000, 1_000) + values = 5 + 2.4 * np.sin(np.linspace(style_shift, 7 + style_shift, bins.shape[0])) + return pd.DataFrame({"chrom": "chr1", "start": bins, "end": bins + 1_000, "value": values}) + + +def bed_intervals() -> pd.DataFrame: + return pd.DataFrame( + { + "chrom": ["chr1", "chr1", "chr1", "chr1"], + "start": [1_008_000, 1_020_000, 1_050_000, 1_066_000], + "end": [1_014_000, 1_032_000, 1_061_000, 1_074_000], + "name": ["a", "b", "c", "d"], + } + ) + + +def narrowpeaks() -> pd.DataFrame: + return pd.DataFrame( + { + "chrom": ["chr1", "chr1", "chr1"], + "start": [1_012_000, 1_038_000, 1_060_000], + "end": [1_018_000, 1_047_000, 1_070_000], + "name": ["np1", "np2", "np3"], + "score": [300, 700, 500], + "strand": [".", ".", "."], + "signalValue": [12.0, 48.0, 30.0], + "pValue": [5.2, 12.3, 8.1], + "qValue": [4.1, 10.0, 6.2], + "peak": [1200, 1800, 2200], + } + ) + + +def link_df() -> pd.DataFrame: + return pd.DataFrame( + { + "chrom1": ["chr1", "chr1", "chr1"], + "start1": [1_010_000, 1_022_000, 1_042_000], + "end1": [1_012_000, 1_024_000, 1_045_000], + "chrom2": ["chr1", "chr1", "chr1"], + "start2": [1_035_000, 1_054_000, 1_072_000], + "end2": [1_037_000, 1_056_000, 1_074_000], + "score": [2.2, 6.5, 9.8], + } + ) + + +def dense_gene_annotations() -> pd.DataFrame: + records = [] + starts = [1_010_000, 1_013_500, 1_017_000, 1_020_500, 1_024_000, 1_028_000, 1_031_500] + lengths = [6_000, 5_500, 6_300, 5_200, 5_800, 5_400, 6_100] + + for index, (start, length) in enumerate(zip(starts, lengths), start=1): + records.append( + { + "chrom": "chr1", + "start": start, + "end": start + length, + "geneid": f"GENE_LONG_NAME_GOES_ON_FOR_A_WHILE_{index}", + "strand": "+" if index % 2 else "-", + "block_count": 3, + "block_starts": [0, int(length * 0.42), int(length * 0.78)], + "block_sizes": [int(length * 0.16), int(length * 0.12), int(length * 0.18)], + } + ) + + return pd.DataFrame.from_records(records) +``` + +## Track coverage + +| Track / alias | Coverage level | Gallery entry | +|---|---|---| +| `scalebar` | Live rendered | [Basic figure](#basic-figure), [Quickstart first plot](#quickstart-first-plot) | +| `axis` | Live rendered | [Basic figure](#basic-figure), [Links, hline, and vline](#links-hline-and-vline) | +| `genes` | Live rendered | [Basic figure](#basic-figure), [Gene label strategies](#gene-label-strategies) | +| `bigwig` | Live rendered | [BigWig styles](#bigwig-styles) | +| `bed` | Live rendered | [BED and narrowPeak](#bed-and-narrowpeak) | +| `narrowpeak` | Live rendered | [BED and narrowPeak](#bed-and-narrowpeak) | +| `links` | Live rendered | [Links, hline, and vline](#links-hline-and-vline) | +| `hline` | Live rendered | [Links, hline, and vline](#links-hline-and-vline) | +| `vline` | Live rendered | [Links, hline, and vline](#links-hline-and-vline) | +| `bigwig_collection` | Live rendered when remote examples are enabled | [BigWig collection and diff](#bigwig-collection-and-diff) | +| `bigwig_diff` | Live rendered when remote examples are enabled | [BigWig collection and diff](#bigwig-collection-and-diff) | +| `cooler` | Live rendered when optional dependencies are installed | [Cooler and cooler_average](#cooler-and-cooler_average) | +| `capcruncher` | Live rendered when optional dependencies are installed | [CapCruncher viewpoint matrix](#capcruncher-viewpoint-matrix) | +| `quantnado_*` | Live rendered when optional dependencies are installed | [QuantNado tracks](#quantnado-coverage-and-stranded-coverage) | +| `highlight` | Live rendered | [Overlay, autoscale, and highlight](#overlay-autoscale-and-highlight) | +| `overlay` | Live rendered | [Overlay, autoscale, and highlight](#overlay-autoscale-and-highlight) | + +## Basic figure + +Minimal scale bar, axis, genes, and one in-memory signal track. + +```{python} +fig = GenomicFigure(width=12, track_height=1.4) +fig.scalebar(position="left") +fig.axis() +fig.genes("hg38", title="Genes") +fig.bigwig( + synthetic_signal(), + title="Synthetic signal", + style="fill", + color="#1f77b4", + alpha=0.8, +) + +fig.plot(REGION) +``` + +## Advanced features + +Autocolor, highlights, interval tracks, grouped autoscaling, and reference lines in one figure. + +```{python} +fig = GenomicFigure(width=12, track_height=1.3) +fig.autocolor("tab10") +fig.highlight("chr1:1,030,000-1,045,000") +fig.highlight_style(color="#f6d55c", alpha=0.2) + +fig.scalebar(position="right") +fig.axis(num_ticks=5) +fig.genes("hg38", title="Genes") +fig.bed(bed_intervals(), title="Intervals", display="expanded", show_labels=True) +fig.bigwig(review_signal(4.0), title="Control", style="fill", autoscale_group="signal") +fig.bigwig(review_signal(5.2, phase=0.8), title="Treatment", style="fill", autoscale_group="signal") +fig.bigwig(review_signal(3.0, phase=1.3), title="Input", style="fragment") +fig.vline(1_060_000, color="#333333", linewidth=1.0) +fig.hline(0.0, color="#666666", linewidth=0.6) + +fig.plot("chr1:1,000,000-1,110,000") +``` + +## Quickstart first plot + +The minimal first plot used in the quickstart flow. + +```{python} +fig = GenomicFigure(width=11) +fig.scalebar() +fig.axis() +fig.genes("hg38", title="Genes") +fig.bigwig( + synthetic_signal(scale=1.2), + title="Synthetic ChIP", + color="#1f77b4", + style="fill", +) + +fig.plot(REGION) +``` + +## Alias and options discovery + +Use `add_track(...)` when track names come from config, CLI inputs, or other runtime values. + +```{python} +fig = GenomicFigure().autocolor("Set2") +fig.add_track("scalebar") +fig.add_track("axis") +fig.add_track("bigwig", data=synthetic_signal(phase=0.0), title="Replicate A", alpha=0.7) +fig.add_track("bigwig", data=synthetic_signal(phase=0.8), title="Replicate B", alpha=0.7) + +fig.plot("chr1:1,005,000-1,070,000") +``` + +## BigWig styles + +Compare `fill`, `fragment`, `scatter`, and `std` BigWig render modes. + +```{python} +fig = GenomicFigure(track_height=1.25) +fig.scalebar() +fig.bigwig(style_signal(0.0), title="fill", style="fill", color="#1f77b4") +fig.bigwig(style_signal(0.8), title="fragment", style="fragment", color="#d62728") +fig.bigwig(style_signal(1.6), title="scatter", style="scatter", color="#2ca02c", scatter_point_size=10) +fig.bigwig(style_signal(2.4), title="std", style="std", color="#9467bd") + +fig.plot(REGION) +``` + +## BED and narrowPeak + +Stack BED intervals and color narrowPeak intervals by a score-like field. + +```{python} +fig = GenomicFigure(track_height=1.25) +fig.scalebar() +fig.axis() +fig.bed(bed_intervals(), title="BED intervals", display="expanded", show_labels=True) +fig.narrowpeak( + narrowpeaks(), + title="narrowPeak", + color_by="signalValue", + cmap="Oranges", + show_labels=True, + show_summit=True, +) + +fig.plot("chr1:1,005,000-1,078,000") +``` + +## Links, hline, and vline + +Draw BEDPE-style links with horizontal and vertical annotation lines. + +```{python} +fig = GenomicFigure(track_height=1.2) +fig.axis() +fig.links(link_df(), title="Loops", color_by_score=True, cmap="viridis", alpha=0.8) +fig.hline(0.1, color="#666666", linewidth=0.8) +fig.vline(1_048_000, color="#222222", linewidth=1.0) + +fig.plot("chr1:1,005,000-1,078,000") +``` + +## BigWig collection and diff + +This remote-backed example is opt-in for docs builds because it streams public Blueprint BigWig data. Set `PLOTNADO_RENDER_REMOTE_EXAMPLES=1` to render it live. + +```{python} +if os.environ.get("PLOTNADO_RENDER_REMOTE_EXAMPLES") == "1": + labels = list(BLUEPRINT_BIGWIGS.keys()) + with tempfile.TemporaryDirectory(prefix="plotnado-blueprint-") as tmpdir: + files = [ + str(cache_bigwig_crop(label, url, Path(tmpdir))) + for label, url in BLUEPRINT_BIGWIGS.items() + ] + + fig = GenomicFigure(track_height=1.15) + fig.axis() + fig.scalebar() + fig.bigwig_collection( + files, + title="Blueprint plasma-cell RNA collection", + labels=labels, + colors=["#d55e00", "#0072b2"], + alpha=0.75, + ) + fig.bigwig_diff( + files[0], + files[1], + title="T12-15 minus T12-16", + positive_color="#b22222", + negative_color="#1f5aa6", + bar_alpha=0.5, + ) + output = fig.plot(BLUEPRINT_REGION) +else: + output = unavailable_example_figure( + "BigWig collection/diff example", + "PLOTNADO_RENDER_REMOTE_EXAMPLES=1", + ) + +output +``` + +## Overlay, autoscale, and highlight + +Synchronize ordinary signal tracks and an overlay panel with one `autoscale_group`. + +```{python} +fig = GenomicFigure(track_height=1.25) +fig.autoscale(True) +fig.highlight("chr1:1,032,000-1,046,000") +fig.highlight_style(color="#ffdd57", alpha=0.22) + +fig.axis() +fig.bigwig(review_signal(2.0), title="Group A", autoscale_group="g1", style="fill", color="#1f77b4") +fig.bigwig(review_signal(10.0, 1.2), title="Group B", autoscale_group="g1", style="fill", color="#d62728") +fig.overlay( + [review_signal(4.2, 0.9), review_signal(3.5, 1.5)], + title="Overlay", + autoscale_group="g1", + colors=["#2ca02c", "#9467bd"], + alpha=0.55, +) + +fig.plot("chr1:1,005,000-1,085,000") +``` + +## Theme, labels, and TOML round-trip + +Apply the publication theme and configure label and scale placement. + +```{python} +fig = GenomicFigure(theme=Theme.publication()) +fig.scalebar(plot_scale=True, scale_size=10) +fig.axis(num_ticks=4) +fig.bigwig( + synthetic_signal(end=1_090_000), + title="Publication theme", + color="#2c7fb8", + label_box_enabled=True, + label_box_alpha=0.85, + title_location="left", + scale_location="right", +) + +fig.plot(REGION) +``` + +The same figure definition can be persisted and loaded again. + +```{python} +with tempfile.TemporaryDirectory() as tmpdir: + toml_path = Path(tmpdir) / "theme_labels.toml" + + fig = GenomicFigure(theme=Theme.publication()) + fig.scalebar(plot_scale=True, scale_size=10) + fig.axis(num_ticks=4) + fig.genes("hg38", title="Genes", title_location="left") + + fig.to_toml(str(toml_path)) + loaded = GenomicFigure.from_toml(str(toml_path)) + output = loaded.plot(REGION) + +output +``` + +## Gene label strategies + +Compare staggered labels, suppressed labels, and automatic row expansion. + +```{python} +genes_df = dense_gene_annotations() + +fig = GenomicFigure(width=12, track_height=1.2) +fig.scalebar() +fig.axis() +fig.genes( + data=genes_df, + title="Method: Staggered labels + connectors", + display="collapsed", + label_overlap_strategy="stagger", + label_max_chars=120, +) +fig.genes( + data=genes_df, + title="Method: Suppress overlaps (offset labels)", + display="collapsed", + label_overlap_strategy="suppress", + label_max_chars=120, +) +fig.genes( + data=genes_df, + title="Method: Auto-expand rows + suppress overlaps", + display="collapsed", + label_overlap_strategy="auto_expand", + max_number_of_rows=6, + label_max_chars=120, +) + +fig.plot("chr1:1,006,000-1,040,000") +``` + +## Cooler and cooler_average + +Use the checked-in cooler fixture for a direct matrix view and a simple averaged view. + +```{python} +cooler_path = TEST_DATA_DIR / "hg19.GM12878-MboI.matrix.2000kb.cool" + +if has_module("cooler") and cooler_path.exists(): + fig = GenomicFigure(track_height=2.1) + fig.axis() + fig.cooler( + str(cooler_path), + title="GM12878 2 Mb cooler", + balance=False, + cmap="YlOrRd", + max_value=60, + ) + fig.cooler_average( + [str(cooler_path), str(cooler_path)], + title="Cooler average (same file twice)", + balance=False, + cmap="viridis", + max_value=60, + ) + output = fig.plot("chr1:0-10,000,000") +else: + output = unavailable_example_figure("Cooler example", "cooler") + +output +``` + +## CapCruncher viewpoint matrix + +Resolve a CapCruncher viewpoint inside the checked-in HDF5 fixture. + +```{python} +capcruncher_path = TEST_DATA_DIR / "capcruncher" / "SAMPLE-A_REP1.hdf5" + +if has_module("capcruncher") and capcruncher_path.exists(): + fig = GenomicFigure(track_height=2.4) + fig.axis() + fig.capcruncher( + str(capcruncher_path), + title="CapCruncher viewpoint Slc25A37", + viewpoint="Slc25A37", + balance=False, + cmap="magma", + ) + output = fig.plot("chr14:69,878,303-69,879,094") +else: + output = unavailable_example_figure("CapCruncher example", "capcruncher") + +output +``` + +## QuantNado coverage and stranded coverage + +Render local on-disk QuantNado ATAC and RNA fixtures. + +```{python} +atac_path = QUANTNADO_FIXTURE_DIR / "atac_chr22.zarr" +rna_path = QUANTNADO_FIXTURE_DIR / "rna_chr22.zarr" + +if has_module("quantnado") and atac_path.exists() and rna_path.exists(): + fig = GenomicFigure(track_height=1.2) + fig.axis() + fig.quantnado_coverage( + "atac", + dataset_path=str(atac_path), + title="ATAC coverage", + color="#1f78b4", + fill=True, + ) + fig.quantnado_stranded_coverage( + "rna", + dataset_path=str(rna_path), + title="RNA stranded coverage", + color="#d7301f", + reverse_color="#4575b4", + ) + output = fig.plot("chr22:10,520,211-10,520,711") +else: + output = unavailable_example_figure("QuantNado signal example", "quantnado") + +output +``` + +## QuantNado methylation + +Render methylation percentages from a local QuantNado fixture. + +```{python} +meth_path = QUANTNADO_FIXTURE_DIR / "meth_chr22.zarr" + +if has_module("quantnado") and meth_path.exists(): + fig = GenomicFigure(track_height=1.2) + fig.axis() + fig.quantnado_methylation( + "meth", + dataset_path=str(meth_path), + title="Methylation percentage", + color="#238b45", + point_size=22, + ) + output = fig.plot("chr22:100-360") +else: + output = unavailable_example_figure("QuantNado methylation example", "quantnado") + +output +``` + +## QuantNado variant + +Render variant allele balance from a local QuantNado fixture. + +```{python} +snp_path = QUANTNADO_FIXTURE_DIR / "snp_chr1.zarr" + +if has_module("quantnado") and snp_path.exists(): + fig = GenomicFigure(track_height=1.2) + fig.axis() + fig.quantnado_variant( + "snp", + dataset_path=str(snp_path), + title="Variant allele balance", + het_color="#756bb1", + hom_alt_color="#d7301f", + marker_size=38, + ) + output = fig.plot("chr1:54,721-55,221") +else: + output = unavailable_example_figure("QuantNado variant example", "quantnado") + +output +``` + +## Runtime option discovery + +The gallery focuses on rendered examples. Use runtime metadata when you need to inspect every available alias or option field. + +```{python} +aliases = sorted(GenomicFigure.available_track_aliases()) +aliases[:10], len(aliases) +``` diff --git a/docs/faq.md b/docs/faq.qmd similarity index 94% rename from docs/faq.md rename to docs/faq.qmd index 34b00cc..f0fb44f 100644 --- a/docs/faq.md +++ b/docs/faq.qmd @@ -33,7 +33,7 @@ Treat the overlay as the unit of scaling: ## What is the recommended coding style? -Use a single `GenomicFigure` instance (`gf`) and chain helper methods. See [Track Construction](quickstart_tracks.md). +Use a single `GenomicFigure` instance (`gf`) and chain helper methods. See [Track Construction](quickstart_tracks.qmd). ## How do I share figure definitions? diff --git a/docs/figure_workflows.md b/docs/figure_workflows.qmd similarity index 83% rename from docs/figure_workflows.md rename to docs/figure_workflows.qmd index 0063320..30e403e 100644 --- a/docs/figure_workflows.md +++ b/docs/figure_workflows.qmd @@ -6,18 +6,30 @@ gf.plot("chr1:1,000,000-1,100,000") ``` +Output: + +![Single region plot output](images/examples/basic_figure.png) + ## Extend around region ```python gf.plot("chr1:1,000,000-1,050,000", extend=0.25) ``` +Output: + +![Extended region plot output](images/examples/basic_figure.png) + ## Plot many regions ```python gf.plot_regions(["chr1:1,000,000-1,050,000", "chr1:1,060,000-1,110,000"], ncols=2) ``` +Output: + +![Multi-region plot output](images/examples/advanced_features.png) + `plot_regions` also accepts a BED path. ## Plot by gene symbol @@ -26,6 +38,10 @@ gf.plot_regions(["chr1:1,000,000-1,050,000", "chr1:1,060,000-1,110,000"], ncols= gf.plot_gene("GNAQ", extend=0.5) ``` +Output: + +![Gene plot output](images/examples/recipe_gene_label_strategies.png) + ## Load from an IGV session Convert a saved IGV `.xml` session directly into a `GenomicFigure`: @@ -37,6 +53,10 @@ fig, locus = GenomicFigure.from_igv_session("session.xml") fig.plot(locus) # locus is the session's saved view region ``` +Output: + +![IGV session plot output](images/examples/basic_figure.png) + Track colors, fixed scales (`min`/`max`), autoscale groups, and gene annotations are all preserved. The session locus is returned alongside the figure so you can use it directly or override it. @@ -52,6 +72,10 @@ fig = GenomicFigure.from_template(session.template) fig.plot(session.locus) ``` +Output: + +![Parsed IGV session output](images/examples/basic_figure.png) + ## Edit tracks after building a figure Access and mutate individual tracks by title (case-insensitive) or index: @@ -108,4 +132,8 @@ loaded = GenomicFigure.from_toml("figure.toml") loaded.save("figure.png", "chr1:1,000,000-1,100,000") ``` +Output: + +![TOML round-trip output](images/examples/recipe_theme_labels_from_toml.png) + See [examples/recipes/02_theme_labels_toml.py](https://github.com/alsmith151/plotnado/blob/main/examples/recipes/02_theme_labels_toml.py) for a full round-trip script. diff --git a/docs/index.md b/docs/index.md deleted file mode 100644 index ba99e6a..0000000 --- a/docs/index.md +++ /dev/null @@ -1,145 +0,0 @@ -# PlotNado - -
-
-

Genome browser figures without the browser

-

Publication-ready genomic tracks in Python.

-

PlotNado composes BigWig signals, genes, peaks, interactions, and overlays into clean genome-browser style figures with a high-level API and a file-driven CLI.

- -
-
- Python API - Chain one track method at a time. -
-
- CLI + YAML - Infer, edit, validate, and render templates. -
-
- Track-first design - Signals, annotations, overlays, and matrices. -
-
-
-
- PlotNado example figure -

A composed PlotNado figure with signal, annotation, and structural tracks.

-
-
- -
-

Pick your entry point

-

Most readers need one of three routes: get a figure rendered quickly, understand which track type to use, or tune the figure until it is publication-ready.

-
- -
-
-

Start Here

-

Install and render your first figure

-

Use the Quick Start when you want a working figure with minimal setup and clear defaults.

- -
-
-

Build With Confidence

-

Choose the right track and input model

-

Learn which track to reach for, what each one expects, and where to find concrete examples.

- -
-
-

Tune The Result

-

Dial in styling, scaling, and layout

-

Use the aesthetics and recipes guides when the figure works but does not yet communicate clearly.

- -
-
- -
-

Two workflows, one rendering model

-

PlotNado offers a fluent Python API for notebooks and pipelines, plus a CLI that turns genomic inputs into editable YAML templates.

-
- -
-
-

Python API

-

Compose tracks directly in code

-

Add tracks one method at a time and save the result in a single region-specific render step.

- -```python -from plotnado import GenomicFigure - -fig = GenomicFigure(theme="publication") -fig.scalebar() -fig.axis() -fig.genes("hg38") -fig.bigwig("signal.bw", title="ChIP signal", style="fill") -fig.save("output.png", region="chr1:1,010,000-1,080,000") -``` -
-
-

CLI + YAML

-

Infer a template, then edit and render it

-

This route is useful when the inputs already exist on disk and you want a reproducible, file-driven workflow.

- -```bash -plotnado init *.bw peaks.narrowpeak --auto --output template.yaml -plotnado validate template.yaml -plotnado plot template.yaml --region chr1:1,000,000-1,100,000 --output out.png -``` -
-
-

Need Options Fast?

-

Inspect the runtime metadata

-

Every track exposes discoverable options, so you do not have to guess field names or dig through source first.

- -```python -from plotnado import GenomicFigure - -GenomicFigure.track_options("overlay") -GenomicFigure.track_options_markdown("bigwig") -``` -
-
- -
-

Rendered examples

-

The docs include plotted outputs, not just code snippets, so you can see what each configuration actually produces.

-
- -
-
- Quick start output -

First figure

-

Start from a minimal stack: scale bar, axis, genes, and one signal track.

-
-
- Overlay autoscale output -

Overlay + autoscale

-

Overlay multiple signals in one panel while keeping y-scaling explicit and readable.

-
-
- Theme and labels output -

Theme-driven polish

-

Refine color, labels, and spacing once the track composition is correct.

-
-
- -
-

Where to go next

-

If the figure is blank or scaling looks wrong, go straight to Troubleshooting. If you are deciding between track types, start with the Track Catalog. If you want exact parameters, jump to Reference and API Reference.

-
diff --git a/docs/installation.md b/docs/installation.qmd similarity index 97% rename from docs/installation.md rename to docs/installation.qmd index 808f081..914d26f 100644 --- a/docs/installation.md +++ b/docs/installation.qmd @@ -87,8 +87,8 @@ uv run plotnado validate --help ## Build docs locally ```bash -uv run mkdocs build --strict -uv run mkdocs serve +quarto render +quarto preview ``` ## Run tests diff --git a/docs/quickstart.md b/docs/quickstart.qmd similarity index 80% rename from docs/quickstart.md rename to docs/quickstart.qmd index 6482574..e51f139 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.qmd @@ -32,12 +32,20 @@ gf.bigwig(signal, title="Synthetic signal", style="fill") gf.save("quickstart.png", "chr1:1,010,000-1,080,000") ``` +Output: + +![Quickstart output](images/quickstart.png) + ## 2) Run a ready-made script ```bash python examples/quickstart/01_first_plot.py ``` +Output: + +![Ready-made quickstart script output](images/examples/quickstart_first_plot.png) + ## 3) Generate a template with the CLI ```bash @@ -46,6 +54,10 @@ plotnado validate template.yaml plotnado plot template.yaml --region chr1:1,000,000-1,100,000 --output quickstart-cli.png ``` +Output: + +![Template-rendered quickstart output](images/examples/quickstart_first_plot.png) + `plotnado plot` also accepts gene symbols such as `--region GNAQ` when the template has a `genome` value. If you want to stay in Python after generating a template: @@ -57,6 +69,10 @@ gf = GenomicFigure.from_template("template.yaml") gf.save("quickstart-from-template.png", region="chr1:1,000,000-1,100,000") ``` +Output: + +![Python-loaded template output](images/examples/quickstart_first_plot.png) + ## 4) Start from a public UCSC hub in a notebook PlotNado can flatten supported tracks from a UCSC-style hub directly into a figure. In notebooks, `track_visibility_widget()` is the fastest way to hide or re-enable tracks without rebuilding the figure. @@ -81,8 +97,8 @@ Set `include_hidden=True` when you want hub tracks that are hidden by default to ## 5) Next steps -- Learn all track-add patterns: [Track Construction](quickstart_tracks.md) -- Learn the template workflow: [CLI](cli.md) -- Review practical defaults: [Best Practices](best_practices.md) -- Explore track families: [Track Catalog](track_catalog.md) -- See a longer live notebook walkthrough: [Worked Examples](worked_examples.ipynb) +- Learn all track-add patterns: [Track Construction](quickstart_tracks.qmd) +- Learn the template workflow: [CLI](cli.qmd) +- Review practical defaults: [Best Practices](best_practices.qmd) +- Explore track families: [Track Catalog](track_catalog.qmd) +- See a longer executable walkthrough: [Worked Examples](worked_examples.qmd) diff --git a/docs/quickstart_tracks.md b/docs/quickstart_tracks.qmd similarity index 84% rename from docs/quickstart_tracks.md rename to docs/quickstart_tracks.qmd index f13e799..a51f6da 100644 --- a/docs/quickstart_tracks.md +++ b/docs/quickstart_tracks.qmd @@ -34,6 +34,10 @@ gf.axis() gf.plot_gene("GNAQ") ``` +Output: + +![Track construction output](images/examples/basic_figure.png) + ## 2) Generic alias entrypoint (`add_track`) Best when alias names come from config/TOML/CLI inputs. @@ -44,6 +48,10 @@ gf.add_track("genes", genome="hg38") gf.add_track("bigwig", data="signal.bw", title="ChIP", style="std") ``` +Output: + +![Alias-added track output](images/examples/quickstart_aliases_and_options.png) + ## 3) Explicit track objects Best when you want strict object construction and reuse track instances. @@ -60,6 +68,10 @@ gf.add_track( ) ``` +Output: + +![Explicit BigWig track output](images/examples/track_bigwig_styles.png) + ## 4) Mixed approach (recommended in real projects) - Use helper methods for most tracks. @@ -80,6 +92,10 @@ gf.overlay( ) ``` +Output: + +![Overlay output](images/aesthetics/overlay_autoscale.png) + Practical rule: - Use `autoscale_group` on the overlay itself when the whole panel should sync with neighboring signal tracks. @@ -117,6 +133,10 @@ gf.bigwig( ) ``` +Output: + +![Automatic kwarg routing output](images/aesthetics/bigwig_styles.png) + ## Color management (`autocolor`, `color_group`) Use `autocolor()` to assign colors automatically from the active theme. @@ -128,6 +148,10 @@ gf.bigwig("sample_a.bw", title="Sample A") gf.bigwig("sample_b.bw", title="Sample B") ``` +Output: + +![Autocolor output](images/aesthetics/autocolor.png) + Use `color_group` when multiple tracks should share the same assigned color. ```python @@ -141,6 +165,10 @@ gf.bed("peaks_b.bigBed", title="B peaks", color_group="B") gf.bigwig("signal_b.bw", title="B signal", color_group="B") ``` +Output: + +![Color group output](images/aesthetics/autocolor.png) + Practical rule: - Same biological sample/condition: same `color_group`. @@ -157,4 +185,4 @@ GenomicFigure.track_options("overlay") GenomicFigure.track_options_markdown("genes") ``` -See [Track Aliases](track_aliases.md) for alias notes, [Example Coverage](example_coverage.md) for runnable script mapping, and [Best Practices](best_practices.md) for production guidance. +See [Track Aliases](track_aliases.qmd) for alias notes, the [Living Gallery](example_coverage.qmd) for rendered code examples, and [Best Practices](best_practices.qmd) for production guidance. diff --git a/docs/recipes.md b/docs/recipes.qmd similarity index 93% rename from docs/recipes.md rename to docs/recipes.qmd index 3378351..cdc1f64 100644 --- a/docs/recipes.md +++ b/docs/recipes.qmd @@ -38,6 +38,8 @@ gf.bed("K562_peaks.bigBed", title="K562 peaks", color_group="K562") gf.bigwig("K562_signal.bw", title="K562 signal", color_group="K562") ``` +![Autocolor and color groups](images/aesthetics/autocolor.png) + ## Track-style comparisons - Compare BigWig styles (`fill`, `fragment`, `scatter`, `std`). @@ -52,4 +54,4 @@ Scripts: - [examples/tracks/01_bigwig_styles.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/01_bigwig_styles.py) - [examples/tracks/02_bed_and_narrowpeak.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/02_bed_and_narrowpeak.py) -More rendered runnable outputs, including quickstart, links/hline/vline, and gene-label examples, are collected on [Example Coverage](example_coverage.md). +More rendered runnable outputs, including quickstart, links/hline/vline, and gene-label examples, are collected in the [Living Gallery](example_coverage.qmd). diff --git a/docs/reference.md b/docs/reference.qmd similarity index 78% rename from docs/reference.md rename to docs/reference.qmd index ea751f9..edfd3ff 100644 --- a/docs/reference.md +++ b/docs/reference.qmd @@ -1,6 +1,6 @@ # Reference -This page is the quickest route to the high-level API contract. For exact field tables, use [Aesthetics Reference](aesthetics_reference.md). For runtime discovery, use `GenomicFigure.track_options(...)` from [API Reference](api_reference.md). +This page is the quickest route to the high-level API contract. For exact field tables, use [Aesthetics Reference](aesthetics_reference.qmd). For runtime discovery, use `GenomicFigure.track_options(...)` from [API Reference](api_reference.qmd). ## GenomicFigure @@ -70,6 +70,10 @@ fig.overlay( ) ``` +Output: + +![Overlay reference output](images/aesthetics/overlay_autoscale.png) + #### `bigwig_diff(data_a, data_b, /, **kwargs)` Difference track between two BigWig sources. `method` selects `"subtraction"`, `"ratio"`, or `"log2ratio"`. @@ -132,6 +136,68 @@ Horizontal or vertical reference lines. `color` and `linewidth` are the main kwa --- +### Matrix + +#### `cooler(data, /, **kwargs)` + +Contact matrix panel for cooler-compatible files. The current region is rendered as a matrix/heatmap. + +Key kwargs: + +| Kwarg | Default | Description | +| --- | --- | --- | +| `balance` | track default | Use balanced matrix values when available | +| `cmap` | track default | Matplotlib colormap | +| `min_value` / `max_value` | — | Fixed color scale limits | + +#### `cooler_average(files, /, **kwargs)` + +Averaged contact matrix panel across multiple cooler-compatible files. + +Key kwargs: `balance`, `cmap`, `min_value`, `max_value`. + +#### `capcruncher(data, /, **kwargs)` + +Viewpoint-centric matrix track from CapCruncher reporters-store output. + +Key kwargs: + +| Kwarg | Default | Description | +| --- | --- | --- | +| `viewpoint` | — | Viewpoint/group to resolve inside the HDF5 container | +| `balance` | track default | Use balanced values when available | +| `cmap` | track default | Matplotlib colormap | + +--- + +### QuantNado + +#### `quantnado_coverage(name, /, **kwargs)` + +Coverage signal read from a QuantNado dataset. + +Key kwargs: `dataset_path`, `color`, `fill`, `min_value`, `max_value`. + +#### `quantnado_stranded_coverage(name, /, **kwargs)` + +Stranded coverage track with separate forward/reverse styling. + +Key kwargs: `dataset_path`, `color`, `reverse_color`, `min_value`, `max_value`. + +#### `quantnado_methylation(name, /, **kwargs)` + +Point-based methylation percentage track. + +Key kwargs: `dataset_path`, `color`, `point_size`, `alpha`. + +#### `quantnado_variant(name, /, **kwargs)` + +Variant allele balance and genotype marker track. + +Key kwargs: `dataset_path`, `het_color`, `hom_alt_color`, `marker_size`. + +--- + ### Annotation #### `genes(genome="hg38", /, **kwargs)` @@ -174,6 +240,10 @@ fig.highlight_style(color="#ffdd57", alpha=0.22) fig.defaults(genome="hg38") # shortcut for autocolor + genes defaults ``` +Output: + +![Figure-level controls output](images/aesthetics/overlay_autoscale.png) + ### Autoscale groups Tracks sharing the same `autoscale_group` string are scaled to the same y-axis range after all data is loaded: @@ -184,6 +254,10 @@ fig.bigwig("a.bw", title="A", autoscale_group="chip") fig.bigwig("b.bw", title="B", autoscale_group="chip") ``` +Output: + +![Autoscale group output](images/aesthetics/overlay_autoscale.png) + ### Color groups When using `autocolor()`, tracks with the same `color_group` string share one palette slot: @@ -195,6 +269,10 @@ fig.bed("a.bed", title="A peaks", color_group="sample_a") fig.bigwig("b.bw", title="B signal", color_group="sample_b") ``` +Output: + +![Color group output](images/aesthetics/autocolor.png) + --- ## Plotting and saving @@ -208,6 +286,10 @@ fig.save("out.png", region="chr1:1,000,000-2,000,000", dpi=200) fig.save("out.pdf", region="GNAQ") ``` +Output: + +![Plotting and saving output](images/examples/basic_figure.png) + --- ## Editing after build @@ -248,6 +330,10 @@ fig.to_toml("figure.toml") fig2 = GenomicFigure.from_toml("figure.toml") ``` +Output: + +![Loaded figure config output](images/examples/recipe_theme_labels_from_toml.png) + --- ## Runtime option discovery diff --git a/docs/reference/SUMMARY.md b/docs/reference/SUMMARY.qmd similarity index 53% rename from docs/reference/SUMMARY.md rename to docs/reference/SUMMARY.qmd index 2100fd5..0e361ed 100644 --- a/docs/reference/SUMMARY.md +++ b/docs/reference/SUMMARY.qmd @@ -11,13 +11,13 @@ This page is a lightweight index to the PlotNado API surface. ## Fastest ways to inspect the API -- Use [Reference](../reference.md) for the high-level method map -- Use [API Reference](../api_reference.md) for runtime option discovery -- Use [Aesthetics Reference](../aesthetics_reference.md) when you need track, aesthetics, and label field groupings quickly +- Use [Reference](../reference.qmd) for the high-level method map +- Use [API Reference](../api_reference.qmd) for runtime option discovery +- Use [Aesthetics Reference](../aesthetics_reference.qmd) when you need track, aesthetics, and label field groupings quickly ## Common workflows -- Compose in Python: [Quick Start](../quickstart.md) -- Build from YAML: [CLI Guide](../cli.md) -- Choose track types: [Track Catalog](../track_catalog.md) -- Debug scale and overlay issues: [Troubleshooting](../troubleshooting.md) \ No newline at end of file +- Compose in Python: [Quick Start](../quickstart.qmd) +- Build from YAML: [CLI Guide](../cli.qmd) +- Choose track types: [Track Catalog](../track_catalog.qmd) +- Debug scale and overlay issues: [Troubleshooting](../troubleshooting.qmd) \ No newline at end of file diff --git a/docs/simple_overlays.ipynb b/docs/simple_overlays.ipynb deleted file mode 100644 index 77c6349..0000000 --- a/docs/simple_overlays.ipynb +++ /dev/null @@ -1,147 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Overlays Example:\n", - "\n", - "This is just a quick example of plotting a number of BigWig files using the package API" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Imports" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import seaborn as sns\n", - "import matplotlib.pyplot as plt\n", - "import plotnado.api as pn\n", - "from plotnado.api import TrackWrapper\n", - "import numpy as np" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Define the BigWig files that we want to plot\n", - "\n", - "**Note**: The BigWig files are not included in the dataset, as it is possible to just use remote URLs to plot the data I'm going to use data stored on the CCB Oxford HPC.\n", - "\n", - "To make life easier I'm going to make a dictionary of the BigWig files that I want to plot. You could just add these one by one to the plot if you wanted to." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "bigwigs = {\n", - " \"MV4;11 0nM EPZ H3K79me2\": \"https://userweb.molbiol.ox.ac.uk/public/project/milne_group/cchahrou/orlando_chip/hg38_dm6/MV4110nMH3K79me2_bigWig.bigWig\",\n", - " \"MV4;11 0.5nM EPZ H3K79me2\": \"https://userweb.molbiol.ox.ac.uk/public/project/milne_group/cchahrou/orlando_chip/hg38_dm6/MV41105nMH3K79me2_bigWig.bigWig\",\n", - " \"MV4;11 2nM EPZ H3K79me2\": \"https://userweb.molbiol.ox.ac.uk/public/project/milne_group/cchahrou/orlando_chip/hg38_dm6/MV4112nMH3K79me2_bigWig.bigWig\",\n", - " \"MV4;11 5nM EPZ H3K79me2\": \"https://userweb.molbiol.ox.ac.uk/public/project/milne_group/cchahrou/orlando_chip/hg38_dm6/MV4115nMH3K79me2_bigWig.bigWig\",\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Generate the plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "figure = pn.Figure()\n", - "figure.add_track(\"scale\") # Add a scale track\n", - "figure.add_track(\n", - " \"genes\", genome=\"hg38\", gene_style=\"normal\", min_gene_length=1e5, height=0.5\n", - ") # Add a gene track, this can specify a file in bed12 format or can be any supported genome name\n", - "\n", - "# Create the bigwig tracks using the TrackWrapper class. This is a simplified wrapper that the add_tracks method uses internally.\n", - "tracks_for_figure = []\n", - "colors = sns.color_palette(\"husl\", len(bigwigs))\n", - "\n", - "for (name, url), color in zip(bigwigs.items(), colors):\n", - " # Create a track for each bigwig file\n", - " track = TrackWrapper(\n", - " \"bigwig\",\n", - " file=url,\n", - " title=name,\n", - " style=\"stairsfilled\",\n", - " color=color,\n", - " alpha=0.5, # Set the transparency of the track\n", - " autoscale_group=\"h3k79me3\", # Autoscale the tracks in the same group, this can be any identifier that you want\n", - " data_range_style='text', # Show the data range as text rather than a bar\n", - " )\n", - "\n", - " # For illustration purposes I will add the track to the figure here, but this is not necessary\n", - " figure.add_track(track)\n", - "\n", - " # Add the track to the list of tracks to be overlayed\n", - " tracks_for_figure.append(track)\n", - "\n", - "# Create an overlay track\n", - "overlay_track = TrackWrapper(\"bigwig_overlay\", tracks_for_figure)\n", - "\n", - "# Add the overlay track to the figure\n", - "figure.add_track(overlay_track)\n", - "\n", - "# Add an x-axis track\n", - "figure.add_track(\"xaxis\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Display the plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "figure.plot_gene(\"GNAQ\", \"hg38\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "plotnado", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.0" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/docs/simple_overlays.qmd b/docs/simple_overlays.qmd new file mode 100644 index 0000000..b4561b1 --- /dev/null +++ b/docs/simple_overlays.qmd @@ -0,0 +1,79 @@ +--- +title: Simple Overlays +jupyter: python3 +execute: + echo: true + warning: false + message: false +--- + +Overlay tracks put several signal series into one panel with one shared y-axis. This page uses in-memory signal tables so the output is deterministic during docs builds. + +## Setup + +```{python} +import numpy as np +import pandas as pd + +from plotnado import GenomicFigure +``` + +```{python} +#| echo: false +REGION = "chr1:1,010,000-1,080,000" + + +def signal(phase=0.0, scale=1.0, step=1_000): + bins = np.arange(1_000_000, 1_100_000, step) + values = scale * (4 + 2 * np.sin(np.linspace(phase, 6 + phase, bins.shape[0]))) + return pd.DataFrame( + {"chrom": "chr1", "start": bins, "end": bins + step, "value": values} + ) +``` + +## Separate signal panels + +```{python} +fig = GenomicFigure(track_height=1.1) +fig.axis() +fig.bigwig(signal(0.0, scale=2.0), title="Sample A", style="fill", color="#1f77b4") +fig.bigwig(signal(1.2, scale=3.0), title="Sample B", style="fill", color="#d62728") + +fig.plot(REGION) +``` + +## Overlay the same signals + +```{python} +fig = GenomicFigure(track_height=1.25) +fig.axis() +fig.overlay( + [signal(0.0, scale=2.0), signal(1.2, scale=3.0)], + title="Sample overlay", + colors=["#1f77b4", "#d62728"], + alpha=0.6, +) + +fig.plot(REGION) +``` + +## Overlay with autoscale and highlight + +```{python} +fig = GenomicFigure(track_height=1.25) +fig.autoscale(True) +fig.highlight("chr1:1,032,000-1,046,000") +fig.highlight_style(color="#ffdd57", alpha=0.22) +fig.axis() +fig.bigwig(signal(0.0, scale=2.0), title="Sample A", autoscale_group="g1", style="fill", color="#1f77b4") +fig.bigwig(signal(1.2, scale=10.0), title="Sample B", autoscale_group="g1", style="fill", color="#d62728") +fig.overlay( + [signal(0.9, scale=4.2), signal(1.5, scale=3.5)], + title="Overlay", + autoscale_group="g1", + colors=["#2ca02c", "#9467bd"], + alpha=0.55, +) + +fig.plot(REGION) +``` diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/plotnado.scss similarity index 57% rename from docs/stylesheets/extra.css rename to docs/stylesheets/plotnado.scss index 712ec0c..256310c 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/plotnado.scss @@ -1,17 +1,25 @@ +/*-- scss:defaults --*/ + +$font-family-sans-serif: "IBM Plex Sans", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; +$font-family-monospace: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; +$body-bg: #f6f3ec; +$body-color: #172026; +$primary: #0f5968; +$link-color: #0f5968; +$code-color: #0a4250; +$border-radius: 0.5rem; + +/*-- scss:rules --*/ + :root { - --md-text-font: "IBM Plex Sans"; - --md-code-font: "IBM Plex Mono"; -} - -[data-md-color-scheme="default"] { - --md-default-bg-color: #f6f3ec; - --md-default-fg-color: #172026; - --md-default-fg-color--light: #53606b; - --md-primary-fg-color: #0f5968; - --md-primary-fg-color--light: #2a7383; - --md-primary-fg-color--dark: #0a4250; - --md-accent-fg-color: #c96a1b; - --md-accent-fg-color--transparent: rgba(201, 106, 27, 0.14); + --pn-bg: #f6f3ec; + --pn-fg: #172026; + --pn-fg-light: #53606b; + --pn-primary: #0f5968; + --pn-primary-light: #2a7383; + --pn-primary-dark: #0a4250; + --pn-accent: #c96a1b; + --pn-accent-transparent: rgba(201, 106, 27, 0.14); --pn-surface: rgba(255, 255, 255, 0.82); --pn-surface-strong: #ffffff; --pn-border: rgba(15, 89, 104, 0.13); @@ -24,15 +32,15 @@ --pn-muted: #4b5a63; } -[data-md-color-scheme="slate"] { - --md-default-bg-color: #0f151a; - --md-default-fg-color: #edf1f4; - --md-default-fg-color--light: #afbcc7; - --md-primary-fg-color: #56b4c6; - --md-primary-fg-color--light: #79c9d7; - --md-primary-fg-color--dark: #3494a8; - --md-accent-fg-color: #ffb15a; - --md-accent-fg-color--transparent: rgba(255, 177, 90, 0.15); +body.quarto-dark { + --pn-bg: #0f151a; + --pn-fg: #edf1f4; + --pn-fg-light: #afbcc7; + --pn-primary: #56b4c6; + --pn-primary-light: #79c9d7; + --pn-primary-dark: #3494a8; + --pn-accent: #ffb15a; + --pn-accent-transparent: rgba(255, 177, 90, 0.15); --pn-surface: rgba(18, 24, 30, 0.78); --pn-surface-strong: rgba(23, 31, 38, 0.94); --pn-border: rgba(86, 180, 198, 0.18); @@ -45,71 +53,100 @@ --pn-muted: #c5d0d8; } -.md-grid { - max-width: 1440px; +body { + background: var(--pn-bg); + color: var(--pn-fg); } -.md-header, -.md-tabs { +.navbar { backdrop-filter: saturate(180%) blur(18px); + box-shadow: 0 1px 0 var(--pn-border); } -.md-typeset h1, -.md-typeset h2, -.md-typeset h3 { - letter-spacing: -0.02em; +.navbar-logo { + border-radius: 6px; +} + +.page-columns { + max-width: 1440px; } -.md-typeset h1 { +.page-layout-full .content.column-page-left { + max-width: 1280px; + margin-left: auto; + margin-right: auto; +} + +.content h1, +.content h2, +.content h3 { + letter-spacing: 0; +} + +.content h1 { font-weight: 700; margin-bottom: 0.8rem; } -.md-typeset p, -.md-typeset li { +.content p, +.content li { line-height: 1.72; } -.md-typeset img { +.content img:not(.navbar-logo) { border-radius: 18px; border: 1px solid var(--pn-border); box-shadow: var(--pn-shadow-soft); } -.md-typeset code { +.content code { border-radius: 0.35rem; } -.md-typeset .admonition, -.md-typeset details { +.callout, +details { border-radius: 18px; box-shadow: var(--pn-shadow-soft); } -.md-typeset table:not([class]) { +.table { border-radius: 16px; overflow: hidden; box-shadow: var(--pn-shadow-soft); } +.cell-output-display { + margin: 0.65rem 0 1.25rem; +} + +.cell-output-display img { + background: var(--pn-surface-strong); +} + .pn-hero { display: grid; - grid-template-columns: minmax(0, 1.1fr) minmax(320px, 0.9fr); - gap: 2rem; + grid-template-columns: minmax(0, 1fr) minmax(380px, 0.95fr); + gap: 2.4rem; align-items: center; - padding: 2.4rem; - margin: 0 0 2rem; + min-height: min(680px, calc(100vh - 7rem)); + padding: clamp(1.6rem, 4vw, 3.4rem); + margin: 0 0 2.4rem; background: var(--pn-hero-gradient); border: 1px solid var(--pn-border); - border-radius: 30px; + border-radius: 24px; box-shadow: var(--pn-shadow); overflow: hidden; } +.content:has(.pn-hero) > #title-block-header { + display: none; +} + .pn-hero__copy h1 { margin: 0; - font-size: clamp(2.2rem, 5vw, 4rem); - line-height: 1.02; + max-width: 12ch; + font-size: clamp(2.8rem, 6vw, 5.3rem); + line-height: 0.98; } .pn-eyebrow { @@ -118,12 +155,12 @@ letter-spacing: 0.16em; font-size: 0.74rem; font-weight: 700; - color: var(--md-accent-fg-color); + color: var(--pn-accent); } .pn-lead { - max-width: 36rem; - font-size: 1.05rem; + max-width: 40rem; + font-size: clamp(1.04rem, 1.5vw, 1.22rem); color: var(--pn-muted); } @@ -152,20 +189,20 @@ } .pn-button--primary { - background: var(--md-primary-fg-color); + background: var(--pn-primary); color: white; box-shadow: var(--pn-shadow-soft); } .pn-button--secondary { background: var(--pn-surface-strong); - color: var(--md-primary-fg-color); + color: var(--pn-primary); border-color: var(--pn-border); } .pn-button--ghost { background: transparent; - color: var(--md-default-fg-color); + color: var(--pn-fg); border-color: var(--pn-border); } @@ -195,12 +232,14 @@ } .pn-hero__visual { - padding: 0.4rem; + padding: 0; } .pn-hero__visual img { width: 100%; height: auto; + background: var(--pn-surface-strong); + border-radius: 16px; } .pn-caption { @@ -210,7 +249,7 @@ } .pn-section { - margin: 2.2rem 0; + margin: 3rem 0 1.1rem; } .pn-section h2 { @@ -223,30 +262,42 @@ } .pn-card-grid, -.pn-showcase { +.pn-showcase, +.pn-track-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 1rem; + gap: 1.1rem; } .pn-card, -.pn-showcase__item { +.pn-showcase__item, +.pn-track-card { height: 100%; - padding: 1.2rem; - border-radius: 22px; + padding: 1.15rem; + border-radius: 8px; background: var(--pn-surface); border: 1px solid var(--pn-border); - box-shadow: var(--pn-shadow-soft); + box-shadow: 0 10px 26px rgba(15, 31, 42, 0.06); +} + +.pn-card .sourceCode { + margin-top: 0.85rem; +} + +.pn-card pre.sourceCode { + font-size: 0.78rem; } .pn-card h3, -.pn-showcase__item h3 { +.pn-showcase__item h3, +.pn-track-card h3 { margin-top: 0.2rem; margin-bottom: 0.45rem; } .pn-card p, -.pn-showcase__item p { +.pn-showcase__item p, +.pn-track-card p { color: var(--pn-muted); } @@ -255,7 +306,7 @@ font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.12em; - color: var(--md-accent-fg-color); + color: var(--pn-accent); font-weight: 700; } @@ -271,12 +322,52 @@ } .pn-showcase__item img { + width: 100%; + aspect-ratio: 16 / 9; + object-fit: contain; + background: var(--pn-surface-strong); margin-bottom: 0.9rem; } +.pn-output-label { + margin: 0.9rem 0 0.45rem; + color: var(--pn-muted); + font-size: 0.82rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.pn-code-output { + width: 100%; + margin-top: 0; +} + +.pn-track-grid { + margin: 1.4rem 0 2.2rem; +} + +.pn-track-card { + padding: 1rem; +} + +.pn-track-card img { + width: 100%; + aspect-ratio: 16 / 9; + object-fit: contain; + margin: 0 0 0.8rem; + background: var(--pn-surface-strong); + border-radius: 8px; +} + +.pn-track-card h3 { + font-size: 0.95rem; + line-height: 1.35; +} + .pn-inline-note { padding: 0.95rem 1rem; - border-left: 4px solid var(--md-accent-fg-color); + border-left: 4px solid var(--pn-accent); background: var(--pn-surface); border-radius: 0 18px 18px 0; } @@ -284,7 +375,8 @@ @media screen and (max-width: 1100px) { .pn-hero, .pn-card-grid, - .pn-showcase { + .pn-showcase, + .pn-track-grid { grid-template-columns: 1fr; } } @@ -304,4 +396,4 @@ .pn-stat { width: 100%; } -} \ No newline at end of file +} diff --git a/docs/track_aliases.md b/docs/track_aliases.qmd similarity index 84% rename from docs/track_aliases.md rename to docs/track_aliases.qmd index 5835d55..0d14362 100644 --- a/docs/track_aliases.md +++ b/docs/track_aliases.qmd @@ -14,6 +14,10 @@ gf.add_track("genes", genome="hg38") gf.add_track("bigwig", data="signal.bw", style="fill") ``` +Output: + +![Alias usage output](images/examples/quickstart_aliases_and_options.png) + ## How kwargs are routed - Track constructor fields: passed directly. @@ -38,4 +42,4 @@ aliases = GenomicFigure.available_track_aliases() print("\n".join(f"{k} -> {v}" for k, v in sorted(aliases.items()))) ``` -Use [Track Catalog](track_catalog.md) for track families and [Aesthetics Reference](aesthetics_reference.md) for full field-level options. +Use [Track Catalog](track_catalog.qmd) for track families and [Aesthetics Reference](aesthetics_reference.qmd) for full field-level options. diff --git a/docs/track_catalog.md b/docs/track_catalog.md deleted file mode 100644 index d1e98a7..0000000 --- a/docs/track_catalog.md +++ /dev/null @@ -1,83 +0,0 @@ -# Track Catalog - -This page summarizes the major track families, when to reach for them, and where to find runnable examples. - -## Structural tracks - -- `scalebar` (`ScaleBar`): genomic distance bar. -- `axis` (`GenomicAxis`): coordinate ticks and labels. -- `spacer` (`Spacer`): layout spacing. - -## Signal tracks - -- `bigwig` (`BigWigTrack`): continuous/interval signal (`style`: `fill`, `fragment`, `scatter`, `std`). -- `overlay` (`OverlayTrack`): multi-signal overlay panel with one shared y-axis derived from all component values. -- `bigwig_overlay` (`BigwigOverlay`): compatibility alias for `overlay`. -- `bigwig_collection` (`BigWigCollection`): multi-BigWig collection workflows. -- `bigwig_diff` (`BigWigDiff`): subtraction/ratio/log2ratio between two tracks. - -Use `overlay` when the signals should be compared directly on one axis. Put `autoscale_group` on the overlay itself when it should match neighboring signal tracks, and use explicit `min_value` / `max_value` only when you want to pin that bound. - -Scripts: - -- [examples/tracks/01_bigwig_styles.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/01_bigwig_styles.py) -- [examples/tracks/04_bigwig_collection_and_diff.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/04_bigwig_collection_and_diff.py) -- [examples/recipes/01_autoscale_overlay_highlight.py](https://github.com/alsmith151/plotnado/blob/main/examples/recipes/01_autoscale_overlay_highlight.py) - -## Interval and annotation tracks - -- `bed` (`BedTrack`): interval rectangles with optional labels. -- `narrowpeak` (`NarrowPeakTrack`): peak intervals with summit/score support. -- `genes` (`Genes`): gene models from bundled or custom annotations. -- `links` (`LinksTrack`): arcs for BEDPE-style interactions. -- `highlight` (`HighlightsFromFile`): region overlays. -- `hline` / `vline`: reference lines. - -Scripts: - -- [examples/tracks/02_bed_and_narrowpeak.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/02_bed_and_narrowpeak.py) -- [examples/tracks/03_links_annotations.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/03_links_annotations.py) - -## Matrix tracks - -- `cooler` (`CoolerTrack`) -- `capcruncher` (`CapcruncherTrack`) -- `cooler_average` (`CoolerAverage`) - -These require cooler-compatible files. - -Scripts: - -- [examples/tracks/05_matrix_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/05_matrix_tracks.py) - -## QuantNado tracks - -- `quantnado_coverage` -- `quantnado_stranded_coverage` -- `quantnado_methylation` -- `quantnado_variant` - -These support object-backed and array-backed workflows. - -Scripts: - -- [examples/tracks/06_quantnado_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/06_quantnado_tracks.py) - -## Field-level options - -- Runtime introspection: `GenomicFigure.track_options("")` -- Full generated table: [Aesthetics Reference](aesthetics_reference.md) -- Script mapping by track: [Example Coverage](example_coverage.md) - -## Coverage snapshot - -| Track / alias | Coverage | Primary example(s) | -|---|---|---| -| `scalebar`, `axis`, `genes` | Runnable script | [examples/quickstart/01_first_plot.py](https://github.com/alsmith151/plotnado/blob/main/examples/quickstart/01_first_plot.py), [examples/recipes/03_gene_label_strategies.py](https://github.com/alsmith151/plotnado/blob/main/examples/recipes/03_gene_label_strategies.py) | -| `bigwig` | Runnable script | [examples/tracks/01_bigwig_styles.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/01_bigwig_styles.py), [examples/basic_figure.py](https://github.com/alsmith151/plotnado/blob/main/examples/basic_figure.py) | -| `bed`, `narrowpeak` | Runnable script | [examples/tracks/02_bed_and_narrowpeak.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/02_bed_and_narrowpeak.py) | -| `links`, `hline`, `vline` | Runnable script | [examples/tracks/03_links_annotations.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/03_links_annotations.py) | -| `highlight`, `overlay` | Runnable script | [examples/recipes/01_autoscale_overlay_highlight.py](https://github.com/alsmith151/plotnado/blob/main/examples/recipes/01_autoscale_overlay_highlight.py) | -| `bigwig_collection`, `bigwig_diff` | Runnable script (remote Blueprint crop staged in a temp dir) | [examples/tracks/04_bigwig_collection_and_diff.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/04_bigwig_collection_and_diff.py) | -| `cooler`, `capcruncher`, `cooler_average` | Runnable script | [examples/tracks/05_matrix_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/05_matrix_tracks.py) | -| QuantNado aliases | Runnable script | [examples/tracks/06_quantnado_tracks.py](https://github.com/alsmith151/plotnado/blob/main/examples/tracks/06_quantnado_tracks.py) | diff --git a/docs/track_catalog.qmd b/docs/track_catalog.qmd new file mode 100644 index 0000000..9c8a436 --- /dev/null +++ b/docs/track_catalog.qmd @@ -0,0 +1,99 @@ +# Track Catalog + +This is the practical reference for choosing a track and understanding what it adds to a `GenomicFigure`. Each entry describes what appears in the figure, what data shape it expects, and where to inspect the full options. + +## Visual Map + +
+
+ Scale bar, genomic axis, genes, and BigWig tracks +

scalebar, axis, genes

+

Structural context tracks plus a gene annotation panel.

+
+
+ BigWig fill, fragment, scatter, and standard-deviation styles +

bigwig, bedgraph

+

Continuous signal rendered as fill, fragments, points, or standard-deviation bands.

+
+
+ Overlay track with highlighted genomic region +

overlay, highlight

+

Multiple signals on one y-axis with a highlighted interval.

+
+
+ BED intervals and narrowPeak intervals +

bed, narrowpeak

+

Interval rectangles, labels, peak scores, and summit markers.

+
+
+ Link arcs with horizontal and vertical reference lines +

links, hline, vline

+

Interaction arcs with horizontal and vertical reference lines.

+
+
+ Cooler and cooler average matrix tracks +

cooler, cooler_average

+

Contact matrix tracks for cooler-compatible files.

+
+
+ +## Signal tracks + +| Track / aliases | What appears in the figure | Input model | Choose it when | Full reference | +|---|---|---|---|---| +| `bigwig`, `bw`, `signal`, `bedgraph` | One quantitative signal panel with its own y-axis. The signal can render as filled area, fragments, scatter points, or standard-deviation bands. | BigWig path/URL or bedGraph-like table with `chrom`, `start`, `end`, `value`. | You have one continuous genomic signal such as coverage, ChIP-seq, ATAC-seq, or bedGraph-like values. | [Options](aesthetics_reference.qmd#options-bigwig), [API](reference.qmd#bigwigdata-kwargs), [Examples](example_coverage.qmd#bigwig-styles) | +| `overlay`, `bigwig_overlay` | One signal panel containing multiple series on a shared y-axis. Component colors distinguish the series. | List of BigWig paths, signal tables, or compatible `BigWigTrack` objects. | You want direct visual comparison between signals and a single scale is meaningful. | [Options](aesthetics_reference.qmd#options-overlay), [API](reference.qmd#overlaytracks-kwargs), [Examples](example_coverage.qmd#overlay-autoscale-and-highlight) | +| `bigwig_collection` | Multi-sample signal collection panel, useful for compact comparison across several BigWigs. | List of BigWig paths plus optional labels/colors. | You have a batch of signal files that should be displayed as a coordinated collection. | [Options](aesthetics_reference.qmd#options-bigwig-collection), [API](reference.qmd#bigwig_collectionfiles-kwargs), [Examples](example_coverage.qmd#bigwig-collection-and-diff) | +| `bigwig_diff` | Difference panel showing positive and negative deviations between two signal files. | Two BigWig sources. | You need subtraction, ratio, or log2-ratio style comparison between matched tracks. | [Options](aesthetics_reference.qmd#options-bigwig-diff), [API](reference.qmd#bigwig_diffdata_a-data_b-kwargs), [Examples](example_coverage.qmd#bigwig-collection-and-diff) | + +When using `overlay`, put `autoscale_group` on the overlay itself if the entire overlay panel should share limits with neighboring signal panels. + +## Interval and annotation tracks + +| Track / aliases | What appears in the figure | Input model | Choose it when | Full reference | +|---|---|---|---|---| +| `bed`, `annotation`, `unknown` | Rectangular genomic intervals, optionally stacked and labeled. | BED/BigBed path or table with at least `chrom`, `start`, `end`; optional `name`. | You need to show intervals such as peaks, enhancers, domains, or hand-curated annotations. | [Options](aesthetics_reference.qmd#options-bed), [API](reference.qmd#beddata-kwargs), [Examples](example_coverage.qmd#bed-and-narrowpeak) | +| `narrowpeak` | Peak intervals with optional score-colored fills and summit markers. | ENCODE narrowPeak file/table. | Your input is a narrowPeak file and summit/score information should be visible. | [Options](aesthetics_reference.qmd#options-narrowpeak), [API](reference.qmd#narrowpeakdata-kwargs), [Examples](example_coverage.qmd#bed-and-narrowpeak) | +| `genes`, `gene` | Gene models with exons/introns and labels, either collapsed or expanded. | Built-in genome name such as `hg38`/`mm10`, or custom annotation data. | You want gene context for the plotted locus. | [Options](aesthetics_reference.qmd#options-genes), [API](reference.qmd#genesgenomehg38-kwargs), [Examples](example_coverage.qmd#gene-label-strategies) | +| `links` | Arc-shaped interactions between two genomic anchors. | BEDPE-like table with `chrom1`, `start1`, `end1`, `chrom2`, `start2`, `end2`; optional `score`. | You need loops, contacts, enhancer-promoter links, or paired anchors. | [Options](aesthetics_reference.qmd#options-links), [API](reference.qmd#linksdata-kwargs), [Examples](example_coverage.qmd#links-hline-and-vline) | +| `highlight` | Shaded vertical region applied behind plotted tracks. It does not create a standalone data panel. | BED-like intervals or inline strings via `fig.highlight("chr:start-end")`. | You want to mark a region of interest across tracks without changing the y-scale. | [Options](aesthetics_reference.qmd#options-highlight), [API](reference.qmd#highlightsdata-kwargs), [Examples](example_coverage.qmd#overlay-autoscale-and-highlight) | +| `hline`, `vline` | Horizontal or vertical reference lines over plot panels. | Numeric y-value for `hline`; genomic coordinate for `vline`. | You need thresholds, cut points, or fixed positional markers. | [Options](aesthetics_reference.qmd#options-lines), [API](reference.qmd#hliney_value-kwargs-vlinex_position-kwargs), [Examples](example_coverage.qmd#links-hline-and-vline) | + +## Matrix tracks + +| Track / aliases | What appears in the figure | Input model | Choose it when | Full reference | +|---|---|---|---|---| +| `cooler` | Contact matrix panel for the current region. | Cooler-compatible file path. | You want to inspect Hi-C or contact-map intensity for a locus. | [Options](aesthetics_reference.qmd#options-cooler), [API](reference.qmd#coolerdata-kwargs), [Examples](example_coverage.qmd#cooler-and-cooler_average) | +| `cooler_average` | Averaged matrix panel across multiple cooler-compatible inputs. | List of cooler-compatible file paths. | You want a mean contact-map view across samples or replicates. | [Options](aesthetics_reference.qmd#options-cooler), [API](reference.qmd#cooler_averagefiles-kwargs), [Examples](example_coverage.qmd#cooler-and-cooler_average) | +| `capcruncher` | Viewpoint-centric matrix panel from CapCruncher output. | CapCruncher HDF5 reporters store plus a `viewpoint`. | You are plotting capture-C / CapCruncher interaction profiles. | [Options](aesthetics_reference.qmd#options-capcruncher), [API](reference.qmd#capcruncherdata-kwargs), [Examples](example_coverage.qmd#capcruncher-viewpoint-matrix) | + +## QuantNado tracks + +| Track / aliases | What appears in the figure | Input model | Choose it when | Full reference | +|---|---|---|---|---| +| `quantnado_coverage` | Coverage signal from a QuantNado dataset. | QuantNado object name and `dataset_path`. | You want file-backed coverage without first converting to BigWig. | [Options](aesthetics_reference.qmd#options-quantnado-coverage), [API](reference.qmd#quantnado_coveragename-kwargs), [Examples](example_coverage.qmd#quantnado-coverage-and-stranded-coverage) | +| `quantnado_stranded_coverage` | Forward/reverse stranded coverage in one track. | QuantNado stranded coverage dataset. | Strand orientation matters, for example RNA-seq signal. | [Options](aesthetics_reference.qmd#options-quantnado-coverage), [API](reference.qmd#quantnado_stranded_coveragename-kwargs), [Examples](example_coverage.qmd#quantnado-coverage-and-stranded-coverage) | +| `quantnado_methylation` | Point-style methylation percentages across the locus. | QuantNado methylation dataset. | You want methylation proportions at genomic positions. | [Options](aesthetics_reference.qmd#options-quantnado-methylation), [API](reference.qmd#quantnado_methylationname-kwargs), [Examples](example_coverage.qmd#quantnado-methylation) | +| `quantnado_variant` | Variant markers colored by genotype/state and allele balance. | QuantNado variant dataset. | You want SNP/variant context alongside other genomic tracks. | [Options](aesthetics_reference.qmd#options-quantnado-variant), [API](reference.qmd#quantnado_variantname-kwargs), [Examples](example_coverage.qmd#quantnado-variant) | + +## Structural tracks + +| Track / aliases | What appears in the figure | Input model | Choose it when | Full reference | +|---|---|---|---|---| +| `scalebar`, `scale` | Genomic distance bar, usually at the top or bottom of the stack. | No external data. | You want a quick visual distance cue. | [Options](aesthetics_reference.qmd#options-structural), [API](reference.qmd#scalebarkwargs), [Examples](example_coverage.qmd#quickstart-first-plot) | +| `axis` | Genomic coordinate ticks and labels. | No external data. | You want exact coordinate context for the rendered interval. | [Options](aesthetics_reference.qmd#options-structural), [API](reference.qmd#axiskwargs), [Examples](example_coverage.qmd#quickstart-first-plot) | +| `spacer` | Blank vertical space between panels. | No external data. | You need to separate dense groups of tracks or tune layout rhythm. | [Options](aesthetics_reference.qmd#options-structural), [API](reference.qmd#spacerheight0.5-kwargs), [Examples](example_coverage.qmd#basic-figure) | + +## Runtime lookup + +Use runtime introspection when you need the exact available fields in your installed version: + +```python +from plotnado import GenomicFigure + +GenomicFigure.available_track_aliases() +GenomicFigure.track_options("bigwig") +GenomicFigure.track_options_markdown("genes") +``` + +For runnable output examples, see the [Living Gallery](example_coverage.qmd). diff --git a/docs/troubleshooting.md b/docs/troubleshooting.qmd similarity index 89% rename from docs/troubleshooting.md rename to docs/troubleshooting.qmd index ebdaff6..27b36ff 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.qmd @@ -30,6 +30,10 @@ fig.overlay( ) ``` +Output: + +![Overlay troubleshooting output](images/aesthetics/overlay_autoscale.png) + If two series need fundamentally different scales to remain interpretable, split them into separate panels instead of forcing them into one overlay. ## Overlay label shows an unexpected range @@ -45,7 +49,7 @@ PlotNado requires Python 3.12+. ```bash uv run python --version -uv run mkdocs build --strict +quarto render ``` ## TOML export/import errors @@ -67,4 +71,4 @@ GenomicFigure.track_options("overlay") GenomicFigure.track_options_markdown("bigwig") ``` -Then compare with [Aesthetics Reference](aesthetics_reference.md) and [Reference](reference.md). +Then compare with [Aesthetics Reference](aesthetics_reference.qmd) and [Reference](reference.qmd). diff --git a/docs/worked_examples.ipynb b/docs/worked_examples.ipynb deleted file mode 100644 index 8927329..0000000 --- a/docs/worked_examples.ipynb +++ /dev/null @@ -1,302 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "b64502d5", - "metadata": {}, - "source": [ - "# Worked Examples\n", - "\n", - "These examples use public Blueprint hub tracks and focus on realistic PlotNado workflows built from real files." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "833b740e", - "metadata": {}, - "outputs": [], - "source": [ - "from pathlib import Path\n", - "import plotnado\n", - "\n", - "from plotnado import GenomicFigure, PlotStyle\n", - "\n", - "rna_gene_of_interest = \"ACTB\"\n", - "monocyte_gene_of_interest = \"LYZ\"\n", - "repo_root = Path(plotnado.__file__).resolve().parent.parent\n", - "blueprint_bigwig_files = {\n", - " \"T12-15 plasma cell RNA\": \"http://ftp.ebi.ac.uk/pub/databases/blueprint/data/homo_sapiens/GRCh38/tonsil/T12-15/plasma_cell/RNA-Seq/IDIBAPS/T12-15-PC.signal.star_grape2_crg.GRCh38.20150815.bw\",\n", - " \"T12-16 plasma cell RNA\": \"http://ftp.ebi.ac.uk/pub/databases/blueprint/data/homo_sapiens/GRCh38/tonsil/T12-16/plasma_cell/RNA-Seq/IDIBAPS/T12-16-PC.signal.star_grape2_crg.GRCh38.20150815.bw\",\n", - "}\n", - "blueprint_monocyte_h3k27ac_signal = \"http://ftp.ebi.ac.uk/pub/databases/blueprint/data/homo_sapiens/GRCh38/venous_blood/C000S5/CD14-positive_CD16-negative_classical_monocyte/ChIP-Seq/NCMLS/C000S5H2.ERX173536.H3K27ac.bwa.GRCh38.20150529.bw\"\n", - "blueprint_monocyte_h3k27ac_peaks = \"http://ftp.ebi.ac.uk/pub/databases/blueprint/data/homo_sapiens/GRCh38/venous_blood/C000S5/CD14-positive_CD16-negative_classical_monocyte/ChIP-Seq/NCMLS/C000S5H2.ERX173536.H3K27ac.bwa.GRCh38.20150527.bb\"\n", - "blueprint_monocyte_candidate_bed = repo_root / \"examples\" / \"data\" / \"blueprint_monocyte_lyz_candidate_peaks.bed\"" - ] - }, - { - "cell_type": "markdown", - "id": "a7a57082", - "metadata": {}, - "source": [ - "## Example 1: stacked Blueprint BigWig tracks with label boxes\n", - "\n", - "This mirrors the main workflow closely: two real Blueprint RNA BigWig files, separate panels, and the track title and scale drawn inside the plotted area with a visible background box." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "7c9d4f76", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABF4AAAFoCAYAAABuXz/oAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAANB5JREFUeJzt3QeUZFW5Pu49MOQkIyJJQECSGAATKkER5JoDgqKCgAKiFzBdFX8omCUYAFG8hqsiqCRBiSKSRBEJAiKKIAomRKJInv6v96x15l9ddJrpOl3pedbqxXR19alzTu0qer/17e/MGhkZGSkAAAAAdNxCnd8kAAAAAIIXAAAAgAapeAEAAABoiOAFAAAAoCGCFwAAAICGCF4AAAAAGiJ4AQAAAGiI4AUAAACgIYIXAAAAgIYIXgAAAAAaIngBAAAAaIjgBQAAAKAhghcAAACAhgheAAAAABoieAEAAABoiOAFAAAAoCGCFwAAAICGCF4AAAAAGiJ4AQAAAGiI4AUAAACgIYIXAAAAgIYIXgAAAAC6Gbwstthi1RfMNGMPAACAfqbiBQAAAKAhs6d6x7lz55atttqqqf2AMT300ENl4YUXdnYAAADoS7NGRkZGprLc45FHHqm+YKYleHn44YedeAAAAAa34mXWrFllyy23bHZvoM0FF1xQjT0AAAAY6IqXeOCBB2Zin8DYAwAAYCBorgsAAADQzYoXAAAAAOafipdp2Geffcqaa65Z9SC58sorx73fgQceWPbbb78xf5bfvfPOO6ezGwAAAECPErxMw/bbb18uuuiissYaa3TuGQEAAAAGhuBlGrbYYouy2mqrzdfvXHvttWWjjTYqZ5xxxrzbDj300LLxxhuXddddt3znO9+Zzi4BAAAA/Xg5aabvvPPOK3vvvXc55phjyiabbDJqudEVV1xRbrzxxvKMZzyjPO95z6uWMAEAAAD9TfAyQ84999xy5plnlrPPPrusvvrqo3721re+tfrvWmutVVXRXHDBBYIXAAAAGACWGs2QddZZpyy00ELlF7/4xaT3TQUMAAAA0P8ELzMkVS4/+clPysc//vHyjW98Y9TP6u9vuummcuGFF5bNN998pnYLAAAAaJClRtOw5557ltNOO638/e9/Ly9+8YvLMsssU/7whz+Me/+VV165WnK03XbblXvuuae6HHU88sgjVXPde++9txx++OGWGQEAAMCAmDUyMjLS7Z0AAAAAGESWGgEAAAA0RPACAAAA0BDBCwAAAEBDBC8AAAAADRG8AAAAADRE8AIAAADQEMELAAAAQEMELwAAAAANEbwAAAAANETwAgAAANAQwQsAAABAQwQvAAAAAA0RvAAAAAA0RPACAAAA0BDBCwAAAEBDBC8AAAAADRG8AAAAADRE8AIAAADQEMELAAAAQEMELwAAAAANEbwAAAAANETwAgAAANAQwQsAAABAQwQvAAAAAA0RvAAAAAA0RPACAAAA0BDBCwAAAEBDBC8AAAAADRG8AAAAADRE8AIAAADQEMELAAAAQEMELwAAAAANEbwAAAAANETwAgAAANAQwQsAAABAQwQvAAAAAA0RvAAAAAA0RPACAAAA0BDBCwAAAEBDBC8AAAAADRG8AAAAADRE8AIAAADQEMELAAAAQEMELwAAAAANEbwAAAAANETwAgAAANAQwQsAAABAQwQvAAAAAA0RvAAAAAA0RPACAAAA0BDBCwAAAEBDBC8AAAAADRG8AAAAADRE8AIAAADQEMELAAAAQEMELwAAAAANEbwAAAAANETwAgAAANAQwQsAAABAQwQvAAAAAA0RvAAAAAA0RPACAAAA0BDBCwAAAEBDBC8AAAAADRG8AAAAADRE8AIAAADQEMELAAAAQEMELwAAAAANEbwAAAAANETwAgAAANAQwQsAAABAQwQvAEBXXHHFFWWRRRYpW2211aN+dv/995dDDjmkbLLJJmWZZZYpyy+/fNlss83KEUccUe66664ye/bsMmvWrHG/9t9///LAAw+URRdddN5tCy+8cFlllVXKe9/73jJ37tyuHDMAMHxmjYyMjHR7JwCA4bP55puX5z//+eXLX/5yueOOO+bdftttt5Vtt9223H333eXDH/5wFbg88sgj5cwzzywHH3xwuf7668s999wz7/577LFH+c9//lOOOeaYebctt9xy5Te/+U155jOfWS6++OLyxCc+sTz44IPlpJNOKu9617vKKaecUl7xilfM+DEDAMNndrd3AAAYPscee2xVxfKOd7yjfPrTny433XRTWXPNNaufvelNb6qClEsvvbS6T2399dcvO++8c1lqqaWqr9pvf/vbssMOO5SVVlpp1GNcfvnlZdllly3Pec5zqoqX2HXXXavgJdsHAJgJghcAYEbde++91VKgM844o6y22mpVdcqVV15ZBS+nn356Oeuss8r5558/KnSpzZkzZ9T3qXy54YYbqiVJ7S677LLq9jp0uf3226vHXWeddcrLX/7yBo8QAOD/p8cLADCjPvnJT5btttuubLDBBtX3G264YRW8xDe/+c2y0UYblS222GLKfWKyanrjjTd+1M9S8XLRRReVpZdeuqqQeexjH1uFMVl61FoxAwDQJMELADBjbrzxxnL00UeXAw88cN5tCVrq4CVhyaabbjrl7eX+j3nMY8paa6016vaHHnqoXH311eWwww6rtp2A5rTTTit//etfy0EHHdTBIwIAmJilRgDAjEl/lX/961/VEqNarjC0+uqrV/9O75VUqMxP8DJWtUsa6+aqRttss021tCjWXXfdqhdMGusCAMwUwQsAMCPOPvvs8rOf/ayqPsnloGtporvbbruVO++8s6p+yX2yfKjuzVJLKLPkkks+KnjJsqV2uT33XW+99Ubdnn4wj3vc4zp+bAAA4xG8AACNy9Kf/fbbr7zvfe8rT3/600f9LFceiiwJ+tCHPlS23nrr6spG++67b3WloltuuaWceuqp1WWmv/rVr877vfvuu69cd911VcPcdunlkgqXW2+9tfo+VTbHHXdc+dGPflS+//3vN368AAA1PV4AgMYdeeSRVfjxzne+81E/e8ITnlBVpyR4SVPdSy65pNx1113lVa96VVWxstNOO5Wbb7657LHHHqN+76qrriqPPPLImFc0SsVLtrfyyiuXVVZZpVpylNvOO++8sv322zd6rAAArWaNpJYXAAAAgI5T8QIAAADQEMELAAAAQEMELwAAAAANEbwAAAAANETwAgAAANAQwQsAAABAQ2ZP9Y533313Rx940UUXLYsvvnhHtwn0tvvvv788+OCD3d4NGtCp9/SZHiPT3W9juvPndJD16nhp4jnr9LEaVzQx5owrYKbMGhkZGZnSHWfN6ugDz5kzp/zlL3/xxxkM0R9Eq666arn99tu7vSs0oBPv6d0YI9PZb2O68+d0kPXyeOn0c9bEsRpXNDHmjCtg4Jca5Y2xFz/1AZqR13svTjjonff0boyR6ey3Md35czrIenm8dPo5a+JYjSuaGHPGFTBT9HgBAAAAaIjgBQAAAKAhghcAYOD87ne/KyeffHK3d2NgOJ+9xfMB0F8ELwDAQDn22GPLV77ylbLd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vyeddFL172c961nl3HPPHXN73/nOd8rKK69c9ttvv7L88suXSy65pHziE58o//jHP6rJ9URuvfXWcvbZZ5cnP/nJZYMNNijXX3/9uPddddVVy8c//vFRtz3xiU+c0jEzs4444oiy9NJLlzvvvLN897vfrYK8hGovfvGLH3Xf/PxNb3pTdf/J3HXXXeXnP/959e+zzjpL8DIkxpo8mJwyiPqlImdQ1Oc4AcSgBRHGD90cc4P0WoLYfffdy2WXXVb9+x3veEd529veNuGJufLKK8tnP/vZ8rvf/a7MmTOn7LDDDuUtb3nLpEUJU5X50CmnnFKuueaacsstt1Rz+Q9+8IOj7nPPPffM++D89ttvL0suuWRVQPH2t7+9bLTRRhNuP9vO/K1dcoN999131G0nn3xylS/8/e9/L2uuuWZ55zvfWbbYYosJt3/88cdXeUGsvfba5cQTTywDGbw85SlPedRtK6200pi3p8pkoYUWKr/61a/GDV4+97nPlcc85jHzvn/mM59ZTZATyKSaIb8/nic96UlV8BJHH330hMHLYostNuY+0nsSotVjIhVWqUxJNUx78JLA7cYbb6zCl4yVyfzkJz8pDz30UBUEJuDLm0jezBgOgzYxglYmyv13vvv9Panf95/eZWwxiJ7+9KdXlfePf/zjJ7zfn//857L33nuX5zznOVVIk1UdWaWROfEuu+zSkX25+OKLq+1uuumm1bx7LA899FBZdNFFq0KIFDBk9Urm5/n+uOOOK2usscakj3PUUUeN+nB8xRVXHPXzM888s3z0ox+t5nGZn+WD8Zyjr3/96+WpT33quNvdeuuty3rrrVe+8pWvlL/97W+lKV0PXubHRKFJrTV0qeVEJv267777quUm09n+dNRLX5LCffGLX6yWNGWinsHxyle+ctzfu+2226r7ZylM/p1B9qIXvahKNzOAW9PADOC//OUvZfHFF69Svgy2BAp16JDU79577y0/+MEPqhfAq1/96rLPPvuUSy+9tHz+858vN998c5U6JlVMANZaNXLRRReVv/71r9WA33jjjattr7DCCpMedwKzY489ttx0001liSWWqPYnKWgqkyLVSEceeWT1or3//vur9DPbTmDSaXn8LDdKCtouFVKvfe1rqxd/liNNNFbqF3e2lX19/etfX4V2+S+9/Ulyr+iFPwR7YR+aNOjH1xSh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4HVjEjnZY/bCxLYX9mFYj6fe137aZwAAFozgBRgKMznB7afJdD/t66Dph3O/IMvxYH7GlfEFwDAQvAAAMCMELQAMI8ELMDQ6/Qd/+/ZMKHrDTD8Pnnc6PZYmGlNj/cwYBIDeJngBMHEBumwq4cmgBiyDelwAUBO8AENFX4HhYCJHPxmW8TosxwkA7QQvwNDxxz/GktfOoBiU97NBOQ56q4GzcQX0CsELwAD+ETmsf2wO63E3TT+j7p/zTt9/JpkAAzDsBC/AQOnlyUe7XglR+umc0TuMm+6fV88BAPSH2d3egWc84xmT3ucjH/lIefnLX16++tWvlssvv7xce+215d///nf51re+VTbccMNR9z3nnHPK6aefXq677rpy9913l9VXX73suOOO5RWveEWZNWvWhI9zxx13lK997Wvl6quvLr///e/L7Nmzy4UXXjjmfR944IHyjW98o3qsf/7zn2XOnDll2223Lfvuu2+ZrqOPProcc8wx4z72MNpjjz3KkksuWT7/+c9P+Rz96le/Knvttde87xdffPGy6qqrlle96lVlhx12KAsvvPC8nx144IHlRz/6UXnJS15SPvrRj47aznve855yzz33lK985SuPeoxDDz20fPe7360e561vfWuHjpb5ZfLRvH48x9nnP31rsW7vxsDQH6m3xnA/viahG/y/gEGz++67l8suu6z69zve8Y7ytre9bcL7X3nlleWzn/1s+d3vflfNWTMPestb3jLp3Hiqfv7zn5dTTjmlXHPNNeWWW26p5t4f/OAHR93nnnvuqeZbmcfffvvt1bwu8/i3v/3tZaONNppw+9l28oB2u+6666Pm3ieffHI1R//73/9e1lxzzfLOd76zbLHFFhNu//jjjy+f+MQnqn+vvfba5cQTTywDGbzkxLSfwDxZ22233bzbVlttteq/J510UvXvZz3rWeXcc88dc3vf+c53ysorr1z222+/svzyy5dLLrmkOpH/+Mc/qsn7RG699dZy9tlnlyc/+cllgw02KNdff/2Y95s7d241Gf/LX/5SDfRVVlml/O1vfyt/+tOfFuAM0LS8UPPCS1iXcOWwww6rgrO84bQ766yzqnFSj7mJPPLII+XHP/5x9e8zzzxT8NJDBumPLJMroJ95DwPovKc//enl3e9+d3n84x8/4f3+/Oc/l7333rs85znPqUKaFBccfvjhZaGFFiq77LJLR/bl4osvrra76aablrvuumvM+zz00ENl0UUXreZZ+SA887LM2/P9cccdV9ZYY41JH+eoo44qSy+99LzvV1xxxVE/z3wsH6Dnw/DkBZnX5Rx9/etfL0996lPH3e7WW29d1ltvvepD9szpm9L14OUpT3nKo25baaWVxrw9k+YMklQyjBe8fO5znyuPecxj5n3/zGc+sxoAeWLzJOT3x/OkJz2pCl7qiorxgpdTTz21SvROOOGEssIKK0zpOOmeJJd1ZdSzn/3sKu3Nc9gevKQ6KoFMXpwf/vCHJ93upZdeWv71r39VL+xf/vKXVZXV+uuv39hxADC8+jHAWJA+NYMSmtNbjC0GzTLLLDNhmFD75je/WZZbbrnymc98piyyyCLVXCirPLKS5A1veEMVhkzXu971rqooITInGsucOXPKpz71qVG3JQzacsstqw+yp7JyIIURKawYz5e+9KXy4he/uAqY6hwggVDm9V/84hfH/b3sW76y7SaDl77q8TJRaFJrDV1qSbDuvffect999017+/GDH/ygvOhFL5rv0CWBUZZWXXTRReV973tfef7zn18Njkz0J5L9zovlNa95TXne855XLbv65Cc/WSWFrc4///zy5je/uWy++eZlq622qv6dx6rl97KdY489trz0pS+tyq5S8vXggw9WYcRuu+1W7dPOO+9c/vCHP4zadpb15Pa8OLbZZpuqomiqFT7Zh2w7+/6CF7ygSjYTUrSWnn3605+uzsVmm21W3vSmN5Vf/OIXpQl5jhOwpQKqXZaW5RizfGwqL7qkqksttVR1DvO7Z5xxRiP7DDMxAevHSV23j3kYzxkzb6xxZuzB/L1mYFj97Gc/q+ZfCV1qWVmS+devf/3rjjzGVOfQ7ZZYYomy2GKLVdUw05UlTpmbpvVHqxxrwqDMd7utr4KXBZV1bSlFyiR5uh5++OEqNEhVTqoiElQkwPjABz5QbrvttiltI6FJlrIccsghVU+RlE2lemY8999/f7W8KWViKQ3LWrj0uqmTxXqwvf/97y9rrbVWtd0kigmH8qJqdcEFF1ShRtbdJQ1MeJD751jS+yQBSKo+sq08Zi1BRdYDZpnO//t//6/6WcKU8crJaqkgSgqaFDFLvj72sY+Vpz3taVVfnMgLLfuRXi05vqw/fOITn1it12sPfzoloUqWh40l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", - "text/plain": [ - "
" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "stacked_fig = GenomicFigure()\n", - "stacked_fig.scalebar()\n", - "stacked_fig.genes(\"hg38\")\n", - "stacked_fig.autocolor()\n", - "\n", - "for name, url in blueprint_bigwig_files.items():\n", - " stacked_fig.bigwig(\n", - " url,\n", - " title=name,\n", - " style=PlotStyle.FRAGMENT,\n", - " height=0.55,\n", - " autoscale_group=\"blueprint_rna\",\n", - " label_on_track=True,\n", - " label_box_enabled=True,\n", - " label_box_alpha=0.95,\n", - " title_height=0.5,\n", - " scale_height=0.5,\n", - " plot_scale=True,\n", - " )\n", - "\n", - "stacked_plot = stacked_fig.plot_gene(rna_gene_of_interest)\n", - "stacked_plot" - ] - }, - { - "cell_type": "markdown", - "id": "7d340bd4", - "metadata": {}, - "source": [ - "## Example 2: Blueprint `bigwig_overlay()` with a shared axis\n", - "\n", - "`bigwig_overlay()` is the clearer API for this case. It resolves to the same implementation as `overlay()`, but the current implementation auto-wraps path inputs as `BigWigTrack`, so the more specific name matches the actual behavior better. The overlay now accepts the same `style` kwarg as `bigwig()`, so this example uses `PlotStyle.FRAGMENT` to keep the signal geometry consistent with the stacked panels above." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "b0313113", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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4yB4AQDG6pZbbmmmAl144YVlv/32a6pTrrjiiiZ4ede73lXe/e53l/e9733bhC6t9evXb/N1Kl++8IUvNFOS5vvYxz7WfL8NXa6//vrmdQ866KDypCc9qeIeAgD8P2u8AABj9bKXvawcd9xx5WEPe1jz9cMf/vAmeIk3vvGN5dBDDy1HH330steJyazpww47bLufpeLlkksuKfe6172aCpn73ve+TRiTqUeDFTMAADUJXgCAsbn66qvLa1/72rJp06at30vQ0gYvCUse+chHLvv58vh73/ve5cADD9zm+1u2bCmf/OQny1lnndU8dwKaCy64oFx77bXlxS9+8Qj3CABgcaYaAQBjk/VVvva1rzVTjFq5w9D+++/ffJ61V1KhspLgZVi1SxbWzV2NHv/4xzdTi+Lggw9u1oLJwroAAOMieAEAxuLiiy8uH/zgB5vqk9wOupVFdJ/61KeWG264oal+yWMyfahdm6WVUOYe97jHdsFLpi3Nl+/nsYcccsg23896MPe///1Hvm8AAAsRvAAA1WXqzxlnnFGe97znlUc84hHb/Cx3HopMCXrBC15Qjj322ObORs961rOaOxV96UtfKu94xzua20y//vWv3/p7t956a/nsZz/bLJg7X9ZySYXLdddd13ydKpvzzz+/vPOd7yxvfvObq+8vAEDLGi8AQHWvetWrmvDjGc94xnY/e9CDHtRUpyR4yaK6H/nIR8qNN95YnvKUpzQVKyeddFL54he/WDZs2LDN733iE58od91119A7GqXiJc+3zz77lH333beZcpTvbd68uRx//PFV9xUAYNC6udTyAgAAADByKl4AAAAAKhG8AAAAAFQieAEAAACoRPACAAAAUIngBQAAAKASwQsAAABAJTst94E33XTTSF94l112KbvttttInxPotttuu63ccccdk94MKhjVOX3cfWSt261Pj75Np1lX+0uNYzbqfdWvqNHn9CtgXNbNzc3NLeuB69aN9IXXr19fvvzlL/vjDGboD6IHPvCB5frrr5/0plDBKM7pk+gja9lufXr0bTrNutxfRn3MauyrfkWNPqdfAVM/1Sgnxi5e9QHqyPu9iwMOunNOn0QfWct269Ojb9Np1uX+MupjVmNf9Stq9Dn9ChgXa7wAAAAAVCJ4AQAAAKhE8AIATJ3Pfe5z5W1ve9ukN2NqaM9ucTwA+kXwAgBMlfPOO6+87nWvK8cdd9ykN2UqaM9ucTwA+kfwAgBMhVtvvbU85znPae7EeNZZZ5Xdd9990pvUa9qzWxwPgP7aadIbAMB0OPHEE8vOO++86t/fsmVL6dN2T2p7Z6EvrNanPvWpcr/73a9cddVV5fzzzy9d0vX+MuyYrbY9a+3rpPpVV3S5f09a199fAOvm5ubmltMMuXo0ajfeeGPZc889HQWYATfddFPZa6+9Jr0ZVLTWc/qk+shqt1ufHn2bjqIi4IUvfGE5/PDDy0knnVS6pOv9ZdgxW2171trXWf+7scv9e9LW0udmvV8B42GqEQAwFTK16Mwzz2w+z5SjDFTRntNC/wboL8ELADBVUg2wYcOGctFFF016U6aC9uwWxwOgf6zxAgBMnUMOOaT5QHtOI/0boF9UvAAAAABUIngBAAAAmLbgZf369WWXXXaZ1MsDY5b3e973TKdRnNMn0UfWst369OjbdJp1ub+M+pjV2Ff9ihp9Tr8COnc76dymbdQnyN12222kzwl022233VbuuOOOSW8GFYzqnD7uPrLW7danR9+m06yr/aXGMRv1vupX1Ohz+hXQueAFAAAAgJWxxgsAAABAJYIXAAAAgEoELwAAAACVCF4AAAAAKhG8AAAAAFQieAEAAACoRPACAAAAUIngBQAAAKASwQsAAABAJYIXAAAAgEoELwAAAACVCF4AAAAAKhG8AAAAAFQieAEAAACoRPACAAAAUIngBQAAAKASwQsAAABAJYIXAAAAgEoELwAAAACVCF4AAAAAKhG8AAAAAFQieAEAAACoRPACAAAAUIngBQAAAKASwQsAAABAJYIXAAAAgEoELwAAAACVCF4AAAAAKhG8AAAAAFQieAEAAACoRPACAAAAUIngBQAAAKASwQsAAABAJYIXAAAAgEoELwAAAACVCF4AAAAAKhG8AAAAAFQieAEAAACoRPACAAAAUIngBQAAAKASwQsAAABAJYIXAAAAgEoELwAAAACVCF4AAAAAKhG8AAAAAFQieAEAAACoRPACAAAAUIngBQAAAKASwQsAAABAJYIXAAAAgEoELwAAAACVCF4AAAAAKhG8AAAAAFQieAEAAACoRPACAAAAUIngBQAAAKASwQsAAABAJYIXAAAAgEoELwAAAACVCF4AAAAAKhG8AAAAAFQieAEAAACoRPACAAAAUIng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", - "text/plain": [ - "
" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "overlay_fig = GenomicFigure()\n", - "overlay_fig.scalebar()\n", - "overlay_fig.genes(\"hg38\")\n", - "overlay_fig.bigwig_overlay(\n", - " list(blueprint_bigwig_files.values()),\n", - " title=\"Blueprint plasma-cell RNA overlay\",\n", - " style=PlotStyle.FRAGMENT,\n", - " colors=[\"#FF9D1B\", \"#1E5DF8\"],\n", - " alpha=0.65,\n", - " height=0.9,\n", - " label_on_track=True,\n", - " label_box_enabled=True,\n", - " label_box_alpha=0.95,\n", - " title_height=0.5,\n", - " scale_height=0.5,\n", - " plot_scale=True,\n", - ")\n", - "\n", - "overlay_plot = overlay_fig.plot_gene(rna_gene_of_interest)\n", - "overlay_plot" - ] - }, - { - "cell_type": "markdown", - "id": "2cdb18be", - "metadata": {}, - "source": [ - "## Example 3: a real review plot with Blueprint `BigWig`, Blueprint `bigBed`, and a checked-in `BED`\n", - "\n", - "This mirrors a more realistic workflow: inspect a public signal track, compare it to the hub's peak calls, and add a checked-in BED file that captures the loci you want to revisit after review." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "eb69c741", - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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dbKHH37YVq1aVeHyR44caZdccokdfvjhdsABB9jpp59uQ4YMsYIwM92OGDHCTjvtNHc9nZd/9dVXFS7/nXfeKd2e//3vf80vadOAon79+vbss8+6/69fv94mTJhgAwcOdDvF5Zdfbpmgc+fOlpeXV6ngpWHDhnbiiSdaxvIreMnJsaEvvWht27axtq3/7Veh/UJhSlatGLPDSg75KSgstPr16tmVl15m2227reXk5tqb77xtN3a92V4eOtRatWwV8bbvjhhhT/Tvb1dcfrlde+217rLhw4dbfn6++32bbbaxKVOmuFBmzpw5dv/995feVgfEUPp7gwYNbKuttrJUieVEPvu9Mdb48ANj7i8SWOf/WPHcnydZ4yMPskwQLdzK/vRLyxn3XczLWj/bv+aWAJDKWQTp8xIfN0xVvcEStf0JvoCUUkhw6623WvPmzaNeb968eXbDDTe4sOPGG2+0mTNn2oABA6xWrVp22WWXJWRdvvvuO7fcDh06WHZ2+N6NP/zwg/36668u2GjXrp0tXrzYnnnmGXfO/9Zbb1m9evUiLl/LVGHGFVdcYY0aNbLJkye786clS5bYfffdV3o9FTI8+OCD7pxcwcsnn3zittELL7xge+21V8TlH3PMMfaf//zHhTlar2ofvOjJ33PPPUt/1zf7s2bNsnHjxqV98KITaZ0Qb7/99pZpgitBQsUcaqSK+rbEur6VDIaaNmliDwa9oOWA/faz4089xcZ98YVdfkn4ypn3Pxxp/+v3pF164UV2w/XXl16uapctttiizH6uqi4deG6++ebSvwW/FrxqLx04/+///s/8FG2mHFkX4wet/BmzYw5eSvLXm9/Wjv/Btrrs7LRsshu6zdd8U77axbPu9xlxLbt4VXKmeAeAZA4hDe7zko7H9bTt65LAYdcEX0BqKYCIFiZ4NIJk8803t0cffdTq1q3rql5UZTJ06FC74IILogYesbrlllusW7du7v8//RT+c+yJJ55o5513nmVl/TsDaatWrdx5vqpSjj322IjLV7VLsP33399yc3Nt2LBhdvfdd1vt2rXd5TqfOuGEE1zA5F1PgZBCmkGDBkVcftOmTd1PkyZNakbwEo6qQEKHYITSiatOWIOfkNdff9369u3rErTgISva4OPHj7c1a9bYjjvuaF26dHHpn+eaa65x96knXmVbGtKx++6721133WWtW7cuc5+6rZbz8ccfuyoX7TAaajR16lR7++23SytZHnjgAXvttdfs6aeftokTJ9rWW2/tysNUiuXdpy73litXX311aYVEKA1ROfTQQ61ly5ZuZ9PjOvroo906qmpCLyrtYG3btnUJ4E477VR6W1USaRt8+umnbt132GEH63zZ5Xbk4YeXGWozbcYMu63rLdZv0EB3sq9lKTDYddddIy+rVSvrfPkVZZYlk6dMsSEvvGBTpv7hPiS1ad3aru18tR24//52aeerrHWrHcoFGwOfecZGf/qJDR44yP57wfnusruCrjPirbdd9Ul+Tq6rhvnks89sxcqV1qJFC5dwhlYOBYoT21h3k002cQepwghTMH446mN75H//swvOPc9uvO66DZU5Gw8IwaGLRwmr1k/7W7i/ewmuDlQ6mHi03ymx1vClpUuXugPGQQcd5MKZzTbbrMztP/roI/e6UJmf1l/7tZ7TbbfdtvQ66/9eaPNuur9SU0uGKvh7YcyhQ8nqyEFPwgQCbrhR3WYHZfR0nkUL4nszKF6bW8k1A4DKK161JimbL+fXP9yMhaj4vWZp34qHI1SH91UAZX377bfuXFGhi0fnSqoC+f333104kYgCiopsEeYcZ5dddnH/Llu2LO771PKUE+gLbAUvCxYssL///tvlAsH0WJ988kk3LCkRIVO1CV68kMUbaqRqF5UUVVVhYaFLvlasWOFKrZo1a2ajRo1yT4zCi+BwYvr06e6Ju+mmm9zvCkz0//fee6/Mk/XGG2+4qoR77723wnBI1znjjDPswgsvtPfff9+FMTrxbdOmjRsPp7+rYqZr167u+lq/aBTyKDjSifPChQvdzqQX06RJk+yiiy5yJ+BPPfWU9ejRw41Z814M99xzjxuDp22gIOnjDz+yO+69xx57uLcdfuihpctXiNG3fz+77NLLrNEWm7shX7fddpt98MEHVmfjNM7By9qhZUsXQIUu6/fJk+zGrl1tj912s7u793BhwPQZ011IIGeceqr1GzjQ9VHxgoLi4mIXunQ68URrtvXW9mivh63HPXfb9ddcYx32be+us9WWG77duuv++919KPBpvcMO9t0P37ttqQRY/VM8gZLwjUjDPW+R8hm9qPWjHi6vvfmG1crKcusYSus+4Omn7byzz7abN6atFQ1z0rhL7VvbbbddxOuoVG7fffctU06o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q622shdffLHMsvQc3nTTTS7EQXzy8/Pt+eeft+7duydtf0zF67FWrVpuuQp9jjvuOKtTJ20OTQAAAABQJWlzdqMTrz333LP0d30bPmvWLBs3blzaBy86OW7QoIFtv/32lk523nlnu/fee8tctttuu7nKgh9++CFi0PP000/bK6+8YnfccYe7bnAFTHBYoyAlOzvbBTI6yddzWK9evTLPo+ikPTc3N+HBUk3w6aefWlFRkR1xxBFx37Yq+2MqXo/HH3+8Pf744/bNN9/YkUce6ct9AAAAAECNHGoUiapAdNIZjU4IX3311TKXqVJDl4cObXjkkUfshBNOsIMOOsguvvhiFz6EG6rz0Ucf2emnn26HHHKIu2zu3Lnl7lOVI6oK0fJ0whhuaIc3JGT69On2f//3f254zplnnumWH3yfEydOdCeb3tAOfevvl80339z9W1hYGPbvuu8XXnjBbr/9dje8JFi4Cpn//Oc/LlSJVlnxySef2KabblpmOJOqbx544IHS7aztMmjQICsoKChzWw1Bee2119y66HnT9tZwqZycyM31vOfh22+/df9qqIye78mTJ5e77ocfj7ELLulshxx5gnU67Rx7+tnnrbi4OGg9V9iDDz9mp599kR165Il21rmX2KBnh5Zbz1ALFy50j03Pu4I50T6j6iGtz7HHHms33HCDu1402lcUuoSrAJk/f76rXtL2O/XUU92wr3DbIdhvv/3mhohpHc4//3z3GtDvum5lXo/aDnreTjnlFPf86HkKN2xuxIgRbh21rtdff71b91AKL/UaCX59AAAAAECmS5uKF/FO6ryhDfp2/YorrqjychUyaLjNihUr3Mlus2bNbNSoUXbzzTe7ao2ddtqp9LoKSdQbRUNjvOoP/f+9995z1RweDdVRRYAqSioKh3QdnXDrBFf9UhQ47L777tamTRtXVaK/66TT68+i9YtGwz0qus/Q8EI///zzjztJ1rCjo446qtz1NKTlueeec31czjvvvJiWrRN5ra+ClXC0nnoedX8avuJRfxKFQLfccos1atTI5s2bZ0OGDHGBzP333196PVVADB8+3G27Aw880IU8CqnWrVtnm20WeZYEPdePPvqoC7a0fPUn6dKliwsAmjZt6q4z7I137KmnB9sF551tXW+6zubMnWfPDHneikuK7aYbNgydWp2dbY0bN7Jbbrre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", - "text/plain": [ - "
" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "review_fig = GenomicFigure()\n", - "review_fig.scalebar()\n", - "review_fig.genes(\"hg38\")\n", - "review_fig.bigwig(\n", - " blueprint_monocyte_h3k27ac_signal,\n", - " title=\"Blueprint monocyte H3K27ac\",\n", - " style=PlotStyle.FRAGMENT,\n", - " height=0.6,\n", - " color=\"#d9485f\",\n", - " label_on_track=True,\n", - " label_box_enabled=True,\n", - " label_box_alpha=0.95,\n", - " title_height=0.5,\n", - " scale_height=0.5,\n", - " plot_scale=True,\n", - ")\n", - "review_fig.bed(\n", - " blueprint_monocyte_h3k27ac_peaks,\n", - " title=\"Blueprint H3K27ac peaks (bigBed)\",\n", - " color=\"#f59e0b\",\n", - " draw_edges=False,\n", - " height=0.42,\n", - " label_on_track=True,\n", - " label_box_enabled=True,\n", - " label_box_alpha=0.95,\n", - " title_height=0.7,\n", - " show_labels=False,\n", - ")\n", - "review_fig.bed(\n", - " str(blueprint_monocyte_candidate_bed),\n", - " title=\"Curated follow-up peaks (BED)\",\n", - " color=\"#0f766e\",\n", - " draw_edges=True,\n", - " show_labels=True,\n", - " label_field=\"name\",\n", - " font_size=7,\n", - " height=0.5,\n", - " label_on_track=True,\n", - " label_box_enabled=True,\n", - " label_box_alpha=0.95,\n", - " title_height=0.7,\n", - ")\n", - "\n", - "review_plot = review_fig.plot_gene(monocyte_gene_of_interest)\n", - "review_plot" - ] - }, - { - "cell_type": "markdown", - "id": "ca908353", - "metadata": {}, - "source": [ - "## Example 4: start from a public UCSC hub and keep the notebook interactive\n", - "\n", - "`GenomicFigure.from_ucsc_hub()` can flatten supported tracks from a UCSC-style hub directly into a PlotNado figure. This Blueprint hub expands to a larger review figure than you usually want on first render, so the example below keeps just the H3K27ac peak/signal pair and the stranded RNA tracks visible, then uses `track_visibility_widget()` to turn the rest back on as needed.\n", - "\n", - "The widget is notebook-only and requires `ipywidgets`, either via `pip install \"plotnado[notebook]\"` or `uv pip install ipywidgets`." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "b733ba8b", - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "020fb407cb6941699f3d22831ea02815", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "VBox(children=(HTML(value='Track visibility'), VBox(children=(Checkbox(value=True, descriptio…" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from plotnado import GenomicFigure\n", - "\n", - "blueprint_hub_url = \"https://ftp.ebi.ac.uk/pub/databases/blueprint/releases/current_release/homo_sapiens/hub/hub.txt\"\n", - "lyz_region = \"chr12:6908053-6997143\"\n", - "\n", - "hub_fig = GenomicFigure.from_ucsc_hub(\n", - " blueprint_hub_url,\n", - " genome=\"hg38\",\n", - " include_hidden=False,\n", - ")\n", - "\n", - "hub_keep_tokens = (\n", - " \"H3K27ac MASC2_peak\",\n", - " \"H3K27ac MACS2_wiggler\",\n", - " \"RNA-Seq RNAMinus \",\n", - " \"RNA-Seq RNAPlus \",\n", - ")\n", - "\n", - "for track in getattr(hub_fig, \"tracks\", []):\n", - " title = getattr(track, \"title\", \"\") or \"\"\n", - " if track.__class__.__name__ in {\"ScaleBar\", \"GenomicAxis\"}:\n", - " continue\n", - " if not any(token in title for token in hub_keep_tokens):\n", - " track.height = 0.0\n", - "\n", - "hub_widget = hub_fig.track_visibility_widget(lyz_region)\n", - "hub_widget" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/worked_examples.qmd b/docs/worked_examples.qmd new file mode 100644 index 0000000..603fd7f --- /dev/null +++ b/docs/worked_examples.qmd @@ -0,0 +1,135 @@ +--- +title: Worked Examples +jupyter: python3 +execute: + echo: true + warning: false + message: false +--- + +These examples use deterministic in-memory data so the documentation build exercises the PlotNado API and produces real plots without depending on remote servers. + +## Setup + +```{python} +import numpy as np +import pandas as pd + +from plotnado import GenomicFigure +``` + +```{python} +#| echo: false +REGION = "chr1:1,010,000-1,080,000" + + +def signal(phase=0.0, scale=1.0, step=1_000): + bins = np.arange(1_000_000, 1_100_000, step) + values = scale * (4 + 2 * np.sin(np.linspace(phase, 6 + phase, bins.shape[0]))) + return pd.DataFrame( + {"chrom": "chr1", "start": bins, "end": bins + step, "value": values} + ) + + +candidate_peaks = pd.DataFrame( + { + "chrom": ["chr1"] * 4, + "start": [1_012_000, 1_026_000, 1_041_000, 1_061_000], + "end": [1_018_000, 1_034_000, 1_050_000, 1_071_000], + "name": ["candidate_1", "candidate_2", "candidate_3", "candidate_4"], + } +) + +links = pd.DataFrame( + { + "chrom1": ["chr1", "chr1", "chr1"], + "start1": [1_010_000, 1_022_000, 1_042_000], + "end1": [1_012_000, 1_024_000, 1_045_000], + "chrom2": ["chr1", "chr1", "chr1"], + "start2": [1_035_000, 1_054_000, 1_072_000], + "end2": [1_037_000, 1_056_000, 1_074_000], + "score": [2.2, 6.5, 9.8], + } +) +``` + +## Example 1: stacked signal tracks + +This mirrors a common review workflow: compare two signal tracks in separate panels and keep the y-axis scaling synchronized. + +```{python} +stacked_fig = GenomicFigure(track_height=1.1) +stacked_fig.scalebar() +stacked_fig.axis() +stacked_fig.genes("hg38", title="Genes") +stacked_fig.bigwig( + signal(0.0, scale=2.0), + title="Sample A signal", + style="fill", + color="#1f77b4", + autoscale_group="review", +) +stacked_fig.bigwig( + signal(1.3, scale=7.0), + title="Sample B signal", + style="fill", + color="#d62728", + autoscale_group="review", +) + +stacked_fig.plot(REGION) +``` + +## Example 2: overlay with a shared axis + +Use an overlay when the series should be compared directly in one panel. + +```{python} +overlay_fig = GenomicFigure(track_height=1.2) +overlay_fig.scalebar() +overlay_fig.axis() +overlay_fig.overlay( + [signal(0.0, scale=2.0), signal(1.3, scale=7.0)], + title="Signal overlay", + colors=["#1f77b4", "#d62728"], + alpha=0.6, +) + +overlay_fig.plot(REGION) +``` + +## Example 3: review plot with signals, intervals, and links + +Combine quantitative signal, candidate intervals, interaction arcs, and a highlighted region in one figure. + +```{python} +review_fig = GenomicFigure(track_height=1.1) +review_fig.autoscale(True) +review_fig.highlight("chr1:1,032,000-1,046,000") +review_fig.highlight_style(color="#ffdd57", alpha=0.22) +review_fig.scalebar() +review_fig.axis() +review_fig.bigwig( + signal(0.4, scale=3.0), + title="Review signal", + style="fill", + color="#0f766e", + autoscale_group="review", +) +review_fig.bed( + candidate_peaks, + title="Candidate peaks", + color="#f59e0b", + display="expanded", + show_labels=True, +) +review_fig.links( + links, + title="Candidate links", + color_by_score=True, + cmap="viridis", + alpha=0.8, +) + +review_fig.plot(REGION) +``` diff --git a/index.qmd b/index.qmd new file mode 100644 index 0000000..2431a8f --- /dev/null +++ b/index.qmd @@ -0,0 +1,199 @@ +--- +title: PlotNado +page-layout: full +title-block-style: none +--- + +::: {.pn-hero} +::: {.pn-hero__copy} +

Genome browser figures without the browser

+ +# Publication‑ready genomic tracks in Python. + +

PlotNado composes BigWig signals, genes, peaks, interactions, and overlays into clean genome-browser style figures with a high-level API and a file-driven CLI.

+ +::: {.pn-actions} +Start with Quick Start +Browse track catalog +Explore the CLI +::: + +::: {.pn-stats} +::: {.pn-stat} +**Python API** + +Chain one track method at a time. +::: + +::: {.pn-stat} +**CLI + YAML** + +Infer, edit, validate, and render templates. +::: + +::: {.pn-stat} +**Track-first design** + +Signals, annotations, overlays, and matrices. +::: +::: +::: + +::: {.pn-hero__visual} +![PlotNado example figure](docs/images/examples/basic_figure.png) + +

A composed PlotNado figure with signal, annotation, and structural tracks.

+::: +::: + +::: {.pn-section} +## Pick your entry point + +Most readers need one of three routes: get a figure rendered quickly, understand which track type to use, or tune the figure until it is publication-ready. +::: + +::: {.pn-card-grid} +::: {.pn-card} +

Start Here

+ +### Install and render your first figure + +Use the Quick Start when you want a working figure with minimal setup and clear defaults. + +::: {.pn-linklist} +[Installation](docs/installation.qmd) +[Quick Start](docs/quickstart.qmd) +[Track construction patterns](docs/quickstart_tracks.qmd) +::: +::: + +::: {.pn-card} +

Build With Confidence

+ +### Choose the right track and input model + +Learn which track to reach for, what each one expects, and where to find concrete examples. + +::: {.pn-linklist} +[Track Catalog](docs/track_catalog.qmd) +[Data Inputs](docs/data_inputs.qmd) +[Living Gallery](docs/example_coverage.qmd) +::: +::: + +::: {.pn-card} +

Tune The Result

+ +### Dial in styling, scaling, and layout + +Use the aesthetics and recipes guides when the figure works but does not yet communicate clearly. + +::: {.pn-linklist} +[Aesthetics](docs/aesthetics.qmd) +[Recipes](docs/recipes.qmd) +[Troubleshooting](docs/troubleshooting.qmd) +::: +::: +::: + +::: {.pn-section} +## Two workflows, one rendering model + +PlotNado offers a fluent Python API for notebooks and pipelines, plus a CLI that turns genomic inputs into editable YAML templates. +::: + +::: {.pn-card-grid} +::: {.pn-card} +

Python API

+ +### Compose tracks directly in code + +Add tracks one method at a time and save the result in a single region-specific render step. + +```python +from plotnado import GenomicFigure + +fig = GenomicFigure(theme="publication") +fig.scalebar() +fig.axis() +fig.genes("hg38") +fig.bigwig("signal.bw", title="ChIP signal", style="fill") +fig.save("output.png", region="chr1:1,010,000-1,080,000") +``` + +

Output

+ +![Output from the Python API composition example](docs/images/examples/basic_figure.png){.pn-code-output} +::: + +::: {.pn-card} +

CLI + YAML

+ +### Infer a template, then edit and render it + +This route is useful when the inputs already exist on disk and you want a reproducible, file-driven workflow. + +```bash +plotnado init *.bw peaks.narrowpeak --auto --output template.yaml +plotnado validate template.yaml +plotnado plot template.yaml --region chr1:1,000,000-1,100,000 --output out.png +``` + +

Output

+ +![Output from a rendered CLI template](docs/images/examples/quickstart_first_plot.png){.pn-code-output} +::: + +::: {.pn-card} +

Need Options Fast?

+ +### Inspect the runtime metadata + +Every track exposes discoverable options, so you do not have to guess field names or dig through source first. + +```python +from plotnado import GenomicFigure + +GenomicFigure.track_options("overlay") +GenomicFigure.track_options_markdown("bigwig") +``` +::: +::: + +::: {.pn-section} +## Rendered examples + +The docs include plotted outputs, not just code snippets, so you can see what each configuration actually produces. +::: + +::: {.pn-showcase} +::: {.pn-showcase__item} +![Quick start output](docs/images/quickstart.png) + +### First figure + +Start from a minimal stack: scale bar, axis, genes, and one signal track. +::: + +::: {.pn-showcase__item} +![Overlay autoscale output](docs/images/aesthetics/overlay_autoscale.png) + +### Overlay + autoscale + +Overlay multiple signals in one panel while keeping y-scaling explicit and readable. +::: + +::: {.pn-showcase__item} +![Theme and labels output](docs/images/examples/recipe_theme_labels.png) + +### Theme-driven polish + +Refine color, labels, and spacing once the track composition is correct. +::: +::: + +::: {.pn-section} +## Where to go next + +

If the figure is blank or scaling looks wrong, go straight to Troubleshooting. If you are deciding between track types, start with the Track Catalog. If you want exact parameters, jump to Reference and API Reference.

+::: diff --git a/mkdocs.yaml b/mkdocs.yaml deleted file mode 100644 index a04d15f..0000000 --- a/mkdocs.yaml +++ /dev/null @@ -1,105 +0,0 @@ -site_name: PlotNado Documentation -site_description: Publication-ready genome browser figures in Python. -nav: - - Home: index.md - - Getting Started: - - Installation: installation.md - - Quick Start: quickstart.md - - Track Construction: quickstart_tracks.md - - Guides: - - Track Catalog: track_catalog.md - - Data Inputs: data_inputs.md - - Figure Workflows: figure_workflows.md - - Aesthetics: aesthetics.md - - Recipes: recipes.md - - Best Practices: best_practices.md - - Reference: - - Overview: reference.md - - API Reference: api_reference.md - - Track Aliases: track_aliases.md - - Example Coverage: example_coverage.md - - CLI: - - CLI Guide: cli.md - - Examples: - - Worked Examples: worked_examples.ipynb - - Basic Notebook: basic_example.ipynb - - Help: - - FAQ: faq.md - - Troubleshooting: troubleshooting.md - - Meta: - - Changelog: changelog.md - -theme: - name: material - logo: images/Logo.jpeg - favicon: images/Logo.jpeg - font: - text: IBM Plex Sans - code: IBM Plex Mono - palette: - - scheme: default - primary: custom - accent: custom - toggle: - icon: material/weather-night - name: Switch to dark mode - - scheme: slate - primary: custom - accent: custom - toggle: - icon: material/weather-sunny - name: Switch to light mode - features: - - navigation.tabs - - navigation.sections - - navigation.expand - - navigation.top - - toc.follow - - search.highlight - - search.suggest - - content.code.copy - - content.action.edit - icon: - repo: fontawesome/brands/github - edit: material/pencil - view: material/eye -markdown_extensions: - - toc: - permalink: true - toc_depth: 4 - - attr_list - - md_in_html - - pymdownx.highlight: - use_pygments: true - line_spans: __span - pygments_lang_class: true - - pymdownx.inlinehilite - - pymdownx.snippets - - pymdownx.superfences - - pymdownx.details - - mkdocs-click - - admonition - - codehilite: - guess_lang: false -extra_css: - - stylesheets/extra.css - -plugins: - - search - - mkdocs-jupyter: - include: - - "*.ipynb" - execute: false - -hooks: - - scripts/mkdocs_hooks.py - -repo_url: https://github.com/alsmith151/plotnado -repo_name: alsmith151/plotnado -edit_uri: edit/main/docs/ - -validation: - nav: - omitted_files: ignore -extra: - generator: false diff --git a/pyproject.toml b/pyproject.toml index 2dd777c..bef004a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,16 +32,12 @@ toml = ["tomli; python_version < '3.11'", "tomli-w"] notebook = ["ipywidgets>=8"] dev = ["pytest", "pytest-cov", "pytest-mock", "pytest-assume", "pre-commit", "ipywidgets>=8"] docs = [ - "mkdocs==1.6.1", - "mkdocs-material>=9.5.18", - "mkdocstrings>=1.0.3", - "mkdocs-literate-nav>=0.6.2", - "mkdocs-gen-files>=0.6.0", - "mkdocs-jupyter>=0.25.1", - "mkdocs-click>=0.9.0", - "pymdown-extensions>=10.21", + "jupyter>=1.1.1", + "ipykernel>=6.29.0", "tomli-w", - "mkdocstrings[python]>=1.0.3", + "cooler", + "capcruncher", + "quantnado", ] all = [ "cooler", @@ -55,15 +51,8 @@ all = [ "pytest-mock", "pytest-assume", "pre-commit", - "mkdocs==1.6.1", - "mkdocs-material>=9.5.18", - "mkdocstrings>=1.0.3", - "mkdocs-literate-nav>=0.6.2", - "mkdocs-gen-files>=0.6.0", - "mkdocs-jupyter>=0.25.1", - "mkdocs-click>=0.9.0", - "pymdown-extensions>=10.21", - "mkdocstrings[python]>=1.0.3", + "jupyter>=1.1.1", + "ipykernel>=6.29.0", ] diff --git a/scripts/gen_ref_pages.py b/scripts/gen_ref_pages.py deleted file mode 100644 index 3b33459..0000000 --- a/scripts/gen_ref_pages.py +++ /dev/null @@ -1,126 +0,0 @@ -"""Generate code and track-option reference pages.""" - -from pathlib import Path - -import mkdocs_gen_files - -from plotnado.figure import GenomicFigure -from plotnado.tracks.registry import registry - -nav = mkdocs_gen_files.Nav() - - -def _markdown_cell(value: object) -> str: - return str(value).replace("|", "\\|") - -for path in sorted(Path("plotnado").rglob("*.py")): - - module_path = path.relative_to(".").with_suffix("") - doc_path = module_path.with_suffix(".md") - full_doc_path = Path("reference", doc_path) - - parts = list(module_path.parts) - - if module_path.stem == "__init__": - continue - elif parts[-1] == "__init__": - parts = parts[:-1] - doc_path = doc_path.with_name("index.md") - full_doc_path = full_doc_path.with_name("index.md") - elif parts[-1] == "__main__": - continue - - nav[parts] = doc_path.as_posix() - - with mkdocs_gen_files.open(full_doc_path, "w") as fd: - identifier = ".".join(parts) - print("::: " + identifier, file=fd) - - mkdocs_gen_files.set_edit_path(full_doc_path, path) - -with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file: - nav_file.writelines(nav.build_literate_nav()) - - -def _format_section_rows(options: dict[str, dict], section: str) -> list[str]: - rows = [ - f"### {section.title()} fields", - "", - "| Name | Type | Default | Choices | Required | Description |", - "|---|---|---|---|---|---|", - ] - section_data = options.get(section, {}) - for field_name, meta in sorted(section_data.items()): - description = _markdown_cell(meta.get("description") or "—") - choices = meta.get("choices") or [] - choices_text = _markdown_cell(", ".join(str(choice) for choice in choices) if choices else "—") - type_cell = _markdown_cell(meta["type"]) - default_cell = _markdown_cell(meta["default"]) - required_cell = _markdown_cell(meta["required"]) - rows.append( - f"| {field_name} | {type_cell} | {default_cell} | {choices_text} | {required_cell} | {description} |" - ) - rows.append("") - return rows - - -def _generate_aesthetics_reference() -> None: - alias_map = GenomicFigure.available_track_aliases() - class_by_aliases: dict[str, list[str]] = {} - class_by_name: dict[str, type] = {} - for alias, class_name in alias_map.items(): - class_by_aliases.setdefault(class_name, []).append(alias) - track_cls = registry.get(alias).cls - class_by_name[class_name] = track_cls - - lines = [ - "# Aesthetics Reference", - "", - "This page is auto-generated from runtime model metadata (`Track.options()`).", - "", - "Track styles are configured through nested `aesthetics=...` models.", - "When using `GenomicFigure` helper methods (for example `gf.bigwig(...)`),", - "aesthetics and label kwargs can also be passed directly and are routed automatically.", - "", - "Shorthand example:", - "", - "```python", - "gf.bigwig(", - " \"signal.bw\",", - " title=\"Sample\",", - " title_color=\"black\",", - " style=\"std\",", - " color=\"#1f77b4\",", - " alpha=0.8,", - ")", - "```", - "", - "Equivalent explicit form:", - "", - "```python", - "gf.bigwig(", - " \"signal.bw\",", - " aesthetics={\"style\": \"std\", \"color\": \"#1f77b4\", \"alpha\": 0.8},", - " label={\"title\": \"Sample\", \"title_color\": \"black\"},", - ")", - "```", - "", - ] - - for class_name in sorted(class_by_name): - track_cls = class_by_name[class_name] - aliases = ", ".join(f"`{alias}`" for alias in sorted(class_by_aliases[class_name])) - lines.append(f"## {class_name}") - lines.append("") - lines.append(f"Aliases: {aliases}") - lines.append("") - options = track_cls.options() - lines.extend(_format_section_rows(options, "track")) - lines.extend(_format_section_rows(options, "aesthetics")) - lines.extend(_format_section_rows(options, "label")) - - with mkdocs_gen_files.open("aesthetics_reference.md", "w") as handle: - handle.write("\n".join(lines)) - - -_generate_aesthetics_reference() diff --git a/scripts/mkdocs_hooks.py b/scripts/mkdocs_hooks.py deleted file mode 100644 index 4d88cee..0000000 --- a/scripts/mkdocs_hooks.py +++ /dev/null @@ -1,56 +0,0 @@ -"""MkDocs hooks for build-time warning hygiene.""" - -from __future__ import annotations - -import logging -import sys -import warnings - - -def on_startup(**_: object) -> None: - warnings.filterwarnings( - "ignore", - message=r".*parseString.*deprecated.*", - ) - warnings.filterwarnings( - "ignore", - message=r".*resetCache.*deprecated.*", - ) - warnings.filterwarnings( - "ignore", - message=r".*enablePackrat.*deprecated.*", - ) - - class _SuppressExternalHtmlNoise(logging.Filter): - def filter(self, record: logging.LogRecord) -> bool: - message = record.getMessage() - if " Div at " in message and "unclosed" in message and "closing implicitly" in message: - return False - return True - - logging.getLogger().addFilter(_SuppressExternalHtmlNoise()) - - class _FilteredStderr: - def __init__(self, wrapped): - self._wrapped = wrapped - - def write(self, text: str) -> int: - if ( - " Div at " in text - and "unclosed" in text - and "closing implicitly" in text - ): - return len(text) - return self._wrapped.write(text) - - def flush(self) -> None: - self._wrapped.flush() - - def isatty(self) -> bool: - return self._wrapped.isatty() - - @property - def encoding(self): - return self._wrapped.encoding - - sys.stderr = _FilteredStderr(sys.stderr) diff --git a/uv.lock b/uv.lock index e977bd2..5e266e5 100644 --- a/uv.lock +++ b/uv.lock @@ -70,6 +70,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" }, ] +[[package]] +name = "anyio" +version = "4.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353 }, +] + [[package]] name = "appnope" version = "0.1.4" @@ -110,6 +123,62 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797, upload-time = "2025-10-18T17:46:45.663Z" }, ] +[[package]] +name = "argon2-cffi" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "argon2-cffi-bindings" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/89/ce5af8a7d472a67cc819d5d998aa8c82c5d860608c4db9f46f1162d7dab9/argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1", size = 45706 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/d3/a8b22fa575b297cd6e3e3b0155c7e25db170edf1c74783d6a31a2490b8d9/argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741", size = 14657 }, +] + +[[package]] +name = "argon2-cffi-bindings" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/2d/db8af0df73c1cf454f71b2bbe5e356b8c1f8041c979f505b3d3186e520a9/argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d", size = 1783441 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/97/3c0a35f46e52108d4707c44b95cfe2afcafc50800b5450c197454569b776/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f", size = 54393 }, + { url = "https://files.pythonhosted.org/packages/9d/f4/98bbd6ee89febd4f212696f13c03ca302b8552e7dbf9c8efa11ea4a388c3/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b", size = 29328 }, + { url = "https://files.pythonhosted.org/packages/43/24/90a01c0ef12ac91a6be05969f29944643bc1e5e461155ae6559befa8f00b/argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a", size = 31269 }, + { url = "https://files.pythonhosted.org/packages/d4/d3/942aa10782b2697eee7af5e12eeff5ebb325ccfb86dd8abda54174e377e4/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44", size = 86558 }, + { url = "https://files.pythonhosted.org/packages/0d/82/b484f702fec5536e71836fc2dbc8c5267b3f6e78d2d539b4eaa6f0db8bf8/argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb", size = 92364 }, + { url = "https://files.pythonhosted.org/packages/c9/c1/a606ff83b3f1735f3759ad0f2cd9e038a0ad11a3de3b6c673aa41c24bb7b/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92", size = 85637 }, + { url = "https://files.pythonhosted.org/packages/44/b4/678503f12aceb0262f84fa201f6027ed77d71c5019ae03b399b97caa2f19/argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85", size = 91934 }, + { url = "https://files.pythonhosted.org/packages/f0/c7/f36bd08ef9bd9f0a9cff9428406651f5937ce27b6c5b07b92d41f91ae541/argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f", size = 28158 }, + { url = "https://files.pythonhosted.org/packages/b3/80/0106a7448abb24a2c467bf7d527fe5413b7fdfa4ad6d6a96a43a62ef3988/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6", size = 32597 }, + { url = "https://files.pythonhosted.org/packages/05/b8/d663c9caea07e9180b2cb662772865230715cbd573ba3b5e81793d580316/argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623", size = 28231 }, + { url = "https://files.pythonhosted.org/packages/1d/57/96b8b9f93166147826da5f90376e784a10582dd39a393c99bb62cfcf52f0/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500", size = 54121 }, + { url = "https://files.pythonhosted.org/packages/0a/08/a9bebdb2e0e602dde230bdde8021b29f71f7841bd54801bcfd514acb5dcf/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44", size = 29177 }, + { url = "https://files.pythonhosted.org/packages/b6/02/d297943bcacf05e4f2a94ab6f462831dc20158614e5d067c35d4e63b9acb/argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0", size = 31090 }, + { url = "https://files.pythonhosted.org/packages/c1/93/44365f3d75053e53893ec6d733e4a5e3147502663554b4d864587c7828a7/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6", size = 81246 }, + { url = "https://files.pythonhosted.org/packages/09/52/94108adfdd6e2ddf58be64f959a0b9c7d4ef2fa71086c38356d22dc501ea/argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a", size = 87126 }, + { url = "https://files.pythonhosted.org/packages/72/70/7a2993a12b0ffa2a9271259b79cc616e2389ed1a4d93842fac5a1f923ffd/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d", size = 80343 }, + { url = "https://files.pythonhosted.org/packages/78/9a/4e5157d893ffc712b74dbd868c7f62365618266982b64accab26bab01edc/argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99", size = 86777 }, + { url = "https://files.pythonhosted.org/packages/74/cd/15777dfde1c29d96de7f18edf4cc94c385646852e7c7b0320aa91ccca583/argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2", size = 27180 }, + { url = "https://files.pythonhosted.org/packages/e2/c6/a759ece8f1829d1f162261226fbfd2c6832b3ff7657384045286d2afa384/argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98", size = 31715 }, + { url = "https://files.pythonhosted.org/packages/42/b9/f8d6fa329ab25128b7e98fd83a3cb34d9db5b059a9847eddb840a0af45dd/argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94", size = 27149 }, +] + +[[package]] +name = "arrow" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/33/032cdc44182491aa708d06a68b62434140d8c50820a087fac7af37703357/arrow-1.4.0.tar.gz", hash = "sha256:ed0cc050e98001b8779e84d461b0098c4ac597e88704a655582b21d116e526d7", size = 152931 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/c9/d7977eaacb9df673210491da99e6a247e93df98c715fc43fd136ce1d3d33/arrow-1.4.0-py3-none-any.whl", hash = "sha256:749f0769958ebdc79c173ff0b0670d59051a535fa26e8eba02953dc19eb43205", size = 68797 }, +] + [[package]] name = "asciitree" version = "0.3.3" @@ -125,6 +194,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, ] +[[package]] +name = "async-lru" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e8/1f/989ecfef8e64109a489fff357450cb73fa73a865a92bd8c272170a6922c2/async_lru-2.3.0.tar.gz", hash = "sha256:89bdb258a0140d7313cf8f4031d816a042202faa61d0ab310a0a538baa1c24b6", size = 16332 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/e2/c2e3abf398f80732e58b03be77bde9022550d221dd8781bf586bd4d97cc1/async_lru-2.3.0-py3-none-any.whl", hash = "sha256:eea27b01841909316f2cc739807acea1c623df2be8c5cfad7583286397bb8315", size = 8403 }, +] + [[package]] name = "attrs" version = "26.1.0" @@ -144,17 +222,84 @@ wheels = [ ] [[package]] -name = "backrefs" -version = "6.2" +name = "bamnado" +version = "0.5.8" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/a6/e325ec73b638d3ede4421b5445d4a0b8b219481826cc079d510100af356c/backrefs-6.2.tar.gz", hash = "sha256:f44ff4d48808b243b6c0cdc6231e22195c32f77046018141556c66f8bab72a49", size = 7012303, upload-time = "2026-02-16T19:10:15.828Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/39/3765df263e08a4df37f4f43cb5aa3c6c17a4bdd42ecfe841e04c26037171/backrefs-6.2-py310-none-any.whl", hash = "sha256:0fdc7b012420b6b144410342caeb8adc54c6866cf12064abc9bb211302e496f8", size = 381075, upload-time = "2026-02-16T19:10:04.322Z" }, - { url = "https://files.pythonhosted.org/packages/0f/f0/35240571e1b67ffb19dafb29ab34150b6f59f93f717b041082cdb1bfceb1/backrefs-6.2-py311-none-any.whl", hash = "sha256:08aa7fae530c6b2361d7bdcbda1a7c454e330cc9dbcd03f5c23205e430e5c3be", size = 392874, upload-time = "2026-02-16T19:10:06.314Z" }, - { url = "https://files.pythonhosted.org/packages/e3/63/77e8c9745b4d227cce9f5e0a6f68041278c5f9b18588b35905f5f19c1beb/backrefs-6.2-py312-none-any.whl", hash = "sha256:c3f4b9cb2af8cda0d87ab4f57800b57b95428488477be164dd2b47be54db0c90", size = 398787, upload-time = "2026-02-16T19:10:08.274Z" }, - { url = "https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl", hash = "sha256:12df81596ab511f783b7d87c043ce26bc5b0288cf3bb03610fe76b8189282b2b", size = 400747, upload-time = "2026-02-16T19:10:09.791Z" }, - { url = "https://files.pythonhosted.org/packages/af/75/be12ba31a6eb20dccef2320cd8ccb3f7d9013b68ba4c70156259fee9e409/backrefs-6.2-py314-none-any.whl", hash = "sha256:e5f805ae09819caa1aa0623b4a83790e7028604aa2b8c73ba602c4454e665de7", size = 412602, upload-time = "2026-02-16T19:10:12.317Z" }, - { url = "https://files.pythonhosted.org/packages/21/f8/d02f650c47d05034dcd6f9c8cf94f39598b7a89c00ecda0ecb2911bc27e9/backrefs-6.2-py39-none-any.whl", hash = "sha256:664e33cd88c6840b7625b826ecf2555f32d491800900f5a541f772c485f7cda7", size = 381077, upload-time = "2026-02-16T19:10:13.74Z" }, +dependencies = [ + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/85/1ca66c652498638fed9edf1f53cb5f58f8ad5a0cd7d285545cf2752c8e95/bamnado-0.5.8.tar.gz", hash = "sha256:d951c9c5c1cb6aecda72839841e25eb0c76b29504e4a3dc7c72fed01d535aa18", size = 98047 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/a5/85da2ce0814ade22bc8e7fde0ea3acee28839697cc3457ecc53820091cbf/bamnado-0.5.8-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:830bfed5dba5fa4ef0d86b6cf90edec8f11706b9989cee06339e728b9ee4acca", size = 755526 }, + { url = "https://files.pythonhosted.org/packages/19/6b/c583b80efec55de7fef3ff4d1e6db868fb8a2bd5e737165a7a0b6c89536d/bamnado-0.5.8-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a379b0c19ec9daa31dab920f87679df2cc4be1ce2c3e08e728d90246a9349e0", size = 710084 }, + { url = "https://files.pythonhosted.org/packages/2b/a4/935262711deb646f8454a01fc7a86a17abe2599003a4199b3aa44965ab21/bamnado-0.5.8-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21bae01516adc6f694530677740510a4678c4923f98059d564869b550601131a", size = 2287257 }, + { url = "https://files.pythonhosted.org/packages/dd/ae/acd15a8e4e4e46a4854fb6bcb6d4841f834d2971620eea9b0bcd2d007abd/bamnado-0.5.8-cp310-abi3-win_amd64.whl", hash = "sha256:e6f53069cf031b2b2e6a7166dd1a9b8eabbf39af5f348d7afd9108b2c7828aa6", size = 900961 }, +] + +[[package]] +name = "bcrypt" +version = "5.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/36/3329e2518d70ad8e2e5817d5a4cac6bba05a47767ec416c7d020a965f408/bcrypt-5.0.0.tar.gz", hash = "sha256:f748f7c2d6fd375cc93d3fba7ef4a9e3a092421b8dbf34d8d4dc06be9492dfdd", size = 25386 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/85/3e65e01985fddf25b64ca67275bb5bdb4040bd1a53b66d355c6c37c8a680/bcrypt-5.0.0-cp313-cp313t-macosx_10_12_universal2.whl", hash = "sha256:f3c08197f3039bec79cee59a606d62b96b16669cff3949f21e74796b6e3cd2be", size = 481806 }, + { url = "https://files.pythonhosted.org/packages/44/dc/01eb79f12b177017a726cbf78330eb0eb442fae0e7b3dfd84ea2849552f3/bcrypt-5.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:200af71bc25f22006f4069060c88ed36f8aa4ff7f53e67ff04d2ab3f1e79a5b2", size = 268626 }, + { url = "https://files.pythonhosted.org/packages/8c/cf/e82388ad5959c40d6afd94fb4743cc077129d45b952d46bdc3180310e2df/bcrypt-5.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:baade0a5657654c2984468efb7d6c110db87ea63ef5a4b54732e7e337253e44f", size = 271853 }, + { url = "https://files.pythonhosted.org/packages/ec/86/7134b9dae7cf0efa85671651341f6afa695857fae172615e960fb6a466fa/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c58b56cdfb03202b3bcc9fd8daee8e8e9b6d7e3163aa97c631dfcfcc24d36c86", size = 269793 }, + { url = "https://files.pythonhosted.org/packages/cc/82/6296688ac1b9e503d034e7d0614d56e80c5d1a08402ff856a4549cb59207/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4bfd2a34de661f34d0bda43c3e4e79df586e4716ef401fe31ea39d69d581ef23", size = 289930 }, + { url = "https://files.pythonhosted.org/packages/d1/18/884a44aa47f2a3b88dd09bc05a1e40b57878ecd111d17e5bba6f09f8bb77/bcrypt-5.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:ed2e1365e31fc73f1825fa830f1c8f8917ca1b3ca6185773b349c20fd606cec2", size = 272194 }, + { url = "https://files.pythonhosted.org/packages/0e/8f/371a3ab33c6982070b674f1788e05b656cfbf5685894acbfef0c65483a59/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_aarch64.whl", hash = "sha256:83e787d7a84dbbfba6f250dd7a5efd689e935f03dd83b0f919d39349e1f23f83", size = 269381 }, + { url = "https://files.pythonhosted.org/packages/b1/34/7e4e6abb7a8778db6422e88b1f06eb07c47682313997ee8a8f9352e5a6f1/bcrypt-5.0.0-cp313-cp313t-manylinux_2_34_x86_64.whl", hash = "sha256:137c5156524328a24b9fac1cb5db0ba618bc97d11970b39184c1d87dc4bf1746", size = 271750 }, + { url = "https://files.pythonhosted.org/packages/c0/1b/54f416be2499bd72123c70d98d36c6cd61a4e33d9b89562c22481c81bb30/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:38cac74101777a6a7d3b3e3cfefa57089b5ada650dce2baf0cbdd9d65db22a9e", size = 303757 }, + { url = "https://files.pythonhosted.org/packages/13/62/062c24c7bcf9d2826a1a843d0d605c65a755bc98002923d01fd61270705a/bcrypt-5.0.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:d8d65b564ec849643d9f7ea05c6d9f0cd7ca23bdd4ac0c2dbef1104ab504543d", size = 306740 }, + { url = "https://files.pythonhosted.org/packages/d5/c8/1fdbfc8c0f20875b6b4020f3c7dc447b8de60aa0be5faaf009d24242aec9/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:741449132f64b3524e95cd30e5cd3343006ce146088f074f31ab26b94e6c75ba", size = 334197 }, + { url = "https://files.pythonhosted.org/packages/a6/c1/8b84545382d75bef226fbc6588af0f7b7d095f7cd6a670b42a86243183cd/bcrypt-5.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:212139484ab3207b1f0c00633d3be92fef3c5f0af17cad155679d03ff2ee1e41", size = 352974 }, + { url = "https://files.pythonhosted.org/packages/10/a6/ffb49d4254ed085e62e3e5dd05982b4393e32fe1e49bb1130186617c29cd/bcrypt-5.0.0-cp313-cp313t-win32.whl", hash = "sha256:9d52ed507c2488eddd6a95bccee4e808d3234fa78dd370e24bac65a21212b861", size = 148498 }, + { url = "https://files.pythonhosted.org/packages/48/a9/259559edc85258b6d5fc5471a62a3299a6aa37a6611a169756bf4689323c/bcrypt-5.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:f6984a24db30548fd39a44360532898c33528b74aedf81c26cf29c51ee47057e", size = 145853 }, + { url = "https://files.pythonhosted.org/packages/2d/df/9714173403c7e8b245acf8e4be8876aac64a209d1b392af457c79e60492e/bcrypt-5.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9fffdb387abe6aa775af36ef16f55e318dcda4194ddbf82007a6f21da29de8f5", size = 139626 }, + { url = "https://files.pythonhosted.org/packages/f8/14/c18006f91816606a4abe294ccc5d1e6f0e42304df5a33710e9e8e95416e1/bcrypt-5.0.0-cp314-cp314t-macosx_10_12_universal2.whl", hash = "sha256:4870a52610537037adb382444fefd3706d96d663ac44cbb2f37e3919dca3d7ef", size = 481862 }, + { url = "https://files.pythonhosted.org/packages/67/49/dd074d831f00e589537e07a0725cf0e220d1f0d5d8e85ad5bbff251c45aa/bcrypt-5.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48f753100931605686f74e27a7b49238122aa761a9aefe9373265b8b7aa43ea4", size = 268544 }, + { url = "https://files.pythonhosted.org/packages/f5/91/50ccba088b8c474545b034a1424d05195d9fcbaaf802ab8bfe2be5a4e0d7/bcrypt-5.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f70aadb7a809305226daedf75d90379c397b094755a710d7014b8b117df1ebbf", size = 271787 }, + { url = "https://files.pythonhosted.org/packages/aa/e7/d7dba133e02abcda3b52087a7eea8c0d4f64d3e593b4fffc10c31b7061f3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:744d3c6b164caa658adcb72cb8cc9ad9b4b75c7db507ab4bc2480474a51989da", size = 269753 }, + { url = "https://files.pythonhosted.org/packages/33/fc/5b145673c4b8d01018307b5c2c1fc87a6f5a436f0ad56607aee389de8ee3/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a28bc05039bdf3289d757f49d616ab3efe8cf40d8e8001ccdd621cd4f98f4fc9", size = 289587 }, + { url = "https://files.pythonhosted.org/packages/27/d7/1ff22703ec6d4f90e62f1a5654b8867ef96bafb8e8102c2288333e1a6ca6/bcrypt-5.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:7f277a4b3390ab4bebe597800a90da0edae882c6196d3038a73adf446c4f969f", size = 272178 }, + { url = "https://files.pythonhosted.org/packages/c8/88/815b6d558a1e4d40ece04a2f84865b0fef233513bd85fd0e40c294272d62/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:79cfa161eda8d2ddf29acad370356b47f02387153b11d46042e93a0a95127493", size = 269295 }, + { url = "https://files.pythonhosted.org/packages/51/8c/e0db387c79ab4931fc89827d37608c31cc57b6edc08ccd2386139028dc0d/bcrypt-5.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a5393eae5722bcef046a990b84dff02b954904c36a194f6cfc817d7dca6c6f0b", size = 271700 }, + { url = "https://files.pythonhosted.org/packages/06/83/1570edddd150f572dbe9fc00f6203a89fc7d4226821f67328a85c330f239/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7f4c94dec1b5ab5d522750cb059bb9409ea8872d4494fd152b53cca99f1ddd8c", size = 334034 }, + { url = "https://files.pythonhosted.org/packages/c9/f2/ea64e51a65e56ae7a8a4ec236c2bfbdd4b23008abd50ac33fbb2d1d15424/bcrypt-5.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0cae4cb350934dfd74c020525eeae0a5f79257e8a201c0c176f4b84fdbf2a4b4", size = 352766 }, + { url = "https://files.pythonhosted.org/packages/d7/d4/1a388d21ee66876f27d1a1f41287897d0c0f1712ef97d395d708ba93004c/bcrypt-5.0.0-cp314-cp314t-win32.whl", hash = "sha256:b17366316c654e1ad0306a6858e189fc835eca39f7eb2cafd6aaca8ce0c40a2e", size = 152449 }, + { url = "https://files.pythonhosted.org/packages/3f/61/3291c2243ae0229e5bca5d19f4032cecad5dfb05a2557169d3a69dc0ba91/bcrypt-5.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:92864f54fb48b4c718fc92a32825d0e42265a627f956bc0361fe869f1adc3e7d", size = 149310 }, + { url = "https://files.pythonhosted.org/packages/3e/89/4b01c52ae0c1a681d4021e5dd3e45b111a8fb47254a274fa9a378d8d834b/bcrypt-5.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dd19cf5184a90c873009244586396a6a884d591a5323f0e8a5922560718d4993", size = 143761 }, + { url = "https://files.pythonhosted.org/packages/84/29/6237f151fbfe295fe3e074ecc6d44228faa1e842a81f6d34a02937ee1736/bcrypt-5.0.0-cp38-abi3-macosx_10_12_universal2.whl", hash = "sha256:fc746432b951e92b58317af8e0ca746efe93e66555f1b40888865ef5bf56446b", size = 494553 }, + { url = "https://files.pythonhosted.org/packages/45/b6/4c1205dde5e464ea3bd88e8742e19f899c16fa8916fb8510a851fae985b5/bcrypt-5.0.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c2388ca94ffee269b6038d48747f4ce8df0ffbea43f31abfa18ac72f0218effb", size = 275009 }, + { url = "https://files.pythonhosted.org/packages/3b/71/427945e6ead72ccffe77894b2655b695ccf14ae1866cd977e185d606dd2f/bcrypt-5.0.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:560ddb6ec730386e7b3b26b8b4c88197aaed924430e7b74666a586ac997249ef", size = 278029 }, + { url = "https://files.pythonhosted.org/packages/17/72/c344825e3b83c5389a369c8a8e58ffe1480b8a699f46c127c34580c4666b/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d79e5c65dcc9af213594d6f7f1fa2c98ad3fc10431e7aa53c176b441943efbdd", size = 275907 }, + { url = "https://files.pythonhosted.org/packages/0b/7e/d4e47d2df1641a36d1212e5c0514f5291e1a956a7749f1e595c07a972038/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2b732e7d388fa22d48920baa267ba5d97cca38070b69c0e2d37087b381c681fd", size = 296500 }, + { url = "https://files.pythonhosted.org/packages/0f/c3/0ae57a68be2039287ec28bc463b82e4b8dc23f9d12c0be331f4782e19108/bcrypt-5.0.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0c8e093ea2532601a6f686edbc2c6b2ec24131ff5c52f7610dd64fa4553b5464", size = 278412 }, + { url = "https://files.pythonhosted.org/packages/45/2b/77424511adb11e6a99e3a00dcc7745034bee89036ad7d7e255a7e47be7d8/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:5b1589f4839a0899c146e8892efe320c0fa096568abd9b95593efac50a87cb75", size = 275486 }, + { url = "https://files.pythonhosted.org/packages/43/0a/405c753f6158e0f3f14b00b462d8bca31296f7ecfc8fc8bc7919c0c7d73a/bcrypt-5.0.0-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:89042e61b5e808b67daf24a434d89bab164d4de1746b37a8d173b6b14f3db9ff", size = 277940 }, + { url = "https://files.pythonhosted.org/packages/62/83/b3efc285d4aadc1fa83db385ec64dcfa1707e890eb42f03b127d66ac1b7b/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e3cf5b2560c7b5a142286f69bde914494b6d8f901aaa71e453078388a50881c4", size = 310776 }, + { url = "https://files.pythonhosted.org/packages/95/7d/47ee337dacecde6d234890fe929936cb03ebc4c3a7460854bbd9c97780b8/bcrypt-5.0.0-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f632fd56fc4e61564f78b46a2269153122db34988e78b6be8b32d28507b7eaeb", size = 312922 }, + { url = "https://files.pythonhosted.org/packages/d6/3a/43d494dfb728f55f4e1cf8fd435d50c16a2d75493225b54c8d06122523c6/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:801cad5ccb6b87d1b430f183269b94c24f248dddbbc5c1f78b6ed231743e001c", size = 341367 }, + { url = "https://files.pythonhosted.org/packages/55/ab/a0727a4547e383e2e22a630e0f908113db37904f58719dc48d4622139b5c/bcrypt-5.0.0-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3cf67a804fc66fc217e6914a5635000259fbbbb12e78a99488e4d5ba445a71eb", size = 359187 }, + { url = "https://files.pythonhosted.org/packages/1b/bb/461f352fdca663524b4643d8b09e8435b4990f17fbf4fea6bc2a90aa0cc7/bcrypt-5.0.0-cp38-abi3-win32.whl", hash = "sha256:3abeb543874b2c0524ff40c57a4e14e5d3a66ff33fb423529c88f180fd756538", size = 153752 }, + { url = "https://files.pythonhosted.org/packages/41/aa/4190e60921927b7056820291f56fc57d00d04757c8b316b2d3c0d1d6da2c/bcrypt-5.0.0-cp38-abi3-win_amd64.whl", hash = "sha256:35a77ec55b541e5e583eb3436ffbbf53b0ffa1fa16ca6782279daf95d146dcd9", size = 150881 }, + { url = "https://files.pythonhosted.org/packages/54/12/cd77221719d0b39ac0b55dbd39358db1cd1246e0282e104366ebbfb8266a/bcrypt-5.0.0-cp38-abi3-win_arm64.whl", hash = "sha256:cde08734f12c6a4e28dc6755cd11d3bdfea608d93d958fffbe95a7026ebe4980", size = 144931 }, + { url = "https://files.pythonhosted.org/packages/5d/ba/2af136406e1c3839aea9ecadc2f6be2bcd1eff255bd451dd39bcf302c47a/bcrypt-5.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:0c418ca99fd47e9c59a301744d63328f17798b5947b0f791e9af3c1c499c2d0a", size = 495313 }, + { url = "https://files.pythonhosted.org/packages/ac/ee/2f4985dbad090ace5ad1f7dd8ff94477fe089b5fab2040bd784a3d5f187b/bcrypt-5.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddb4e1500f6efdd402218ffe34d040a1196c072e07929b9820f363a1fd1f4191", size = 275290 }, + { url = "https://files.pythonhosted.org/packages/e4/6e/b77ade812672d15cf50842e167eead80ac3514f3beacac8902915417f8b7/bcrypt-5.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7aeef54b60ceddb6f30ee3db090351ecf0d40ec6e2abf41430997407a46d2254", size = 278253 }, + { url = "https://files.pythonhosted.org/packages/36/c4/ed00ed32f1040f7990dac7115f82273e3c03da1e1a1587a778d8cea496d8/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f0ce778135f60799d89c9693b9b398819d15f1921ba15fe719acb3178215a7db", size = 276084 }, + { url = "https://files.pythonhosted.org/packages/e7/c4/fa6e16145e145e87f1fa351bbd54b429354fd72145cd3d4e0c5157cf4c70/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a71f70ee269671460b37a449f5ff26982a6f2ba493b3eabdd687b4bf35f875ac", size = 297185 }, + { url = "https://files.pythonhosted.org/packages/24/b4/11f8a31d8b67cca3371e046db49baa7c0594d71eb40ac8121e2fc0888db0/bcrypt-5.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f8429e1c410b4073944f03bd778a9e066e7fad723564a52ff91841d278dfc822", size = 278656 }, + { url = "https://files.pythonhosted.org/packages/ac/31/79f11865f8078e192847d2cb526e3fa27c200933c982c5b2869720fa5fce/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:edfcdcedd0d0f05850c52ba3127b1fce70b9f89e0fe5ff16517df7e81fa3cbb8", size = 275662 }, + { url = "https://files.pythonhosted.org/packages/d4/8d/5e43d9584b3b3591a6f9b68f755a4da879a59712981ef5ad2a0ac1379f7a/bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:611f0a17aa4a25a69362dcc299fda5c8a3d4f160e2abb3831041feb77393a14a", size = 278240 }, + { url = "https://files.pythonhosted.org/packages/89/48/44590e3fc158620f680a978aafe8f87a4c4320da81ed11552f0323aa9a57/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:db99dca3b1fdc3db87d7c57eac0c82281242d1eabf19dcb8a6b10eb29a2e72d1", size = 311152 }, + { url = "https://files.pythonhosted.org/packages/5f/85/e4fbfc46f14f47b0d20493669a625da5827d07e8a88ee460af6cd9768b44/bcrypt-5.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:5feebf85a9cefda32966d8171f5db7e3ba964b77fdfe31919622256f80f9cf42", size = 313284 }, + { url = "https://files.pythonhosted.org/packages/25/ae/479f81d3f4594456a01ea2f05b132a519eff9ab5768a70430fa1132384b1/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:3ca8a166b1140436e058298a34d88032ab62f15aae1c598580333dc21d27ef10", size = 341643 }, + { url = "https://files.pythonhosted.org/packages/df/d2/36a086dee1473b14276cd6ea7f61aef3b2648710b5d7f1c9e032c29b859f/bcrypt-5.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:61afc381250c3182d9078551e3ac3a41da14154fbff647ddf52a769f588c4172", size = 359698 }, + { url = "https://files.pythonhosted.org/packages/c0/f6/688d2cd64bfd0b14d805ddb8a565e11ca1fb0fd6817175d58b10052b6d88/bcrypt-5.0.0-cp39-abi3-win32.whl", hash = "sha256:64d7ce196203e468c457c37ec22390f1a61c85c6f0b8160fd752940ccfb3a683", size = 153725 }, + { url = "https://files.pythonhosted.org/packages/9f/b9/9d9a641194a730bda138b3dfe53f584d61c58cd5230e37566e83ec2ffa0d/bcrypt-5.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:64ee8434b0da054d830fa8e89e1c8bf30061d539044a39524ff7dec90481e5c2", size = 150912 }, + { url = "https://files.pythonhosted.org/packages/27/44/d2ef5e87509158ad2187f4dd0852df80695bb1ee0cfe0a684727b01a69e0/bcrypt-5.0.0-cp39-abi3-win_arm64.whl", hash = "sha256:f2347d3534e76bf50bca5500989d6c1d05ed64b440408057a37673282c654927", size = 144953 }, ] [[package]] @@ -984,6 +1129,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647, upload-time = "2026-03-13T13:54:22.735Z" }, ] +[[package]] +name = "fqdn" +version = "1.5.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/30/3e/a80a8c077fd798951169626cde3e239adeba7dab75deb3555716415bd9b0/fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", size = 6015 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014", size = 9121 }, +] + [[package]] name = "fsspec" version = "2026.3.0" @@ -1005,30 +1159,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, ] -[[package]] -name = "gitdb" -version = "4.0.12" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "smmap" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, -] - -[[package]] -name = "gitpython" -version = "3.1.50" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "gitdb" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/33/f6/354ae6491228b5eb40e10d89c4d13c651fe1cf7556e35ebdded50cff57ce/gitpython-3.1.50.tar.gz", hash = "sha256:80da2d12504d52e1f998772dc5baf6e553f8d2fcfe1fcc226c9d9a2ee3372dcc", size = 219798, upload-time = "2026-05-06T04:01:26.571Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl", hash = "sha256:d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9", size = 212507, upload-time = "2026-05-06T04:01:23.799Z" }, -] - [[package]] name = "google-crc32c" version = "1.8.0" @@ -1052,15 +1182,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/08/a5/7b059810934a09fb3ccb657e0843813c1fee1183d3bc2c8041800374aa2c/google_crc32c-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:d511b3153e7011a27ab6ee6bb3a5404a55b994dc1a7322c0b87b29606d9790e2", size = 34878, upload-time = "2025-12-16T00:35:23.142Z" }, ] -[[package]] -name = "griffelib" -version = "2.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461, upload-time = "2026-03-27T11:34:51.091Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357, upload-time = "2026-03-27T11:34:46.275Z" }, -] - [[package]] name = "gtfreader" version = "0.2.0" @@ -1074,6 +1195,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/56/239b5706022121ddee92b036fe51fd93d0f482c7efe386cf08b4ec894a66/gtfreader-0.2.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e98c7c4e5c2e2d44341261ab3bd8bb47846b4cf6b494c97fc57e0b6a886e9b5d", size = 215090, upload-time = "2026-03-18T21:28:56.176Z" }, ] +[[package]] +name = "h11" +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515 }, +] + [[package]] name = "h5py" version = "3.16.0" @@ -1134,8 +1264,36 @@ wheels = [ ] [[package]] -name = "humanfriendly" -version = "10.0" +name = "httpcore" +version = "1.0.9" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "iced" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyreadline3", marker = "sys_platform == 'win32'" }, @@ -1265,6 +1423,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/56/6d/0d9848617b9f753b87f214f1c682592f7ca42de085f564352f10f0843026/ipywidgets-8.1.8-py3-none-any.whl", hash = "sha256:ecaca67aed704a338f88f67b1181b58f821ab5dc89c1f0f5ef99db43c1c2921e", size = 139808, upload-time = "2025-11-01T21:18:10.956Z" }, ] +[[package]] +name = "isoduration" +version = "20.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "arrow" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/1a/3c8edc664e06e6bd06cce40c6b22da5f1429aa4224d0c590f3be21c91ead/isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", size = 11649 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042", size = 11321 }, +] + [[package]] name = "jedi" version = "0.19.2" @@ -1311,6 +1481,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, ] +[[package]] +name = "json5" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9c/4b/6f8906aaf67d501e259b0adab4d312945bb7211e8b8d4dcc77c92320edaa/json5-0.14.0.tar.gz", hash = "sha256:b3f492fad9f6cdbced8b7d40b28b9b1c9701c5f561bef0d33b81c2ff433fefcb", size = 52656 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/42/cf027b4ac873b076189d935b135397675dac80cb29acb13e1ab86ad6c631/json5-0.14.0-py3-none-any.whl", hash = "sha256:56cf861bab076b1178eb8c92e1311d273a9b9acea2ccc82c276abf839ebaef3a", size = 36271 }, +] + +[[package]] +name = "jsonpointer" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/c7/af399a2e7a67fd18d63c40c5e62d3af4e67b836a2107468b6a5ea24c4304/jsonpointer-3.1.1.tar.gz", hash = "sha256:0b801c7db33a904024f6004d526dcc53bbb8a4a0f4e32bfd10beadf60adf1900", size = 9068 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl", hash = "sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca", size = 7659 }, +] + [[package]] name = "jsonschema" version = "4.26.0" @@ -1326,6 +1514,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" }, ] +[package.optional-dependencies] +format-nongpl = [ + { name = "fqdn" }, + { name = "idna" }, + { name = "isoduration" }, + { name = "jsonpointer" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "rfc3987-syntax" }, + { name = "uri-template" }, + { name = "webcolors" }, +] + [[package]] name = "jsonschema-specifications" version = "2025.9.1" @@ -1338,6 +1539,23 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" }, ] +[[package]] +name = "jupyter" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipykernel" }, + { name = "ipywidgets" }, + { name = "jupyter-console" }, + { name = "jupyterlab" }, + { name = "nbconvert" }, + { name = "notebook" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/f3/af28ea964ab8bc1e472dba2e82627d36d470c51f5cd38c37502eeffaa25e/jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a", size = 5714959 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/64/285f20a31679bf547b75602702f7800e74dbabae36ef324f716c02804753/jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83", size = 2657 }, +] + [[package]] name = "jupyter-client" version = "8.8.0" @@ -1354,6 +1572,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2d/0b/ceb7694d864abc0a047649aec263878acb9f792e1fec3e676f22dc9015e3/jupyter_client-8.8.0-py3-none-any.whl", hash = "sha256:f93a5b99c5e23a507b773d3a1136bd6e16c67883ccdbd9a829b0bbdb98cd7d7a", size = 107371, upload-time = "2026-01-08T13:55:45.562Z" }, ] +[[package]] +name = "jupyter-console" +version = "6.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipykernel" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "pyzmq" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/2d/e2fd31e2fc41c14e2bcb6c976ab732597e907523f6b2420305f9fc7fdbdb/jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539", size = 34363 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/77/71d78d58f15c22db16328a476426f7ac4a60d3a5a7ba3b9627ee2f7903d4/jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485", size = 24510 }, +] + [[package]] name = "jupyter-core" version = "5.9.1" @@ -1368,56 +1605,136 @@ wheels = [ ] [[package]] -name = "jupyterlab-pygments" -version = "0.3.0" +name = "jupyter-events" +version = "0.12.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900, upload-time = "2023-11-23T09:26:37.44Z" } +dependencies = [ + { name = "jsonschema", extra = ["format-nongpl"] }, + { name = "packaging" }, + { name = "python-json-logger" }, + { name = "pyyaml" }, + { name = "referencing" }, + { name = "rfc3339-validator" }, + { name = "rfc3986-validator" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/18/f8/475c4241b2b75af0deaae453ed003c6c851766dbc44d332d8baf245dc931/jupyter_events-0.12.1.tar.gz", hash = "sha256:faff25f77218335752f35f23c5fe6e4a392a7bd99a5939ccb9b8fbf594636cf3", size = 62854 } wheels = [ - { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884, upload-time = "2023-11-23T09:26:34.325Z" }, + { url = "https://files.pythonhosted.org/packages/eb/6c/6fcde0c8f616ed360ffd3587f7db9e225a7e62b583a04494d2f069cf64ea/jupyter_events-0.12.1-py3-none-any.whl", hash = "sha256:c366585253f537a627da52fa7ca7410c5b5301fe893f511e7b077c2d93ec8bcf", size = 19512 }, ] [[package]] -name = "jupyterlab-widgets" -version = "3.0.16" +name = "jupyter-lsp" +version = "2.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423, upload-time = "2025-11-01T21:11:29.724Z" } +dependencies = [ + { name = "jupyter-server" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/ff/1e4a61f5170a9a1d978f3ac3872449de6c01fc71eaf89657824c878b1549/jupyter_lsp-2.3.1.tar.gz", hash = "sha256:fdf8a4aa7d85813976d6e29e95e6a2c8f752701f926f2715305249a3829805a6", size = 55677 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926, upload-time = "2025-11-01T21:11:28.008Z" }, + { url = "https://files.pythonhosted.org/packages/23/e8/9d61dcbd1dce8ef418f06befd4ac084b4720429c26b0b1222bc218685eff/jupyter_lsp-2.3.1-py3-none-any.whl", hash = "sha256:71b954d834e85ff3096400554f2eefaf7fe37053036f9a782b0f7c5e42dadb81", size = 77513 }, ] [[package]] -name = "jupytext" -version = "1.19.1" +name = "jupyter-server" +version = "2.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "markdown-it-py" }, - { name = "mdit-py-plugins" }, + { name = "anyio" }, + { name = "argon2-cffi" }, + { name = "jinja2" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "jupyter-events" }, + { name = "jupyter-server-terminals" }, + { name = "nbconvert" }, { name = "nbformat" }, { name = "packaging" }, - { name = "pyyaml" }, + { name = "prometheus-client" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "pyzmq" }, + { name = "send2trash" }, + { name = "terminado" }, + { name = "tornado" }, + { name = "traitlets" }, + { name = "websocket-client" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/13/a5/80c02f307c8ce863cb33e27daf049315e9d96979e14eead700923b5ec9cc/jupytext-1.19.1.tar.gz", hash = "sha256:82587c07e299173c70ed5e8ec7e75183edf1be289ed518bab49ad0d4e3d5f433", size = 4307829, upload-time = "2026-01-25T21:35:13.276Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/ac/e040ec363d7b6b1f11304cc9f209dac4517ece5d5e01821366b924a64a50/jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5", size = 731949 } wheels = [ - { url = "https://files.pythonhosted.org/packages/16/5a/736dd2f4535dbf3bf26523f9158c011389ef88dd06ec2eef67fd744f1c7b/jupytext-1.19.1-py3-none-any.whl", hash = "sha256:d8975035155d034bdfde5c0c37891425314b7ea8d3a6c4b5d18c294348714cd9", size = 170478, upload-time = "2026-01-25T21:35:11.17Z" }, + { url = "https://files.pythonhosted.org/packages/92/80/a24767e6ca280f5a49525d987bf3e4d7552bf67c8be07e8ccf20271f8568/jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f", size = 388221 }, ] [[package]] -name = "keras" -version = "3.14.1" +name = "jupyter-server-terminals" +version = "0.5.4" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "absl-py" }, - { name = "h5py" }, - { name = "ml-dtypes" }, - { name = "namex" }, - { name = "numpy" }, - { name = "optree" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "terminado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/a7/bcd0a9b0cbba88986fe944aaaf91bfda603e5a50bda8ed15123f381a3b2f/jupyter_server_terminals-0.5.4.tar.gz", hash = "sha256:bbda128ed41d0be9020349f9f1f2a4ab9952a73ed5f5ac9f1419794761fb87f5", size = 31770 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/2d/6674563f71c6320841fc300911a55143925112a72a883e2ca71fba4c618d/jupyter_server_terminals-0.5.4-py3-none-any.whl", hash = "sha256:55be353fc74a80bc7f3b20e6be50a55a61cd525626f578dcb66a5708e2007d14", size = 13704 }, +] + +[[package]] +name = "jupyterlab" +version = "4.5.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "async-lru" }, + { name = "httpx" }, + { name = "ipykernel" }, + { name = "jinja2" }, + { name = "jupyter-core" }, + { name = "jupyter-lsp" }, + { name = "jupyter-server" }, + { name = "jupyterlab-server" }, + { name = "notebook-shim" }, { name = "packaging" }, - { name = "rich" }, + { name = "setuptools" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/22/8440ec827762146e7cdecf04335bd348795899d29dc6ae82238707353a2c/jupyterlab-4.5.7.tar.gz", hash = "sha256:55a9822c4754da305f41e113452c68383e214dcf96de760146af89ce5d5117b0", size = 23992763 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/aa/537b8f7d80e799af19af35fb3ddfc970b951088a13c57dd9387dcfbb7f61/jupyterlab-4.5.7-py3-none-any.whl", hash = "sha256:fba4cb0e2c44a52859669d8c98b45de029d5e515f8407bf8534d2a8fc5f0964d", size = 12450123 }, ] -sdist = { url = "https://files.pythonhosted.org/packages/35/e7/97a7664581b73e4f9ff1d3a767a493b6ac5d3e0ed1926bd2b6b2c8bbccd7/keras-3.14.1.tar.gz", hash = "sha256:ef479173102ad29db89b53c232efdc3fb5ad57c28bc27ead59f3e78a1eecd05b", size = 1263647, upload-time = "2026-05-07T21:43:35.112Z" } + +[[package]] +name = "jupyterlab-pygments" +version = "0.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/51/9187be60d989df97f5f0aba133fa54e7300f17616e065d1ada7d7646b6d6/jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d", size = 512900 } wheels = [ - { url = "https://files.pythonhosted.org/packages/02/03/184267c1d09783dd070f1ddfd0d4beb7503139dfc7bd75b422867cf282fd/keras-3.14.1-py3-none-any.whl", hash = "sha256:ebd2c14d2af3c9de18083604d408483996407fc7d2f9ebd1d565961f96608c29", size = 1628606, upload-time = "2026-05-07T21:43:32.737Z" }, + { url = "https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780", size = 15884 }, +] + +[[package]] +name = "jupyterlab-server" +version = "2.28.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel" }, + { name = "jinja2" }, + { name = "json5" }, + { name = "jsonschema" }, + { name = "jupyter-server" }, + { name = "packaging" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/2c/90153f189e421e93c4bb4f9e3f59802a1f01abd2ac5cf40b152d7f735232/jupyterlab_server-2.28.0.tar.gz", hash = "sha256:35baa81898b15f93573e2deca50d11ac0ae407ebb688299d3a5213265033712c", size = 76996 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/07/a000fe835f76b7e1143242ab1122e6362ef1c03f23f83a045c38859c2ae0/jupyterlab_server-2.28.0-py3-none-any.whl", hash = "sha256:e4355b148fdcf34d312bbbc80f22467d6d20460e8b8736bf235577dd18506968", size = 59830 }, +] + +[[package]] +name = "jupyterlab-widgets" +version = "3.0.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/2d/ef58fed122b268c69c0aa099da20bc67657cdfb2e222688d5731bd5b971d/jupyterlab_widgets-3.0.16.tar.gz", hash = "sha256:423da05071d55cf27a9e602216d35a3a65a3e41cdf9c5d3b643b814ce38c19e0", size = 897423 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/b5/36c712098e6191d1b4e349304ef73a8d06aed77e56ceaac8c0a306c7bda1/jupyterlab_widgets-3.0.16-py3-none-any.whl", hash = "sha256:45fa36d9c6422cf2559198e4db481aa243c7a32d9926b500781c830c80f7ecf8", size = 914926 }, ] [[package]] @@ -1535,6 +1852,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/47/15/7d459a8e2a43f17c1db129b997b7bb7aa7f000a0967bab87c28b8c5cf448/leidenalg-0.11.0-cp38-abi3-win_amd64.whl", hash = "sha256:5e789c0960008d185413344a402d0587580c441644d4d20bf57c96f25d4d1710", size = 1990321, upload-time = "2025-10-31T17:14:40.892Z" }, ] +[[package]] +name = "lark" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/34/28fff3ab31ccff1fd4f6c7c7b0ceb2b6968d8ea4950663eadcb5720591a0/lark-1.3.1.tar.gz", hash = "sha256:b426a7a6d6d53189d318f2b6236ab5d6429eaf09259f1ca33eb716eed10d2905", size = 382732 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3d/14ce75ef66813643812f3093ab17e46d3a206942ce7376d31ec2d36229e7/lark-1.3.1-py3-none-any.whl", hash = "sha256:c629b661023a014c37da873b4ff58a817398d12635d3bbb2c5a03be7fe5d1e12", size = 113151 }, +] + [[package]] name = "llvmlite" version = "0.47.0" @@ -1586,15 +1912,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/03/0a/4f6fed21aa246c6b49b561ca55facacc2a44b87d65b8b92362a8e99ba202/loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb", size = 62549, upload-time = "2023-09-11T15:24:35.016Z" }, ] -[[package]] -name = "markdown" -version = "3.10.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, -] - [[package]] name = "markdown-it-py" version = "4.0.0" @@ -1736,18 +2053,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, ] -[[package]] -name = "mdit-py-plugins" -version = "0.5.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205, upload-time = "2025-08-11T07:25:47.597Z" }, -] - [[package]] name = "mdurl" version = "0.1.2" @@ -1773,15 +2078,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/93/84/648dc9326dbb31d2dbcbe30d089f2f126a274f8e0b8a539818b9d1bd6f55/memelite-0.2.0-py3-none-any.whl", hash = "sha256:6ccb38963e09b938b8412a05cab8244caa87e03e44c9c985af6a913b0d2ef5f0", size = 23419, upload-time = "2025-06-05T10:52:39.48Z" }, ] -[[package]] -name = "mergedeep" -version = "1.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, -] - [[package]] name = "mistune" version = "3.2.0" @@ -1792,183 +2088,8 @@ wheels = [ ] [[package]] -name = "mkdocs" -version = "1.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "ghp-import" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mergedeep" }, - { name = "mkdocs-get-deps" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "pyyaml" }, - { name = "pyyaml-env-tag" }, - { name = "watchdog" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, -] - -[[package]] -name = "mkdocs-autorefs" -version = "1.4.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mkdocs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/52/c0/f641843de3f612a6b48253f39244165acff36657a91cc903633d456ae1ac/mkdocs_autorefs-1.4.4.tar.gz", hash = "sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197", size = 56588, upload-time = "2026-02-10T15:23:55.105Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl", hash = "sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089", size = 25530, upload-time = "2026-02-10T15:23:53.817Z" }, -] - -[[package]] -name = "mkdocs-click" -version = "0.9.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "markdown" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/c7/8c25f3a3b379def41e6d0bb5c4beeab7aa8a394b17e749f498504102cfa5/mkdocs_click-0.9.0.tar.gz", hash = "sha256:6050917628d4740517541422b607404d044117bc31b770c4f9e9e1939a50c908", size = 18720, upload-time = "2025-04-07T16:59:36.387Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/fc/9124ab36e2341e78d8d9c669511bd70f52ea0de8105760c31fabec1f9396/mkdocs_click-0.9.0-py3-none-any.whl", hash = "sha256:5208e828f4f68f63c847c1ef7be48edee9964090390afc8f5b3d4cbe5ea9bbed", size = 15104, upload-time = "2025-04-07T16:59:34.807Z" }, -] - -[[package]] -name = "mkdocs-gen-files" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mkdocs" }, - { name = "properdocs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/43/43/428f312149c161cae557eecd35f3c4a82b867998b1d47fb29fdfe927be26/mkdocs_gen_files-0.6.1.tar.gz", hash = "sha256:57d7ff2229e23d077e46d14a33db6d37c8823f6ce1a503c874c1764a71679763", size = 8746, upload-time = "2026-03-16T23:26:09.31Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/1b/3075eb67fe66e19db059f0a25744c4e56978a309603a20e1d3353d545b5e/mkdocs_gen_files-0.6.1-py3-none-any.whl", hash = "sha256:b3182bfc6219e35b8d26658cb988368659d5d023aac30c2a819247558fc12189", size = 8282, upload-time = "2026-03-16T23:26:08.292Z" }, -] - -[[package]] -name = "mkdocs-get-deps" -version = "0.2.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mergedeep" }, - { name = "platformdirs" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047, upload-time = "2026-03-10T02:46:33.632Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555, upload-time = "2026-03-10T02:46:32.256Z" }, -] - -[[package]] -name = "mkdocs-jupyter" -version = "0.26.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ipykernel" }, - { name = "jupytext" }, - { name = "mkdocs" }, - { name = "mkdocs-material" }, - { name = "nbconvert" }, - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/aa/f8d15409a9a3112486994a80d5a975694c7d145c4f8b5b484aeb383420ef/mkdocs_jupyter-0.26.3.tar.gz", hash = "sha256:e1e8bd48a1b96542e84e3028e3066112bac7b94d95ab69f8b91305c84003ca26", size = 1628353, upload-time = "2026-04-17T18:56:31.517Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/13/95/cf3f7fe4910cf0365fa8ea0c731f4b8a624d97cd76ea777913ac8d0868e2/mkdocs_jupyter-0.26.3-py3-none-any.whl", hash = "sha256:cd6644fb578131157194d750fd4d10fc2fd8f1e84e00036ee62df3b5b4b84c82", size = 1459740, upload-time = "2026-04-17T18:56:30.031Z" }, -] - -[[package]] -name = "mkdocs-literate-nav" -version = "0.6.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mkdocs" }, - { name = "properdocs" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/01/af/dd3776a7a713f798f79bec7eb9c661d5cfb83ddc17d9a3667595e53e1559/mkdocs_literate_nav-0.6.3.tar.gz", hash = "sha256:edbaca22343f861fe4e34aac47d55a0c9955c640dbf02eea99fe631e914cf9ee", size = 17526, upload-time = "2026-03-16T23:26:50.688Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/4e/2c/bcf1ae903975ad6f169abb05c1eb0f94395478364deb89270cf034081b29/mkdocs_literate_nav-0.6.3-py3-none-any.whl", hash = "sha256:2c421561280fa9184f88cbf399bebbd4cc17ee507e978a31ce11fd6f3aabf233", size = 13355, upload-time = "2026-03-16T23:26:49.562Z" }, -] - -[[package]] -name = "mkdocs-material" -version = "9.7.6" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "babel" }, - { name = "backrefs" }, - { name = "colorama" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "mkdocs" }, - { name = "mkdocs-material-extensions" }, - { name = "paginate" }, - { name = "pygments" }, - { name = "pymdown-extensions" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/29/6d2bcf41ae40802c4beda2432396fff97b8456fb496371d1bc7aad6512ec/mkdocs_material-9.7.6.tar.gz", hash = "sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69", size = 4097959, upload-time = "2026-03-19T15:41:58.161Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl", hash = "sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba", size = 9305470, upload-time = "2026-03-19T15:41:55.217Z" }, -] - -[[package]] -name = "mkdocs-material-extensions" -version = "1.3.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, -] - -[[package]] -name = "mkdocstrings" -version = "1.0.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "jinja2" }, - { name = "markdown" }, - { name = "markupsafe" }, - { name = "mkdocs" }, - { name = "mkdocs-autorefs" }, - { name = "pymdown-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1d/5d/f888d4d3eb31359b327bc9b17a212d6ef03fe0b0682fbb3fc2cb849fb12b/mkdocstrings-1.0.4.tar.gz", hash = "sha256:3969a6515b77db65fd097b53c1b7aa4ae840bd71a2ee62a6a3e89503446d7172", size = 100088, upload-time = "2026-04-15T09:16:53.376Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/94/be70f8ee9c45f2f62b39a1f0e9303bc20e138a8f3b8e50ffd89498e177e1/mkdocstrings-1.0.4-py3-none-any.whl", hash = "sha256:63464b4b29053514f32a1dbbf604e52876d5e638111b0c295ab7ed3cac73ca9b", size = 35560, upload-time = "2026-04-15T09:16:51.436Z" }, -] - -[package.optional-dependencies] -python = [ - { name = "mkdocstrings-python" }, -] - -[[package]] -name = "mkdocstrings-python" -version = "2.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "griffelib" }, - { name = "mkdocs-autorefs" }, - { name = "mkdocstrings" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/29/33/c225eaf898634bdda489a6766fc35d1683c640bffe0e0acd10646b13536d/mkdocstrings_python-2.0.3.tar.gz", hash = "sha256:c518632751cc869439b31c9d3177678ad2bfa5c21b79b863956ad68fc92c13b8", size = 199083, upload-time = "2026-02-20T10:38:36.368Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl", hash = "sha256:0b83513478bdfd803ff05aa43e9b1fca9dd22bcd9471f09ca6257f009bc5ee12", size = 104779, upload-time = "2026-02-20T10:38:34.517Z" }, -] - -[[package]] -name = "ml-dtypes" -version = "0.4.1" +name = "msgpack" +version = "1.1.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "numpy" }, @@ -2164,6 +2285,34 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, ] +[[package]] +name = "notebook" +version = "7.5.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, + { name = "jupyterlab" }, + { name = "jupyterlab-server" }, + { name = "notebook-shim" }, + { name = "tornado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/c2/cf59bd2e6f2c8b976b52477e3e53bf6f97bc714ed046a51821afb428eaee/notebook-7.5.6.tar.gz", hash = "sha256:621174aade80108f0020b0f00738000b215f75fa3cd90771ad7aa0f24536a4e1", size = 14170814 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/d6/1fd0646b9bbd9efbb0b8ae21b2325fbef515769a5621c03e31d8eb8da587/notebook-7.5.6-py3-none-any.whl", hash = "sha256:4dde3f8fb55fa8fb7946d58c6e869ce9baf46d00fc070664f62604569d0faca0", size = 14581730 }, +] + +[[package]] +name = "notebook-shim" +version = "0.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-server" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/d2/92fa3243712b9a3e8bafaf60aac366da1cada3639ca767ff4b5b3654ec28/notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb", size = 13167 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/33/bd5b9137445ea4b680023eb0469b2bb969d61303dedb2aac6560ff3d14a1/notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef", size = 13307 }, +] + [[package]] name = "numba" version = "0.65.0" @@ -2465,15 +2614,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, ] -[[package]] -name = "paginate" -version = "0.5.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, -] - [[package]] name = "pandas" version = "2.1.1" @@ -2528,15 +2668,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" }, ] -[[package]] -name = "pathspec" -version = "1.0.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, -] - [[package]] name = "pexpect" version = "4.9.0" @@ -2629,6 +2760,7 @@ wheels = [ [[package]] name = "plotnado" +version = "0.4.dev13" source = { editable = "." } dependencies = [ { name = "loguru" }, @@ -2650,16 +2782,10 @@ dependencies = [ all = [ { name = "capcruncher" }, { name = "cooler" }, + { name = "ipykernel" }, { name = "ipywidgets" }, - { name = "mkdocs" }, - { name = "mkdocs-click" }, - { name = "mkdocs-gen-files" }, - { name = "mkdocs-jupyter" }, - { name = "mkdocs-literate-nav" }, - { name = "mkdocs-material" }, - { name = "mkdocstrings", extra = ["python"] }, + { name = "jupyter" }, { name = "pre-commit" }, - { name = "pymdown-extensions" }, { name = "pytest" }, { name = "pytest-assume" }, { name = "pytest-cov" }, @@ -2680,14 +2806,11 @@ dev = [ { name = "pytest-mock" }, ] docs = [ - { name = "mkdocs" }, - { name = "mkdocs-click" }, - { name = "mkdocs-gen-files" }, - { name = "mkdocs-jupyter" }, - { name = "mkdocs-literate-nav" }, - { name = "mkdocs-material" }, - { name = "mkdocstrings", extra = ["python"] }, - { name = "pymdown-extensions" }, + { name = "capcruncher" }, + { name = "cooler" }, + { name = "ipykernel" }, + { name = "jupyter" }, + { name = "quantnado" }, { name = "tomli-w" }, ] notebook = [ @@ -2704,29 +2827,19 @@ toml = [ requires-dist = [ { name = "capcruncher", marker = "extra == 'all'" }, { name = "capcruncher", marker = "extra == 'cooler'" }, + { name = "capcruncher", marker = "extra == 'docs'" }, { name = "cooler", marker = "extra == 'all'" }, { name = "cooler", marker = "extra == 'cooler'" }, + { name = "cooler", marker = "extra == 'docs'" }, + { name = "ipykernel", marker = "extra == 'all'", specifier = ">=6.29.0" }, + { name = "ipykernel", marker = "extra == 'docs'", specifier = ">=6.29.0" }, { name = "ipywidgets", marker = "extra == 'all'", specifier = ">=8" }, { name = "ipywidgets", marker = "extra == 'dev'", specifier = ">=8" }, { name = "ipywidgets", marker = "extra == 'notebook'", specifier = ">=8" }, + { name = "jupyter", marker = "extra == 'all'", specifier = ">=1.1.1" }, + { name = "jupyter", marker = "extra == 'docs'", specifier = ">=1.1.1" }, { name = "loguru" }, { name = "matplotlib" }, - { name = "mkdocs", marker = "extra == 'all'", specifier = "==1.6.1" }, - { name = "mkdocs", marker = "extra == 'docs'", specifier = "==1.6.1" }, - { name = "mkdocs-click", marker = "extra == 'all'", specifier = ">=0.9.0" }, - { name = "mkdocs-click", marker = "extra == 'docs'", specifier = ">=0.9.0" }, - { name = "mkdocs-gen-files", marker = "extra == 'all'", specifier = ">=0.6.0" }, - { name = "mkdocs-gen-files", marker = "extra == 'docs'", specifier = ">=0.6.0" }, - { name = "mkdocs-jupyter", marker = "extra == 'all'", specifier = ">=0.25.1" }, - { name = "mkdocs-jupyter", marker = "extra == 'docs'", specifier = ">=0.25.1" }, - { name = "mkdocs-literate-nav", marker = "extra == 'all'", specifier = ">=0.6.2" }, - { name = "mkdocs-literate-nav", marker = "extra == 'docs'", specifier = ">=0.6.2" }, - { name = "mkdocs-material", marker = "extra == 'all'", specifier = ">=9.5.18" }, - { name = "mkdocs-material", marker = "extra == 'docs'", specifier = ">=9.5.18" }, - { name = "mkdocstrings", marker = "extra == 'all'", specifier = ">=1.0.3" }, - { name = "mkdocstrings", marker = "extra == 'docs'", specifier = ">=1.0.3" }, - { name = "mkdocstrings", extras = ["python"], marker = "extra == 'all'", specifier = ">=1.0.3" }, - { name = "mkdocstrings", extras = ["python"], marker = "extra == 'docs'", specifier = ">=1.0.3" }, { name = "numpy" }, { name = "pandas" }, { name = "pandera" }, @@ -2734,8 +2847,6 @@ requires-dist = [ { name = "pre-commit", marker = "extra == 'dev'" }, { name = "pybigtools" }, { name = "pydantic", specifier = ">=2.0.0" }, - { name = "pymdown-extensions", marker = "extra == 'all'", specifier = ">=10.21" }, - { name = "pymdown-extensions", marker = "extra == 'docs'", specifier = ">=10.21" }, { name = "pyranges1" }, { name = "pytest", marker = "extra == 'all'" }, { name = "pytest", marker = "extra == 'dev'" }, @@ -2747,6 +2858,7 @@ requires-dist = [ { name = "pytest-mock", marker = "extra == 'dev'" }, { name = "pyyaml" }, { name = "quantnado", marker = "extra == 'all'" }, + { name = "quantnado", marker = "extra == 'docs'" }, { name = "quantnado", marker = "extra == 'quantnado'" }, { name = "rich" }, { name = "tomli", marker = "python_full_version < '3.11' and extra == 'all'" }, @@ -2813,53 +2925,24 @@ wheels = [ ] [[package]] -name = "prompt-toolkit" -version = "3.0.52" +name = "prometheus-client" +version = "0.25.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wcwidth" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/fb/d9aa83ffe43ce1f19e557c0971d04b90561b0cfd50762aafb01968285553/prometheus_client-0.25.0.tar.gz", hash = "sha256:5e373b75c31afb3c86f1a52fa1ad470c9aace18082d39ec0d2f918d11cc9ba28", size = 86035 } wheels = [ - { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9b/d4b1e644385499c8346fa9b622a3f030dce14cd6ef8a1871c221a17a67e7/prometheus_client-0.25.0-py3-none-any.whl", hash = "sha256:d5aec89e349a6ec230805d0df882f3807f74fd6c1a2fa86864e3c2279059fed1", size = 64154 }, ] [[package]] -name = "properdocs" -version = "1.6.7" +name = "prompt-toolkit" +version = "3.0.52" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "click" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "ghp-import" }, - { name = "jinja2" }, - { name = "markdown" }, - { name = "markupsafe" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, - { name = "pyyaml" }, - { name = "pyyaml-env-tag" }, - { name = "watchdog" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ec/29/f27a4e1eddf72ed3db6e47818fbafe6debbf09fd7051f9c1a007239b46ef/properdocs-1.6.7.tar.gz", hash = "sha256:adc7b16e562890af0e098a7e5b02e3a81c20894a87d6a28d345c9300de73c26e", size = 276141, upload-time = "2026-03-20T20:07:48.167Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/4d/fc923f5c85318ee8cc903566dc4e0ebe41b2dfc1d2ecf5546db232397ed6/properdocs-1.6.7-py3-none-any.whl", hash = "sha256:6fa0cfa2e01bf338f684892c8a506cf70ea88ae7f3479c933b6fa20168101cbd", size = 225406, upload-time = "2026-03-20T20:07:46.875Z" }, + { name = "wcwidth" }, ] - -[[package]] -name = "protobuf" -version = "7.34.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6b/6b/a0e95cad1ad7cc3f2c6821fcab91671bd5b78bd42afb357bb4765f29bc41/protobuf-7.34.1.tar.gz", hash = "sha256:9ce42245e704cc5027be797c1db1eb93184d44d1cdd71811fb2d9b25ad541280", size = 454708, upload-time = "2026-03-20T17:34:47.036Z" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198 } wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/11/3325d41e6ee15bf1125654301211247b042563bcc898784351252549a8ad/protobuf-7.34.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8b2cc79c4d8f62b293ad9b11ec3aebce9af481fa73e64556969f7345ebf9fc7", size = 429247, upload-time = "2026-03-20T17:34:37.024Z" }, - { url = "https://files.pythonhosted.org/packages/eb/9d/aa69df2724ff63efa6f72307b483ce0827f4347cc6d6df24b59e26659fef/protobuf-7.34.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:5185e0e948d07abe94bb76ec9b8416b604cfe5da6f871d67aad30cbf24c3110b", size = 325753, upload-time = "2026-03-20T17:34:38.751Z" }, - { url = "https://files.pythonhosted.org/packages/92/e8/d174c91fd48e50101943f042b09af9029064810b734e4160bbe282fa1caa/protobuf-7.34.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:403b093a6e28a960372b44e5eb081775c9b056e816a8029c61231743d63f881a", size = 340198, upload-time = "2026-03-20T17:34:39.871Z" }, - { url = "https://files.pythonhosted.org/packages/53/1b/3b431694a4dc6d37b9f653f0c64b0a0d9ec074ee810710c0c3da21d67ba7/protobuf-7.34.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:8ff40ce8cd688f7265326b38d5a1bed9bfdf5e6723d49961432f83e21d5713e4", size = 324267, upload-time = "2026-03-20T17:34:41.1Z" }, - { url = "https://files.pythonhosted.org/packages/85/29/64de04a0ac142fb685fd09999bc3d337943fb386f3a0ec57f92fd8203f97/protobuf-7.34.1-cp310-abi3-win32.whl", hash = "sha256:34b84ce27680df7cca9f231043ada0daa55d0c44a2ddfaa58ec1d0d89d8bf60a", size = 426628, upload-time = "2026-03-20T17:34:42.536Z" }, - { url = "https://files.pythonhosted.org/packages/4d/87/cb5e585192a22b8bd457df5a2c16a75ea0db9674c3a0a39fc9347d84e075/protobuf-7.34.1-cp310-abi3-win_amd64.whl", hash = "sha256:e97b55646e6ce5cbb0954a8c28cd39a5869b59090dfaa7df4598a7fba869468c", size = 437901, upload-time = "2026-03-20T17:34:44.112Z" }, - { url = "https://files.pythonhosted.org/packages/88/95/608f665226bca68b736b79e457fded9a2a38c4f4379a4a7614303d9db3bc/protobuf-7.34.1-py3-none-any.whl", hash = "sha256:bb3812cd53aefea2b028ef42bd780f5b96407247f20c6ef7c679807e9d188f11", size = 170715, upload-time = "2026-03-20T17:34:45.384Z" }, + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431 }, ] [[package]] @@ -3113,21 +3196,8 @@ wheels = [ ] [[package]] -name = "pymdown-extensions" -version = "10.21.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/df/08/f1c908c581fd11913da4711ea7ba32c0eee40b0190000996bb863b0c9349/pymdown_extensions-10.21.2.tar.gz", hash = "sha256:c3f55a5b8a1d0edf6699e35dcbea71d978d34ff3fa79f3d807b8a5b3fa90fbdc", size = 853922, upload-time = "2026-03-29T15:01:55.233Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl", hash = "sha256:5c0fd2a2bea14eb39af8ff284f1066d898ab2187d81b889b75d46d4348c01638", size = 268901, upload-time = "2026-03-29T15:01:53.244Z" }, -] - -[[package]] -name = "pyparsing" -version = "3.3.2" +name = "pynacl" +version = "1.6.2" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } wheels = [ @@ -3296,6 +3366,33 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/dd/96da98f892250475bdf2328112d7468abdd4acc7b902b6af23f4ed958ea0/pytz-2026.2-py2.py3-none-any.whl", hash = "sha256:04156e608bee23d3792fd45c94ae47fae1036688e75032eea2e3bf0323d1f126", size = 510141, upload-time = "2026-05-04T01:35:27.408Z" }, ] +[[package]] +name = "python-json-logger" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/ff/3cc9165fd44106973cd7ac9facb674a65ed853494592541d339bdc9a30eb/python_json_logger-4.1.0.tar.gz", hash = "sha256:b396b9e3ed782b09ff9d6e4f1683d46c83ad0d35d2e407c09a9ebbf038f88195", size = 17573 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/be/0631a861af4d1c875f096c07d34e9a63639560a717130e7a87cbc82b7e3f/python_json_logger-4.1.0-py3-none-any.whl", hash = "sha256:132994765cf75bf44554be9aa49b06ef2345d23661a96720262716438141b6b2", size = 15021 }, +] + +[[package]] +name = "pywinpty" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/54/37c7370ba91f579235049dc26cd2c5e657d2a943e01820844ffc81f32176/pywinpty-3.0.3.tar.gz", hash = "sha256:523441dc34d231fb361b4b00f8c99d3f16de02f5005fd544a0183112bcc22412", size = 31309 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/d4/aeb5e1784d2c5bff6e189138a9ca91a090117459cea0c30378e1f2db3d54/pywinpty-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:c9081df0e49ffa86d15db4a6ba61530630e48707f987df42c9d3313537e81fc0", size = 2113098 }, + { url = "https://files.pythonhosted.org/packages/b9/53/7278223c493ccfe4883239cf06c823c56460a8010e0fc778eef67858dc14/pywinpty-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:15e79d870e18b678fb8a5a6105fd38496b55697c66e6fc0378236026bc4d59e9", size = 234901 }, + { url = "https://files.pythonhosted.org/packages/e5/cb/58d6ed3fd429c96a90ef01ac9a617af10a6d41469219c25e7dc162abbb71/pywinpty-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9c91dbb026050c77bdcef964e63a4f10f01a639113c4d3658332614544c467ab", size = 2112686 }, + { url = "https://files.pythonhosted.org/packages/fd/50/724ed5c38c504d4e58a88a072776a1e880d970789deaeb2b9f7bd9a5141a/pywinpty-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:fe1f7911805127c94cf51f89ab14096c6f91ffdcacf993d2da6082b2142a2523", size = 234591 }, + { url = "https://files.pythonhosted.org/packages/f7/ad/90a110538696b12b39fd8758a06d70ded899308198ad2305ac68e361126e/pywinpty-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:3f07a6cf1c1d470d284e614733c3d0f726d2c85e78508ea10a403140c3c0c18a", size = 2112360 }, + { url = "https://files.pythonhosted.org/packages/44/0f/7ffa221757a220402bc79fda44044c3f2cc57338d878ab7d622add6f4581/pywinpty-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:15c7c0b6f8e9d87aabbaff76468dabf6e6121332c40fc1d83548d02a9d6a3759", size = 233107 }, + { url = "https://files.pythonhosted.org/packages/28/88/2ff917caff61e55f38bcdb27de06ee30597881b2cae44fbba7627be015c4/pywinpty-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:d4b6b7b0fe0cdcd02e956bd57cfe9f4e5a06514eecf3b5ae174da4f951b58be9", size = 2113282 }, + { url = "https://files.pythonhosted.org/packages/63/32/40a775343ace542cc43ece3f1d1fce454021521ecac41c4c4573081c2336/pywinpty-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:34789d685fc0d547ce0c8a65e5a70e56f77d732fa6e03c8f74fefb8cbb252019", size = 234207 }, + { url = "https://files.pythonhosted.org/packages/8d/54/5d5e52f4cb75028104ca6faf36c10f9692389b1986d34471663b4ebebd6d/pywinpty-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:0c37e224a47a971d1a6e08649a1714dac4f63c11920780977829ed5c8cadead1", size = 2112910 }, + { url = "https://files.pythonhosted.org/packages/0a/44/dcd184824e21d4620b06c7db9fbb15c3ad0a0f1fa2e6de79969fb82647ec/pywinpty-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:c4e9c3dff7d86ba81937438d5819f19f385a39d8f592d4e8af67148ceb4f6ab5", size = 233425 }, +] + [[package]] name = "pyyaml" version = "6.0.3" @@ -3342,18 +3439,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, ] -[[package]] -name = "pyyaml-env-tag" -version = "1.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, -] - [[package]] name = "pyzmq" version = "27.1.0" @@ -3453,6 +3538,39 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/66/11/e295e07d4ae500144177f875a8de11daa4d86b8246ab41c76a98ce9280ca/reretry-0.11.8-py2.py3-none-any.whl", hash = "sha256:5ec1084cd9644271ee386d34cd5dd24bdb3e91d55961b076d1a31d585ad68a79", size = 5609, upload-time = "2022-12-18T11:08:49.1Z" }, ] +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490 }, +] + +[[package]] +name = "rfc3986-validator" +version = "0.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/88/f270de456dd7d11dcc808abfa291ecdd3f45ff44e3b549ffa01b126464d0/rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055", size = 6760 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 }, +] + +[[package]] +name = "rfc3987-syntax" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "lark" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2c/06/37c1a5557acf449e8e406a830a05bf885ac47d33270aec454ef78675008d/rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d", size = 14239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/71/44ce230e1b7fadd372515a97e32a83011f906ddded8d03e3c6aafbdedbb7/rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f", size = 8046 }, +] + [[package]] name = "rich" version = "15.0.0" @@ -3725,6 +3843,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e1/e3/c164c88b2e5ce7b24d667b9bd83589cf4f3520d97cad01534cd3c4f55fdb/setuptools-81.0.0-py3-none-any.whl", hash = "sha256:fdd925d5c5d9f62e4b74b30d6dd7828ce236fd6ed998a08d81de62ce5a6310d6", size = 1062021, upload-time = "2026-02-06T21:10:37.175Z" }, ] +[[package]] +name = "send2trash" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c5/f0/184b4b5f8d00f2a92cf96eec8967a3d550b52cf94362dad1100df9e48d57/send2trash-2.1.0.tar.gz", hash = "sha256:1c72b39f09457db3c05ce1d19158c2cbef4c32b8bedd02c155e49282b7ea7459", size = 17255 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1c/78/504fdd027da3b84ff1aecd9f6957e65f35134534ccc6da8628eb71e76d3f/send2trash-2.1.0-py3-none-any.whl", hash = "sha256:0da2f112e6d6bb22de6aa6daa7e144831a4febf2a87261451c4ad849fe9a873c", size = 17610 }, +] + +[[package]] +name = "setuptools" +version = "82.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", hash = "sha256:7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9", size = 1152316 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", hash = "sha256:a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb", size = 1006223 }, +] + [[package]] name = "shellingham" version = "1.5.4" @@ -3934,6 +4070,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/24/99/4772b8e00a136f3e01236de33b0efda31ee7077203ba5967fcc76da94d65/texttable-1.7.0-py2.py3-none-any.whl", hash = "sha256:72227d592c82b3d7f672731ae73e4d1f88cd8e2ef5b075a7a7f01a23a3743917", size = 10768, upload-time = "2023-10-03T09:48:10.434Z" }, ] +[[package]] +name = "terminado" +version = "0.18.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess", marker = "os_name != 'nt'" }, + { name = "pywinpty", marker = "os_name == 'nt'" }, + { name = "tornado" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/11/965c6fd8e5cc254f1fe142d547387da17a8ebfd75a3455f637c663fb38a0/terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e", size = 32701 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", size = 14154 }, +] + [[package]] name = "threadpoolctl" version = "3.6.0" @@ -4159,6 +4309,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" }, ] +[[package]] +name = "uri-template" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/c7/0336f2bd0bcbada6ccef7aaa25e443c118a704f828a0620c6fa0207c1b64/uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", size = 21678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363", size = 11140 }, +] + [[package]] name = "urllib3" version = "2.6.3" @@ -4213,36 +4372,21 @@ wheels = [ ] [[package]] -name = "watchdog" -version = "6.0.0" +name = "wcwidth" +version = "0.6.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684 } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, - { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, - { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, - { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, - { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, - { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, - { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, - { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, - { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, - { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, - { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, - { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, - { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, - { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, + { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189 }, ] [[package]] -name = "wcwidth" -version = "0.6.0" +name = "webcolors" +version = "25.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/7a/eb316761ec35664ea5174709a68bbd3389de60d4a1ebab8808bfc264ed67/webcolors-25.10.0.tar.gz", hash = "sha256:62abae86504f66d0f6364c2a8520de4a0c47b80c03fc3a5f1815fedbef7c19bf", size = 53491 } wheels = [ - { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, + { url = "https://files.pythonhosted.org/packages/e2/cc/e097523dd85c9cf5d354f78310927f1656c422bd7b2613b2db3e3f9a0f2c/webcolors-25.10.0-py3-none-any.whl", hash = "sha256:032c727334856fc0b968f63daa252a1ac93d33db2f5267756623c210e57a4f1d", size = 14905 }, ] [[package]] @@ -4254,6 +4398,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" }, ] +[[package]] +name = "websocket-client" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/41/aa4bf9664e4cda14c3b39865b12251e8e7d239f4cd0e3cc1b6c2ccde25c1/websocket_client-1.9.0.tar.gz", hash = "sha256:9e813624b6eb619999a97dc7958469217c3176312b3a16a4bd1bc7e08a46ec98", size = 70576 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/34/db/b10e48aa8fff7407e67470363eac595018441cf32d5e1001567a7aeba5d2/websocket_client-1.9.0-py3-none-any.whl", hash = "sha256:af248a825037ef591efbf6ed20cc5faa03d3b47b9e5a2230a529eeee1c1fc3ef", size = 82616 }, +] + [[package]] name = "widgetsnbextension" version = "4.0.15"