| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| options (SKIP=1) | ||
| load data | ||
| infile '/opt/oracle/data/astronauts.csv' | ||
| into table "astronauts" | ||
| fields csv with embedded | ||
| ("id", | ||
| "number", | ||
| "nationwide_number", | ||
| "name", | ||
| "original_name", | ||
| "sex", | ||
| "year_of_birth", | ||
| "nationality", | ||
| "military_civilian", | ||
| "selection", | ||
| "year_of_selection", | ||
| "mission_number", | ||
| "total_number_of_missions", | ||
| "occupation", | ||
| "year_of_mission", | ||
| "mission_title", | ||
| "ascend_shuttle", | ||
| "in_orbit", | ||
| "descend_shuttle", | ||
| "hours_mission", | ||
| "total_hrs_sum", | ||
| "field21", | ||
| "eva_hrs_mission", | ||
| "total_eva_hrs") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| FROM eclipse-temurin:8u342-b07-jre-jammy | ||
|
|
||
| WORKDIR /opt | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| .quarto | ||
| _output | ||
|
|
||
| *.ddb | ||
|
|
||
| site_libs | ||
|
|
||
| *.csv | ||
| *.parquet | ||
| *.delta | ||
| *.zip | ||
| *.db | ||
| diamonds.json | ||
| *.ndjson | ||
| reference/ | ||
| objects.json | ||
| *support_matrix.csv | ||
|
|
||
| # generated notebooks and files | ||
| *.ipynb | ||
| *_files | ||
|
|
||
| # inventories | ||
| _inv | ||
| objects.txt | ||
|
|
||
| /.quarto/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # Page not found | ||
|
|
||
| The page you requested cannot be found (perhaps it was moved or renamed). | ||
|
|
||
| You may want to try searching to find the page's new location. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Ibis documentation with Quarto | ||
|
|
||
| TODO: update this README closer to merging. | ||
|
|
||
| ## Setup | ||
|
|
||
| Checkout this PR/branch. | ||
|
|
||
| 0. Create a Python environment with everything installed | ||
| 1. Install Quarto | ||
| 2. Install justfile | ||
| 3. `just preview` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| ::: {.callout-warning} | ||
| This backend is experimental and is subject to backwards incompatible changes. | ||
| ::: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| ::: {.callout-warning} | ||
| Note that the `ibis-framework` package is _not_ the same as the `ibis` package in PyPI. These two libraries cannot coexist in the same Python environment, as they are both imported with the `ibis` module name. | ||
| ::: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| ## Data platforms | ||
|
|
||
| You can connect Ibis to any supported backend to read and write data in backend-native tables. | ||
|
|
||
| ```{python} | ||
| # | code-fold: true | ||
| con = ibis.duckdb.connect("penguins.ddb") | ||
| t = con.create_table("penguins", t.to_pyarrow(), overwrite=True) | ||
| ``` | ||
|
|
||
| ```{python} | ||
| con = ibis.duckdb.connect("penguins.ddb") # <1> | ||
| t = con.table("penguins") # <2> | ||
| t.head(3) # <3> | ||
| ``` | ||
|
|
||
| 1. Connect to a backend. | ||
| 2. Load a table. | ||
| 3. Display the table. | ||
|
|
||
| ```{python} | ||
| grouped = ( # <1> | ||
| t.group_by(["species", "island"]) # <1> | ||
| .aggregate(count=ibis._.count()) # <1> | ||
| .order_by(ibis.desc("count")) # <1> | ||
| ) # <1> | ||
| con.create_table("penguins_grouped", grouped.to_pyarrow(), overwrite=True) # <2> | ||
| ``` | ||
|
|
||
| 1. Create a lazily evaluated Ibis expression. | ||
| 2. Write to a table. | ||
|
|
||
| ## File formats | ||
|
|
||
| Depending on the backend, you can read and write data in several file formats. | ||
|
|
||
| ::: {.panel-tabset} | ||
|
|
||
| ## CSV | ||
|
|
||
| ```{.bash} | ||
| pip install 'ibis-framework[duckdb]' | ||
| ``` | ||
|
|
||
| ```{python} | ||
| t.to_csv("penguins.csv") # <1> | ||
| ibis.read_csv("penguins.csv").head(3) # <2> | ||
| ``` | ||
| 1. Write the table to a CSV file. Dependent on backend. | ||
| 2. Read the CSV file into a table. Dependent on backend. | ||
|
|
||
| ## Delta Lake | ||
|
|
||
| ```{.bash} | ||
| pip install 'ibis-framework[duckdb,deltalake]' | ||
| ``` | ||
|
|
||
| ```{python} | ||
| t.to_delta("penguins.delta", mode="overwrite") # <1> | ||
| ibis.read_delta("penguins.delta").head(3) # <2> | ||
| ``` | ||
|
|
||
| 1. Write the table to a Delta Lake table. Dependent on backend. | ||
| 2. Read the Delta Lake table into a table. Dependent on backend. | ||
|
|
||
| ## Parquet | ||
|
|
||
| ```{.bash} | ||
| pip install 'ibis-framework[duckdb]' | ||
| ``` | ||
|
|
||
| ```{python} | ||
| t.to_parquet("penguins.parquet") # <1> | ||
| ibis.read_parquet("penguins.parquet").head(3) # <2> | ||
| ``` | ||
|
|
||
| 1. Write the table to a Parquet file. Dependent on backend. | ||
| 2. Read the Parquet file into a table. Dependent on backend. | ||
|
|
||
| ::: | ||
|
|
||
| ## With other Python libraries | ||
|
|
||
| Ibis uses [Apache Arrow](https://arrow.apache.org/) for efficient data transfer | ||
| to and from other libraries. Ibis tables implement the `__dataframe__` and | ||
| `__array__` protocols, so you can pass them to any library that supports these | ||
| protocols. | ||
|
|
||
| ::: {.panel-tabset} | ||
|
|
||
| ## `pandas` | ||
|
|
||
| You can convert Ibis tables to pandas dataframes. | ||
|
|
||
| ```bash | ||
| pip install pandas | ||
| ``` | ||
|
|
||
| ```{python} | ||
| df = t.to_pandas() # <1> | ||
| df.head(3) | ||
| ``` | ||
|
|
||
| 1. Returns a pandas dataframe. | ||
|
|
||
| Or you can convert pandas dataframes to Ibis tables. | ||
|
|
||
| ```{python} | ||
| t = ibis.memtable(df) # <1> | ||
| t.head(3) | ||
| ``` | ||
|
|
||
| 1. Returns an Ibis table. | ||
|
|
||
| ## `polars` | ||
|
|
||
| You can convert Ibis tables to Polars dataframes. | ||
|
|
||
| ```bash | ||
| pip install polars | ||
| ``` | ||
|
|
||
| ```{python} | ||
| import polars as pl | ||
| df = pl.from_arrow(t.to_pyarrow()) | ||
| df.head(3) | ||
| ``` | ||
|
|
||
| Or Polars dataframes to Ibis tables. | ||
|
|
||
| ```{python} | ||
| t = ibis.memtable(df) | ||
| t.head(3) | ||
| ``` | ||
|
|
||
| ## `pyarrow` | ||
|
|
||
| You can convert Ibis tables to PyArrow tables. | ||
|
|
||
| ```bash | ||
| pip install pyarrow | ||
| ``` | ||
|
|
||
| ```{python} | ||
| t.to_pyarrow() | ||
| ``` | ||
|
|
||
| Or PyArrow batches: | ||
|
|
||
| ```{python} | ||
| t.to_pyarrow_batches() | ||
| ``` | ||
|
|
||
| And you can convert PyArrow tables to Ibis tables. | ||
|
|
||
| ```{python} | ||
| ibis.memtable(t.to_pyarrow()).head(3) | ||
| ``` | ||
|
|
||
| ## `torch` | ||
|
|
||
| You can convert Ibis tables to torch tensors. | ||
|
|
||
| ```bash | ||
| pip install torch | ||
| ``` | ||
|
|
||
| ```python | ||
| t.select(s.numeric()).limit(3).to_torch() | ||
| ``` | ||
|
|
||
| ``` | ||
| {'col2': tensor([39.1000, 39.5000, 40.3000], dtype=torch.float64), | ||
| 'col3': tensor([18.7000, 17.4000, 18.0000], dtype=torch.float64), | ||
| 'col4': tensor([181., 186., 195.], dtype=torch.float64), | ||
| 'col5': tensor([3750., 3800., 3250.], dtype=torch.float64), | ||
| 'col7': tensor([2007, 2007, 2007], dtype=torch.int16)} | ||
| ``` | ||
|
|
||
| ## `__dataframe__` | ||
|
|
||
| You can directly call the `__dataframe__` protocol on Ibis tables, though this is typically handled by the library you're using. | ||
|
|
||
| ```{python} | ||
| t.__dataframe__() | ||
| ``` | ||
|
|
||
| ## `__array__` | ||
|
|
||
| You can directly call the `__array__` protocol on Ibis tables, though this is typically handled by the library you're using. | ||
|
|
||
| ```{python} | ||
| t.__array__() | ||
| ``` | ||
|
|
||
| ::: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| ```{python} | ||
| import ibis # <1> | ||
| import ibis.selectors as s # <1> | ||
| ibis.options.interactive = True # <2> | ||
| t = ibis.examples.penguins.fetch() # <3> | ||
| t.head(3) # <4> | ||
| ``` | ||
|
|
||
| 1. Ensure you install Ibis first. | ||
| 2. Use interactive mode for exploratory data analysis (EDA) or demos. | ||
| 3. Load a dataset from the built-in examples. | ||
| 4. Display the table. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| *.html | ||
| *_files/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| title: Interlinks | ||
| author: Michael Chow | ||
| version: 1.1.0 | ||
| quarto-required: ">=1.2.0" | ||
| contributes: | ||
| filters: | ||
| - interlinks.lua |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,252 @@ | ||
| local function read_inv_text(filename) | ||
| -- read file | ||
| local file = io.open(filename, "r") | ||
| if file == nil then | ||
| return nil | ||
| end | ||
| local str = file:read("a") | ||
| file:close() | ||
|
|
||
|
|
||
| local project = str:match("# Project: (%S+)") | ||
| local version = str:match("# Version: (%S+)") | ||
|
|
||
| local data = {project = project, version = version, items = {}} | ||
|
|
||
| local ptn_data = | ||
| "^" .. | ||
| "(.-)%s+" .. -- name | ||
| "([%S:]-):" .. -- domain | ||
| "([%S]+)%s+" .. -- role | ||
| "(%-?%d+)%s+" .. -- priority | ||
| "(%S*)%s+" .. -- uri | ||
| "(.-)\r?$" -- dispname | ||
|
|
||
|
|
||
| -- Iterate through each line in the file content | ||
| for line in str:gmatch("[^\r\n]+") do | ||
| if not line:match("^#") then | ||
| -- Match each line against the pattern | ||
| local name, domain, role, priority, uri, dispName = line:match(ptn_data) | ||
|
|
||
| -- if name is nil, raise an error | ||
| if name == nil then | ||
| error("Error parsing line: " .. line) | ||
| end | ||
|
|
||
| data.items[#data.items + 1] = { | ||
| name = name, | ||
| domain = domain, | ||
| role = role, | ||
| priority = priority, | ||
| uri = uri, | ||
| dispName = dispName | ||
| } | ||
| end | ||
| end | ||
| return data | ||
| end | ||
|
|
||
| local function read_json(filename) | ||
|
|
||
| local file = io.open(filename, "r") | ||
| if file == nil then | ||
| return nil | ||
| end | ||
| local str = file:read("a") | ||
| file:close() | ||
|
|
||
| local decoded = quarto.json.decode(str) | ||
| return decoded | ||
| end | ||
|
|
||
| local function read_inv_text_or_json(base_name) | ||
| local file = io.open(base_name .. ".txt", "r") | ||
| if file then | ||
| -- TODO: refactors so we don't just close the file immediately | ||
| io.close(file) | ||
| json = read_inv_text(base_name .. ".txt") | ||
|
|
||
| else | ||
| json = read_json(base_name .. ".json") | ||
| end | ||
|
|
||
| return json | ||
| end | ||
|
|
||
| local inventory = {} | ||
|
|
||
| local function lookup(search_object) | ||
|
|
||
| local results = {} | ||
| for _, inv in ipairs(inventory) do | ||
| for _, item in ipairs(inv.items) do | ||
| -- e.g. :external+<inv_name>:<domain>:<role>:`<name>` | ||
| if item.inv_name and item.inv_name ~= search_object.inv_name then | ||
| goto continue | ||
| end | ||
|
|
||
| if item.name ~= search_object.name then | ||
| goto continue | ||
| end | ||
|
|
||
| if search_object.role and item.role ~= search_object.role then | ||
| goto continue | ||
| end | ||
|
|
||
| if search_object.domain and item.domain ~= search_object.domain then | ||
| goto continue | ||
| else | ||
| if search_object.domain or item.domain == "py" then | ||
| table.insert(results, item) | ||
| end | ||
|
|
||
| goto continue | ||
| end | ||
|
|
||
| ::continue:: | ||
| end | ||
| end | ||
|
|
||
| if #results == 1 then | ||
| return results[1] | ||
| end | ||
| if #results > 1 then | ||
| quarto.log.warning("Found multiple matches for " .. search_object.name .. ", using the first match.") | ||
| return results[1] | ||
| end | ||
| if #results == 0 then | ||
| quarto.log.warning("Found no matches for object:\n", search_object) | ||
| end | ||
|
|
||
| return nil | ||
| end | ||
|
|
||
| local function mysplit (inputstr, sep) | ||
| if sep == nil then | ||
| sep = "%s" | ||
| end | ||
| local t={} | ||
| for str in string.gmatch(inputstr, "([^"..sep.."]+)") do | ||
| table.insert(t, str) | ||
| end | ||
| return t | ||
| end | ||
|
|
||
| local function normalize_role(role) | ||
| if role == "func" then | ||
| return "function" | ||
| end | ||
| return role | ||
| end | ||
|
|
||
| local function build_search_object(str) | ||
| local starts_with_colon = str:sub(1, 1) == ":" | ||
| local search = {} | ||
| if starts_with_colon then | ||
| local t = mysplit(str, ":") | ||
| if #t == 2 then | ||
| -- e.g. :py:func:`my_func` | ||
| search.role = normalize_role(t[1]) | ||
| search.name = t[2]:match("%%60(.*)%%60") | ||
| elseif #t == 3 then | ||
| -- e.g. :py:func:`my_func` | ||
| search.domain = t[1] | ||
| search.role = normalize_role(t[2]) | ||
| search.name = t[3]:match("%%60(.*)%%60") | ||
| elseif #t == 4 then | ||
| -- e.g. :ext+inv:py:func:`my_func` | ||
| search.external = true | ||
|
|
||
| search.inv_name = t[1]:match("external%+(.*)") | ||
| search.domain = t[2] | ||
| search.role = normalize_role(t[3]) | ||
| search.name = t[4]:match("%%60(.*)%%60") | ||
| else | ||
| quarto.log.warning("couldn't parse this link: " .. str) | ||
| return {} | ||
| end | ||
| else | ||
| search.name = str:match("%%60(.*)%%60") | ||
| end | ||
|
|
||
| if search.name == nil then | ||
| quarto.log.warning("couldn't parse this link: " .. str) | ||
| return {} | ||
| end | ||
|
|
||
| if search.name:sub(1, 1) == "~" then | ||
| search.shortened = true | ||
| search.name = search.name:sub(2, -1) | ||
| end | ||
| return search | ||
| end | ||
|
|
||
| local function report_broken_link(link, search_object, replacement) | ||
| -- TODO: how to unescape html elements like [? | ||
| return pandoc.Code(pandoc.utils.stringify(link.content)) | ||
| end | ||
|
|
||
| function Link(link) | ||
| -- do not process regular links ---- | ||
| if not link.target:match("%%60") then | ||
| return link | ||
| end | ||
|
|
||
| -- lookup item ---- | ||
| local search = build_search_object(link.target) | ||
| local item = lookup(search) | ||
|
|
||
| -- determine replacement, used if no link text specified ---- | ||
| local original_text = pandoc.utils.stringify(link.content) | ||
| local replacement = search.name | ||
| if search.shortened then | ||
| local t = mysplit(search.name, ".") | ||
| replacement = t[#t] | ||
| end | ||
|
|
||
| -- set link text ---- | ||
| if original_text == "" and replacement ~= nil then | ||
| link.content = pandoc.Code(replacement) | ||
| end | ||
|
|
||
| -- report broken links ---- | ||
| if item == nil then | ||
| return report_broken_link(link, search) | ||
| end | ||
| link.target = item.uri:gsub("%$$", search.name) | ||
|
|
||
|
|
||
| return link | ||
| end | ||
|
|
||
| local function fixup_json(json, prefix) | ||
| for _, item in ipairs(json.items) do | ||
| item.uri = prefix .. item.uri | ||
| end | ||
| table.insert(inventory, json) | ||
| end | ||
|
|
||
| return { | ||
| { | ||
| Meta = function(meta) | ||
| local json | ||
| local prefix | ||
| for k, v in pairs(meta.interlinks.sources) do | ||
| local base_name = quarto.project.offset .. "/_inv/" .. k .. "_objects" | ||
| json = read_inv_text_or_json(base_name) | ||
| prefix = pandoc.utils.stringify(v.url) | ||
| if json ~= nil then | ||
| fixup_json(json, prefix) | ||
| end | ||
| end | ||
| json = read_inv_text_or_json(quarto.project.offset .. "/objects") | ||
| if json ~= nil then | ||
| fixup_json(json, "/") | ||
| end | ||
| end | ||
| }, | ||
| { | ||
| Link = Link | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| title: Font Awesome support | ||
| author: Carlos Scheidegger | ||
| version: 1.1.0 | ||
| quarto-required: ">=1.2.269" | ||
| contributes: | ||
| shortcodes: | ||
| - fontawesome.lua |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| .fa-tiny { | ||
| font-size: 0.5em; | ||
| } | ||
| .fa-scriptsize { | ||
| font-size: 0.7em; | ||
| } | ||
| .fa-footnotesize { | ||
| font-size: 0.8em; | ||
| } | ||
| .fa-small { | ||
| font-size: 0.9em; | ||
| } | ||
| .fa-normalsize { | ||
| font-size: 1em; | ||
| } | ||
| .fa-large { | ||
| font-size: 1.2em; | ||
| } | ||
| .fa-Large { | ||
| font-size: 1.5em; | ||
| } | ||
| .fa-LARGE { | ||
| font-size: 1.75em; | ||
| } | ||
| .fa-huge { | ||
| font-size: 2em; | ||
| } | ||
| .fa-Huge { | ||
| font-size: 2.5em; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| local function ensureLatexDeps() | ||
| quarto.doc.use_latex_package("fontawesome5") | ||
| end | ||
|
|
||
| local function ensureHtmlDeps() | ||
| quarto.doc.add_html_dependency({ | ||
| name = 'fontawesome6', | ||
| version = '0.1.0', | ||
| stylesheets = {'assets/css/all.css', 'assets/css/latex-fontsize.css'} | ||
| }) | ||
| end | ||
|
|
||
| local function isEmpty(s) | ||
| return s == nil or s == '' | ||
| end | ||
|
|
||
| local function isValidSize(size) | ||
| local validSizes = { | ||
| "tiny", | ||
| "scriptsize", | ||
| "footnotesize", | ||
| "small", | ||
| "normalsize", | ||
| "large", | ||
| "Large", | ||
| "LARGE", | ||
| "huge", | ||
| "Huge" | ||
| } | ||
| for _, v in ipairs(validSizes) do | ||
| if v == size then | ||
| return size | ||
| end | ||
| end | ||
| return "" | ||
| end | ||
|
|
||
| return { | ||
| ["fa"] = function(args, kwargs) | ||
|
|
||
| local group = "solid" | ||
| local icon = pandoc.utils.stringify(args[1]) | ||
| if #args > 1 then | ||
| group = icon | ||
| icon = pandoc.utils.stringify(args[2]) | ||
| end | ||
|
|
||
| local title = pandoc.utils.stringify(kwargs["title"]) | ||
| if not isEmpty(title) then | ||
| title = " title=\"" .. title .. "\"" | ||
| end | ||
|
|
||
| local label = pandoc.utils.stringify(kwargs["label"]) | ||
| if isEmpty(label) then | ||
| label = " aria-label=\"" .. icon .. "\"" | ||
| else | ||
| label = " aria-label=\"" .. label .. "\"" | ||
| end | ||
|
|
||
| local size = pandoc.utils.stringify(kwargs["size"]) | ||
|
|
||
| -- detect html (excluding epub which won't handle fa) | ||
| if quarto.doc.is_format("html:js") then | ||
| ensureHtmlDeps() | ||
| if not isEmpty(size) then | ||
| size = " fa-" .. size | ||
| end | ||
| return pandoc.RawInline( | ||
| 'html', | ||
| "<i class=\"fa-" .. group .. " fa-" .. icon .. size .. "\"" .. title .. label .. "></i>" | ||
| ) | ||
| -- detect pdf / beamer / latex / etc | ||
| elseif quarto.doc.is_format("pdf") then | ||
| ensureLatexDeps() | ||
| if isEmpty(isValidSize(size)) then | ||
| return pandoc.RawInline('tex', "\\faIcon{" .. icon .. "}") | ||
| else | ||
| return pandoc.RawInline('tex', "{\\" .. size .. "\\faIcon{" .. icon .. "}}") | ||
| end | ||
| else | ||
| return pandoc.Null() | ||
| end | ||
| end | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| - source: project | ||
| netlify: | ||
| - id: 3832aec3-a89a-4f2e-b185-232eb2f077da | ||
| url: 'https://ibis-quarto.netlify.app' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| from __future__ import annotations | ||
|
|
||
| import quartodoc as qd | ||
| import toolz | ||
| from plum import dispatch | ||
|
|
||
|
|
||
| class Renderer(qd.MdRenderer): | ||
| style = "ibis" | ||
|
|
||
| @dispatch | ||
| def render(self, el: qd.ast.ExampleCode) -> str: | ||
| lines = el.value.splitlines() | ||
|
|
||
| result = [] | ||
|
|
||
| prompt = ">>> " | ||
| continuation = "..." | ||
|
|
||
| skip_doctest = "doctest: +SKIP" | ||
| expect_failure = "quartodoc: +EXPECTED_FAILURE" | ||
| quartodoc_skip_doctest = "quartodoc: +SKIP" | ||
|
|
||
| chunker = lambda line: line.startswith((prompt, continuation)) | ||
| should_skip = ( | ||
| lambda line: quartodoc_skip_doctest in line or skip_doctest in line | ||
| ) | ||
|
|
||
| for chunk in toolz.partitionby(chunker, lines): | ||
| first, *rest = chunk | ||
|
|
||
| # only attempt to execute or render code blocks that start with the | ||
| # >>> prompt | ||
| if first.startswith(prompt): | ||
| # check whether to skip execution and if so, render the code | ||
| # block as `python` (not `{python}`) if it's marked with | ||
| # skip_doctest, expect_failure or quartodoc_skip_doctest | ||
| if not any(map(should_skip, chunk)): | ||
| start, end = "{}" | ||
| else: | ||
| start = end = "" | ||
|
|
||
| result.append(f"```{start}python{end}") | ||
|
|
||
| # if we expect failures, don't fail the notebook execution and | ||
| # render the error message | ||
| if expect_failure in first or any( | ||
| expect_failure in line for line in rest | ||
| ): | ||
| assert ( | ||
| start and end | ||
| ), "expected failure should never occur alongside a skipped doctest example" | ||
| result.append("#| error: true") | ||
|
|
||
| # remove the quartodoc markers from the rendered code | ||
| result.append( | ||
| first.replace(f"# {quartodoc_skip_doctest}", "") | ||
| .replace(quartodoc_skip_doctest, "") | ||
| .replace(f"# {expect_failure}", "") | ||
| .replace(expect_failure, "") | ||
| ) | ||
| result.extend(rest) | ||
| result.append("```\n") | ||
|
|
||
| return "\n".join(result) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| You can install Ibis and a supported backend with `pip`, `conda`, `mamba`, or `pixi`. | ||
|
|
||
| ::: {.panel-tabset} | ||
|
|
||
| ```{python} | ||
| #| echo: false | ||
| #| output: asis | ||
|
|
||
| backends = [ | ||
| {"name": "BigQuery", "module": "bigquery"}, | ||
| {"name": "ClickHouse", "module": "clickhouse"}, | ||
| {"name": "Dask", "module": "dask"}, | ||
| {"name": "DataFusion", "module": "datafusion"}, | ||
| {"name": "Druid", "module": "druid"}, | ||
| {"name": "DuckDB", "module": "duckdb"}, | ||
| {"name": "Impala", "module": "impala"}, | ||
| {"name": "MSSQL", "module": "mssql"}, | ||
| {"name": "MySQL", "module": "mysql"}, | ||
| {"name": "Oracle", "module": "oracle"}, | ||
| {"name": "pandas", "module": "pandas"}, | ||
| {"name": "Polars", "module": "polars"}, | ||
| {"name": "PostgreSQL", "module": "postgres"}, | ||
| {"name": "PySpark", "module": "pyspark"}, | ||
| {"name": "Snowflake", "module": "snowflake"}, | ||
| {"name": "SQLite", "module": "sqlite"}, | ||
| {"name": "Trino", "module": "trino"}, | ||
| ] | ||
|
|
||
| installers = [ | ||
| {"name": "pip", "line": "Install with the `{extra}` extra:", "cmd": "pip install 'ibis-framework[{extra}]'"}, | ||
| {"name": "conda", "line": "Install the `ibis-{extra}` package:", "cmd": "conda install -c conda-forge ibis-{extra}"}, | ||
| {"name": "mamba", "line": "Install the `ibis-{extra}` package:", "cmd": "mamba install -c conda-forge ibis-{extra}"}, | ||
| {"name": "pixi", "line": "Add the `ibis-{extra}` package:", "cmd": "pixi add ibis-{extra}"}, | ||
| ] | ||
|
|
||
| for installer in installers: | ||
| name = installer["name"] | ||
| cmd = installer["cmd"] | ||
| line = installer["line"] | ||
|
|
||
| print(f"## `{name}`") | ||
|
|
||
| print("::: {.panel-tabset}") | ||
| print() | ||
|
|
||
| for backend in backends: | ||
| name = backend["name"] | ||
| mod = backend["module"] | ||
| extra = backend.get("extra", mod) | ||
|
|
||
| print(f"## {name}") | ||
| print() | ||
| print(line.format(extra=extra)) | ||
| print() | ||
| print(f"```bash\n{cmd.format(extra=extra)}\n```") | ||
| print() | ||
| print(f"Connect using [`ibis.{mod}.connect`](./backends/{name.lower()}.qmd#ibis.{mod}.connect).") | ||
| print() | ||
|
|
||
| if name == "pip": | ||
| print("{{< include /_callouts/pypi_warning.qmd >}}") | ||
|
|
||
| print() | ||
| print(":::") | ||
| print() | ||
| ``` | ||
|
|
||
| ::: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| We recommend starting with the default (DuckDB) backend for a performant, fully-featured local experience. You can install Ibis with `pip`, `conda`, `mamba`, or `pixi`. | ||
|
|
||
| ::: {.panel-tabset} | ||
|
|
||
| ## Using `pip` | ||
|
|
||
| ```bash | ||
| pip install 'ibis-framework[duckdb]' | ||
| ``` | ||
|
|
||
| {{< include /_callouts/pypi_warning.qmd >}} | ||
|
|
||
| ## Using `conda` | ||
|
|
||
| ```bash | ||
| conda install -c conda-forge ibis-duckdb | ||
| ``` | ||
|
|
||
| ## Using `mamba` | ||
|
|
||
| ```bash | ||
| mamba install -c conda-forge ibis-duckdb | ||
| ``` | ||
|
|
||
| ## Using `pixi` | ||
|
|
||
| ```bash | ||
| pixi add ibis-duckdb | ||
| ``` | ||
|
|
||
| ::: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| ```{python} | ||
| #| echo: false | ||
| #| output: asis | ||
|
|
||
| from _utils import get_backend, render_methods | ||
|
|
||
| # defined in the backend qmd, e.g., ../bigquery.qmd | ||
| module = BACKEND.lower() | ||
| backend = get_backend(module) | ||
|
|
||
| print(f"## `{module}.Backend` {{ #{backend.canonical_path} }}") | ||
|
|
||
| methods = sorted( | ||
| key for key, value in backend.members.items() | ||
| if value.is_function | ||
| if not value.name.startswith("_") | ||
| if value.name != "do_connect" | ||
| ) | ||
|
|
||
| render_methods(backend, *methods, level=3) | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from functools import cache, partial | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| from quartodoc import MdRenderer, get_object | ||
|
|
||
| if TYPE_CHECKING: | ||
| from collections.abc import Iterator | ||
|
|
||
|
|
||
| @cache | ||
| def get_renderer(level: int) -> MdRenderer: | ||
| return MdRenderer(header_level=level) | ||
|
|
||
|
|
||
| @cache | ||
| def get_backend(backend: str): | ||
| return get_object(f"ibis.backends.{backend}", "Backend") | ||
|
|
||
|
|
||
| def get_callable(obj, name): | ||
| try: | ||
| return obj.get_member(name) | ||
| except KeyError: | ||
| return obj.functions[name] | ||
|
|
||
|
|
||
| def find_member_with_docstring(member): | ||
| """Find the first inherited member with a docstring.""" | ||
| if member.docstring is not None: | ||
| return member | ||
|
|
||
| cls = member.parent | ||
| for base in cls.resolved_bases: | ||
| try: | ||
| parent_member = get_callable(base, member.name) | ||
| except KeyError: | ||
| continue | ||
| else: | ||
| if parent_member.docstring is not None: | ||
| return parent_member | ||
| return member | ||
|
|
||
|
|
||
| def render_method(*, member, renderer: MdRenderer) -> Iterator[str]: | ||
| header_level = renderer.crnt_header_level | ||
| header = "#" * header_level | ||
| name = member.name | ||
| try: | ||
| params = renderer.render(member.parameters) | ||
| except AttributeError: | ||
| params = None | ||
| yield "\n" | ||
| yield f"{header} {name} {{ #{member.path} }}" | ||
| yield "\n" | ||
| if params is not None: | ||
| yield f"`{name}({params})`" | ||
| yield "\n" | ||
|
|
||
| yield renderer.render(find_member_with_docstring(member)) | ||
|
|
||
|
|
||
| def render_methods(obj, *methods: str, level: int) -> None: | ||
| renderer = get_renderer(level) | ||
| get = partial(get_callable, obj) | ||
| print( # noqa: T201 | ||
| "\n".join( | ||
| line | ||
| for member in map(get, methods) | ||
| for line in render_method(member=member, renderer=renderer) | ||
| ) | ||
| ) | ||
|
|
||
|
|
||
| def render_do_connect(backend, level: int = 4) -> None: | ||
| render_methods(get_backend(backend), "do_connect", level=level) |