diff --git a/.ai/skills/audit-skill-md/SKILL.md b/.ai/skills/audit-skill-md/SKILL.md deleted file mode 100644 index ba5255a59..000000000 --- a/.ai/skills/audit-skill-md/SKILL.md +++ /dev/null @@ -1,290 +0,0 @@ - - ---- -name: audit-skill-md -description: Audit the user-facing skill at skills/datafusion_python/SKILL.md against the current public Python API. Find new APIs that should be documented, stale mentions of removed/renamed APIs, examples that drifted from current idiomatic style, and places that need a "requires datafusion-python NN or newer" note. Run after upstream syncs and before each release. -argument-hint: [scope] (e.g., "session-context", "dataframe", "expr", "functions", "patterns", "pitfalls", "version-notes", "all") ---- - -# Audit `skills/datafusion_python/SKILL.md` - -You are auditing the user-facing skill at -[`skills/datafusion_python/SKILL.md`](../../skills/datafusion_python/SKILL.md) -against the current state of the Python API. The skill is the source of truth -for how AI coding assistants are taught to write `datafusion-python` code, so -it must match what the project actually ships. This skill identifies gaps -caused by upstream syncs, refactors, or renames, and (if asked) applies the -edits directly to `SKILL.md`. - -The skill is most usefully run **after** the `check-upstream` step of an -upstream sync (see `dev/release/upstream-sync.md`) — once any new APIs are -exposed, this skill makes sure they get documented. - -## What the skill covers - -The user-facing `SKILL.md` documents these public surfaces. This list is not -exhaustive — if a new top-level area is added (e.g., a new `Catalog` API -exposed at the package root), include it. - -| Surface | Module | Sections in SKILL.md | -|---|---|---| -| `SessionContext` | `python/datafusion/context.py` | "Data Loading" | -| `DataFrame` | `python/datafusion/dataframe.py` | "DataFrame Operations Quick Reference", "Executing and Collecting Results", "Idiomatic Patterns" | -| `Expr` | `python/datafusion/expr.py` | "Expression Building", "Common Pitfalls" | -| `functions` | `python/datafusion/functions/__init__.py` | "Available Functions (Categorized)", scattered uses throughout | -| `functions.spark` | `python/datafusion/functions/spark.py` | "Available Functions (Categorized)" → "Spark-Compatible Functions" subsection | -| Top-level helpers (`col`, `lit`, `WindowFrame`, ...) | `python/datafusion/__init__.py` | "Import Conventions", "Core Abstractions" | - -## Scope argument - -The user may specify a scope via `$ARGUMENTS` to limit the audit. If no scope -is given or `all` is specified, audit every area. - -| Scope | Audit target | -|---|---| -| `session-context` | `SessionContext` methods and the "Data Loading" section | -| `dataframe` | `DataFrame` methods and the operations / executing / patterns sections | -| `expr` | `Expr` methods/operators and the "Expression Building" section | -| `functions` | `functions/__init__.py` `__all__` and the "Available Functions (Categorized)" section | -| `spark-functions` | `functions/spark.py` `__all__`, the "Spark-Compatible Functions" subsection, and the divergent-semantics table | -| `patterns` | "Idiomatic Patterns" section — confirm patterns still match recommended style | -| `pitfalls` | "Common Pitfalls" — confirm each pitfall still reproduces, drop ones fixed upstream | -| `version-notes` | Cross-check version annotations (see below) | -| `all` | Everything above | - -## Inputs to read - -Before producing the report: - -1. `skills/datafusion_python/SKILL.md` — the document being audited. -2. The relevant Python module(s) for the chosen scope. Public surface is the - `__all__` list (where defined) plus `class` and `def` symbols not prefixed - with `_`. -3. `Cargo.toml` (root) for the current `datafusion-python` version — read - the `version` field under `[workspace.package]` (format `NN.0.0`). The - major version always matches the upstream `datafusion` crate, so a - single `datafusion-python` version expresses both. - `python/datafusion/__init__.py`'s `__version__` is the same value - exposed at runtime. -4. Recent commits touching the relevant module(s) for context on what - changed since the last sync: - ```bash - git log --oneline -- python/datafusion/dataframe.py | head -20 - ``` - -## What to look for - -Walk through each scoped area and flag four kinds of issues. - -### 1. New APIs not mentioned - -For each public symbol in the module's `__all__` (or each public class -method), check whether it appears anywhere in `SKILL.md`. A symbol is -"covered" if it shows up in: - -- A code block (the strongest signal — it's demonstrated). -- The "Available Functions (Categorized)" list. -- The SQL-to-DataFrame Reference table. - -**Decide whether each missing symbol deserves an entry.** Not every public -symbol belongs in `SKILL.md` — the skill is curated for the patterns users -hit daily, not exhaustive API reference. Use these heuristics: - -- **Add it** if it replaces or supersedes something already in the skill - (e.g., a new operation that is the idiomatic alternative to a documented - workaround). -- **Add it** if it fits a category already present (a new aggregate function - goes in the aggregate list; a new join type goes in the joining section). -- **Add it** if it changes how a documented pattern should be written. -- **Skip it** if it is genuinely niche / advanced / experimental. -- **Skip it** if it is internal plumbing exposed for FFI but not user-facing. - -When you flag a missing symbol, include a one-line proposed insertion point -(which section / which table row) so a reviewer can decide quickly. - -### 2. Stale mentions - -For each function name, method name, or import shown in `SKILL.md`, verify it -still exists in the current API: - -- Function names mentioned in prose or in the categorized list should appear - in `python/datafusion/functions/__init__.py`'s `__all__`. -- Spark function names mentioned in the "Spark-Compatible Functions" - subsection should appear in `python/datafusion/functions/spark.py`'s - `__all__`. Also confirm the divergent-semantics table still matches the - current spark vs. main signatures. -- Method calls in code blocks should resolve against the current class. -- Imports (`from datafusion import ...`) should succeed against the current - `__init__.py`. - -A quick way to check imports without running them: - -```bash -python -c "from datafusion import SessionContext, col, lit; from datafusion import functions as F; print('ok')" -``` - -For each stale mention, propose either: -- a rename to the current name, or -- removal if the API is gone with no replacement. - -### 3. Examples that drifted from idiomatic style - -The skill teaches a Pythonic style: prefer plain strings to `col(...)` when a -column reference is all you need; prefer raw Python values to `lit(...)` -where auto-wrapping applies. Recent refactors (see the `make-pythonic` -skill) keep moving more functions toward accepting native types. - -For each code example in `SKILL.md`, check: - -- Does it use `lit(value)` where a raw value would work? Comparison RHS, - arithmetic with a column, etc. all auto-wrap. (Reserve `lit()` for the - cases listed in pitfall #2.) -- Does it use `col("name")` where a plain string would work? `select(...)`, - `aggregate([keys], ...)`, `sort(...)`, `sort_by(...)` all accept plain - name strings. -- Do `functions.py` calls match the current pythonic signature for that - function? If `make-pythonic` recently changed a signature (e.g., - `repeat(string, n: Expr | int)`), the example should pass `3` rather than - `lit(3)`. -- Does any example use a deprecated or removed parameter name? - -For drift, propose the updated snippet. If the change is purely stylistic -and the older form still works, mark the suggestion as **non-blocking**. - -### 4. Missing or stale version notes - -When an API depends on a specific version, the skill should say so — -otherwise an agent referencing the skill in an older project will write -code that fails at import or at runtime. - -`datafusion-python` shares its major version number with the upstream -`datafusion` crate (e.g., `datafusion-python 53.x` tracks upstream -`datafusion 53`). Always express version requirements in terms of -`datafusion-python` only — there is no need to call out upstream and -package versions separately. - -Add a version note when: - -- A method or function shown in the skill was added in a specific release - (e.g., a new `DataFrame` method that didn't exist before 53). -- A breaking change altered behavior in a specific release (signature - change, default-value change, new required argument). -- A pitfall was fixed in a specific release. Either annotate the pitfall - block with "fixed in datafusion-python NN, kept here for users on older - versions" or remove it once the supported floor moves past that version. - -Format for version notes (inline, italicized): - -```markdown -*Requires datafusion-python 53 or newer.* -``` - -For each missing/stale version note, propose the exact line and where it -belongs. - -## How to discover changes since the last audit - -If the user supplies a previous version or commit SHA where the audit was -last run, diff against it: - -```bash -# Public-API-relevant changes since SHA -git log --oneline ..HEAD -- python/datafusion/ - -# Whose signatures actually moved -git diff ..HEAD -- python/datafusion/functions.py | grep '^[+-]def ' -``` - -If no prior audit point is given, fall back to "since the last upstream -sync" by inspecting commits that touch `Cargo.toml`'s `datafusion` pin: - -```bash -git log --oneline -- Cargo.toml | grep -i datafusion | head -5 -``` - -## Output Format - -Produce a report grouped by scope. Each finding is one bullet with a -proposed action, so a maintainer can review the list quickly and apply -edits in order. - -``` -## SKILL.md Audit (scope: ) - -Audited against: -- skills/datafusion_python/SKILL.md @ -- datafusion-python - -### New APIs to cover -- `DataFrame.foo()` — added in datafusion-python 53. Insert in "DataFrame Operations Quick Reference" under . - Proposed snippet: - ```python - df.foo(...) - ``` - -### Stale mentions -- "old_function_name" referenced in the categorized list (line N) — renamed to "new_function_name". Replace. - -### Drifted examples -- "Filtering" section, `df.filter(col("a") > lit(10))` — drop `lit(10)`, auto-wrap applies. (non-blocking) -- "Aggregation" section, `df.aggregate([col("region")], ...)` — pass `"region"` as a plain string per "Projection" guidance. - -### Version notes -- `DataFrame.foo()` block needs *Requires datafusion-python 53 or newer.* -- "Common Pitfalls" #N — fixed in datafusion-python 53; remove the pitfall and update the SQL-to-DataFrame row to no longer flag the workaround. - -### No-change confirmed -- `SessionContext` data-loading section — all entries match current API. -``` - -If asked to apply the changes, edit `skills/datafusion_python/SKILL.md` -directly with `Edit` tool calls, one finding at a time, and re-run the -relevant doctest sanity check at the end: - -```bash -pytest --doctest-modules python/datafusion -q -``` - -## What NOT to flag - -- **Internal helpers / underscored names.** Private symbols are not part of - the user-facing surface. -- **Functions intentionally omitted.** Niche / advanced APIs (custom - catalogs, raw FFI plumbing, low-level execution plan accessors) live in - the API reference, not the skill. If an omission was deliberate and a - comment / commit explains why, leave it out. -- **Style nits inside explanatory prose.** The skill mixes example code and - prose; only enforce the pythonic style on actual code blocks. -- **Function-by-function coverage of every `functions.py` symbol.** The - "Available Functions (Categorized)" list is curated by category, not - exhaustive. Adding a single new aggregate to the aggregate list is - enough — the user follows the pointer to the API reference for the rest. - -## Coordination with other skills - -- Run `/check-upstream` first to expose any missing upstream APIs into the - Python layer. Without that, this skill cannot recommend documenting - something that is not yet exposed. -- Run `/make-pythonic` before this skill if a Pythonic-signature pass is - planned for a release — that way this skill can update examples to the - final signature in one shot rather than churning them twice. -- The order during an upstream sync (PR 3 of `dev/release/upstream-sync.md`) - is therefore: `/check-upstream` → `/make-pythonic` (optional) → - `/audit-skill-md`. diff --git a/.ai/skills/check-upstream/SKILL.md b/.ai/skills/check-upstream/SKILL.md deleted file mode 100644 index a3d82a670..000000000 --- a/.ai/skills/check-upstream/SKILL.md +++ /dev/null @@ -1,477 +0,0 @@ - - ---- -name: check-upstream -description: Check if upstream Apache DataFusion features (functions, DataFrame ops, SessionContext methods, FFI types) are exposed in this Python project. Use when adding missing functions, auditing API coverage, or ensuring parity with upstream. -argument-hint: [area] (e.g., "scalar functions", "aggregate functions", "window functions", "dataframe", "session context", "ffi types", "all") ---- - -# Check Upstream DataFusion Feature Coverage - -You are auditing the datafusion-python project to find features from the upstream Apache DataFusion Rust library that are **not yet exposed** in this Python binding project. Your goal is to identify gaps and, if asked, implement the missing bindings. - -**IMPORTANT: The Python API is the source of truth for coverage.** A function or method is considered "exposed" if it exists in the Python API (e.g., `python/datafusion/functions.py`), even if there is no corresponding entry in the Rust bindings. Many upstream functions are aliases of other functions — the Python layer can expose these aliases by calling a different underlying Rust binding. Do NOT report a function as missing if it appears in the Python `__all__` list and has a working implementation, regardless of whether a matching `#[pyfunction]` exists in Rust. - -**IMPORTANT: audit the total upstream surface, not the delta since the last pin.** Gaps accumulate across syncs. A patch-release bump with a "bug fixes only" changelog does not mean there is nothing to find — pre-existing gaps from earlier majors still need to be surfaced. Always run the full comparison. - -## Compile-Signal Triggers - -If a recent upstream bump required *any* of the following while fixing -compile errors in `crates/core/` or the FFI example, treat that as a -**hard signal** that user-facing surface area grew and run this skill -before considering the bump done. Each pattern corresponds to a class of -gap that frequently shows up in the audit: - -| Signal during PR 1 compile fix | Likely gap to check | -|---|---| -| New `Expr::*` variant added to a non-exhaustive `match` (`HigherOrderFunction`, `Lambda`, `LambdaVariable`, …) | New lambda / higher-order scalar functions (`any_match`, `array_transform`, `list_transform`, …) | -| New `ScalarValue::*` variant (`ListView`, `LargeListView`, …) | New scalar / array functions that consume or produce the type | -| New required trait method on `ExecutionPlan` / `TableProvider` / `*UDFImpl` (`apply_expressions`, …) | Corresponding capability on the Python wrapper class | -| Renamed or restructured struct field (e.g. `Cast.data_type` → `Cast.field: FieldRef`) | Any Python accessor / SKILL.md doc that read the old field | -| Newly deprecated trait method with a `_with_args` / `_with_options` replacement | The `*_with_options` variant frequently warrants a separate Python entry point | - -PR 1 of `dev/release/upstream-sync.md` asks you to log these signals as -they appear. When you run this skill, use that log as a checklist: every -entry must either show up in the audit output or be explicitly skipped -with a reason. - -## Areas to Check - -The user may specify an area via `$ARGUMENTS`. If no area is specified or "all" is given, check all areas. - -### 1. Scalar Functions - -**Upstream source of truth:** -- Rust docs: https://docs.rs/datafusion/latest/datafusion/functions/index.html -- User docs: https://datafusion.apache.org/user-guide/sql/scalar_functions.html - -**Where they are exposed in this project:** -- Python API: `python/datafusion/functions.py` — each function wraps a call to `datafusion._internal.functions` -- Rust bindings: `crates/core/src/functions.rs` — `#[pyfunction]` definitions registered via `init_module()` - -**Evaluated and not requiring separate Python exposure:** -- `get_field_path` — already covered by `get_field(expr, *names)`, which takes a - variadic field path and dispatches to the same underlying - `functions::core::get_field` UDF as the upstream `get_field_path` helper. - -**How to check:** -1. Fetch the upstream scalar function documentation page -2. Compare against functions listed in `python/datafusion/functions.py` (check the `__all__` list and function definitions) -3. A function is covered if it exists in the Python API — it does NOT need a dedicated Rust `#[pyfunction]`. Many functions are aliases that reuse another function's Rust binding. -4. Check against the "evaluated and not requiring exposure" list before flagging as a gap -5. Only report functions that are missing from the Python `__all__` list / function definitions - -### 2. Aggregate Functions - -**Upstream source of truth:** -- Rust docs: https://docs.rs/datafusion/latest/datafusion/functions_aggregate/index.html -- User docs: https://datafusion.apache.org/user-guide/sql/aggregate_functions.html - -**Where they are exposed in this project:** -- Python API: `python/datafusion/functions.py` (aggregate functions are mixed in with scalar functions) -- Rust bindings: `crates/core/src/functions.rs` - -**Evaluated and not requiring separate Python exposure:** -- `count_distinct` — covered by `count(expr, distinct=True)`. Both forms call - `count_udaf` with `distinct: bool = true` and produce the same logical plan. -- `sum_distinct` — covered by `sum(expr, distinct=True)`. -- `avg_distinct` — covered by `avg(expr, distinct=True)`. - -**How to check:** -1. Fetch the upstream aggregate function documentation page -2. Compare against aggregate functions in `python/datafusion/functions.py` (check `__all__` list and function definitions) -3. A function is covered if it exists in the Python API, even if it aliases another function's Rust binding -4. Check against the "evaluated and not requiring exposure" list before flagging as a gap -5. Report only functions missing from the Python API - -### 3. Window Functions - -**Upstream source of truth:** -- Rust docs: https://docs.rs/datafusion/latest/datafusion/functions_window/index.html -- User docs: https://datafusion.apache.org/user-guide/sql/window_functions.html - -**Where they are exposed in this project:** -- Python API: `python/datafusion/functions.py` (window functions like `rank`, `dense_rank`, `lag`, `lead`, etc.) -- Rust bindings: `crates/core/src/functions.rs` - -**How to check:** -1. Fetch the upstream window function documentation page -2. Compare against window functions in `python/datafusion/functions.py` (check `__all__` list and function definitions) -3. A function is covered if it exists in the Python API, even if it aliases another function's Rust binding -4. Report only functions missing from the Python API - -### 4. Table Functions - -**Upstream source of truth:** -- Rust docs: https://docs.rs/datafusion/latest/datafusion/functions_table/index.html -- User docs: https://datafusion.apache.org/user-guide/sql/table_functions.html (if available) - -**Where they are exposed in this project:** -- Python API: `python/datafusion/functions.py` and `python/datafusion/user_defined.py` (TableFunction/udtf) -- Rust bindings: `crates/core/src/functions.rs` and `crates/core/src/udtf.rs` - -**How to check:** -1. Fetch the upstream table function documentation -2. Compare against what's available in the Python API -3. A function is covered if it exists in the Python API, even if it aliases another function's Rust binding -4. Report only functions missing from the Python API - -### 5. DataFrame Operations - -**Upstream source of truth:** -- Rust docs: https://docs.rs/datafusion/latest/datafusion/dataframe/struct.DataFrame.html - -**Where they are exposed in this project:** -- Python API: `python/datafusion/dataframe.py` — the `DataFrame` class -- Rust bindings: `crates/core/src/dataframe.rs` — `PyDataFrame` with `#[pymethods]` - -**Evaluated and not requiring separate Python exposure:** -- `show_limit` — already covered by `DataFrame.show()`, which provides the same functionality with a simpler API -- `with_param_values` — already covered by the `param_values` argument on `SessionContext.sql()`, which accomplishes the same thing more robustly -- `union_by_name_distinct` — already covered by `DataFrame.union_by_name(distinct=True)`, which provides a more Pythonic API - -**How to check:** -1. Fetch the upstream DataFrame documentation page listing all methods -2. Compare against methods in `python/datafusion/dataframe.py` — this is the source of truth for coverage -3. The Rust bindings (`crates/core/src/dataframe.rs`) may be consulted for context, but a method is covered if it exists in the Python API -4. Check against the "evaluated and not requiring exposure" list before flagging as a gap -5. Report only methods missing from the Python API - -### 6. SessionContext Methods - -**Upstream source of truth:** -- Rust docs: https://docs.rs/datafusion/latest/datafusion/execution/context/struct.SessionContext.html - -**Where they are exposed in this project:** -- Python API: `python/datafusion/context.py` — the `SessionContext` class -- Rust bindings: `crates/core/src/context.rs` — `PySessionContext` with `#[pymethods]` - -**How to check:** -1. Fetch the upstream SessionContext documentation page listing all methods -2. Compare against methods in `python/datafusion/context.py` — this is the source of truth for coverage -3. The Rust bindings (`crates/core/src/context.rs`) may be consulted for context, but a method is covered if it exists in the Python API -4. Report only methods missing from the Python API - -### 7. FFI Types (datafusion-ffi) - -**Upstream source of truth:** -- Crate source: https://github.com/apache/datafusion/tree/main/datafusion/ffi/src -- Rust docs: https://docs.rs/datafusion-ffi/latest/datafusion_ffi/ - -**Where they are exposed in this project:** -- Rust bindings: various files under `crates/core/src/` and `crates/util/src/` -- FFI example: `examples/datafusion-ffi-example/src/` -- Dependency declared in root `Cargo.toml` and `crates/core/Cargo.toml` - -**Discovering currently supported FFI types:** -Grep for `use datafusion_ffi::` in `crates/core/src/` and `crates/util/src/` to find all FFI types currently imported and used. - -**Evaluated and not requiring direct Python exposure:** -These upstream FFI types have been reviewed and do not need to be independently exposed to end users: -- `FFI_ExecutionPlan` — already used indirectly through table providers; no need for direct exposure -- `FFI_PhysicalExpr` / `FFI_PhysicalSortExpr` — internal physical planning types not expected to be needed by end users -- `FFI_RecordBatchStream` — one level deeper than FFI_ExecutionPlan, used internally when execution plans stream results -- `FFI_SessionRef` / `ForeignSession` — session sharing across FFI; Python manages sessions natively via SessionContext -- `FFI_SessionConfig` — Python can configure sessions natively without FFI -- `FFI_ConfigOptions` / `FFI_TableOptions` — internal configuration plumbing -- `FFI_PlanProperties` / `FFI_Boundedness` / `FFI_EmissionType` — read from existing plans, not user-facing -- `FFI_Partitioning` — supporting type for physical planning -- Supporting/utility types (`FFI_Option`, `FFI_Result`, `WrappedSchema`, `WrappedArray`, `FFI_ColumnarValue`, `FFI_Volatility`, `FFI_InsertOp`, `FFI_AccumulatorArgs`, `FFI_Accumulator`, `FFI_GroupsAccumulator`, `FFI_EmitTo`, `FFI_AggregateOrderSensitivity`, `FFI_PartitionEvaluator`, `FFI_PartitionEvaluatorArgs`, `FFI_Range`, `FFI_SortOptions`, `FFI_Distribution`, `FFI_ExprProperties`, `FFI_SortProperties`, `FFI_Interval`, `FFI_TableProviderFilterPushDown`, `FFI_TableType`) — used as building blocks within the types above, not independently exposed - -**How to check:** -1. Discover currently supported types by grepping for `use datafusion_ffi::` in `crates/core/src/` and `crates/util/src/`, then compare against the upstream `datafusion-ffi` crate's `lib.rs` exports -2. If new FFI types appear upstream, evaluate whether they represent a user-facing capability -3. Check against the "evaluated and not requiring exposure" list before flagging as a gap -4. Report any genuinely new types that enable user-facing functionality -5. For each currently supported FFI type, verify the full pipeline is present using the checklist from "Adding a New FFI Type": - - Rust PyO3 wrapper with `from_pycapsule()` method - - Python Protocol type (e.g., `ScalarUDFExportable`) for FFI objects - - Python wrapper class with full type hints on all public methods - - ABC base class (if the type can be user-implemented) - - Registered in Rust `init_module()` and Python `__init__.py` - - FFI example in `examples/datafusion-ffi-example/` - - Type appears in union type hints where accepted - -### 8. Spark-Compatible Functions (`datafusion-spark` crate) - -**Upstream source of truth:** -- Crate source: https://github.com/apache/datafusion/tree/main/datafusion/spark/src -- Rust docs: https://docs.rs/datafusion-spark/latest/datafusion_spark/ - -**Where they are exposed in this project:** -- Python API: `python/datafusion/functions/spark.py` — each function wraps - a call to `datafusion._internal.functions.spark`; the public surface is - the module's `__all__` list. -- Rust bindings: `crates/core/src/spark_functions.rs` — `#[pyfunction]` - definitions registered via `init_module()` and re-exported under - `datafusion._internal.functions.spark`. - -**Coverage policy:** The spark namespace mirrors -`pyspark.sql.functions` parameter names and shapes exactly so pyspark -callers can paste code unchanged. Extras over pyspark are permitted as -long as positional pyspark calls still work — for example, the spark -`avg` / `try_sum` / `collect_list` / `collect_set` retain the -`distinct`/`filter`/`order_by`/`null_treatment` kwargs from the main -namespace while pyspark's single-positional form continues to work. - -**How to check:** -1. Fetch the upstream `datafusion-spark` function list from the crate - source under `datafusion/spark/src/function/` (each subdirectory is a - category: `string/`, `math/`, `datetime/`, etc.). The crate's - `function.rs` collects all `ScalarUDF` factories. -2. Cross-reference against `pyspark.sql.functions` for the public-facing - shape — pyspark is the contract this namespace is matching. -3. Compare against the functions listed in - `python/datafusion/functions/spark.py`'s `__all__`. A function is - covered if it exists in the Python `spark` namespace, even if it - aliases another function's Rust binding. -4. Report functions that are missing from the Python spark namespace. - -### 9. `__all__` Hygiene (functions.py and functions/spark.py) - -Independent of upstream parity, also flag public `def` symbols in -`python/datafusion/functions.py` **and** `python/datafusion/functions/spark.py` -that are missing from that file's `__all__`. These are functions a user -can call but that do not show up in -`from datafusion.functions import *`, in tab-completion against the -namespace, or in generated API docs — typically an oversight rather than -an intentional omission. - -**How to check:** -1. Grep for `^def ([a-z_][a-z0-9_]*)\(` in each file to enumerate every - public function definition. -2. Read the `__all__` list at the top of the same file. -3. Report any function in (1) that is not in (2). Skip private helpers - (names starting with `_`). - -A historical example: `instr` and `position` shipped as public `def`s but -were absent from `__all__` until the gap was caught here. - -For each finding, propose adding the name to `__all__` in alphabetical -position with the existing entries. - -## Checking for Existing GitHub Issues - -After identifying missing APIs, search the open issues at https://github.com/apache/datafusion-python/issues for each gap to see if an issue already exists requesting that API be exposed. Search using the function or method name as the query. - -- If an existing issue is found, include a link to it in the report. Do NOT create a new issue. -- If no existing issue is found, note that no issue exists yet. If the user asks to create issues for missing APIs, each issue should specify that Python test coverage is required as part of the implementation. - -## Output Format - -For each area checked, produce a report like: - -``` -## [Area Name] Coverage Report - -### Currently Exposed (X functions/methods) -- list of what's already available - -### Missing from Upstream (Y functions/methods) -- function_name — brief description of what it does (existing issue: #123) -- function_name — brief description of what it does (no existing issue) - -### Notes -- Any relevant observations about partial implementations, naming differences, etc. -``` - -## Implementation Pattern - -If the user asks you to implement missing features, follow these patterns: - -### Adding a New Function (Scalar/Aggregate/Window) - -**Step 1: Rust binding** in `crates/core/src/functions.rs`: -```rust -#[pyfunction] -#[pyo3(signature = (arg1, arg2))] -fn new_function_name(arg1: PyExpr, arg2: PyExpr) -> PyResult { - Ok(datafusion::functions::module::expr_fn::new_function_name(arg1.expr, arg2.expr).into()) -} -``` -Then register in `init_module()`: -```rust -m.add_wrapped(wrap_pyfunction!(new_function_name))?; -``` - -**Step 2: Python wrapper** in `python/datafusion/functions.py`: -```python -def new_function_name(arg1: Expr, arg2: Expr) -> Expr: - """Description of what the function does. - - Args: - arg1: Description of first argument. - arg2: Description of second argument. - - Returns: - Description of return value. - """ - return Expr(f.new_function_name(arg1.expr, arg2.expr)) -``` -Add to `__all__` list. - -### Adding a New DataFrame Method - -**Step 1: Rust binding** in `crates/core/src/dataframe.rs`: -```rust -#[pymethods] -impl PyDataFrame { - fn new_method(&self, py: Python, param: PyExpr) -> PyDataFusionResult { - let df = self.df.as_ref().clone().new_method(param.into())?; - Ok(Self::new(df)) - } -} -``` - -**Step 2: Python wrapper** in `python/datafusion/dataframe.py`: -```python -def new_method(self, param: Expr) -> DataFrame: - """Description of the method.""" - return DataFrame(self.df.new_method(param.expr)) -``` - -### Adding a New SessionContext Method - -**Step 1: Rust binding** in `crates/core/src/context.rs`: -```rust -#[pymethods] -impl PySessionContext { - pub fn new_method(&self, py: Python, param: String) -> PyDataFusionResult { - let df = wait_for_future(py, self.ctx.new_method(¶m))?; - Ok(PyDataFrame::new(df)) - } -} -``` - -**Step 2: Python wrapper** in `python/datafusion/context.py`: -```python -def new_method(self, param: str) -> DataFrame: - """Description of the method.""" - return DataFrame(self.ctx.new_method(param)) -``` - -### Adding a New FFI Type - -FFI types require a full pipeline from C struct through to a typed Python wrapper. Each layer must be present. - -**Step 1: Rust PyO3 wrapper class** in a new or existing file under `crates/core/src/`: -```rust -use datafusion_ffi::new_type::FFI_NewType; - -#[pyclass(from_py_object, frozen, name = "RawNewType", module = "datafusion.module_name", subclass)] -pub struct PyNewType { - pub inner: Arc, -} - -#[pymethods] -impl PyNewType { - #[staticmethod] - fn from_pycapsule(obj: &Bound<'_, PyAny>) -> PyDataFusionResult { - let capsule = obj - .getattr("__datafusion_new_type__")? - .call0()? - .downcast::()?; - let ffi_ptr = unsafe { capsule.reference::() }; - let provider: Arc = ffi_ptr.into(); - Ok(Self { inner: provider }) - } - - fn some_method(&self) -> PyResult<...> { - // wrap inner trait method - } -} -``` -Register in the appropriate `init_module()`: -```rust -m.add_class::()?; -``` - -**Step 2: Python Protocol type** in the appropriate Python module (e.g., `python/datafusion/catalog.py`): -```python -class NewTypeExportable(Protocol): - """Type hint for objects providing a __datafusion_new_type__ PyCapsule.""" - - def __datafusion_new_type__(self) -> object: ... -``` - -**Step 3: Python wrapper class** in the same module: -```python -class NewType: - """Description of the type. - - This class wraps a DataFusion NewType, which can be created from a native - Python implementation or imported from an FFI-compatible library. - """ - - def __init__( - self, - new_type: df_internal.module_name.RawNewType | NewTypeExportable, - ) -> None: - if isinstance(new_type, df_internal.module_name.RawNewType): - self._raw = new_type - else: - self._raw = df_internal.module_name.RawNewType.from_pycapsule(new_type) - - def some_method(self) -> ReturnType: - """Description of the method.""" - return self._raw.some_method() -``` - -**Step 4: ABC base class** (if users should be able to subclass and provide custom implementations in Python): -```python -from abc import ABC, abstractmethod - -class NewTypeProvider(ABC): - """Abstract base class for implementing a custom NewType in Python.""" - - @abstractmethod - def some_method(self) -> ReturnType: - """Description of the method.""" - ... -``` - -**Step 5: Module exports** — add to the appropriate `__init__.py`: -- Add the wrapper class (`NewType`) to `python/datafusion/__init__.py` -- Add the ABC (`NewTypeProvider`) if applicable -- Add the Protocol type (`NewTypeExportable`) if it should be public - -**Step 6: FFI example** — add an example implementation under `examples/datafusion-ffi-example/src/`: -```rust -// examples/datafusion-ffi-example/src/new_type.rs -use datafusion_ffi::new_type::FFI_NewType; -// ... example showing how an external Rust library exposes this type via PyCapsule -``` - -**Checklist for each FFI type:** -- [ ] Rust PyO3 wrapper with `from_pycapsule()` method -- [ ] Python Protocol type (e.g., `NewTypeExportable`) for FFI objects -- [ ] Python wrapper class with full type hints on all public methods -- [ ] ABC base class (if the type can be user-implemented) -- [ ] Registered in Rust `init_module()` and Python `__init__.py` -- [ ] FFI example in `examples/datafusion-ffi-example/` -- [ ] Type appears in union type hints where accepted (e.g., `Table | TableProviderExportable`) - -## Important Notes - -- The upstream DataFusion version used by this project is specified in `crates/core/Cargo.toml` — check the `datafusion` dependency version to ensure you're comparing against the right upstream version. -- Some upstream features may intentionally not be exposed (e.g., internal-only APIs). Use judgment about what's user-facing. -- When fetching upstream docs, prefer the published docs.rs documentation as it matches the crate version. -- Function aliases (e.g., `array_append` / `list_append`) should both be exposed if upstream supports them. -- Check the `__all__` list in `functions.py` to see what's publicly exported vs just defined. diff --git a/.ai/skills/make-pythonic/SKILL.md b/.ai/skills/make-pythonic/SKILL.md deleted file mode 100644 index 7d490ec03..000000000 --- a/.ai/skills/make-pythonic/SKILL.md +++ /dev/null @@ -1,465 +0,0 @@ - - ---- -name: make-pythonic -description: Audit and improve datafusion-python functions to accept native Python types (int, float, str, bool) instead of requiring explicit lit() or col() wrapping. Analyzes function signatures, checks upstream Rust implementations for type constraints, and applies the appropriate coercion pattern. -argument-hint: [scope] (e.g., "string functions", "datetime functions", "array functions", "math functions", "all", or a specific function name like "split_part") ---- - -# Make Python API Functions More Pythonic - -You are improving the datafusion-python API to feel more natural to Python users. The goal is to allow functions to accept native Python types (int, float, str, bool, etc.) for arguments that are contextually always or typically literal values, instead of requiring users to manually wrap them in `lit()`. - -**Core principle:** A Python user should be able to write `split_part(col("a"), ",", 2)` instead of `split_part(col("a"), lit(","), lit(2))` when the arguments are contextually obvious literals. - -## Scope: `functions` vs `functions.spark` - -Both `python/datafusion/functions/__init__.py` and -`python/datafusion/functions/spark.py` are in scope. We want both to feel -pythonic — accept native Python types where the argument is contextually -a literal — but `functions.spark` carries an additional constraint: -**every signature must remain compatible with `pyspark.sql.functions`**. - -Compatibility rules for the spark namespace: - -- **Parameter names must match pyspark exactly.** Pyspark callers pass by - keyword (`spark.shiftleft(col=..., numBits=...)`), so renames break - them. Do NOT rename a parameter just because it would be more pythonic - in the main namespace. -- **Positional order must match pyspark exactly.** Reordering breaks - positional pyspark calls. -- **Type unions may widen the input set, never narrow it.** Pyspark - accepts `Column` or `str` (column name) for most args; we accept - `Expr` already, and widening to `Expr | int` / `Expr | str` for - literal-friendly arguments is on-brand because the int/str case is - exactly what a pyspark caller would also try. Just verify the widened - set is a superset of what pyspark accepts for that arg. -- **Extra keyword arguments are allowed** as long as they default to - `None` and pyspark's positional/keyword form still works (e.g. the - spark `avg`/`try_sum`/`collect_list`/`collect_set` retain DataFusion's - `distinct`/`filter`/`order_by`/`null_treatment` kwargs). - -Practical effect: in `functions.spark`, apply Categories A and (where -pyspark exposes the same arg as a non-`Expr`) B normally, but cross-check -each proposed signature against `pyspark.sql.functions` before landing -it. When pyspark's own type hint is `Column | str` for a "column name" -arg, prefer leaving the spark wrapper at `Expr` — Category C -("`Expr | str` meaning column name") is unusual in `functions.py` and -should remain so in `functions.spark`. - -## How to Identify Candidates - -The user may specify a scope via `$ARGUMENTS`. If no scope is given or "all" is specified, audit all functions in `python/datafusion/functions/__init__.py` **and** `python/datafusion/functions/spark.py`. When updating a spark-namespace function, apply the compatibility rules from "Scope" above on top of the standard analysis. - -For each function, determine if any parameter can accept native Python types by evaluating **two complementary signals**: - -### Signal 1: Contextual Understanding - -Some arguments are contextually always or almost always literal values based on what the function does: - -| Context | Typical Arguments | Examples | -|---------|------------------|----------| -| **String position/count** | Character counts, indices, repetition counts | `left(str, n)`, `right(str, n)`, `repeat(str, n)`, `lpad(str, count, ...)` | -| **Delimiters/separators** | Fixed separator characters | `split_part(str, delim, idx)`, `concat_ws(sep, ...)` | -| **Search/replace patterns** | Literal search strings, replacements | `replace(str, from, to)`, `regexp_replace(str, pattern, replacement, flags)` | -| **Date/time parts** | Part names from a fixed set | `date_part(part, date)`, `date_trunc(part, date)` | -| **Rounding precision** | Decimal place counts | `round(val, places)`, `trunc(val, places)` | -| **Fill characters** | Padding characters | `lpad(str, count, fill)`, `rpad(str, count, fill)` | - -### Signal 2: Upstream Rust Implementation - -Check the Rust binding in `crates/core/src/functions.rs` and the upstream DataFusion function implementation to determine type constraints. The upstream source is cached locally at: - -``` -~/.cargo/registry/src/index.crates.io-*/datafusion-functions-/src/ -``` - -Check the DataFusion version in `crates/core/Cargo.toml` to find the right directory. Key subdirectories: `string/`, `datetime/`, `math/`, `regex/`. - -For **aggregate functions**, the upstream source is in a separate crate: - -``` -~/.cargo/registry/src/index.crates.io-*/datafusion-functions-aggregate-/src/ -``` - -There are five concrete techniques to check, in order of signal strength: - -#### Technique 1: Check `invoke_with_args()` for literal-only enforcement (strongest signal) - -Some functions pattern-match on `ColumnarValue::Scalar` in their `invoke_with_args()` method and **return an error** if the argument is a column/array. This means the argument **must** be a literal — passing a column expression will fail at runtime. - -Example from `date_trunc.rs`: -```rust -let granularity_str = if let ColumnarValue::Scalar(ScalarValue::Utf8(Some(v))) = granularity { - v.to_lowercase() -} else { - return exec_err!("Granularity of `date_trunc` must be non-null scalar Utf8"); -}; -``` - -**If you find this pattern:** The argument is **Category B** — accept only the corresponding native Python type (e.g., `str`), not `Expr`. The function will error at runtime with a column expression anyway. - -#### Technique 1a: Check `accumulator()` for literal-only enforcement (aggregate functions) - -Technique 1 applies to scalar UDFs. Aggregate functions do not have `invoke_with_args()` — instead, they enforce literal-only arguments in their `accumulator()` (or `create_accumulator()`) method, which runs at planning time before any data is processed. - -Look for these patterns inside `accumulator()`: - -- `get_scalar_value(expr)` — evaluates the expression against an empty batch and errors if it's not a scalar -- `validate_percentile_expr(expr)` — specific helper used by percentile functions -- `downcast_ref::()` — checks that the physical expression is a literal constant - -Example from `approx_percentile_cont.rs`: -```rust -fn accumulator(&self, args: AccumulatorArgs) -> Result { - let percentile = - validate_percentile_expr(&args.exprs[1], "APPROX_PERCENTILE_CONT")?; - // ... -} -``` - -Where `validate_percentile_expr` calls `get_scalar_value` and errors with `"must be a literal"`. - -Example from `string_agg.rs`: -```rust -fn accumulator(&self, acc_args: AccumulatorArgs) -> Result> { - let Some(lit) = acc_args.exprs[1].as_any().downcast_ref::() else { - return not_impl_err!( - "The second argument of the string_agg function must be a string literal" - ); - }; - // ... -} -``` - -**If you find this pattern:** The argument is **Category B** — accept only the corresponding native Python type, not `Expr`. The function will error at planning time with a non-literal expression. - -To discover which aggregate functions have literal-only arguments, search the upstream aggregate crate for `get_scalar_value`, `validate_percentile_expr`, and `downcast_ref::()` inside `accumulator()` methods. For example, you should expect to find `approx_percentile_cont` (percentile) and `string_agg` (delimiter) among the results. - -#### Technique 1b: Check `partition_evaluator()` for literal-only enforcement (window functions) - -Window functions do not have `invoke_with_args()` or `accumulator()`. Instead, they enforce literal-only arguments in their `partition_evaluator()` method, which constructs the evaluator that processes each partition. - -The upstream source is in a separate crate: - -``` -~/.cargo/registry/src/index.crates.io-*/datafusion-functions-window-/src/ -``` - -Look for `get_scalar_value_from_args()` calls inside `partition_evaluator()`. This helper (defined in the window crate's `utils.rs`) calls `downcast_ref::()` and errors with `"There is only support Literal types for field at idx: {index} in Window Function"`. - -Example from `ntile.rs`: -```rust -fn partition_evaluator( - &self, - partition_evaluator_args: PartitionEvaluatorArgs, -) -> Result> { - let scalar_n = - get_scalar_value_from_args(partition_evaluator_args.input_exprs(), 0)? - .ok_or_else(|| { - exec_datafusion_err!("NTILE requires a positive integer") - })?; - // ... -} -``` - -**If you find this pattern:** The argument is **Category B** — accept only the corresponding native Python type, not `Expr`. The function will error at planning time with a non-literal expression. - -To discover which window functions have literal-only arguments, search the upstream window crate for `get_scalar_value_from_args` inside `partition_evaluator()` methods. For example, you should expect to find `ntile` (n) and `lead`/`lag` (offset, default_value) among the results. - -#### Technique 2: Check the `Signature` for data type constraints - -Each function defines a `Signature::coercible(...)` that specifies what data types each argument accepts, using `Coercion` entries. This tells you the expected **data type** even if it doesn't enforce literal-only. - -Example from `repeat.rs`: -```rust -signature: Signature::coercible( - vec![ - Coercion::new_exact(TypeSignatureClass::Native(logical_string())), - Coercion::new_implicit( - TypeSignatureClass::Native(logical_int64()), - vec![TypeSignatureClass::Integer], - NativeType::Int64, - ), - ], - Volatility::Immutable, -), -``` - -This tells you arg 2 (`n`) must be an integer type coerced to Int64. Use this to choose the correct Python type (e.g., `int` not `str` or `float`). - -Common mappings: -| Rust Type Constraint | Python Type | -|---------------------|-------------| -| `logical_int64()` / `TypeSignatureClass::Integer` | `int` | -| `logical_float64()` / `TypeSignatureClass::Numeric` | `int \| float` | -| `logical_string()` / `TypeSignatureClass::String` | `str` | -| `LogicalType::Boolean` | `bool` | - -**Important:** In Python's type system (PEP 484), `float` already accepts `int` values, so `int | float` is redundant and will fail the `ruff` linter (rule PYI041). Use `float` alone when the Rust side accepts a float/numeric type — Python users can still pass integer literals like `log(10, col("a"))` or `power(col("a"), 3)` without issue. Only use `int` when the Rust side strictly requires an integer (e.g., `logical_int64()`). - -#### Technique 3: Check `return_field_from_args()` for `scalar_arguments` usage - -Functions that inspect literal values at query planning time use `args.scalar_arguments.get(n)` in their `return_field_from_args()` method. This indicates the argument is **expected to be a literal** for optimal behavior (e.g., to determine output type precision), but may still work as a column. - -Example from `round.rs`: -```rust -let decimal_places: Option = match args.scalar_arguments.get(1) { - None => Some(0), - Some(None) => None, // argument is not a literal (column) - Some(Some(scalar)) if scalar.is_null() => Some(0), - Some(Some(scalar)) => Some(decimal_places_from_scalar(scalar)?), -}; -``` - -**If you find this pattern:** The argument is **Category A** — accept native types AND `Expr`. It works as a column but is primarily used as a literal. - -#### Decision flow - -``` -What kind of function is this? - Scalar UDF: - Is argument rejected at runtime if not a literal? - (check invoke_with_args for ColumnarValue::Scalar-only match + exec_err!) - → YES: Category B — accept only native type, no Expr - → NO: continue below - Aggregate: - Is argument rejected at planning time if not a literal? - (check accumulator() for get_scalar_value / validate_percentile_expr / - downcast_ref::() + error) - → YES: Category B — accept only native type, no Expr - → NO: continue below - Window: - Is argument rejected at planning time if not a literal? - (check partition_evaluator() for get_scalar_value_from_args / - downcast_ref::() + error) - → YES: Category B — accept only native type, no Expr - → NO: continue below - -Does the Signature constrain it to a specific data type? - → YES: Category A — accept Expr | - → NO: Leave as Expr only -``` - -## Coercion Categories - -When making a function more pythonic, apply the correct coercion pattern based on **what the argument represents**: - -### Category A: Arguments That Should Accept Native Types AND Expr - -These are arguments that are *typically* literals but *could* be column references in advanced use cases. For these, accept a union type and coerce native types to `Expr.literal()`. - -**Type hint pattern:** `Expr | int`, `Expr | str`, `Expr | int | str`, etc. - -**When to use:** When the argument could plausibly come from a column in some use case (e.g., the repeat count might come from a column in a data-driven scenario). - -```python -def repeat(string: Expr, n: Expr | int) -> Expr: - """Repeats the ``string`` to ``n`` times. - - Examples: - >>> ctx = dfn.SessionContext() - >>> df = ctx.from_pydict({"a": ["ha"]}) - >>> result = df.select( - ... dfn.functions.repeat(dfn.col("a"), 3).alias("r")) - >>> result.collect_column("r")[0].as_py() - 'hahaha' - """ - if not isinstance(n, Expr): - n = Expr.literal(n) - return Expr(f.repeat(string.expr, n.expr)) -``` - -### Category B: Arguments That Should ONLY Accept Specific Native Types - -These are arguments where an `Expr` never makes sense because the value must be a fixed literal known at query-planning time (not a per-row value). For these, accept only the native type(s) and wrap internally. - -**Type hint pattern:** `str`, `int`, `list[str]`, etc. (no `Expr` in the union) - -**When to use:** When the argument is from a fixed enumeration or is always a compile-time constant, **AND** the parameter was not previously typed as `Expr`: -- Separator in `concat_ws` (already typed as `str` in the Rust binding) -- Index in `array_position` (already typed as `int` in the Rust binding) -- Values that the Rust implementation already accepts as native types - -**Backward compatibility rule:** If a parameter was previously typed as `Expr`, you **must** keep `Expr` in the union even if the Rust side requires a literal. Removing `Expr` would break existing user code like `date_part(lit("year"), col("a"))`. Use **Category A** instead — accept `Expr | str` — and let users who pass column expressions discover the runtime error from the Rust side. Never silently break backward compatibility. - -```python -def concat_ws(separator: str, *args: Expr) -> Expr: - """Concatenates the list ``args`` with the separator. - - ``separator`` is already typed as ``str`` in the Rust binding, so - there is no backward-compatibility concern. - - Examples: - >>> ctx = dfn.SessionContext() - >>> df = ctx.from_pydict({"a": ["hello"], "b": ["world"]}) - >>> result = df.select( - ... dfn.functions.concat_ws("-", dfn.col("a"), dfn.col("b")).alias("c")) - >>> result.collect_column("c")[0].as_py() - 'hello-world' - """ - args = [arg.expr for arg in args] - return Expr(f.concat_ws(separator, args)) -``` - -### Category C: Arguments That Should Accept str as Column Name - -In some contexts a string argument naturally refers to a column name rather than a literal. This is the pattern used by DataFrame methods. - -**Type hint pattern:** `Expr | str` - -**When to use:** Only when the string contextually means a column name (rare in `functions.py`, more common in DataFrame methods). - -```python -# Use _to_raw_expr() from expr.py for this pattern -from datafusion.expr import _to_raw_expr - -def some_function(column: Expr | str) -> Expr: - raw = _to_raw_expr(column) # str -> col(str) - return Expr(f.some_function(raw)) -``` - -**IMPORTANT:** In `functions.py`, string arguments almost never mean column names. Functions operate on expressions, and column references should use `col()`. Category C applies mainly to DataFrame methods and context APIs, not to scalar/aggregate/window functions. Do NOT convert string arguments to column expressions in `functions.py` unless there is a very clear reason to do so. - -## Implementation Steps - -For each function being updated: - -### Step 1: Analyze the Function - -1. Read the current Python function signature in `python/datafusion/functions/__init__.py` -2. Read the Rust binding in `crates/core/src/functions.rs` -3. Optionally check the upstream DataFusion docs for the function -4. Determine which category (A, B, or C) applies to each parameter - -### Step 2: Update the Python Function - -1. **Change the type hints** to accept native types (e.g., `Expr` -> `Expr | int`) -2. **Add coercion logic** at the top of the function body -3. **Update the docstring** examples to use the simpler calling convention -4. **Preserve backward compatibility** — existing code using `Expr` must still work - -### Step 3: Update Alias Type Hints - -After updating a primary function, find all alias functions that delegate to it (e.g., `instr` and `position` delegate to `strpos`). Update each alias's **parameter type hints** to match the primary function's new signature. Do not add coercion logic to aliases — the primary function handles that. - -### Step 4: Update Docstring Examples (primary functions only) - -Per the project's CLAUDE.md rules: -- Every function must have doctest-style examples -- Optional parameters need examples both without and with the optional args, using keyword argument syntax -- Reuse the same input data across examples where possible - -**Update examples to demonstrate the pythonic calling convention:** - -```python -# BEFORE (old style - still works but verbose) -dfn.functions.left(dfn.col("a"), dfn.lit(3)) - -# AFTER (new style - shown in examples) -dfn.functions.left(dfn.col("a"), 3) -``` - -### Step 5: Run Tests - -After making changes, run the doctests to verify: -```bash -python -m pytest --doctest-modules python/datafusion/functions/__init__.py -v -``` - -## Coercion Helper Pattern - -Use the coercion helpers from `datafusion.expr` to convert native Python values to `Expr`. These are the complement of `ensure_expr()` — where `ensure_expr` *rejects* non-`Expr` values, the coercion helpers *wrap* them via `Expr.literal()`. - -**For required parameters** use `coerce_to_expr`: - -```python -from datafusion.expr import coerce_to_expr - -def left(string: Expr, n: Expr | int) -> Expr: - n = coerce_to_expr(n) - return Expr(f.left(string.expr, n.expr)) -``` - -**For optional nullable parameters** use `coerce_to_expr_or_none`: - -```python -from datafusion.expr import coerce_to_expr, coerce_to_expr_or_none - -def regexp_count( - string: Expr, - pattern: Expr | str, - start: Expr | int | None = None, - flags: Expr | str | None = None, -) -> Expr: - pattern = coerce_to_expr(pattern) - start = coerce_to_expr_or_none(start) - flags = coerce_to_expr_or_none(flags) - return Expr( - f.regexp_count( - string.expr, - pattern.expr, - start.expr if start is not None else None, - flags.expr if flags is not None else None, - ) - ) -``` - -Both helpers are defined in `python/datafusion/expr.py` alongside `ensure_expr`. Import them in `functions.py` via: - -```python -from datafusion.expr import coerce_to_expr, coerce_to_expr_or_none -``` - -## What NOT to Change - -- **Do not change arguments that represent data columns.** If an argument is the primary data being operated on (e.g., the `string` in `left(string, n)` or the `array` in `array_sort(array)`), it should remain `Expr` only. Users should use `col()` for column references. -- **Do not change variadic `*args: Expr` parameters.** These represent multiple expressions and should stay as `Expr`. -- **Do not change arguments where the coercion is ambiguous.** If it is unclear whether a string should be a column name or a literal, leave it as `Expr` and let the user be explicit. -- **Do not add coercion logic to simple aliases.** If a function is just `return other_function(...)`, the primary function handles coercion. However, you **must update the alias's type hints** to match the primary function's signature so that type checkers and documentation accurately reflect what the alias accepts. -- **Do not change the Rust bindings.** All coercion happens in the Python layer. The Rust functions continue to accept `PyExpr`. - -## Priority Order - -When auditing functions, process them in this order: - -1. **Date/time functions** — `date_part`, `date_trunc`, `date_bin` — these have the clearest literal arguments -2. **String functions** — `left`, `right`, `repeat`, `lpad`, `rpad`, `split_part`, `substring`, `replace`, `regexp_replace`, `regexp_match`, `regexp_count` — common and verbose without coercion -3. **Math functions** — `round`, `trunc`, `power` — numeric literal arguments -4. **Array functions** — `array_slice`, `array_position`, `array_remove_n`, `array_replace_n`, `array_resize`, `array_element` — index and count arguments -5. **Other functions** — any remaining functions with literal arguments - -## Output Format - -For each function analyzed, report: - -``` -## [Function Name] - -**Current signature:** `function(arg1: Expr, arg2: Expr) -> Expr` -**Proposed signature:** `function(arg1: Expr, arg2: Expr | int) -> Expr` -**Category:** A (accepts native + Expr) -**Arguments changed:** -- `arg2`: Expr -> Expr | int (always a literal count) -**Rust binding:** Takes PyExpr, wraps to literal internally -**Status:** [Changed / Skipped / Needs Discussion] -``` - -If asked to implement (not just audit), make the changes directly and show a summary of what was updated. diff --git a/.claude/skills b/.claude/skills deleted file mode 120000 index 6838a1160..000000000 --- a/.claude/skills +++ /dev/null @@ -1 +0,0 @@ -../.ai/skills \ No newline at end of file diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 411e60291..000000000 --- a/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.cargo -.github -.pytest_cache -ci -conda -dev -docs -examples -parquet -target -testing -venv \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 5600dab98..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index d9883dd45..000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this project -title: '' -labels: enhancement -assignees: '' - ---- - -**Is your feature request related to a problem or challenge? Please describe what you are trying to do.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -(This section helps Arrow developers understand the context and *why* for this feature, in addition to the *what*) - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/actions/build-wheel/action.yml b/.github/actions/build-wheel/action.yml deleted file mode 100644 index 25f75d1f8..000000000 --- a/.github/actions/build-wheel/action.yml +++ /dev/null @@ -1,110 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# Composite action that builds a datafusion-python wheel with maturin. -# Centralises the abi3-vs-free-threaded argument logic so platform jobs -# stay short and changes to wheel-build flags happen in one place. - -name: "Build wheel" -description: "Build datafusion-python wheel with maturin (abi3 or free-threaded)" - -inputs: - target: - description: "Rust target triple (e.g. x86_64-unknown-linux-gnu). Required when manylinux is set; ignored for native builds." - required: false - default: "" - python-tag: - description: "abi3 (covers 3.10..3.14 GIL builds) or a free-threaded interpreter such as 3.13t / 3.14t" - required: true - build-mode: - description: "release or debug" - required: true - features: - description: "Comma-separated extra features (in addition to those implied by the python-tag)" - required: false - default: "substrait" - manylinux: - description: "manylinux tag for maturin-action (e.g. 2_28). Leave empty to use uv-run maturin natively." - required: false - default: "" - out-dir: - description: "Output directory for built wheels" - required: false - default: "dist" - -outputs: - args: - description: "Computed maturin args (for debugging)" - value: ${{ steps.args.outputs.args }} - -runs: - using: "composite" - steps: - - name: Compute maturin args - id: args - shell: bash - run: | - set -euo pipefail - FEATURES="${{ inputs.features }}" - TAG="${{ inputs.python-tag }}" - if [ "$TAG" = "abi3" ]; then - # Default features include the `abi3` cargo feature. - # One wheel covers Python 3.10..3.14 (GIL builds only). - BUILD_ARGS="--features ${FEATURES}" - else - # Free-threaded build: disable abi3, force mimalloc back in, pin interpreter. - if [ "${RUNNER_OS:-}" = "Windows" ]; then - # Windows free-threaded builds ship as `python.exe` (no `tN` - # suffix). Resolve sys.executable so the path is independent of - # PATH ordering, and assert the interpreter is actually - # free-threaded before we hand the wheel off. - INTERP=$(python -c 'import sys; print(sys.executable)') - python -c "import sysconfig, sys; \ - v = sysconfig.get_config_var('Py_GIL_DISABLED'); \ - sys.exit(0 if v == 1 else f'expected free-threaded interpreter, got Py_GIL_DISABLED={v!r} at {sys.executable}')" - # Backslashes in BUILD_ARGS would be parsed as escapes when the - # output is re-expanded in the next step; use forward slashes - # (maturin/Rust accept them on Windows). - INTERP="${INTERP//\\//}" - else - INTERP="python${TAG}" - fi - BUILD_ARGS="--no-default-features --features mimalloc,${FEATURES} --interpreter ${INTERP}" - fi - if [ "${{ inputs.build-mode }}" = "release" ]; then - BUILD_ARGS="--release --strip ${BUILD_ARGS}" - fi - BUILD_ARGS="${BUILD_ARGS} --out ${{ inputs.out-dir }}" - echo "args=${BUILD_ARGS}" >> "$GITHUB_OUTPUT" - echo "maturin args: ${BUILD_ARGS}" - - - name: Build via maturin-action (manylinux container) - if: inputs.manylinux != '' - uses: PyO3/maturin-action@v1 - with: - target: ${{ inputs.target }} - manylinux: ${{ inputs.manylinux }} - maturin-version: "1.13.3" - args: ${{ steps.args.outputs.args }} - rustup-components: rust-std - - - name: Build via native maturin - if: inputs.manylinux == '' - shell: bash - # Use `uvx` so maturin is available even when `uv sync` was skipped - # (free-threaded matrix entries don't pre-populate the project venv). - run: uvx maturin@1.13.3 build ${{ steps.args.outputs.args }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 4058e8a6e..000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,36 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# -version: 2 -updates: - - - package-ecosystem: "cargo" - directory: "/" - schedule: - interval: "weekly" - day: "saturday" - open-pull-requests-limit: 20 - target-branch: main - - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - day: "sunday" - open-pull-requests-limit: 20 - target-branch: main diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 18b90943f..000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,27 +0,0 @@ -# Which issue does this PR close? - - - -Closes #. - - # Rationale for this change - - -# What changes are included in this PR? - - -# Are there any user-facing changes? - - - \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index c35801b11..000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,578 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# Reusable workflow for running building -# This ensures the same tests run for both debug (PRs) and release (main/tags) builds - -name: Build - -on: - workflow_call: - inputs: - build_mode: - description: 'Build mode: debug or release' - required: true - type: string - run_wheels: - description: 'Whether to build distribution wheels' - required: false - type: boolean - default: false - -env: - CARGO_TERM_COLOR: always - RUST_BACKTRACE: 1 - UV_LOCKED: true - -jobs: - # ============================================ - # Linting Jobs - # ============================================ - lint-rust: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Setup Rust - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 - with: - toolchain: "nightly" - components: rustfmt - - - name: Cache Cargo - uses: Swatinem/rust-cache@v2 - - - name: Check formatting - run: cargo +nightly fmt --all -- --check - - lint-python: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Install Python - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 - with: - enable-cache: true - - - name: Install dependencies - run: uv sync --dev --no-install-package datafusion - - - name: Run Ruff - run: | - uv run --no-project ruff check --output-format=github python/ - uv run --no-project ruff format --check python/ - - - name: Run codespell - run: | - uv run --no-project codespell --toml pyproject.toml - - lint-toml: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Install taplo - uses: taiki-e/install-action@v2 - with: - tool: taplo-cli - - # if you encounter an error, try running 'taplo format' to fix the formatting automatically. - - name: Check Cargo.toml formatting - run: taplo format --check - - check-crates-patch: - if: inputs.build_mode == 'release' && startsWith(github.ref, 'refs/tags/') - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - name: Ensure [patch.crates-io] is empty - run: python3 dev/check_crates_patch.py - - generate-license: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 - with: - enable-cache: true - - - name: Install cargo-license - uses: taiki-e/install-action@v2 - with: - tool: cargo-license - - - name: Generate license file - run: uv run --no-project python ./dev/create_license.py - - - uses: actions/upload-artifact@v7 - with: - name: python-wheel-license - path: LICENSE.txt - - # ============================================ - # Build - Linux x86_64 - # ============================================ - build-manylinux-x86_64: - needs: [generate-license, lint-rust, lint-python] - name: Linux x86_64 (${{ matrix.python-tag }}) - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-tag: ["abi3", "3.14t"] - steps: - - uses: actions/checkout@v6 - - - run: rm LICENSE.txt - - name: Download LICENSE.txt - uses: actions/download-artifact@v8 - with: - name: python-wheel-license - path: . - - - name: Setup Rust - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 - - - name: Cache Cargo - uses: Swatinem/rust-cache@v2 - with: - key: ${{ inputs.build_mode }}-${{ matrix.python-tag }} - - - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 - with: - enable-cache: true - - - name: Add extra swap for release build - if: inputs.build_mode == 'release' - run: | - set -euxo pipefail - sudo swapoff -a || true - sudo rm -f /swapfile - sudo fallocate -l 8G /swapfile || sudo dd if=/dev/zero of=/swapfile bs=1M count=8192 - sudo chmod 600 /swapfile - sudo mkswap /swapfile - sudo swapon /swapfile - free -h - swapon --show - - - name: Build wheel - uses: ./.github/actions/build-wheel - with: - target: x86_64-unknown-linux-gnu - python-tag: ${{ matrix.python-tag }} - build-mode: ${{ inputs.build_mode }} - features: "protoc,substrait" - manylinux: "2_28" - - # FFI test wheel only needs to be built once per platform; gate to abi3. - - name: Build FFI test library - if: matrix.python-tag == 'abi3' - uses: PyO3/maturin-action@v1 - with: - target: x86_64-unknown-linux-gnu - manylinux: "2_28" - working-directory: examples/datafusion-ffi-example - args: --out dist - rustup-components: rust-std - - - name: Archive wheels - uses: actions/upload-artifact@v7 - with: - name: dist-manylinux-x86_64-${{ matrix.python-tag }} - path: dist/* - - - name: Archive FFI test wheel - if: matrix.python-tag == 'abi3' - uses: actions/upload-artifact@v7 - with: - name: test-ffi-manylinux-x86_64 - path: examples/datafusion-ffi-example/dist/* - - # ============================================ - # Build - Linux ARM64 - # ============================================ - build-manylinux-aarch64: - needs: [generate-license, lint-rust, lint-python] - name: Linux arm64 (${{ matrix.python-tag }}) - runs-on: ubuntu-24.04-arm - strategy: - fail-fast: false - matrix: - python-tag: ["abi3", "3.14t"] - steps: - - uses: actions/checkout@v6 - - - run: rm LICENSE.txt - - name: Download LICENSE.txt - uses: actions/download-artifact@v8 - with: - name: python-wheel-license - path: . - - - name: Setup Rust - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 - - - name: Cache Cargo - uses: Swatinem/rust-cache@v2 - with: - key: ${{ inputs.build_mode }}-${{ matrix.python-tag }} - - - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 - with: - enable-cache: true - - - name: Add extra swap for release build - if: inputs.build_mode == 'release' - run: | - set -euxo pipefail - sudo swapoff -a || true - sudo rm -f /swapfile - sudo fallocate -l 8G /swapfile || sudo dd if=/dev/zero of=/swapfile bs=1M count=8192 - sudo chmod 600 /swapfile - sudo mkswap /swapfile - sudo swapon /swapfile - free -h - swapon --show - - - name: Build wheel - uses: ./.github/actions/build-wheel - with: - target: aarch64-unknown-linux-gnu - python-tag: ${{ matrix.python-tag }} - build-mode: ${{ inputs.build_mode }} - features: "protoc,substrait" - manylinux: "2_28" - - - name: Archive wheels - uses: actions/upload-artifact@v7 - if: inputs.build_mode == 'release' - with: - name: dist-manylinux-aarch64-${{ matrix.python-tag }} - path: dist/* - - # ============================================ - # Build - macOS arm64 / Windows - # ============================================ - build-python-mac-win: - needs: [generate-license, lint-rust, lint-python] - name: ${{ matrix.os == 'macos-latest' && 'macOS arm64' || 'Windows x86_64' }} (${{ matrix.python-tag }}) - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [macos-latest, windows-latest] - python-tag: ["abi3", "3.14t"] - steps: - - uses: actions/checkout@v6 - - - name: Setup Rust - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 - - - run: rm LICENSE.txt - - name: Download LICENSE.txt - uses: actions/download-artifact@v8 - with: - name: python-wheel-license - path: . - - - name: Cache Cargo - uses: Swatinem/rust-cache@v2 - with: - key: ${{ inputs.build_mode }}-${{ matrix.python-tag }} - - - name: Setup Python (free-threaded) - if: matrix.python-tag != 'abi3' - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-tag }} - freethreaded: true - - - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 - with: - enable-cache: true - - - name: Install Protoc - uses: arduino/setup-protoc@v3 - with: - version: "27.4" - repo-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Install dependencies - if: matrix.python-tag == 'abi3' - run: uv sync --dev --no-install-package datafusion - - # Clippy is interpreter-agnostic; run once per OS (against the abi3 entry) - # so the matrix doesn't pay the cost three times. - - name: Run Clippy - if: matrix.os != 'windows-latest' && matrix.python-tag == 'abi3' - run: cargo clippy --no-deps --all-targets --features substrait -- -D warnings - - - name: Build wheel - uses: ./.github/actions/build-wheel - with: - python-tag: ${{ matrix.python-tag }} - build-mode: ${{ inputs.build_mode }} - features: "substrait" - out-dir: "target/wheels" - - - name: List Windows wheels - if: matrix.os == 'windows-latest' - run: dir target\wheels\ - # since the runner is dynamic shellcheck (from actionlint) can't infer this is powershell - # so we specify it explicitly - shell: powershell - - - name: List Mac wheels - if: matrix.os != 'windows-latest' - run: find target/wheels/ - - - name: Archive wheels - uses: actions/upload-artifact@v7 - if: inputs.build_mode == 'release' - with: - name: dist-${{ matrix.os }}-${{ matrix.python-tag }} - path: target/wheels/* - - # ============================================ - # Build - macOS x86_64 (release only) - # ============================================ - build-macos-x86_64: - if: inputs.build_mode == 'release' - needs: [generate-license, lint-rust, lint-python] - name: macOS x86_64 (${{ matrix.python-tag }}) - runs-on: macos-15-intel - strategy: - fail-fast: false - matrix: - python-tag: ["abi3", "3.14t"] - steps: - - uses: actions/checkout@v6 - - - name: Setup Rust - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 - - - run: rm LICENSE.txt - - name: Download LICENSE.txt - uses: actions/download-artifact@v8 - with: - name: python-wheel-license - path: . - - - name: Cache Cargo - uses: Swatinem/rust-cache@v2 - with: - key: ${{ inputs.build_mode }}-${{ matrix.python-tag }} - - - name: Setup Python (free-threaded) - if: matrix.python-tag != 'abi3' - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-tag }} - freethreaded: true - - - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 - with: - enable-cache: true - - - name: Install Protoc - uses: arduino/setup-protoc@v3 - with: - version: "27.4" - repo-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Install dependencies - if: matrix.python-tag == 'abi3' - run: uv sync --dev --no-install-package datafusion - - - name: Build wheel - uses: ./.github/actions/build-wheel - with: - python-tag: ${{ matrix.python-tag }} - build-mode: ${{ inputs.build_mode }} - features: "substrait" - out-dir: "target/wheels" - - - name: List Mac wheels - run: find target/wheels/ - - - name: Archive wheels - uses: actions/upload-artifact@v7 - with: - name: dist-macos-aarch64-${{ matrix.python-tag }} - path: target/wheels/* - - # ============================================ - # Build - Source Distribution - # ============================================ - - build-sdist: - needs: [generate-license] - name: Source distribution - if: inputs.build_mode == 'release' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - run: rm LICENSE.txt - - name: Download LICENSE.txt - uses: actions/download-artifact@v8 - with: - name: python-wheel-license - path: . - - run: cat LICENSE.txt - - name: Build sdist - uses: PyO3/maturin-action@v1 - with: - rust-toolchain: stable - manylinux: auto - rustup-components: rust-std rustfmt - args: --release --sdist --out dist --features protoc,substrait - - name: Assert sdist build does not generate wheels - run: | - if [ "$(ls -A target/wheels)" ]; then - echo "Error: Sdist build generated wheels" - exit 1 - else - echo "Directory is clean" - fi - shell: bash - - # ============================================ - # Build - Source Distribution - # ============================================ - - merge-build-artifacts: - runs-on: ubuntu-latest - name: Merge build artifacts - if: inputs.build_mode == 'release' - needs: - - build-python-mac-win - - build-macos-x86_64 - - build-manylinux-x86_64 - - build-manylinux-aarch64 - - build-sdist - steps: - - name: Merge Build Artifacts - uses: actions/upload-artifact/merge@v7 - with: - name: dist - pattern: dist-* - - # ============================================ - # Build - Documentation - # ============================================ - # Documentation build job that runs after wheels are built - build-docs: - name: Build docs - runs-on: ubuntu-latest - needs: [build-manylinux-x86_64] # Only need the Linux wheel for docs - # Only run docs on main branch pushes, tags, or PRs - if: github.event_name == 'push' || github.event_name == 'pull_request' - steps: - - name: Set target branch - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag') - id: target-branch - run: | - set -x - if test '${{ github.ref }}' = 'refs/heads/main'; then - echo "value=asf-staging" >> "$GITHUB_OUTPUT" - elif test '${{ github.ref_type }}' = 'tag'; then - echo "value=asf-site" >> "$GITHUB_OUTPUT" - else - echo "Unsupported input: ${{ github.ref }} / ${{ github.ref_type }}" - exit 1 - fi - - - name: Checkout docs sources - uses: actions/checkout@v6 - - - name: Checkout docs target branch - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag') - uses: actions/checkout@v6 - with: - fetch-depth: 0 - ref: ${{ steps.target-branch.outputs.value }} - path: docs-target - - - name: Setup Python - uses: actions/setup-python@v6 - with: - python-version: "3.10" - - - name: Install dependencies - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 - with: - enable-cache: true - - # Download the Linux wheel built in the previous job. - # Docs only need the abi3 wheel — interpreter doesn't matter for sphinx. - - name: Download pre-built Linux wheel - uses: actions/download-artifact@v8 - with: - name: dist-manylinux-x86_64-abi3 - path: wheels/ - - # Install from the pre-built wheels - - name: Install from pre-built wheels - run: | - set -x - uv venv - # Install documentation dependencies - uv sync --dev --no-install-package datafusion --group docs - # Install all pre-built wheels - WHEELS=$(find wheels/ -name "*.whl") - if [ -n "$WHEELS" ]; then - echo "Installing wheels:" - echo "$WHEELS" - uv pip install wheels/*.whl - else - echo "ERROR: No wheels found!" - exit 1 - fi - - - name: Build docs - run: | - set -x - cd docs - # build.sh downloads the example data, registers the Jupyter kernel - # myst-nb needs, symlinks the data next to each executed page, and - # runs sphinx. Using it here keeps CI identical to a local build. - uv run --no-project bash ./build.sh - - - name: Copy & push the generated HTML - if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref_type == 'tag') - run: | - set -x - cd docs-target - # delete anything but: 1) '.'; 2) '..'; 3) .git/ - find ./ | grep -vE "^./$|^../$|^./.git" | xargs rm -rf - cp ../.asf.yaml . - cp -r ../docs/build/html/* . - git status --porcelain - if [ "$(git status --porcelain)" != "" ]; then - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add --all - git commit -m 'Publish built docs triggered by ${{ github.sha }}' - git push || git push --force - fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index ab284b522..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,41 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# CI workflow for pull requests - runs tests in DEBUG mode for faster feedback - -name: CI - -on: - pull_request: - branches: ["main"] - -concurrency: - group: ${{ github.repository }}-${{ github.head_ref || github.sha }}-${{ github.workflow }} - cancel-in-progress: true - -jobs: - build: - uses: ./.github/workflows/build.yml - with: - build_mode: debug - run_wheels: false - secrets: inherit - - test: - needs: build - uses: ./.github/workflows/test.yml - secrets: inherit diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 2d0f166ba..000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,54 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -name: "CodeQL" - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - schedule: - - cron: '16 4 * * 1' - -permissions: - contents: read - -jobs: - analyze: - name: Analyze Actions - runs-on: ubuntu-latest - permissions: - contents: read - security-events: write - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - persist-credentials: false - - - name: Initialize CodeQL - uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 - with: - languages: actions - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 - with: - category: "/language:actions" diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml deleted file mode 100644 index 841bf205a..000000000 --- a/.github/workflows/dev.yml +++ /dev/null @@ -1,39 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -name: Dev -on: - push: - branches: - - main - - branch-* - pull_request: - -jobs: - - rat: - name: Release Audit Tool (RAT) - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v6 - - name: Setup Python - uses: actions/setup-python@v6 - with: - python-version: "3.14" - - name: Audit licenses - run: ./dev/release/run-rat.sh . diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index bddc89eac..000000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,49 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# Release workflow - runs tests in RELEASE mode and builds distribution wheels -# Triggered on: -# - Merges to main -# - Release candidate tags (*-rc*) -# - Release tags (e.g., 45.0.0) - -name: Release Build - -on: - push: - branches: - - "main" - tags: - - "*-rc*" # Release candidates (e.g., 45.0.0-rc1) - - "[0-9]+.*" # Release tags (e.g., 45.0.0) - -concurrency: - group: ${{ github.repository }}-${{ github.head_ref || github.sha }}-${{ github.workflow }} - cancel-in-progress: true - -jobs: - build: - uses: ./.github/workflows/build.yml - with: - build_mode: release - run_wheels: true - secrets: inherit - - test: - needs: build - uses: ./.github/workflows/test.yml - secrets: inherit diff --git a/.github/workflows/take.yml b/.github/workflows/take.yml deleted file mode 100644 index 86dc190ad..000000000 --- a/.github/workflows/take.yml +++ /dev/null @@ -1,41 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -name: Assign the issue via a `take` comment -on: - issue_comment: - types: created - -permissions: - issues: write - -jobs: - issue_assign: - runs-on: ubuntu-latest - if: (!github.event.issue.pull_request) && github.event.comment.body == 'take' - concurrency: - group: ${{ github.actor }}-issue-assign - steps: - - run: | - CODE=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -LI https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/assignees/${{ github.event.comment.user.login }} -o /dev/null -w '%{http_code}\n' -s) - if [ "$CODE" -eq "204" ] - then - echo "Assigning issue ${{ github.event.issue.number }} to ${{ github.event.comment.user.login }}" - curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -d '{"assignees": ["${{ github.event.comment.user.login }}"]}' https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/assignees - else - echo "Cannot assign issue ${{ github.event.issue.number }} to ${{ github.event.comment.user.login }}" - fi \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 558e751c8..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,137 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# Reusable workflow for running tests. -# Single matrix covers both GIL (abi3 wheel) and free-threaded -# (per-interpreter wheels) builds. - -name: Test - -on: - workflow_call: - -env: - UV_LOCKED: true - -jobs: - test-matrix: - runs-on: ubuntu-latest - # Backstop: a hung multiprocessing worker (e.g. during a pickle regression) - # should not block CI longer than this. - timeout-minutes: 30 - strategy: - fail-fast: false - matrix: - include: - # GIL builds — all share the same abi3 wheel. - - { python-version: "3.10", wheel-tag: "abi3", freethreaded: false } - - { python-version: "3.11", wheel-tag: "abi3", freethreaded: false } - - { python-version: "3.12", wheel-tag: "abi3", freethreaded: false } - - { python-version: "3.13", wheel-tag: "abi3", freethreaded: false } - - { python-version: "3.14", wheel-tag: "abi3", freethreaded: false } - # Free-threaded builds — one wheel per interpreter. - - { python-version: "3.14t", wheel-tag: "3.14t", freethreaded: true } - steps: - - uses: actions/checkout@v6 - - - name: Setup Python - id: setup-python - uses: actions/setup-python@v6 - with: - python-version: ${{ matrix.python-version }} - freethreaded: ${{ matrix.freethreaded }} - - - name: Cache Cargo - uses: actions/cache@v5 - with: - path: ~/.cargo - key: cargo-cache-stable-${{ hashFiles('Cargo.lock') }} - - - name: Install dependencies - uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 - with: - enable-cache: true - - - name: Download pre-built Linux wheel - uses: actions/download-artifact@v8 - with: - name: dist-manylinux-x86_64-${{ matrix.wheel-tag }} - path: wheels/ - - # FFI test wheel only built once (under the abi3 matrix entry in build.yml). - - name: Download pre-built FFI test wheel - if: matrix.wheel-tag == 'abi3' - uses: actions/download-artifact@v8 - with: - name: test-ffi-manylinux-x86_64 - path: wheels/ - - - name: Install from pre-built wheels - run: | - set -x - # Create the venv with the setup-python interpreter, then point - # every uv command explicitly at the venv's own interpreter. - # uv's interpreter discovery skips free-threaded builds unless - # asked by exact path, so plain `uv sync` (even with an activated - # venv or --active) re-picks the system 3.12 and recreates .venv. - # Targeting .venv/bin/python keeps sync and pip install in the - # same 3.14t environment as the cp314t wheel. - uv venv --python "${{ steps.setup-python.outputs.python-path }}" - VENV_PY="$PWD/.venv/bin/python" - uv sync --python "$VENV_PY" --dev --no-install-package datafusion - WHEELS=$(find wheels/ -name "*.whl") - if [ -n "$WHEELS" ]; then - echo "Installing wheels:" - echo "$WHEELS" - uv pip install --python "$VENV_PY" wheels/*.whl - else - echo "ERROR: No wheels found!" - exit 1 - fi - - - name: Run tests - env: - RUST_BACKTRACE: 1 - # On free-threaded interpreters, fail loud if any C extension - # re-enables the GIL implicitly. - PYTHON_GIL: ${{ matrix.freethreaded && '0' || '' }} - run: | - git submodule update --init - # Use the .venv interpreter directly; uv discovery would skip the - # free-threaded build and re-pick the system 3.12 (see install step). - uv run --python "$PWD/.venv/bin/python" --no-project pytest -v --import-mode=importlib - - # FFI + TPC-H examples only need to run once; gate to abi3 entries. - - name: FFI unit tests - if: matrix.wheel-tag == 'abi3' - run: | - cd examples/datafusion-ffi-example - uv run --no-project pytest python/tests/_test*.py - - - name: Run tpchgen-cli to create 1 Gb dataset - if: matrix.wheel-tag == 'abi3' - run: | - mkdir examples/tpch/data - cd examples/tpch/data - uv pip install tpchgen-cli - uv run --no-project tpchgen-cli -s 1 --format=parquet - - - name: Run TPC-H examples - if: matrix.wheel-tag == 'abi3' - run: | - cd examples/tpch - uv run --no-project pytest _tests.py diff --git a/.github/workflows/verify-release-candidate.yml b/.github/workflows/verify-release-candidate.yml deleted file mode 100644 index 6ecb547b5..000000000 --- a/.github/workflows/verify-release-candidate.yml +++ /dev/null @@ -1,83 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -name: Verify Release Candidate - -# NOTE: This workflow is intended to be run manually via workflow_dispatch. - -on: - workflow_dispatch: - inputs: - version: - description: Version number (e.g., 52.0.0) - required: true - type: string - rc_number: - description: Release candidate number (e.g., 1) - required: true - type: string - -concurrency: - group: ${{ github.repository }}-${{ github.ref }}-${{ github.workflow }} - cancel-in-progress: true - -jobs: - verify: - name: Verify RC (${{ matrix.os }}-${{ matrix.arch }}) - strategy: - fail-fast: false - matrix: - include: - # Linux - - os: linux - arch: x64 - runner: ubuntu-latest - - os: linux - arch: arm64 - runner: ubuntu-24.04-arm - - # macOS - - os: macos - arch: arm64 - runner: macos-latest - - os: macos - arch: x64 - runner: macos-15-intel - - # Windows - - os: windows - arch: x64 - runner: windows-latest - runs-on: ${{ matrix.runner }} - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - - name: Set up protoc - uses: arduino/setup-protoc@v3 - with: - version: "27.4" - repo-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set RUSTFLAGS for Windows GNU linker - if: matrix.os == 'windows' - shell: bash - run: echo "RUSTFLAGS=-C link-arg=-Wl,--exclude-libs=ALL" >> "$GITHUB_ENV" - - - name: Run release candidate verification - shell: bash - run: ./dev/release/verify-release-candidate.sh "${{ inputs.version }}" "${{ inputs.rc_number }}" diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 614d82327..000000000 --- a/.gitignore +++ /dev/null @@ -1,38 +0,0 @@ -target -/venv -.idea -/docs/temp -/docs/build -.DS_Store -.vscode - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# Python dist ignore -dist - -# C extensions -*.so - -# Python dist -dist - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -.python-version -venv -.venv - -apache-rat-*.jar -*rat.txt -.env -CHANGELOG.md.bak - -docs/mdbook/book - -.pyo3_build_config - diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index a3b1b5157..000000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "testing"] - path = testing - url = https://github.com/apache/arrow-testing.git -[submodule "parquet"] - path = parquet - url = https://github.com/apache/parquet-testing.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 0a212480b..000000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -repos: - - repo: https://github.com/rhysd/actionlint - rev: v1.7.12 - hooks: - - id: actionlint-docker - - repo: https://github.com/astral-sh/ruff-pre-commit - # Ruff version. - rev: v0.15.1 - hooks: - # Run the linter. - - id: ruff - # Run the formatter. - - id: ruff-format - - repo: local - hooks: - - id: rust-fmt - name: Rust fmt - description: Run cargo fmt on files included in the commit. rustfmt should be installed before-hand. - entry: cargo +nightly fmt --all -- - pass_filenames: true - types: [file, rust] - language: system - - id: rust-clippy - name: Rust clippy - description: Run cargo clippy on files included in the commit. clippy should be installed before-hand. - entry: cargo clippy --all-targets --all-features -- -Dclippy::all -D warnings -Aclippy::redundant_closure - pass_filenames: false - types: [file, rust] - language: system - - - repo: https://github.com/codespell-project/codespell - rev: v2.4.1 - hooks: - - id: codespell - args: [ --toml, "pyproject.toml"] - additional_dependencies: - - tomli - - - repo: https://github.com/astral-sh/uv-pre-commit - # uv version. - rev: 0.10.7 - hooks: - # Update the uv lockfile - - id: uv-lock - -default_language_version: - python: python3 diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index fda08b23c..000000000 --- a/AGENTS.md +++ /dev/null @@ -1,92 +0,0 @@ - - -# Agent Instructions for Contributors - -This file is for agents working **on** the datafusion-python project (developing, -testing, reviewing). If you need to **use** the DataFusion DataFrame API (write -queries, build expressions, understand available functions), see the user-facing -skill at [`SKILL.md`](skills/datafusion_python/SKILL.md). - -## Skills - -This project uses AI agent skills stored in `.ai/skills/`. Each skill is a directory containing a `SKILL.md` file with instructions for performing a specific task. - -Skills follow the [Agent Skills](https://agentskills.io) open standard. Each skill directory contains: - -- `SKILL.md` — The skill definition with YAML frontmatter (name, description, argument-hint) and detailed instructions. -- Additional supporting files as needed. - -To discover what skills are available, list `.ai/skills/` and read each -`SKILL.md`. The frontmatter `name` and `description` fields summarize the -skill's purpose. - -## Pull Requests - -Every pull request must follow the template in -`.github/pull_request_template.md`. The description must include these sections: - -1. **Which issue does this PR close?** — Link the issue with `Closes #NNN`. -2. **Rationale for this change** — Why the change is needed (skip if the issue - already explains it clearly). -3. **What changes are included in this PR?** — Summarize the individual changes. -4. **Are there any user-facing changes?** — Note any changes visible to users - (new APIs, changed behavior, new files shipped in the package, etc.). If - there are breaking changes to public APIs, add the `api change` label. - -## Pre-commit Checks - -Always run pre-commit checks **before** committing. The hooks are defined in -`.pre-commit-config.yaml` and run automatically on `git commit` if pre-commit -is installed as a git hook. To run all hooks manually: - -```bash -pre-commit run --all-files -``` - -Fix any failures before committing. - -## Python Function Docstrings - -Every Python function must include a docstring with usage examples. - -- **Examples are required**: Each function needs at least one doctest-style example - demonstrating basic usage. -- **Optional parameters**: If a function has optional parameters, include separate - examples that show usage both without and with the optional arguments. Pass - optional arguments using their keyword name (e.g., `step=dfn.lit(3)`) so readers - can immediately see which parameter is being demonstrated. -- **Reuse input data**: Use the same input data across examples wherever possible. - The examples should demonstrate how different optional arguments change the output - for the same input, making the effect of each option easy to understand. -- **Alias functions**: Functions that are simple aliases (e.g., `list_sort` aliasing - `array_sort`) only need a one-line description and a `See Also` reference to the - primary function. They do not need their own examples. - -## Aggregate and Window Function Documentation - -When adding or updating an aggregate or window function, ensure the corresponding -site documentation is kept in sync: - -- **Aggregations**: `docs/source/user-guide/common-operations/aggregations.md` — - add new aggregate functions to the "Aggregate Functions" list and include usage - examples if appropriate. -- **Window functions**: `docs/source/user-guide/common-operations/windows.md` — - add new window functions to the "Available Functions" list and include usage - examples if appropriate. diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index ae40911d8..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,22 +0,0 @@ - - -# DataFusion Python Changelog - -The changelogs have now moved [here](./dev/changelog). diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 120000 index 47dc3e3d8..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -AGENTS.md \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index ab222b177..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,4645 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "const-random", - "getrandom 0.3.4", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "ar_archive_writer" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348" -dependencies = [ - "object", -] - -[[package]] -name = "arc-swap" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" -dependencies = [ - "rustversion", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "arrow" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61d285d16bce7d0be61912f7928342b673067b6b7d7ef6cc179258ba7de1fecf" -dependencies = [ - "arrow-arith", - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-csv", - "arrow-data", - "arrow-ipc", - "arrow-json", - "arrow-ord", - "arrow-pyarrow", - "arrow-row", - "arrow-schema", - "arrow-select", - "arrow-string", -] - -[[package]] -name = "arrow-arith" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757ef1836251e88222542a7da2623bc1c9cb9e20afefa6db2c41e79991cd91d4" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema", - "chrono", - "num-traits", -] - -[[package]] -name = "arrow-array" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9a4a4b2b5ecd0e04df03471661cb61f28bed3c7fd50994715129b01b2edb97" -dependencies = [ - "ahash", - "arrow-buffer", - "arrow-data", - "arrow-schema", - "chrono", - "chrono-tz", - "half", - "hashbrown 0.17.1", - "libc", - "num-complex", - "num-integer", - "num-traits", -] - -[[package]] -name = "arrow-avro" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb45cd6bd2b25c0965793b83200eaca82214273a8030fbbc2d783e4c7c65a61" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-schema", - "bytes", - "bzip2", - "crc", - "flate2", - "indexmap", - "liblzma", - "rand 0.9.4", - "serde", - "serde_json", - "snap", - "strum_macros", - "uuid", - "zstd", -] - -[[package]] -name = "arrow-buffer" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c12b576ef18c1deb80925a248b25ad84f419198d791b8e293fc6aaa60441fe90" -dependencies = [ - "bytes", - "half", - "num-bigint 0.5.1", - "num-traits", -] - -[[package]] -name = "arrow-cast" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68338a9096a5dc9bc11927c58c43a8526d96bf6abd2012ef6c0c9f505991cc79" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-ord", - "arrow-schema", - "arrow-select", - "atoi", - "base64 0.23.1", - "chrono", - "comfy-table", - "half", - "lexical-core", - "num-traits", - "ryu", -] - -[[package]] -name = "arrow-csv" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25011b52b346407d497ef0030e12b45e4f2d0cc279efc09c4f3d09106db30e36" -dependencies = [ - "arrow-array", - "arrow-cast", - "arrow-schema", - "chrono", - "csv", - "csv-core", - "regex", -] - -[[package]] -name = "arrow-data" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "723fe4aeed7604e00b9883a465af4ff0a0e6c44c03e41a68c3d1cbc403e0e44d" -dependencies = [ - "arrow-buffer", - "arrow-schema", - "half", - "num-integer", - "num-traits", -] - -[[package]] -name = "arrow-ipc" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "149437b14371f5b9ec60f5ddc751483ae99d7a7072653c0075e5e469156eea7b" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema", - "arrow-select", - "flatbuffers", - "lz4_flex", - "zstd", -] - -[[package]] -name = "arrow-json" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f18b9123ccfec418a663f821c9a034af339711678c11ffe00d3ec07da5ff9f7e" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-cast", - "arrow-ord", - "arrow-schema", - "arrow-select", - "chrono", - "half", - "indexmap", - "itoa", - "lexical-core", - "memchr", - "num-traits", - "ryu", - "serde_core", - "serde_json", - "simdutf8", -] - -[[package]] -name = "arrow-ord" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c08dff0686cf23ca4f562803f191ccbeb726dbae6309cd4b4aaf65e0f2c979" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema", - "arrow-select", -] - -[[package]] -name = "arrow-pyarrow" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c196ecc25b3a8dcbc1d842f2619cee653dcfa2fb8b56a291bc0481c3cf5c3821" -dependencies = [ - "arrow-array", - "arrow-data", - "arrow-schema", - "pyo3", -] - -[[package]] -name = "arrow-row" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbec439386df71ad570e6758a946111322b9e9dc8db83b5527321f0b4c9119c2" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema", - "half", -] - -[[package]] -name = "arrow-schema" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6fed2ca0d1eade57e811cbe73b98ad50cc08a1183e13b2d2aa43a7df593f40e" -dependencies = [ - "bitflags", - "serde_core", - "serde_json", -] - -[[package]] -name = "arrow-select" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "466b19cf75130b891dc1b23a84b343c714c62c64c9c62e365c76aa0ff90a53fb" -dependencies = [ - "ahash", - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema", - "num-traits", -] - -[[package]] -name = "arrow-string" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c838a25bb3691e919e0f617616ac51a4ff8517a952e29ca133cf0c22b2ce65b1" -dependencies = [ - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-schema", - "arrow-select", - "memchr", - "num-traits", - "regex", - "regex-syntax", -] - -[[package]] -name = "async-compression" -version = "0.4.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" -dependencies = [ - "compression-codecs", - "compression-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "async-ffi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4de21c0feef7e5a556e51af767c953f0501f7f300ba785cc99c47bdc8081a50" - -[[package]] -name = "async-recursion" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "atoi" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" -dependencies = [ - "num-traits", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "autocfg" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" - -[[package]] -name = "bigdecimal" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6867f1565b3aad85681f1015055b087fcfd840d6aeee6eee7f2da317603695" -dependencies = [ - "autocfg", - "libm", - "num-bigint 0.4.6", - "num-integer", - "num-traits", -] - -[[package]] -name = "bitflags" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" - -[[package]] -name = "blake2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "blake3" -version = "1.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "cpufeatures", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" -dependencies = [ - "hybrid-array", -] - -[[package]] -name = "brotli" -version = "8.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "5.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" - -[[package]] -name = "bzip2" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" -dependencies = [ - "libbz2-rs-sys", -] - -[[package]] -name = "cc" -version = "1.2.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chacha20" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" -dependencies = [ - "cfg-if", - "cpufeatures", - "rand_core 0.10.1", -] - -[[package]] -name = "chrono" -version = "0.4.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" -dependencies = [ - "iana-time-zone", - "num-traits", - "serde", - "windows-link", -] - -[[package]] -name = "chrono-tz" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" -dependencies = [ - "chrono", - "phf", -] - -[[package]] -name = "cmake" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" -dependencies = [ - "cc", -] - -[[package]] -name = "comfy-table" -version = "7.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958c5d6ecf1f214b4c2bbbbf6ab9523a864bd136dcf71a7e8904799acfe1ad47" -dependencies = [ - "unicode-segmentation", - "unicode-width", -] - -[[package]] -name = "compression-codecs" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" -dependencies = [ - "bzip2", - "compression-core", - "flate2", - "liblzma", - "memchr", - "zstd", - "zstd-safe", -] - -[[package]] -name = "compression-core" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" - -[[package]] -name = "const-oid" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" - -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom 0.2.17", - "once_cell", - "tiny-keccak", -] - -[[package]] -name = "constant_time_eq" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - -[[package]] -name = "crc" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "crypto-common" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" -dependencies = [ - "hybrid-array", -] - -[[package]] -name = "cstr" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68523903c8ae5aacfa32a0d9ae60cadeb764e1da14ee0d26b1f3089f13a54636" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "csv" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde_core", -] - -[[package]] -name = "csv-core" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" -dependencies = [ - "memchr", -] - -[[package]] -name = "dashmap" -version = "6.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core", -] - -[[package]] -name = "datafusion" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "arrow-schema", - "async-trait", - "bzip2", - "chrono", - "datafusion-catalog", - "datafusion-catalog-listing", - "datafusion-common", - "datafusion-common-runtime", - "datafusion-datasource", - "datafusion-datasource-arrow", - "datafusion-datasource-avro", - "datafusion-datasource-csv", - "datafusion-datasource-json", - "datafusion-datasource-parquet", - "datafusion-execution", - "datafusion-expr", - "datafusion-expr-common", - "datafusion-functions", - "datafusion-functions-aggregate", - "datafusion-functions-nested", - "datafusion-functions-table", - "datafusion-functions-window", - "datafusion-optimizer", - "datafusion-physical-expr", - "datafusion-physical-expr-adapter", - "datafusion-physical-expr-common", - "datafusion-physical-optimizer", - "datafusion-physical-plan", - "datafusion-session", - "datafusion-sql", - "flate2", - "futures", - "indexmap", - "itertools 0.15.0", - "liblzma", - "log", - "object_store", - "parking_lot", - "parquet", - "sqlparser", - "tempfile", - "tokio", - "url", - "uuid", - "zstd", -] - -[[package]] -name = "datafusion-catalog" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "async-trait", - "dashmap", - "datafusion-common", - "datafusion-common-runtime", - "datafusion-datasource", - "datafusion-execution", - "datafusion-expr", - "datafusion-physical-expr", - "datafusion-physical-plan", - "datafusion-session", - "futures", - "itertools 0.15.0", - "log", - "object_store", - "parking_lot", - "tokio", -] - -[[package]] -name = "datafusion-catalog-listing" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "async-trait", - "datafusion-catalog", - "datafusion-common", - "datafusion-datasource", - "datafusion-execution", - "datafusion-expr", - "datafusion-physical-expr", - "datafusion-physical-expr-adapter", - "datafusion-physical-expr-common", - "datafusion-physical-plan", - "futures", - "itertools 0.15.0", - "log", - "object_store", - "percent-encoding", -] - -[[package]] -name = "datafusion-common" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "arrow-ipc", - "arrow-schema", - "chrono", - "foldhash 0.2.0", - "half", - "hashbrown 0.17.1", - "indexmap", - "itertools 0.15.0", - "libc", - "log", - "num-traits", - "object_store", - "parquet", - "recursive", - "sqlparser", - "tokio", - "uuid", - "web-time", -] - -[[package]] -name = "datafusion-common-runtime" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "futures", - "log", - "tokio", -] - -[[package]] -name = "datafusion-datasource" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "async-compression", - "async-trait", - "bytes", - "bzip2", - "chrono", - "datafusion-common", - "datafusion-common-runtime", - "datafusion-execution", - "datafusion-expr", - "datafusion-physical-expr", - "datafusion-physical-expr-adapter", - "datafusion-physical-expr-common", - "datafusion-physical-plan", - "datafusion-proto-models", - "datafusion-session", - "flate2", - "futures", - "glob", - "itertools 0.15.0", - "liblzma", - "log", - "object_store", - "parking_lot", - "rand 0.9.4", - "tokio", - "tokio-util", - "url", - "zstd", -] - -[[package]] -name = "datafusion-datasource-arrow" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "arrow-ipc", - "async-trait", - "bytes", - "datafusion-common", - "datafusion-common-runtime", - "datafusion-datasource", - "datafusion-execution", - "datafusion-expr", - "datafusion-physical-expr-common", - "datafusion-physical-plan", - "datafusion-proto-models", - "datafusion-session", - "futures", - "itertools 0.15.0", - "object_store", - "tokio", -] - -[[package]] -name = "datafusion-datasource-avro" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "arrow-avro", - "async-trait", - "bytes", - "datafusion-common", - "datafusion-datasource", - "datafusion-physical-expr-adapter", - "datafusion-physical-plan", - "datafusion-session", - "futures", - "object_store", -] - -[[package]] -name = "datafusion-datasource-csv" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "async-trait", - "bytes", - "datafusion-common", - "datafusion-common-runtime", - "datafusion-datasource", - "datafusion-execution", - "datafusion-expr", - "datafusion-physical-expr-common", - "datafusion-physical-plan", - "datafusion-proto-models", - "datafusion-session", - "futures", - "object_store", - "regex", - "tokio", -] - -[[package]] -name = "datafusion-datasource-json" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "async-trait", - "bytes", - "datafusion-common", - "datafusion-common-runtime", - "datafusion-datasource", - "datafusion-execution", - "datafusion-expr", - "datafusion-physical-expr-common", - "datafusion-physical-plan", - "datafusion-proto-models", - "datafusion-session", - "futures", - "object_store", - "tokio", - "tokio-stream", -] - -[[package]] -name = "datafusion-datasource-parquet" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "arrow-schema", - "async-trait", - "bytes", - "datafusion-common", - "datafusion-common-runtime", - "datafusion-datasource", - "datafusion-execution", - "datafusion-expr", - "datafusion-functions", - "datafusion-functions-aggregate-common", - "datafusion-physical-expr", - "datafusion-physical-expr-adapter", - "datafusion-physical-expr-common", - "datafusion-physical-plan", - "datafusion-proto-models", - "datafusion-pruning", - "datafusion-session", - "futures", - "itertools 0.15.0", - "log", - "object_store", - "parking_lot", - "parquet", - "tokio", -] - -[[package]] -name = "datafusion-doc" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" - -[[package]] -name = "datafusion-execution" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "arrow-buffer", - "async-trait", - "bytes", - "dashmap", - "datafusion-common", - "datafusion-expr", - "datafusion-physical-expr-common", - "futures", - "log", - "object_store", - "parking_lot", - "pin-project-lite", - "rand 0.9.4", - "tempfile", - "tokio", - "tokio-util", - "url", -] - -[[package]] -name = "datafusion-expr" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "arrow-schema", - "async-trait", - "chrono", - "datafusion-common", - "datafusion-doc", - "datafusion-expr-common", - "datafusion-functions-aggregate-common", - "datafusion-functions-window-common", - "datafusion-physical-expr-common", - "datafusion-proto-common", - "datafusion-proto-models", - "indexmap", - "itertools 0.15.0", - "recursive", - "serde_json", - "sqlparser", -] - -[[package]] -name = "datafusion-expr-common" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "datafusion-common", - "indexmap", - "itertools 0.15.0", -] - -[[package]] -name = "datafusion-ffi" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "arrow-schema", - "async-ffi", - "async-trait", - "chrono", - "datafusion-catalog", - "datafusion-common", - "datafusion-datasource", - "datafusion-execution", - "datafusion-expr", - "datafusion-functions-aggregate-common", - "datafusion-physical-expr", - "datafusion-physical-expr-common", - "datafusion-physical-optimizer", - "datafusion-physical-plan", - "datafusion-proto", - "datafusion-proto-common", - "datafusion-session", - "futures", - "libloading", - "log", - "prost", - "semver", - "stabby", - "tokio", -] - -[[package]] -name = "datafusion-ffi-example" -version = "54.0.0" -dependencies = [ - "arrow", - "arrow-array", - "arrow-schema", - "async-trait", - "datafusion", - "datafusion-catalog", - "datafusion-common", - "datafusion-expr", - "datafusion-ffi", - "datafusion-functions-aggregate", - "datafusion-functions-window", - "datafusion-proto", - "datafusion-python-util", - "pyo3", - "pyo3-build-config", - "pyo3-log", -] - -[[package]] -name = "datafusion-functions" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "arrow-buffer", - "base64 0.23.1", - "blake2", - "blake3", - "chrono", - "chrono-tz", - "datafusion-common", - "datafusion-doc", - "datafusion-execution", - "datafusion-expr", - "datafusion-expr-common", - "datafusion-macros", - "datafusion-physical-expr-common", - "hex", - "itertools 0.15.0", - "log", - "md-5 0.11.0", - "memchr", - "num-traits", - "rand 0.9.4", - "regex", - "sha2", - "uuid", -] - -[[package]] -name = "datafusion-functions-aggregate" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "datafusion-common", - "datafusion-doc", - "datafusion-execution", - "datafusion-expr", - "datafusion-functions-aggregate-common", - "datafusion-macros", - "datafusion-physical-expr", - "datafusion-physical-expr-common", - "half", - "hashbrown 0.17.1", - "log", - "num-traits", -] - -[[package]] -name = "datafusion-functions-aggregate-common" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "datafusion-common", - "datafusion-expr-common", - "datafusion-physical-expr-common", -] - -[[package]] -name = "datafusion-functions-nested" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "arrow-ord", - "datafusion-common", - "datafusion-doc", - "datafusion-execution", - "datafusion-expr", - "datafusion-expr-common", - "datafusion-functions", - "datafusion-functions-aggregate", - "datafusion-functions-aggregate-common", - "datafusion-macros", - "datafusion-physical-expr-common", - "hashbrown 0.17.1", - "itertools 0.15.0", - "itoa", - "log", - "memchr", -] - -[[package]] -name = "datafusion-functions-table" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "async-trait", - "datafusion-catalog", - "datafusion-common", - "datafusion-expr", - "datafusion-physical-expr", - "datafusion-physical-plan", - "parking_lot", -] - -[[package]] -name = "datafusion-functions-window" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "datafusion-common", - "datafusion-doc", - "datafusion-expr", - "datafusion-functions-window-common", - "datafusion-macros", - "datafusion-physical-expr", - "datafusion-physical-expr-common", - "log", -] - -[[package]] -name = "datafusion-functions-window-common" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "datafusion-common", - "datafusion-physical-expr-common", -] - -[[package]] -name = "datafusion-macros" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "datafusion-doc", - "quote", - "syn 3.0.3", -] - -[[package]] -name = "datafusion-optimizer" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "chrono", - "datafusion-common", - "datafusion-expr", - "datafusion-expr-common", - "datafusion-physical-expr", - "indexmap", - "itertools 0.15.0", - "log", - "recursive", - "regex", - "regex-syntax", -] - -[[package]] -name = "datafusion-physical-expr" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "datafusion-common", - "datafusion-expr", - "datafusion-expr-common", - "datafusion-functions-aggregate-common", - "datafusion-physical-expr-common", - "datafusion-proto-models", - "half", - "hashbrown 0.17.1", - "indexmap", - "itertools 0.15.0", - "parking_lot", - "petgraph", - "recursive", - "tokio", -] - -[[package]] -name = "datafusion-physical-expr-adapter" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "datafusion-common", - "datafusion-expr", - "datafusion-functions", - "datafusion-physical-expr", - "datafusion-physical-expr-common", - "itertools 0.15.0", -] - -[[package]] -name = "datafusion-physical-expr-common" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "chrono", - "datafusion-common", - "datafusion-expr-common", - "datafusion-proto-models", - "hashbrown 0.17.1", - "indexmap", - "itertools 0.15.0", - "parking_lot", - "pin-project", -] - -[[package]] -name = "datafusion-physical-optimizer" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "datafusion-common", - "datafusion-execution", - "datafusion-expr", - "datafusion-expr-common", - "datafusion-physical-expr", - "datafusion-physical-expr-common", - "datafusion-physical-plan", - "datafusion-pruning", - "datafusion-session", - "itertools 0.15.0", - "recursive", -] - -[[package]] -name = "datafusion-physical-plan" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "arrow-data", - "arrow-ipc", - "arrow-ord", - "arrow-schema", - "async-trait", - "bytes", - "datafusion-common", - "datafusion-common-runtime", - "datafusion-execution", - "datafusion-expr", - "datafusion-functions", - "datafusion-functions-aggregate-common", - "datafusion-functions-window-common", - "datafusion-physical-expr", - "datafusion-physical-expr-common", - "datafusion-proto-common", - "datafusion-proto-models", - "futures", - "half", - "hashbrown 0.17.1", - "indexmap", - "itertools 0.15.0", - "log", - "num-traits", - "parking_lot", - "pin-project-lite", - "serde_json", - "tokio", -] - -[[package]] -name = "datafusion-proto" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "datafusion-catalog", - "datafusion-catalog-listing", - "datafusion-common", - "datafusion-datasource", - "datafusion-datasource-arrow", - "datafusion-datasource-csv", - "datafusion-datasource-json", - "datafusion-datasource-parquet", - "datafusion-execution", - "datafusion-expr", - "datafusion-functions-table", - "datafusion-physical-expr", - "datafusion-physical-expr-common", - "datafusion-physical-plan", - "datafusion-proto-common", - "datafusion-proto-models", - "object_store", - "prost", -] - -[[package]] -name = "datafusion-proto-common" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "datafusion-common", - "prost", -] - -[[package]] -name = "datafusion-proto-models" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "datafusion-common", - "datafusion-proto-common", - "prost", -] - -[[package]] -name = "datafusion-pruning" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "datafusion-common", - "datafusion-datasource", - "datafusion-expr-common", - "datafusion-physical-expr", - "datafusion-physical-expr-common", - "datafusion-physical-plan", - "log", -] - -[[package]] -name = "datafusion-python" -version = "54.0.0" -dependencies = [ - "arrow", - "arrow-select", - "async-trait", - "chrono", - "cstr", - "datafusion", - "datafusion-ffi", - "datafusion-proto", - "datafusion-python-util", - "datafusion-spark", - "datafusion-substrait", - "futures", - "log", - "mimalloc", - "object_store", - "parking_lot", - "prost", - "prost-types", - "pyo3", - "pyo3-async-runtimes", - "pyo3-build-config", - "pyo3-log", - "serde_json", - "tokio", - "url", - "uuid", -] - -[[package]] -name = "datafusion-python-util" -version = "54.0.0" -dependencies = [ - "arrow", - "datafusion", - "datafusion-ffi", - "datafusion-proto", - "prost", - "pyo3", - "tokio", -] - -[[package]] -name = "datafusion-session" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow-schema", - "async-trait", - "datafusion-common", - "datafusion-execution", - "datafusion-expr", - "datafusion-physical-plan", - "parking_lot", -] - -[[package]] -name = "datafusion-spark" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "bigdecimal", - "chrono", - "crc32fast", - "datafusion", - "datafusion-catalog", - "datafusion-common", - "datafusion-execution", - "datafusion-expr", - "datafusion-functions", - "datafusion-functions-aggregate", - "datafusion-functions-aggregate-common", - "datafusion-functions-nested", - "log", - "num-traits", - "percent-encoding", - "rand 0.9.4", - "serde_json", - "sha1", - "sha2", - "twox-hash", - "url", -] - -[[package]] -name = "datafusion-sql" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "arrow", - "bigdecimal", - "chrono", - "datafusion-common", - "datafusion-expr", - "datafusion-functions-nested", - "indexmap", - "log", - "recursive", - "regex", - "sqlparser", - "stacker", -] - -[[package]] -name = "datafusion-substrait" -version = "55.0.0" -source = "git+https://github.com/apache/datafusion?rev=55.0.0-rc3#d5552342012888b7d1a3ab88d92e3d292fc0cde0" -dependencies = [ - "async-recursion", - "async-trait", - "chrono", - "datafusion", - "half", - "itertools 0.15.0", - "object_store", - "pbjson-types", - "prost", - "substrait", - "tokio", - "url", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common 0.1.7", - "subtle", -] - -[[package]] -name = "digest" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" -dependencies = [ - "block-buffer 0.12.1", - "const-oid", - "crypto-common 0.2.2", -] - -[[package]] -name = "displaydoc" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "either" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - -[[package]] -name = "flatbuffers" -version = "25.12.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35f6839d7b3b98adde531effaf34f0c2badc6f4735d26fe74709d8e513a96ef3" -dependencies = [ - "bitflags", - "rustc_version", -] - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", - "zlib-rs", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi 5.3.0", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" -dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", - "rand_core 0.10.1", -] - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "h2" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "num-traits", - "zerocopy", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash 0.1.5", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.2.0", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.2.0", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "http" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "humantime" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" - -[[package]] -name = "hybrid-array" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" -dependencies = [ - "typenum", -] - -[[package]] -name = "hyper" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-native-certs", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" -dependencies = [ - "cfg-if", - "futures-util", - "wasm-bindgen", -] - -[[package]] -name = "lexical-core" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d8d125a277f807e55a77304455eb7b1cb52f2b18c143b60e766c120bd64a594" -dependencies = [ - "lexical-parse-float", - "lexical-parse-integer", - "lexical-util", - "lexical-write-float", - "lexical-write-integer", -] - -[[package]] -name = "lexical-parse-float" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a9f232fbd6f550bc0137dcb5f99ab674071ac2d690ac69704593cb4abbea56" -dependencies = [ - "lexical-parse-integer", - "lexical-util", -] - -[[package]] -name = "lexical-parse-integer" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34" -dependencies = [ - "lexical-util", -] - -[[package]] -name = "lexical-util" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17" - -[[package]] -name = "lexical-write-float" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c438c87c013188d415fbabbb1dceb44249ab81664efbd31b14ae55dabb6361" -dependencies = [ - "lexical-util", - "lexical-write-integer", -] - -[[package]] -name = "lexical-write-integer" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "409851a618475d2d5796377cad353802345cba92c867d9fbcde9cf4eac4e14df" -dependencies = [ - "lexical-util", -] - -[[package]] -name = "libbz2-rs-sys" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" - -[[package]] -name = "libc" -version = "0.2.186" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" - -[[package]] -name = "libloading" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" -dependencies = [ - "cfg-if", - "windows-link", -] - -[[package]] -name = "liblzma" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6033b77c21d1f56deeae8014eb9fbe7bdf1765185a6c508b5ca82eeaed7f899" -dependencies = [ - "liblzma-sys", -] - -[[package]] -name = "liblzma-sys" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a60851d15cd8c5346eca4ab8babff585be2ae4bc8097c067291d3ffe2add3b6" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "libmimalloc-sys" -version = "0.1.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a45a52f43e1c16f667ccfe4dd8c85b7f7c204fd5e3bf46c5b0db9a5c3c0b8e9" -dependencies = [ - "cc", -] - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "lz4_flex" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecbdfe44b1bd960b68170b417450a628c43f7cf56bb3c5317e61cb230ee7f226" -dependencies = [ - "twox-hash", -] - -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest 0.10.7", -] - -[[package]] -name = "md-5" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" -dependencies = [ - "cfg-if", - "digest 0.11.3", -] - -[[package]] -name = "memchr" -version = "2.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" - -[[package]] -name = "mimalloc" -version = "0.1.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4139bb28d14ad1facf21d5eb8825051b326e172d216b39f6d31df53cc97862" -dependencies = [ - "libmimalloc-sys", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "multimap" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" - -[[package]] -name = "num-bigint" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93e7820bc0a80a0238e650327316f929ba18d5be054b647490a3a6a339f3e7c0" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "object_store" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622acbc9100d3c10e2ee15804b0caa40e55c933d5aa53814cd520805b7958a49" -dependencies = [ - "async-trait", - "base64 0.22.1", - "bytes", - "chrono", - "form_urlencoded", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body-util", - "httparse", - "humantime", - "hyper", - "itertools 0.14.0", - "md-5 0.10.6", - "parking_lot", - "percent-encoding", - "quick-xml", - "rand 0.10.1", - "reqwest", - "ring", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "thiserror", - "tokio", - "tracing", - "url", - "walkdir", - "wasm-bindgen-futures", - "web-time", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-link", -] - -[[package]] -name = "parquet" -version = "59.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7065842956a20c2a536924ce8e4d9955f7422451511b9eb7500d7bfe5077e59c" -dependencies = [ - "ahash", - "arrow-array", - "arrow-buffer", - "arrow-data", - "arrow-ipc", - "arrow-schema", - "arrow-select", - "base64 0.23.1", - "brotli", - "bytes", - "chrono", - "flate2", - "futures", - "half", - "hashbrown 0.17.1", - "lz4_flex", - "num-bigint 0.5.1", - "num-integer", - "num-traits", - "object_store", - "seq-macro", - "simdutf8", - "snap", - "tokio", - "twox-hash", - "zstd", -] - -[[package]] -name = "pbjson" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "898bac3fa00d0ba57a4e8289837e965baa2dee8c3749f3b11d45a64b4223d9c3" -dependencies = [ - "base64 0.22.1", - "serde", -] - -[[package]] -name = "pbjson-build" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af22d08a625a2213a78dbb0ffa253318c5c79ce3133d32d296655a7bdfb02095" -dependencies = [ - "heck", - "itertools 0.14.0", - "prost", - "prost-types", -] - -[[package]] -name = "pbjson-types" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e748e28374f10a330ee3bb9f29b828c0ac79831a32bab65015ad9b661ead526" -dependencies = [ - "bytes", - "chrono", - "pbjson", - "pbjson-build", - "prost", - "prost-build", - "serde", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "petgraph" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" -dependencies = [ - "fixedbitset", - "hashbrown 0.15.5", - "indexmap", - "serde", -] - -[[package]] -name = "phf" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" -dependencies = [ - "phf_shared", -] - -[[package]] -name = "phf_shared" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pkg-config" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" - -[[package]] -name = "portable-atomic" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.118", -] - -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "prost" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" -dependencies = [ - "heck", - "itertools 0.14.0", - "log", - "multimap", - "petgraph", - "prettyplease", - "prost", - "prost-types", - "regex", - "syn 2.0.118", - "tempfile", -] - -[[package]] -name = "prost-derive" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "prost-types" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" -dependencies = [ - "prost", -] - -[[package]] -name = "protobuf-src" -version = "2.1.1+27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6217c3504da19b85a3a4b2e9a5183d635822d83507ba0986624b5c05b83bfc40" -dependencies = [ - "cmake", -] - -[[package]] -name = "psm" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" -dependencies = [ - "ar_archive_writer", - "cc", -] - -[[package]] -name = "pyo3" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd274650b21d4bfc26a0a47587962c1edb425f69287324355cd040c3ea66071c" -dependencies = [ - "libc", - "once_cell", - "portable-atomic", - "pyo3-build-config", - "pyo3-ffi", - "pyo3-macros", -] - -[[package]] -name = "pyo3-async-runtimes" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ef68daa7316a3fac65e5e18b2203f010346de1c1c53456811a2624673ab046" -dependencies = [ - "futures-channel", - "futures-util", - "once_cell", - "pin-project-lite", - "pyo3", - "tokio", -] - -[[package]] -name = "pyo3-build-config" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e2a7d2f0d013342f295c048ad19237add5154a55b1c5a254c0ec93d4109078" -dependencies = [ - "target-lexicon", -] - -[[package]] -name = "pyo3-ffi" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca85c467da1bbc8d866eea5deff9cf29ea5f7785054a17da36e65bda9c05845b" -dependencies = [ - "libc", - "pyo3-build-config", -] - -[[package]] -name = "pyo3-log" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64083bd3a16a353d9d62335808e8e13d0552d2a2b83fdb084496192dcfa9fcd" -dependencies = [ - "arc-swap", - "log", - "pyo3", -] - -[[package]] -name = "pyo3-macros" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac53762fd065daa3194dd09337a38bd793a188100fd1a9304c4ab312d901771" -dependencies = [ - "proc-macro2", - "pyo3-macros-backend", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "pyo3-macros-backend" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca3a1557399783172dc5bf39cfca835157732532cba56b71d2292161e53b362" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "quick-xml" -version = "0.39.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.4", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha", - "rand_core 0.9.5", -] - -[[package]] -name = "rand" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" -dependencies = [ - "chacha20", - "getrandom 0.4.3", - "rand_core 0.10.1", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "rand_core" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" - -[[package]] -name = "recursive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0786a43debb760f491b1bc0269fe5e84155353c67482b9e60d0cfb596054b43e" -dependencies = [ - "recursive-proc-macro-impl", - "stacker", -] - -[[package]] -name = "recursive-proc-macro-impl" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76009fbe0614077fc1a2ce255e3a1881a2e3a3527097d5dc6d8212c585e7e38b" -dependencies = [ - "quote", - "syn 2.0.118", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" - -[[package]] -name = "regress" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2057b2325e68a893284d1538021ab90279adac1139957ca2a74426c6f118fb48" -dependencies = [ - "hashbrown 0.16.1", - "memchr", -] - -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64 0.22.1", - "bytes", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tokio-util", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" -dependencies = [ - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "schemars" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" -dependencies = [ - "dyn-clone", - "schemars_derive", - "serde", - "serde_json", -] - -[[package]] -name = "schemars_derive" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" -dependencies = [ - "proc-macro2", - "quote", - "serde_derive_internals", - "syn 2.0.118", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "seq-macro" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "serde_derive_internals" -version = "0.29.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "serde_json" -version = "1.0.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" -dependencies = [ - "indexmap", - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_tokenstream" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c49585c52c01f13c5c2ebb333f14f6885d76daa768d8a037d28017ec538c69" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn 2.0.118", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "sha1" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.11.3", -] - -[[package]] -name = "sha2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest 0.11.3", -] - -[[package]] -name = "sha2-const-stable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "simd-adler32" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" - -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] -name = "siphasher" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" - -[[package]] -name = "snap" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" - -[[package]] -name = "socket2" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "sqlparser" -version = "0.62.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c6d1b651dc4edf07eead2a0c6c78016ce971bc2c10da5266861b13f25e7cec" -dependencies = [ - "log", - "recursive", - "sqlparser_derive", -] - -[[package]] -name = "sqlparser_derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6dd45d8fc1c79299bfbb7190e42ccbbdf6a5f52e4a6ad98d92357ea965bd289" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "stabby" -version = "72.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7b834ec7ced12095fea1e4b07dcb7e8cf2b59b18afa3eac52494d835965a5ec" -dependencies = [ - "rustversion", - "stabby-abi", -] - -[[package]] -name = "stabby-abi" -version = "72.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1a4f477858a5bdf927c9fab7f579899de9b13e39f8b3b3b300c89fbab632f4" -dependencies = [ - "rustc_version", - "rustversion", - "sha2-const-stable", - "stabby-macros", -] - -[[package]] -name = "stabby-macros" -version = "72.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b31c4b2434980b67ad83f300a58088ba14d59454dcd79ba3d87419bbd924d31e" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "stacker" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.61.2", -] - -[[package]] -name = "strum_macros" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "substrait" -version = "0.63.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e620ff4d5c02fd6f7752931aa74b16a26af66a63022cc1ad412c77edbe0bab47" -dependencies = [ - "heck", - "indexmap", - "pbjson", - "pbjson-build", - "pbjson-types", - "prettyplease", - "prost", - "prost-build", - "prost-types", - "protobuf-src", - "regress", - "schemars", - "semver", - "serde", - "serde_json", - "serde_yaml", - "syn 2.0.118", - "typify", - "walkdir", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "target-lexicon" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.3", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.52.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.25.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" -dependencies = [ - "indexmap", - "toml_datetime", - "toml_parser", - "winnow", -] - -[[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" -dependencies = [ - "winnow", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http", - "http-body", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", - "url", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "twox-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" -dependencies = [ - "rand 0.9.4", -] - -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - -[[package]] -name = "typify" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5bcc6f62eb1fa8aa4098f39b29f93dcb914e17158b76c50360911257aa629" -dependencies = [ - "typify-impl", - "typify-macro", -] - -[[package]] -name = "typify-impl" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1eb359f7ffa4f9ebe947fa11a1b2da054564502968db5f317b7e37693cb2240" -dependencies = [ - "heck", - "log", - "proc-macro2", - "quote", - "regress", - "schemars", - "semver", - "serde", - "serde_json", - "syn 2.0.118", - "thiserror", - "unicode-ident", -] - -[[package]] -name = "typify-macro" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "911c32f3c8514b048c1b228361bebb5e6d73aeec01696e8cc0e82e2ffef8ab7a" -dependencies = [ - "proc-macro2", - "quote", - "schemars", - "semver", - "serde", - "serde_json", - "serde_tokenstream", - "syn 2.0.118", - "typify-impl", -] - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-segmentation" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" - -[[package]] -name = "unicode-width" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", -] - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.23.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" -dependencies = [ - "getrandom 0.4.3", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.4+wasi-0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.118", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.125" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "web-sys" -version = "0.3.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "winnow" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "yoke" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "zerofrom" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.118", -] - -[[package]] -name = "zlib-rs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" - -[[package]] -name = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index a9e15d7e9..000000000 --- a/Cargo.toml +++ /dev/null @@ -1,84 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -[workspace.package] -version = "54.0.0" -homepage = "https://datafusion.apache.org/python" -repository = "https://github.com/apache/datafusion-python" -authors = ["Apache DataFusion "] -description = "Apache DataFusion DataFrame and SQL Query Engine" -readme = "README.md" -license = "Apache-2.0" -edition = "2024" -rust-version = "1.88" - -[workspace] -members = ["crates/core", "crates/util", "examples/datafusion-ffi-example"] -resolver = "3" - -[workspace.dependencies] -tokio = { version = "1.52" } -pyo3 = { version = "0.29" } -pyo3-async-runtimes = { version = "0.29" } -pyo3-log = "0.13.3" -chrono = { version = "0.4", default-features = false } -arrow = { version = "59" } -arrow-array = { version = "59" } -arrow-schema = { version = "59" } -arrow-select = { version = "59" } -datafusion = { version = "55.0.0" } -datafusion-substrait = { version = "55.0.0" } -datafusion-proto = { version = "55.0.0" } -datafusion-ffi = { version = "55.0.0" } -datafusion-catalog = { version = "55.0.0", default-features = false } -datafusion-common = { version = "55.0.0", default-features = false } -datafusion-functions-aggregate = { version = "55.0.0" } -datafusion-functions-window = { version = "55.0.0" } -datafusion-spark = { version = "55.0.0" } -datafusion-expr = { version = "55.0.0" } -prost = "0.14.3" -serde_json = "1" -uuid = { version = "1.23" } -mimalloc = { version = "0.1", default-features = false } -async-trait = "0.1.89" -futures = "0.3" -cstr = "0.2" -object_store = { version = "0.13.1" } -url = "2" -log = "0.4.29" -parking_lot = "0.12" -prost-types = "0.14.3" # keep in line with `datafusion-substrait` -pyo3-build-config = "0.29" -datafusion-python-util = { path = "crates/util", version = "54.0.0" } - -[profile.release] -lto = "thin" -codegen-units = 2 - -# We cannot publish to crates.io with any patches in the below section. Developers -# must remove any entries in this section before creating a release candidate. -[patch.crates-io] -datafusion = { git = "https://github.com/apache/datafusion", rev = "55.0.0-rc3" } -datafusion-substrait = { git = "https://github.com/apache/datafusion", rev = "55.0.0-rc3" } -datafusion-proto = { git = "https://github.com/apache/datafusion", rev = "55.0.0-rc3" } -datafusion-ffi = { git = "https://github.com/apache/datafusion", rev = "55.0.0-rc3" } -datafusion-catalog = { git = "https://github.com/apache/datafusion", rev = "55.0.0-rc3" } -datafusion-common = { git = "https://github.com/apache/datafusion", rev = "55.0.0-rc3" } -datafusion-functions-aggregate = { git = "https://github.com/apache/datafusion", rev = "55.0.0-rc3" } -datafusion-functions-window = { git = "https://github.com/apache/datafusion", rev = "55.0.0-rc3" } -datafusion-spark = { git = "https://github.com/apache/datafusion", rev = "55.0.0-rc3" } -datafusion-expr = { git = "https://github.com/apache/datafusion", rev = "55.0.0-rc3" } diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index d64569567..000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/README.md b/README.md deleted file mode 100644 index f6ee662d0..000000000 --- a/README.md +++ /dev/null @@ -1,364 +0,0 @@ - - -# DataFusion in Python - -[![Python test](https://github.com/apache/datafusion-python/actions/workflows/test.yaml/badge.svg)](https://github.com/apache/datafusion-python/actions/workflows/test.yaml) -[![Python Release Build](https://github.com/apache/datafusion-python/actions/workflows/build.yml/badge.svg)](https://github.com/apache/datafusion-python/actions/workflows/build.yml) - -This is a Python library that binds to [Apache Arrow](https://arrow.apache.org/) in-memory query engine [DataFusion](https://github.com/apache/datafusion). - -DataFusion's Python bindings can be used as a foundation for building new data systems in Python. Here are some examples: - -- [Dask SQL](https://github.com/dask-contrib/dask-sql) uses DataFusion's Python bindings for SQL parsing, query - planning, and logical plan optimizations, and then transpiles the logical plan to Dask operations for execution. -- [DataFusion Ballista](https://github.com/apache/datafusion-ballista) is a distributed SQL query engine that extends - DataFusion's Python bindings for distributed use cases. -- [DataFusion Ray](https://github.com/apache/datafusion-ray) is another distributed query engine that uses - DataFusion's Python bindings. - -## Features - -- Execute queries using SQL or DataFrames against CSV, Parquet, and JSON data sources. -- Queries are optimized using DataFusion's query optimizer. -- Execute user-defined Python code from SQL. -- Exchange data with Pandas and other DataFrame libraries that support PyArrow. -- Serialize and deserialize query plans in Substrait format. -- Experimental support for transpiling SQL queries to DataFrame calls with Polars, Pandas, and cuDF. - -For tips on tuning parallelism, see -[Maximizing CPU Usage](docs/source/user-guide/configuration.rst#maximizing-cpu-usage) -in the configuration guide. - -## Example Usage - -The following example demonstrates running a SQL query against a Parquet file using DataFusion, storing the results -in a Pandas DataFrame, and then plotting a chart. - -The Parquet file used in this example can be downloaded from the following page: - -- https://www.nyc.gov/site/tlc/about/tlc-trip-record-data.page - -```python -from datafusion import SessionContext - -# Create a DataFusion context -ctx = SessionContext() - -# Register table with context -ctx.register_parquet('taxi', 'yellow_tripdata_2021-01.parquet') - -# Execute SQL -df = ctx.sql("select passenger_count, count(*) " - "from taxi " - "where passenger_count is not null " - "group by passenger_count " - "order by passenger_count") - -# convert to Pandas -pandas_df = df.to_pandas() - -# create a chart -fig = pandas_df.plot(kind="bar", title="Trip Count by Number of Passengers").get_figure() -fig.savefig('chart.png') -``` - -This produces the following chart: - -![Chart](examples/chart.png) - -## Registering a DataFrame as a View - -You can use SessionContext's `register_view` method to convert a DataFrame into a view and register it with the context. - -```python -from datafusion import SessionContext, col, literal - -# Create a DataFusion context -ctx = SessionContext() - -# Create sample data -data = {"a": [1, 2, 3, 4, 5], "b": [10, 20, 30, 40, 50]} - -# Create a DataFrame from the dictionary -df = ctx.from_pydict(data, "my_table") - -# Filter the DataFrame (for example, keep rows where a > 2) -df_filtered = df.filter(col("a") > literal(2)) - -# Register the dataframe as a view with the context -ctx.register_view("view1", df_filtered) - -# Now run a SQL query against the registered view -df_view = ctx.sql("SELECT * FROM view1") - -# Collect the results -results = df_view.collect() - -# Convert results to a list of dictionaries for display -result_dicts = [batch.to_pydict() for batch in results] - -print(result_dicts) -``` - -This will output: - -```python -[{'a': [3, 4, 5], 'b': [30, 40, 50]}] -``` - -## Configuration - -It is possible to configure runtime (memory and disk settings) and configuration settings when creating a context. - -```python -runtime = ( - RuntimeEnvBuilder() - .with_disk_manager_os() - .with_fair_spill_pool(10000000) -) -config = ( - SessionConfig() - .with_create_default_catalog_and_schema(True) - .with_default_catalog_and_schema("foo", "bar") - .with_target_partitions(8) - .with_information_schema(True) - .with_repartition_joins(False) - .with_repartition_aggregations(False) - .with_repartition_windows(False) - .with_parquet_pruning(False) - .set("datafusion.execution.parquet.pushdown_filters", "true") -) -ctx = SessionContext(config, runtime) -``` - -Refer to the [API documentation](https://arrow.apache.org/datafusion-python/#api-reference) for more information. - -Printing the context will show the current configuration settings. - -```python -print(ctx) -``` - -## Extensions - -For information about how to extend DataFusion Python, please see the extensions page of the -[online documentation](https://datafusion.apache.org/python/). - -## More Examples - -See [examples](examples/README.md) for more information. - -### Executing Queries with DataFusion - -- [Query a Parquet file using SQL](https://github.com/apache/datafusion-python/blob/main/examples/sql-parquet.py) -- [Query a Parquet file using the DataFrame API](https://github.com/apache/datafusion-python/blob/main/examples/dataframe-parquet.py) -- [Run a SQL query and store the results in a Pandas DataFrame](https://github.com/apache/datafusion-python/blob/main/examples/sql-to-pandas.py) -- [Run a SQL query with a Python user-defined function (UDF)](https://github.com/apache/datafusion-python/blob/main/examples/sql-using-python-udf.py) -- [Run a SQL query with a Python user-defined aggregation function (UDAF)](https://github.com/apache/datafusion-python/blob/main/examples/sql-using-python-udaf.py) -- [Query PyArrow Data](https://github.com/apache/datafusion-python/blob/main/examples/query-pyarrow-data.py) -- [Create dataframe](https://github.com/apache/datafusion-python/blob/main/examples/import.py) -- [Export dataframe](https://github.com/apache/datafusion-python/blob/main/examples/export.py) - -### Running User-Defined Python Code - -- [Register a Python UDF with DataFusion](https://github.com/apache/datafusion-python/blob/main/examples/python-udf.py) -- [Register a Python UDAF with DataFusion](https://github.com/apache/datafusion-python/blob/main/examples/python-udaf.py) - -### Substrait Support - -- [Serialize query plans using Substrait](https://github.com/apache/datafusion-python/blob/main/examples/substrait.py) - -## How to install - -### uv - -```bash -uv add datafusion -``` - -### Pip - -```bash -pip install datafusion -# or -python -m pip install datafusion -``` - -### Conda - -```bash -conda install -c conda-forge datafusion -``` - -You can verify the installation by running: - -```python ->>> import datafusion ->>> datafusion.__version__ -'0.6.0' -``` - -## Using DataFusion with AI coding assistants - -This project ships a [`SKILL.md`](skills/datafusion_python/SKILL.md) that -teaches AI coding assistants how to write idiomatic DataFusion Python. It follows the -[Agent Skills](https://agentskills.io) open standard. - -**Preferred:** `npx skills add apache/datafusion-python` — installs the skill in -Claude Code, Cursor, Windsurf, Cline, Codex, Copilot, Gemini CLI, and other -supported agents. - -**Manual:** paste this line into your project's `AGENTS.md` / `CLAUDE.md`: - -``` -For DataFusion Python code, see https://github.com/apache/datafusion-python/blob/main/skills/datafusion_python/SKILL.md -``` - -## How to develop - -This assumes that you have rust and cargo installed. We use the workflow recommended by [pyo3](https://github.com/PyO3/pyo3) and [maturin](https://github.com/PyO3/maturin). The Maturin tools used in this workflow can be installed either via `uv` or `pip`. Both approaches should offer the same experience. It is recommended to use `uv` since it has significant performance improvements -over `pip`. - -Currently for protobuf support either [protobuf](https://protobuf.dev/installation/) or cmake must be installed. - -Bootstrap (`uv`): - -By default `uv` will attempt to build the datafusion python package. For our development we prefer to build manually. This means -that when creating your virtual environment using `uv sync` you need to pass in the additional `--no-install-package datafusion` -and for `uv run` commands the additional parameter `--no-project` - -```bash -# fetch this repo -git clone git@github.com:apache/datafusion-python.git -# cd to the repo root -cd datafusion-python/ -# create the virtual environment -uv sync --dev --no-install-package datafusion -# activate the environment -source .venv/bin/activate -``` - -Bootstrap (`pip`): - -```bash -# fetch this repo -git clone git@github.com:apache/datafusion-python.git -# cd to the repo root -cd datafusion-python/ -# prepare development environment (used to build wheel / install in development) -python3 -m venv .venv -# activate the venv -source .venv/bin/activate -# update pip itself if necessary -python -m pip install -U pip -# install dependencies -python -m pip install -r pyproject.toml -``` - -The tests rely on test data in git submodules. - -```bash -git submodule update --init -``` - -Whenever rust code changes (your changes or via `git pull`): - -```bash -# make sure you activate the venv using "source venv/bin/activate" first -maturin develop --uv -python -m pytest -``` - -Alternatively if you are using `uv` you can do the following without -needing to activate the virtual environment: - -```bash -uv run --no-project maturin develop --uv -uv run --no-project pytest -``` - -To run the FFI tests within the examples folder, after you have built -`datafusion-python` with the previous commands: - -```bash -cd examples/datafusion-ffi-example -uv run --no-project maturin develop --uv -uv run --no-project pytest python/tests/_test_*py -``` - -### Running & Installing pre-commit hooks - -`datafusion-python` takes advantage of [pre-commit](https://pre-commit.com/) to assist developers with code linting to help reduce -the number of commits that ultimately fail in CI due to linter errors. Using the pre-commit hooks is optional for the -developer but certainly helpful for keeping PRs clean and concise. - -Our pre-commit hooks can be installed by running `pre-commit install`, which will install the configurations in -your DATAFUSION_PYTHON_ROOT/.github directory and run each time you perform a commit, failing to complete -the commit if an offending lint is found allowing you to make changes locally before pushing. - -The pre-commit hooks can also be run adhoc without installing them by simply running `pre-commit run --all-files`. - -NOTE: the current `pre-commit` hooks require docker, and cmake. See note on protobuf above. - -## Running linters without using pre-commit - -There are scripts in `ci/scripts` for running Rust and Python linters. - -```shell -./ci/scripts/python_lint.sh -./ci/scripts/rust_clippy.sh -./ci/scripts/rust_fmt.sh -./ci/scripts/rust_toml_fmt.sh -``` - -## Checking Upstream DataFusion Coverage - -This project includes an [AI agent skill](.ai/skills/check-upstream/SKILL.md) for auditing which -features from the upstream Apache DataFusion Rust library are not yet exposed in these Python -bindings. This is useful when adding missing functions, auditing API coverage, or ensuring parity -with upstream. - -The skill accepts an optional area argument: - -``` -scalar functions -aggregate functions -window functions -dataframe -session context -ffi types -all -``` - -If no argument is provided, it defaults to checking all areas. The skill will fetch the upstream -DataFusion documentation, compare it against the functions and methods exposed in this project, and -produce a coverage report listing what is currently exposed and what is missing. - -The skill definition lives in `.ai/skills/check-upstream/SKILL.md` and follows the -[Agent Skills](https://agentskills.io) open standard. It can be used by any AI coding agent that -supports skill discovery, or followed manually. - -## How to update dependencies - -To change test dependencies, change the `pyproject.toml` and run - -```bash -uv sync --dev --no-install-package datafusion -``` diff --git a/docs/source/images/jupyter_lab_df_view.png b/_images/jupyter_lab_df_view.png similarity index 100% rename from docs/source/images/jupyter_lab_df_view.png rename to _images/jupyter_lab_df_view.png diff --git a/_sources/autoapi/datafusion/catalog/index.rst.txt b/_sources/autoapi/datafusion/catalog/index.rst.txt new file mode 100644 index 000000000..dcd217251 --- /dev/null +++ b/_sources/autoapi/datafusion/catalog/index.rst.txt @@ -0,0 +1,378 @@ +datafusion.catalog +================== + +.. py:module:: datafusion.catalog + +.. autoapi-nested-parse:: + + Data catalog providers. + + + +Classes +------- + +.. autoapisummary:: + + datafusion.catalog.Catalog + datafusion.catalog.CatalogList + datafusion.catalog.CatalogProvider + datafusion.catalog.CatalogProviderList + datafusion.catalog.Schema + datafusion.catalog.SchemaProvider + datafusion.catalog.Table + + +Module Contents +--------------- + +.. py:class:: Catalog(catalog: datafusion._internal.catalog.RawCatalog) + + DataFusion data catalog. + + This constructor is not typically called by the end user. + + + .. py:method:: __repr__() -> str + + Print a string representation of the catalog. + + + + .. py:method:: deregister_schema(name: str, cascade: bool = True) -> Schema | None + + Deregister a schema from this catalog. + + + + .. py:method:: memory_catalog(ctx: datafusion.SessionContext | None = None) -> Catalog + :staticmethod: + + + Create an in-memory catalog provider. + + + + .. py:method:: names() -> set[str] + + This is an alias for `schema_names`. + + + + .. py:method:: register_schema(name: str, schema: Schema | SchemaProvider | SchemaProviderExportable) -> Schema | None + + Register a schema with this catalog. + + + + .. py:method:: schema(name: str = 'public') -> Schema + + Returns the database with the given ``name`` from this catalog. + + + + .. py:method:: schema_names() -> set[str] + + Returns the list of schemas in this catalog. + + + + .. py:attribute:: catalog + + +.. py:class:: CatalogList(catalog_list: datafusion._internal.catalog.RawCatalogList) + + DataFusion data catalog list. + + This constructor is not typically called by the end user. + + + .. py:method:: __repr__() -> str + + Print a string representation of the catalog list. + + + + .. py:method:: catalog(name: str = 'datafusion') -> Catalog + + Returns the catalog with the given ``name`` from this catalog. + + + + .. py:method:: catalog_names() -> set[str] + + Returns the list of schemas in this catalog. + + + + .. py:method:: memory_catalog(ctx: datafusion.SessionContext | None = None) -> CatalogList + :staticmethod: + + + Create an in-memory catalog provider list. + + + + .. py:method:: names() -> set[str] + + This is an alias for `catalog_names`. + + + + .. py:method:: register_catalog(name: str, catalog: Catalog | CatalogProvider | CatalogProviderExportable) -> Catalog | None + + Register a catalog with this catalog list. + + + + .. py:attribute:: catalog_list + + +.. py:class:: CatalogProvider + + Bases: :py:obj:`abc.ABC` + + + Abstract class for defining a Python based Catalog Provider. + + + .. py:method:: deregister_schema(name: str, cascade: bool) -> None + + Remove a schema from this catalog. + + This method is optional. If your catalog provides a fixed list of schemas, you + do not need to implement this method. + + :param name: The name of the schema to remove. + :param cascade: If true, deregister the tables within the schema. + + + + .. py:method:: register_schema(name: str, schema: SchemaProviderExportable | SchemaProvider | Schema) -> None + + Add a schema to this catalog. + + This method is optional. If your catalog provides a fixed list of schemas, you + do not need to implement this method. + + + + .. py:method:: schema(name: str) -> Schema | None + :abstractmethod: + + + Retrieve a specific schema from this catalog. + + + + .. py:method:: schema_names() -> set[str] + :abstractmethod: + + + Set of the names of all schemas in this catalog. + + + +.. py:class:: CatalogProviderList + + Bases: :py:obj:`abc.ABC` + + + Abstract class for defining a Python based Catalog Provider List. + + + .. py:method:: catalog(name: str) -> CatalogProviderExportable | CatalogProvider | Catalog | None + :abstractmethod: + + + Retrieve a specific catalog from this catalog list. + + + + .. py:method:: catalog_names() -> set[str] + :abstractmethod: + + + Set of the names of all catalogs in this catalog list. + + + + .. py:method:: register_catalog(name: str, catalog: CatalogProviderExportable | CatalogProvider | Catalog) -> None + + Add a catalog to this catalog list. + + This method is optional. If your catalog provides a fixed list of catalogs, you + do not need to implement this method. + + + +.. py:class:: Schema(schema: datafusion._internal.catalog.RawSchema) + + DataFusion Schema. + + This constructor is not typically called by the end user. + + + .. py:method:: __repr__() -> str + + Print a string representation of the schema. + + + + .. py:method:: deregister_table(name: str) -> None + + Deregister a table provider from this schema. + + + + .. py:method:: memory_schema(ctx: datafusion.SessionContext | None = None) -> Schema + :staticmethod: + + + Create an in-memory schema provider. + + + + .. py:method:: names() -> set[str] + + This is an alias for `table_names`. + + + + .. py:method:: register_table(name: str, table: Table | datafusion.context.TableProviderExportable | datafusion.DataFrame | pyarrow.dataset.Dataset) -> None + + Register a table in this schema. + + + + .. py:method:: table(name: str) -> Table + + Return the table with the given ``name`` from this schema. + + + + .. py:method:: table_exist(name: str) -> bool + + Determines if a table exists in this schema. + + + + .. py:method:: table_names() -> set[str] + + Returns the list of all tables in this schema. + + + + .. py:attribute:: _raw_schema + + +.. py:class:: SchemaProvider + + Bases: :py:obj:`abc.ABC` + + + Abstract class for defining a Python based Schema Provider. + + + .. py:method:: deregister_table(name: str, cascade: bool) -> None + + Remove a table from this schema. + + This method is optional. If your schema provides a fixed list of tables, you do + not need to implement this method. + + + + .. py:method:: owner_name() -> str | None + + Returns the owner of the schema. + + This is an optional method. The default return is None. + + + + .. py:method:: register_table(name: str, table: Table | datafusion.context.TableProviderExportable | Any) -> None + + Add a table to this schema. + + This method is optional. If your schema provides a fixed list of tables, you do + not need to implement this method. + + + + .. py:method:: table(name: str) -> Table | None + :abstractmethod: + + + Retrieve a specific table from this schema. + + + + .. py:method:: table_exist(name: str) -> bool + :abstractmethod: + + + Returns true if the table exists in this schema. + + + + .. py:method:: table_names() -> set[str] + :abstractmethod: + + + Set of the names of all tables in this schema. + + + +.. py:class:: Table(table: Table | datafusion.context.TableProviderExportable | datafusion.DataFrame | pyarrow.dataset.Dataset, ctx: datafusion.SessionContext | None = None) + + A DataFusion table. + + Internally we currently support the following types of tables: + + - Tables created using built-in DataFusion methods, such as + reading from CSV or Parquet + - pyarrow datasets + - DataFusion DataFrames, which will be converted into a view + - Externally provided tables implemented with the FFI PyCapsule + interface (advanced) + + Constructor. + + + .. py:method:: __repr__() -> str + + Print a string representation of the table. + + + + .. py:method:: from_dataset(dataset: pyarrow.dataset.Dataset) -> Table + :staticmethod: + + + Turn a :mod:`pyarrow.dataset` ``Dataset`` into a :class:`Table`. + + + + .. py:attribute:: __slots__ + :value: ('_inner',) + + + + .. py:attribute:: _inner + + + .. py:property:: kind + :type: str + + + Returns the kind of table. + + + .. py:property:: schema + :type: pyarrow.Schema + + + Returns the schema associated with this table. + + diff --git a/_sources/autoapi/datafusion/context/index.rst.txt b/_sources/autoapi/datafusion/context/index.rst.txt new file mode 100644 index 000000000..fe3bde4b5 --- /dev/null +++ b/_sources/autoapi/datafusion/context/index.rst.txt @@ -0,0 +1,1769 @@ +datafusion.context +================== + +.. py:module:: datafusion.context + +.. autoapi-nested-parse:: + + :py:class:`SessionContext` — entry point for running DataFusion queries. + + A :py:class:`SessionContext` holds registered tables, catalogs, and + configuration for the current session. It is the first object most programs + create: from it you register data, run SQL strings + (:py:meth:`SessionContext.sql`), read files + (:py:meth:`SessionContext.read_csv`, + :py:meth:`SessionContext.read_parquet`, ...), and construct + :py:class:`~datafusion.dataframe.DataFrame` objects in memory + (:py:meth:`SessionContext.from_pydict`, + :py:meth:`SessionContext.from_arrow`). + + Session behavior (memory limits, batch size, configured optimizer passes, + ...) is controlled by :py:class:`SessionConfig` and + :py:class:`RuntimeEnvBuilder`; SQL dialect limits are controlled by + :py:class:`SQLOptions`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> ctx.sql("SELECT 1 AS n").to_pydict() + {'n': [1]} + + See :ref:`user_guide_concepts` in the online documentation for the broader + execution model. + + + +Classes +------- + +.. autoapisummary:: + + datafusion.context.ArrowArrayExportable + datafusion.context.ArrowStreamExportable + datafusion.context.PhysicalOptimizerRuleExportable + datafusion.context.RuntimeEnvBuilder + datafusion.context.SQLOptions + datafusion.context.SessionConfig + datafusion.context.SessionContext + datafusion.context.TableProviderExportable + + +Module Contents +--------------- + +.. py:class:: ArrowArrayExportable + + Bases: :py:obj:`Protocol` + + + Type hint for object exporting Arrow C Array via Arrow PyCapsule Interface. + + https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html + + + .. py:method:: __arrow_c_array__(requested_schema: object | None = None) -> tuple[object, object] + + +.. py:class:: ArrowStreamExportable + + Bases: :py:obj:`Protocol` + + + Type hint for object exporting Arrow C Stream via Arrow PyCapsule Interface. + + https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html + + + .. py:method:: __arrow_c_stream__(requested_schema: object | None = None) -> object + + +.. py:class:: PhysicalOptimizerRuleExportable + + Bases: :py:obj:`Protocol` + + + Type hint for object that has __datafusion_physical_optimizer_rule__ PyCapsule. + + The method returns a PyCapsule wrapping an ``FFI_PhysicalOptimizerRule``, + typically produced by a separate compiled extension. + + + .. py:method:: __datafusion_physical_optimizer_rule__() -> object + + +.. py:class:: RuntimeEnvBuilder + + Runtime configuration options. + + Create a new :py:class:`RuntimeEnvBuilder` with default values. + + + .. py:method:: with_disk_manager_disabled() -> RuntimeEnvBuilder + + Disable the disk manager, attempts to create temporary files will error. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + + + .. py:method:: with_disk_manager_os() -> RuntimeEnvBuilder + + Use the operating system's temporary directory for disk manager. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + + + .. py:method:: with_disk_manager_specified(*paths: str | pathlib.Path) -> RuntimeEnvBuilder + + Use the specified paths for the disk manager's temporary files. + + :param paths: Paths to use for the disk manager's temporary files. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + + + .. py:method:: with_fair_spill_pool(size: int) -> RuntimeEnvBuilder + + Use a fair spill pool with the specified size. + + This pool works best when you know beforehand the query has multiple spillable + operators that will likely all need to spill. Sometimes it will cause spills + even when there was sufficient memory (reserved for other operators) to avoid + doing so:: + + ┌───────────────────────z──────────────────────z───────────────┐ + │ z z │ + │ z z │ + │ Spillable z Unspillable z Free │ + │ Memory z Memory z Memory │ + │ z z │ + │ z z │ + └───────────────────────z──────────────────────z───────────────┘ + + :param size: Size of the memory pool in bytes. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + .. rubric:: Examples + + >>> config = dfn.RuntimeEnvBuilder().with_fair_spill_pool(1024) + + + + .. py:method:: with_greedy_memory_pool(size: int) -> RuntimeEnvBuilder + + Use a greedy memory pool with the specified size. + + This pool works well for queries that do not need to spill or have a single + spillable operator. See :py:func:`with_fair_spill_pool` if there are + multiple spillable operators that all will spill. + + :param size: Size of the memory pool in bytes. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + .. rubric:: Examples + + >>> config = dfn.RuntimeEnvBuilder().with_greedy_memory_pool(1024) + + + + .. py:method:: with_temp_file_path(path: str | pathlib.Path) -> RuntimeEnvBuilder + + Use the specified path to create any needed temporary files. + + :param path: Path to use for temporary files. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + .. rubric:: Examples + + >>> config = dfn.RuntimeEnvBuilder().with_temp_file_path("/tmp") + + + + .. py:method:: with_unbounded_memory_pool() -> RuntimeEnvBuilder + + Use an unbounded memory pool. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + + + .. py:attribute:: config_internal + + +.. py:class:: SQLOptions + + Options to be used when performing SQL queries. + + Create a new :py:class:`SQLOptions` with default values. + + The default values are: + - DDL commands are allowed + - DML commands are allowed + - Statements are allowed + + + .. py:method:: with_allow_ddl(allow: bool = True) -> SQLOptions + + Should DDL (Data Definition Language) commands be run? + + Examples of DDL commands include ``CREATE TABLE`` and ``DROP TABLE``. + + :param allow: Allow DDL commands to be run. + + :returns: A new :py:class:`SQLOptions` object with the updated setting. + + .. rubric:: Examples + + >>> options = dfn.SQLOptions().with_allow_ddl(True) + + + + .. py:method:: with_allow_dml(allow: bool = True) -> SQLOptions + + Should DML (Data Manipulation Language) commands be run? + + Examples of DML commands include ``INSERT INTO`` and ``DELETE``. + + :param allow: Allow DML commands to be run. + + :returns: A new :py:class:`SQLOptions` object with the updated setting. + + .. rubric:: Examples + + >>> options = dfn.SQLOptions().with_allow_dml(True) + + + + .. py:method:: with_allow_statements(allow: bool = True) -> SQLOptions + + Should statements such as ``SET VARIABLE`` and ``BEGIN TRANSACTION`` be run? + + :param allow: Allow statements to be run. + + :returns: py:class:SQLOptions` object with the updated setting. + :rtype: A new + + .. rubric:: Examples + + >>> options = dfn.SQLOptions().with_allow_statements(True) + + + + .. py:attribute:: options_internal + + +.. py:class:: SessionConfig(config_options: dict[str, str] | None = None) + + Session configuration options. + + Create a new :py:class:`SessionConfig` with the given configuration options. + + :param config_options: Configuration options. + + + .. py:method:: set(key: str, value: str) -> SessionConfig + + Set a configuration option. + + Args: + key: Option key. + value: Option value. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_batch_size(batch_size: int) -> SessionConfig + + Customize batch size. + + :param batch_size: Batch size. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_create_default_catalog_and_schema(enabled: bool = True) -> SessionConfig + + Control if the default catalog and schema will be automatically created. + + :param enabled: Whether the default catalog and schema will be + automatically created. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_default_catalog_and_schema(catalog: str, schema: str) -> SessionConfig + + Select a name for the default catalog and schema. + + :param catalog: Catalog name. + :param schema: Schema name. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_extension(extension: Any) -> SessionConfig + + Create a new configuration using an extension. + + :param extension: A custom configuration extension object. These are + :param shared from another DataFusion extension library.: + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_information_schema(enabled: bool = True) -> SessionConfig + + Enable or disable the inclusion of ``information_schema`` virtual tables. + + :param enabled: Whether to include ``information_schema`` virtual tables. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_parquet_pruning(enabled: bool = True) -> SessionConfig + + Enable or disable the use of pruning predicate for parquet readers. + + Pruning predicates will enable the reader to skip row groups. + + :param enabled: Whether to use pruning predicate for parquet readers. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_aggregations(enabled: bool = True) -> SessionConfig + + Enable or disable the use of repartitioning for aggregations. + + Enabling this improves parallelism. + + :param enabled: Whether to use repartitioning for aggregations. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_file_min_size(size: int) -> SessionConfig + + Set minimum file range size for repartitioning scans. + + :param size: Minimum file range size. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_file_scans(enabled: bool = True) -> SessionConfig + + Enable or disable the use of repartitioning for file scans. + + :param enabled: Whether to use repartitioning for file scans. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_joins(enabled: bool = True) -> SessionConfig + + Enable or disable the use of repartitioning for joins to improve parallelism. + + :param enabled: Whether to use repartitioning for joins. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_sorts(enabled: bool = True) -> SessionConfig + + Enable or disable the use of repartitioning for window functions. + + This may improve parallelism. + + :param enabled: Whether to use repartitioning for window functions. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_windows(enabled: bool = True) -> SessionConfig + + Enable or disable the use of repartitioning for window functions. + + This may improve parallelism. + + :param enabled: Whether to use repartitioning for window functions. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_target_partitions(target_partitions: int) -> SessionConfig + + Customize the number of target partitions for query execution. + + Increasing partitions can increase concurrency. + + :param target_partitions: Number of target partitions. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:attribute:: config_internal + + +.. py:class:: SessionContext(config: SessionConfig | None = None, runtime: RuntimeEnvBuilder | None = None) + + This is the main interface for executing queries and creating DataFrames. + + See :ref:`user_guide_concepts` in the online documentation for more information. + + Main interface for executing queries with DataFusion. + + Maintains the state of the connection between a user and an instance + of the connection between a user and an instance of the DataFusion + engine. + + :param config: Session configuration options. + :param runtime: Runtime configuration options. + + Example usage: + + The following example demonstrates how to use the context to execute + a query against a CSV data source using the :py:class:`DataFrame` API:: + + from datafusion import SessionContext + + ctx = SessionContext() + df = ctx.read_csv("data.csv") + + + .. py:method:: __datafusion_logical_extension_codec__() -> Any + + Access the PyCapsule FFI_LogicalExtensionCodec. + + + + .. py:method:: __datafusion_physical_extension_codec__() -> Any + + Access the PyCapsule FFI_PhysicalExtensionCodec. + + + + .. py:method:: __datafusion_task_context_provider__() -> Any + + Access the PyCapsule FFI_TaskContextProvider. + + + + .. py:method:: __repr__() -> str + + Print a string representation of the Session Context. + + + + .. py:method:: _convert_file_sort_order(file_sort_order: collections.abc.Sequence[collections.abc.Sequence[datafusion.expr.SortKey]] | None) -> list[list[datafusion._internal.expr.SortExpr]] | None + :staticmethod: + + + Convert nested ``SortKey`` sequences into raw sort expressions. + + Each ``SortKey`` can be a column name string, an ``Expr``, or a + ``SortExpr`` and will be converted using + :func:`datafusion.expr.sort_list_to_raw_sort_list`. + + + + .. py:method:: _convert_table_partition_cols(table_partition_cols: list[tuple[str, str | pyarrow.DataType]]) -> list[tuple[str, pyarrow.DataType]] + :staticmethod: + + + + .. py:method:: _register_object_store_for_path(path: str | pathlib.Path, store: Any) -> None + + Parse a URL path and register the given object store for its scheme and host. + + This is a convenience helper used by methods like + :py:meth:`register_parquet` and :py:meth:`read_parquet` to + automatically register an object store when an ``object_store`` + parameter is provided. + + :param path: A URL-style path (e.g. ``"s3://bucket/key.parquet"`` or + ``"file:///tmp/data.parquet"``). + :param store: An object store instance to register. + + :raises ValueError: If the path does not contain a URL scheme, or if + a non-file scheme is missing a host/bucket component. + + + + .. py:method:: add_physical_optimizer_rule(rule: PhysicalOptimizerRuleExportable) -> None + + Append a user-defined physical optimizer rule to the session. + + The rule is imported via its ``__datafusion_physical_optimizer_rule__`` + PyCapsule, typically produced by a separate compiled extension. The + underlying :class:`SessionState` is rebuilt from its current state + with the new rule appended, so previously registered tables, UDFs, + and catalogs are preserved. + + :param rule: Object exposing ``__datafusion_physical_optimizer_rule__``, + a :class:`PhysicalOptimizerRuleExportable`. + + .. rubric:: Examples + + >>> from datafusion import SessionContext + >>> ctx = SessionContext() + >>> from my_extension import MyPhysicalOptimizerRule # doctest: +SKIP + >>> rule = MyPhysicalOptimizerRule() # doctest: +SKIP + >>> ctx.add_physical_optimizer_rule(rule) # doctest: +SKIP + + + + .. py:method:: catalog(name: str = 'datafusion') -> datafusion.catalog.Catalog + + Retrieve a catalog by name. + + + + .. py:method:: catalog_names() -> set[str] + + Returns the list of catalogs in this context. + + + + .. py:method:: copied_config() -> SessionConfig + + Return a copy of the active :py:class:`SessionConfig`. + + Mutating the returned config does not affect this context; use + the result when you need a starting point for a new context or + want to inspect the current settings independent of further + changes here. + + .. rubric:: Examples + + >>> ctx = SessionContext(SessionConfig().with_batch_size(1024)) + >>> isinstance(ctx.copied_config(), SessionConfig) + True + + + + .. py:method:: create_dataframe(partitions: list[list[pyarrow.RecordBatch]], name: str | None = None, schema: pyarrow.Schema | None = None) -> datafusion.dataframe.DataFrame + + Create and return a dataframe using the provided partitions. + + :param partitions: :py:class:`pa.RecordBatch` partitions to register. + :param name: Resultant dataframe name. + :param schema: Schema for the partitions. + + :returns: DataFrame representation of the SQL query. + + + + .. py:method:: create_dataframe_from_logical_plan(plan: datafusion.plan.LogicalPlan) -> datafusion.dataframe.DataFrame + + Create a :py:class:`~datafusion.dataframe.DataFrame` from an existing plan. + + :param plan: Logical plan. + + :returns: DataFrame representation of the logical plan. + + + + .. py:method:: deregister_object_store(schema: str, host: str | None = None) -> None + + Remove an object store from the session. + + :param schema: The data source schema (e.g. ``"s3://"``). + :param host: URL for the host (e.g. bucket name). + + + + .. py:method:: deregister_table(name: str) -> None + + Remove a table from the session. + + + + .. py:method:: deregister_udaf(name: str) -> None + + Remove a user-defined aggregate function from the session. + + :param name: Name of the UDAF to deregister. + + + + .. py:method:: deregister_udf(name: str) -> None + + Remove a user-defined scalar function from the session. + + :param name: Name of the UDF to deregister. + + + + .. py:method:: deregister_udtf(name: str) -> None + + Remove a user-defined table function from the session. + + :param name: Name of the UDTF to deregister. + + + + .. py:method:: deregister_udwf(name: str) -> None + + Remove a user-defined window function from the session. + + :param name: Name of the UDWF to deregister. + + + + .. py:method:: empty_table() -> datafusion.dataframe.DataFrame + + Create an empty :py:class:`~datafusion.dataframe.DataFrame`. + + + + .. py:method:: enable_ident_normalization() -> bool + + Return whether identifier normalization (lowercasing) is enabled. + + .. rubric:: Examples + + >>> ctx = SessionContext() + >>> ctx.enable_ident_normalization() + True + + + + .. py:method:: enable_spark_functions() -> None + + Register all Spark-compatible functions for SQL access. + + Registers every UDF/UDAF/UDWF from the ``datafusion-spark`` crate, + overriding any DataFusion built-ins of the same name with their + Spark-semantics version (e.g. ``substring`` becomes 1-indexed, + ``concat`` propagates NULL, ``round`` uses HALF_UP rounding). + + For DataFrame use, import the typed wrappers from + :py:mod:`datafusion.functions.spark` directly; this method is only + needed for SQL queries. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> ctx.enable_spark_functions() + >>> ctx.sql( + ... "SELECT sha2('hello', 256) AS h" + ... ).collect_column("h")[0].as_py() + '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824' + + + + .. py:method:: enable_url_table() -> SessionContext + + Control if local files can be queried as tables. + + :returns: A new :py:class:`SessionContext` object with url table enabled. + + + + .. py:method:: execute(plan: datafusion.plan.ExecutionPlan, partitions: int) -> datafusion.record_batch.RecordBatchStream + + Execute the ``plan`` and return the results. + + + + .. py:method:: execute_logical_plan(plan: datafusion.plan.LogicalPlan) -> datafusion.dataframe.DataFrame + + Execute a :py:class:`~datafusion.plan.LogicalPlan` and return a DataFrame. + + :param plan: Logical plan to execute. + + :returns: DataFrame resulting from the execution. + + .. rubric:: Examples + + >>> ctx = SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> plan = df.logical_plan() + >>> df2 = ctx.execute_logical_plan(plan) + >>> df2.collect()[0].column(0) + + [ + 1, + 2, + 3 + ] + + + + .. py:method:: from_arrow(data: ArrowStreamExportable | ArrowArrayExportable, name: str | None = None) -> datafusion.dataframe.DataFrame + + Create a :py:class:`~datafusion.dataframe.DataFrame` from an Arrow source. + + The Arrow data source can be any object that implements either + ``__arrow_c_stream__`` or ``__arrow_c_array__``. For the latter, it must return + a struct array. + + Arrow data can be Polars, Pandas, Pyarrow etc. + + :param data: Arrow data source. + :param name: Name of the DataFrame. + + :returns: DataFrame representation of the Arrow table. + + + + .. py:method:: from_pandas(data: pandas.DataFrame, name: str | None = None) -> datafusion.dataframe.DataFrame + + Create a :py:class:`~datafusion.dataframe.DataFrame` from a Pandas DataFrame. + + :param data: Pandas DataFrame. + :param name: Name of the DataFrame. + + :returns: DataFrame representation of the Pandas DataFrame. + + + + .. py:method:: from_polars(data: polars.DataFrame, name: str | None = None) -> datafusion.dataframe.DataFrame + + Create a :py:class:`~datafusion.dataframe.DataFrame` from a Polars DataFrame. + + :param data: Polars DataFrame. + :param name: Name of the DataFrame. + + :returns: DataFrame representation of the Polars DataFrame. + + + + .. py:method:: from_pydict(data: dict[str, list[Any]], name: str | None = None) -> datafusion.dataframe.DataFrame + + Create a :py:class:`~datafusion.dataframe.DataFrame` from a dictionary. + + :param data: Dictionary of lists. + :param name: Name of the DataFrame. + + :returns: DataFrame representation of the dictionary of lists. + + + + .. py:method:: from_pylist(data: list[dict[str, Any]], name: str | None = None) -> datafusion.dataframe.DataFrame + + Create a :py:class:`~datafusion.dataframe.DataFrame` from a list. + + :param data: List of dictionaries. + :param name: Name of the DataFrame. + + :returns: DataFrame representation of the list of dictionaries. + + + + .. py:method:: global_ctx() -> SessionContext + :classmethod: + + + Retrieve the global context as a `SessionContext` wrapper. + + :returns: A `SessionContext` object that wraps the global `SessionContextInternal`. + + + + .. py:method:: parse_capacity_limit(config_name: str, limit: str) -> int + :staticmethod: + + + Parse a size string into a byte count. + + Accepts strings like ``"100M"``, ``"1.5G"``, or ``"512K"``. + ``"0"`` is accepted and returns 0. ``config_name`` is used purely + for error messages and identifies which configuration setting the + limit belongs to. Use this helper when constructing a + :py:class:`RuntimeEnvBuilder` from a human-friendly size string. + + .. rubric:: Examples + + >>> SessionContext.parse_capacity_limit( + ... "datafusion.runtime.memory_limit", "1M" + ... ) + 1048576 + >>> SessionContext.parse_capacity_limit( + ... "datafusion.runtime.memory_limit", "0" + ... ) + 0 + + + + .. py:method:: parse_sql_expr(sql: str, schema: datafusion.common.DFSchema) -> datafusion.expr.Expr + + Parse a SQL expression string into a logical expression. + + :param sql: SQL expression string. + :param schema: Schema to use for resolving column references. + + :returns: Parsed expression. + + .. rubric:: Examples + + >>> from datafusion.common import DFSchema + >>> ctx = SessionContext() + >>> schema = DFSchema.empty() + >>> ctx.parse_sql_expr("1 + 2", schema=schema) + Expr(Int64(1) + Int64(2)) + + + + .. py:method:: read_arrow(path: str | pathlib.Path, schema: pyarrow.Schema | None = None, file_extension: str = '.arrow', file_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, object_store: Any | None = None) -> datafusion.dataframe.DataFrame + + Create a :py:class:`DataFrame` for reading an Arrow IPC data source. + + :param path: Path to the Arrow IPC file. + :param schema: The data source schema. + :param file_extension: File extension to select. + :param file_partition_cols: Partition columns. + :param object_store: A pre-configured object store instance to use for + accessing the file. When provided, the store is automatically + registered for the URL scheme and host parsed from ``path``. + + :returns: DataFrame representation of the read Arrow IPC file. + + .. rubric:: Examples + + >>> import tempfile, os + >>> ctx = dfn.SessionContext() + >>> table = pa.table({"a": [1, 2, 3]}) + >>> with tempfile.TemporaryDirectory() as tmpdir: + ... path = os.path.join(tmpdir, "data.arrow") + ... with pa.ipc.new_file(path, table.schema) as writer: + ... writer.write_table(table) + ... df = ctx.read_arrow(path) + ... df.collect()[0].column(0) + + [ + 1, + 2, + 3 + ] + + Provide an explicit ``schema`` to override schema inference: + + >>> with tempfile.TemporaryDirectory() as tmpdir: + ... path = os.path.join(tmpdir, "data.arrow") + ... with pa.ipc.new_file(path, table.schema) as writer: + ... writer.write_table(table) + ... df = ctx.read_arrow(path, schema=pa.schema([("a", pa.int64())])) + ... df.collect()[0].column(0) + + [ + 1, + 2, + 3 + ] + + Use ``file_extension`` to read files with a non-default extension: + + >>> with tempfile.TemporaryDirectory() as tmpdir: + ... path = os.path.join(tmpdir, "data.ipc") + ... with pa.ipc.new_file(path, table.schema) as writer: + ... writer.write_table(table) + ... df = ctx.read_arrow(path, file_extension=".ipc") + ... df.collect()[0].column(0) + + [ + 1, + 2, + 3 + ] + + + + .. py:method:: read_avro(path: str | pathlib.Path, schema: pyarrow.Schema | None = None, file_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, file_extension: str = '.avro', object_store: Any | None = None) -> datafusion.dataframe.DataFrame + + Create a :py:class:`DataFrame` for reading Avro data source. + + :param path: Path to the Avro file. + :param schema: The data source schema. + :param file_partition_cols: Partition columns. + :param file_extension: File extension to select. + :param object_store: A pre-configured object store instance to use for + accessing the file. When provided, the store is automatically + registered for the URL scheme and host parsed from ``path``. + + :returns: DataFrame representation of the read Avro file + + + + .. py:method:: read_batch(batch: pyarrow.RecordBatch) -> datafusion.dataframe.DataFrame + + Return a :py:class:`~datafusion.DataFrame` reading a single batch. + + Convenience wrapper around :py:meth:`read_batches` for the single-batch + case. Unlike :py:meth:`register_batch`, this does not register the + batch as a named table; it returns an anonymous + :py:class:`~datafusion.DataFrame` directly. + + :param batch: Record batch to wrap as a DataFrame. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> batch = pa.RecordBatch.from_pydict({"a": [1, 2, 3]}) + >>> ctx.read_batch(batch).to_pydict() + {'a': [1, 2, 3]} + + + + .. py:method:: read_batches(batches: collections.abc.Iterable[pyarrow.RecordBatch]) -> datafusion.dataframe.DataFrame + + Return a :py:class:`~datafusion.DataFrame` reading the given batches. + + All batches must share the same schema. Any iterable of + :py:class:`pa.RecordBatch` is accepted (list, tuple, generator); + it is materialized into a list before being handed to the + underlying Rust binding. Unlike :py:meth:`register_record_batches`, + this does not register the batches as a named table; it returns + an anonymous :py:class:`~datafusion.DataFrame` directly. + + :param batches: Record batches to wrap as a DataFrame. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> b1 = pa.RecordBatch.from_pydict({"a": [1, 2]}) + >>> b2 = pa.RecordBatch.from_pydict({"a": [3, 4]}) + >>> ctx.read_batches([b1, b2]).to_pydict() + {'a': [1, 2, 3, 4]} + + A generator works too: + + >>> ctx.read_batches(b for b in [b1, b2]).to_pydict() + {'a': [1, 2, 3, 4]} + + + + .. py:method:: read_csv(path: str | pathlib.Path | list[str] | list[pathlib.Path], schema: pyarrow.Schema | None = None, has_header: bool = True, delimiter: str = ',', schema_infer_max_records: int = DEFAULT_MAX_INFER_SCHEMA, file_extension: str = '.csv', table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, file_compression_type: str | None = None, options: datafusion.options.CsvReadOptions | None = None, object_store: Any | None = None) -> datafusion.dataframe.DataFrame + + Read a CSV data source. + + :param path: Path to the CSV file + :param schema: An optional schema representing the CSV files. If None, the + CSV reader will try to infer it based on data in file. + :param has_header: Whether the CSV file have a header. If schema inference + is run on a file with no headers, default column names are + created. + :param delimiter: An optional column delimiter. + :param schema_infer_max_records: Maximum number of rows to read from CSV + files for schema inference if needed. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param table_partition_cols: Partition columns. + :param file_compression_type: File compression type. + :param options: Set advanced options for CSV reading. This cannot be + combined with any of the other options in this method. + :param object_store: A pre-configured object store instance to use for + accessing the file. When provided, the store is automatically + registered for the URL scheme and host parsed from ``path``. + + :returns: DataFrame representation of the read CSV files + + + + .. py:method:: read_empty() -> datafusion.dataframe.DataFrame + + Create an empty :py:class:`DataFrame` with no columns or rows. + + .. seealso:: This is an alias for :meth:`empty_table`. + + + + .. py:method:: read_json(path: str | pathlib.Path, schema: pyarrow.Schema | None = None, schema_infer_max_records: int = 1000, file_extension: str = '.json', table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, file_compression_type: str | None = None, object_store: Any | None = None) -> datafusion.dataframe.DataFrame + + Read a line-delimited JSON data source. + + :param path: Path to the JSON file. + :param schema: The data source schema. + :param schema_infer_max_records: Maximum number of rows to read from JSON + files for schema inference if needed. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param table_partition_cols: Partition columns. + :param file_compression_type: File compression type. + :param object_store: A pre-configured object store instance to use for + accessing the file. When provided, the store is automatically + registered for the URL scheme and host parsed from ``path``. + + :returns: DataFrame representation of the read JSON files. + + + + .. py:method:: read_parquet(path: str | pathlib.Path, table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, parquet_pruning: bool = True, file_extension: str = '.parquet', skip_metadata: bool = True, schema: pyarrow.Schema | None = None, file_sort_order: collections.abc.Sequence[collections.abc.Sequence[datafusion.expr.SortKey]] | None = None, object_store: Any | None = None) -> datafusion.dataframe.DataFrame + + Read a Parquet source into a :py:class:`~datafusion.dataframe.Dataframe`. + + :param path: Path to the Parquet file. + :param table_partition_cols: Partition columns. + :param parquet_pruning: Whether the parquet reader should use the predicate + to prune row groups. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param skip_metadata: Whether the parquet reader should skip any metadata + that may be in the file schema. This can help avoid schema + conflicts due to metadata. + :param schema: An optional schema representing the parquet files. If None, + the parquet reader will try to infer it based on data in the + file. + :param file_sort_order: Sort order for the file. Each sort key can be + specified as a column name (``str``), an expression + (``Expr``), or a ``SortExpr``. + :param object_store: A pre-configured object store instance (e.g. + :py:class:`~datafusion.object_store.AmazonS3`, + :py:class:`~datafusion.object_store.GoogleCloud`, + :py:class:`~datafusion.object_store.MicrosoftAzure`) to use + for accessing the file. When provided, the store is + automatically registered for the URL scheme and host parsed + from ``path``, removing the need to call + :py:meth:`register_object_store` separately. This is + especially useful in multi-threaded environments where + setting credentials via ``os.environ`` is not thread-safe. + + :returns: DataFrame representation of the read Parquet files + + .. rubric:: Examples + + Read a local Parquet file: + + >>> import datafusion + >>> ctx = datafusion.SessionContext() + >>> df = ctx.read_parquet("data.parquet") # doctest: +SKIP + + Read from S3 with inline credentials (thread-safe): + + >>> from datafusion.object_store import AmazonS3 # doctest: +SKIP + >>> store = AmazonS3( + ... bucket_name="my-bucket", + ... region="us-east-1", + ... access_key_id="...", + ... secret_access_key="...", + ... ) # doctest: +SKIP + >>> df = ctx.read_parquet( + ... "s3://my-bucket/data.parquet", + ... object_store=store, + ... ) # doctest: +SKIP + + + + .. py:method:: read_table(table: datafusion.catalog.Table | TableProviderExportable | datafusion.dataframe.DataFrame | pyarrow.dataset.Dataset) -> datafusion.dataframe.DataFrame + + Creates a :py:class:`~datafusion.dataframe.DataFrame` from a table. + + + + .. py:method:: refresh_catalogs() -> None + + Refresh catalog metadata. + + .. rubric:: Examples + + >>> ctx = SessionContext() + >>> ctx.refresh_catalogs() + + + + .. py:method:: register_arrow(name: str, path: str | pathlib.Path, schema: pyarrow.Schema | None = None, file_extension: str = '.arrow', table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, object_store: Any | None = None) -> None + + Register an Arrow IPC file as a table. + + The registered table can be referenced from SQL statements executed + against this context. + + :param name: Name of the table to register. + :param path: Path to the Arrow IPC file. + :param schema: The data source schema. + :param file_extension: File extension to select. + :param table_partition_cols: Partition columns. + :param object_store: A pre-configured object store instance to use for + accessing the file. When provided, the store is automatically + registered for the URL scheme and host parsed from ``path``. + + .. rubric:: Examples + + >>> import tempfile, os + >>> ctx = dfn.SessionContext() + >>> table = pa.table({"x": [10, 20, 30]}) + >>> with tempfile.TemporaryDirectory() as tmpdir: + ... path = os.path.join(tmpdir, "data.arrow") + ... with pa.ipc.new_file(path, table.schema) as writer: + ... writer.write_table(table) + ... ctx.register_arrow("arrow_tbl", path) + ... ctx.sql("SELECT * FROM arrow_tbl").collect()[0].column(0) + + [ + 10, + 20, + 30 + ] + + Provide an explicit ``schema`` to override schema inference: + + >>> with tempfile.TemporaryDirectory() as tmpdir: + ... path = os.path.join(tmpdir, "data.arrow") + ... with pa.ipc.new_file(path, table.schema) as writer: + ... writer.write_table(table) + ... ctx.register_arrow( + ... "arrow_schema", + ... path, + ... schema=pa.schema([("x", pa.int64())]), + ... ) + ... ctx.sql("SELECT * FROM arrow_schema").collect()[0].column(0) + + [ + 10, + 20, + 30 + ] + + Use ``file_extension`` to read files with a non-default extension: + + >>> with tempfile.TemporaryDirectory() as tmpdir: + ... path = os.path.join(tmpdir, "data.ipc") + ... with pa.ipc.new_file(path, table.schema) as writer: + ... writer.write_table(table) + ... ctx.register_arrow( + ... "arrow_ipc", path, file_extension=".ipc" + ... ) + ... ctx.sql("SELECT * FROM arrow_ipc").collect()[0].column(0) + + [ + 10, + 20, + 30 + ] + + + + .. py:method:: register_avro(name: str, path: str | pathlib.Path, schema: pyarrow.Schema | None = None, file_extension: str = '.avro', table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, object_store: Any | None = None) -> None + + Register an Avro file as a table. + + The registered table can be referenced from SQL statement executed against + this context. + + :param name: Name of the table to register. + :param path: Path to the Avro file. + :param schema: The data source schema. + :param file_extension: File extension to select. + :param table_partition_cols: Partition columns. + :param object_store: A pre-configured object store instance to use for + accessing the file. When provided, the store is automatically + registered for the URL scheme and host parsed from ``path``. + + + + .. py:method:: register_batch(name: str, batch: pyarrow.RecordBatch) -> None + + Register a single :py:class:`pa.RecordBatch` as a table. + + :param name: Name of the resultant table. + :param batch: Record batch to register as a table. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> batch = pa.RecordBatch.from_pydict({"a": [1, 2, 3]}) + >>> ctx.register_batch("batch_tbl", batch) + >>> ctx.sql("SELECT * FROM batch_tbl").collect()[0].column(0) + + [ + 1, + 2, + 3 + ] + + + + .. py:method:: register_catalog_provider(name: str, provider: datafusion.catalog.CatalogProviderExportable | datafusion.catalog.CatalogProvider | datafusion.catalog.Catalog) -> None + + Register a catalog provider. + + + + .. py:method:: register_catalog_provider_list(provider: datafusion.catalog.CatalogProviderListExportable | datafusion.catalog.CatalogProviderList | datafusion.catalog.CatalogList) -> None + + Register a catalog provider list. + + + + .. py:method:: register_csv(name: str, path: str | pathlib.Path | list[str | pathlib.Path], schema: pyarrow.Schema | None = None, has_header: bool = True, delimiter: str = ',', schema_infer_max_records: int = DEFAULT_MAX_INFER_SCHEMA, file_extension: str = '.csv', file_compression_type: str | None = None, options: datafusion.options.CsvReadOptions | None = None, object_store: Any | None = None) -> None + + Register a CSV file as a table. + + The registered table can be referenced from SQL statement executed against. + + :param name: Name of the table to register. + :param path: Path to the CSV file. It also accepts a list of Paths. + :param schema: An optional schema representing the CSV file. If None, the + CSV reader will try to infer it based on data in file. + :param has_header: Whether the CSV file have a header. If schema inference + is run on a file with no headers, default column names are + created. + :param delimiter: An optional column delimiter. + :param schema_infer_max_records: Maximum number of rows to read from CSV + files for schema inference if needed. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param file_compression_type: File compression type. + :param options: Set advanced options for CSV reading. This cannot be + combined with any of the other options in this method. + :param object_store: A pre-configured object store instance to use for + accessing the file. When provided, the store is automatically + registered for the URL scheme and host parsed from ``path``. + + + + .. py:method:: register_dataset(name: str, dataset: pyarrow.dataset.Dataset) -> None + + Register a :py:class:`pa.dataset.Dataset` as a table. + + :param name: Name of the table to register. + :param dataset: PyArrow dataset. + + + + .. py:method:: register_json(name: str, path: str | pathlib.Path, schema: pyarrow.Schema | None = None, schema_infer_max_records: int = 1000, file_extension: str = '.json', table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, file_compression_type: str | None = None, object_store: Any | None = None) -> None + + Register a JSON file as a table. + + The registered table can be referenced from SQL statement executed + against this context. + + :param name: Name of the table to register. + :param path: Path to the JSON file. + :param schema: The data source schema. + :param schema_infer_max_records: Maximum number of rows to read from JSON + files for schema inference if needed. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param table_partition_cols: Partition columns. + :param file_compression_type: File compression type. + :param object_store: A pre-configured object store instance to use for + accessing the file. When provided, the store is automatically + registered for the URL scheme and host parsed from ``path``. + + + + .. py:method:: register_listing_table(name: str, path: str | pathlib.Path, table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, file_extension: str = '.parquet', schema: pyarrow.Schema | None = None, file_sort_order: collections.abc.Sequence[collections.abc.Sequence[datafusion.expr.SortKey]] | None = None) -> None + + Register multiple files as a single table. + + Registers a :py:class:`~datafusion.catalog.Table` that can assemble multiple + files from locations in an :py:class:`~datafusion.object_store.ObjectStore` + instance. + + :param name: Name of the resultant table. + :param path: Path to the file to register. + :param table_partition_cols: Partition columns. + :param file_extension: File extension of the provided table. + :param schema: The data source schema. + :param file_sort_order: Sort order for the file. Each sort key can be + specified as a column name (``str``), an expression + (``Expr``), or a ``SortExpr``. + + + + .. py:method:: register_object_store(schema: str, store: Any, host: str | None = None) -> None + + Add a new object store into the session. + + :param schema: The data source schema. + :param store: The :py:class:`~datafusion.object_store.ObjectStore` to register. + :param host: URL for the host. + + + + .. py:method:: register_parquet(name: str, path: str | pathlib.Path, table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, parquet_pruning: bool = True, file_extension: str = '.parquet', skip_metadata: bool = True, schema: pyarrow.Schema | None = None, file_sort_order: collections.abc.Sequence[collections.abc.Sequence[datafusion.expr.SortKey]] | None = None, object_store: Any | None = None) -> None + + Register a Parquet file as a table. + + The registered table can be referenced from SQL statement executed + against this context. + + :param name: Name of the table to register. + :param path: Path to the Parquet file. + :param table_partition_cols: Partition columns. + :param parquet_pruning: Whether the parquet reader should use the + predicate to prune row groups. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param skip_metadata: Whether the parquet reader should skip any metadata + that may be in the file schema. This can help avoid schema + conflicts due to metadata. + :param schema: The data source schema. + :param file_sort_order: Sort order for the file. Each sort key can be + specified as a column name (``str``), an expression + (``Expr``), or a ``SortExpr``. + :param object_store: A pre-configured object store instance (e.g. + :py:class:`~datafusion.object_store.AmazonS3`, + :py:class:`~datafusion.object_store.GoogleCloud`, + :py:class:`~datafusion.object_store.MicrosoftAzure`) to use + for accessing the file. When provided, the store is + automatically registered for the URL scheme and host parsed + from ``path``, removing the need to call + :py:meth:`register_object_store` separately. This is + especially useful in multi-threaded environments where + setting credentials via ``os.environ`` is not thread-safe. + + .. rubric:: Examples + + Register a local Parquet file: + + >>> import datafusion + >>> ctx = datafusion.SessionContext() + >>> ctx.register_parquet("my_table", "data.parquet") # doctest: +SKIP + + Register from S3 with inline credentials (thread-safe): + + >>> from datafusion.object_store import AmazonS3 # doctest: +SKIP + >>> store = AmazonS3( + ... bucket_name="my-bucket", + ... region="us-east-1", + ... access_key_id="...", + ... secret_access_key="...", + ... ) # doctest: +SKIP + >>> ctx.register_parquet( + ... "my_table", + ... "s3://my-bucket/data.parquet", + ... object_store=store, + ... ) # doctest: +SKIP + + + + .. py:method:: register_record_batches(name: str, partitions: list[list[pyarrow.RecordBatch]]) -> None + + Register record batches as a table. + + This function will convert the provided partitions into a table and + register it into the session using the given name. + + :param name: Name of the resultant table. + :param partitions: Record batches to register as a table. + + + + .. py:method:: register_table(name: str, table: datafusion.catalog.Table | TableProviderExportable | datafusion.dataframe.DataFrame | pyarrow.dataset.Dataset) -> None + + Register a :py:class:`~datafusion.Table` with this context. + + The registered table can be referenced from SQL statements executed against + this context. + + :param name: Name of the resultant table. + :param table: Any object that can be converted into a :class:`Table`. + + + + .. py:method:: register_table_factory(format: str, factory: datafusion.catalog.TableProviderFactory | datafusion.catalog.TableProviderFactoryExportable) -> None + + Register a :py:class:`~datafusion.TableProviderFactoryExportable`. + + The registered factory can be referenced from SQL DDL statements executed + against this context. + + :param format: The value to be used in `STORED AS ${format}` clause. + :param factory: A PyCapsule that implements :class:`TableProviderFactoryExportable` + + + + .. py:method:: register_table_provider(name: str, provider: datafusion.catalog.Table | TableProviderExportable | datafusion.dataframe.DataFrame | pyarrow.dataset.Dataset) -> None + + Register a table provider. + + Deprecated: use :meth:`register_table` instead. + + + + .. py:method:: register_udaf(udaf: datafusion.user_defined.AggregateUDF) -> None + + Register a user-defined aggregation function (UDAF) with the context. + + + + .. py:method:: register_udf(udf: datafusion.user_defined.ScalarUDF) -> None + + Register a user-defined function (UDF) with the context. + + + + .. py:method:: register_udtf(func: datafusion.user_defined.TableFunction) -> None + + Register a user defined table function. + + + + .. py:method:: register_udwf(udwf: datafusion.user_defined.WindowUDF) -> None + + Register a user-defined window function (UDWF) with the context. + + + + .. py:method:: register_view(name: str, df: datafusion.dataframe.DataFrame) -> None + + Register a :py:class:`~datafusion.dataframe.DataFrame` as a view. + + :param name: The name to register the view under. + :type name: str + :param df: The DataFrame to be converted into a view and registered. + :type df: DataFrame + + + + .. py:method:: remove_optimizer_rule(name: str) -> bool + + Remove an optimizer rule by name. + + :param name: Name of the optimizer rule to remove. + + :returns: True if a rule with the given name was found and removed. + + .. rubric:: Examples + + >>> ctx = SessionContext() + >>> ctx.remove_optimizer_rule("nonexistent_rule") + False + + + + .. py:method:: session_id() -> str + + Return an id that uniquely identifies this :py:class:`SessionContext`. + + + + .. py:method:: session_start_time() -> str + + Return the session start time as an RFC 3339 formatted string. + + .. rubric:: Examples + + >>> ctx = SessionContext() + >>> ctx.session_start_time() # doctest: +SKIP + '2026-01-01T12:34:56.123456789+00:00' + + + + .. py:method:: sql(query: str, options: SQLOptions | None = None, param_values: dict[str, Any] | None = None, **named_params: Any) -> datafusion.dataframe.DataFrame + + Create a :py:class:`~datafusion.DataFrame` from SQL query text. + + See the online documentation for a description of how to perform + parameterized substitution via either the ``param_values`` option + or passing in ``named_params``. + + Note: This API implements DDL statements such as ``CREATE TABLE`` and + ``CREATE VIEW`` and DML statements such as ``INSERT INTO`` with in-memory + default implementation.See + :py:func:`~datafusion.context.SessionContext.sql_with_options`. + + :param query: SQL query text. + :param options: If provided, the query will be validated against these options. + :param param_values: Provides substitution of scalar values in the query + after parsing. + :param named_params: Provides string or DataFrame substitution in the query string. + + :returns: DataFrame representation of the SQL query. + + + + .. py:method:: sql_with_options(query: str, options: SQLOptions, param_values: dict[str, Any] | None = None, **named_params: Any) -> datafusion.dataframe.DataFrame + + Create a :py:class:`~datafusion.dataframe.DataFrame` from SQL query text. + + This function will first validate that the query is allowed by the + provided options. + + :param query: SQL query text. + :param options: SQL options. + :param param_values: Provides substitution of scalar values in the query + after parsing. + :param named_params: Provides string or DataFrame substitution in the query string. + + :returns: DataFrame representation of the SQL query. + + + + .. py:method:: table(name: str) -> datafusion.dataframe.DataFrame + + Retrieve a previously registered table by name. + + + + .. py:method:: table_exist(name: str) -> bool + + Return whether a table with the given name exists. + + + + .. py:method:: table_provider(name: str) -> datafusion.catalog.Table + + Return the :py:class:`~datafusion.catalog.Table` for the given table name. + + :param name: Name of the table. + + :returns: The table provider. + + :raises KeyError: If the table is not found. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> ctx = SessionContext() + >>> batch = pa.RecordBatch.from_pydict({"x": [1, 2]}) + >>> ctx.register_record_batches("my_table", [[batch]]) + >>> tbl = ctx.table_provider("my_table") + >>> tbl.schema + x: int64 + + + + .. py:method:: udaf(name: str) -> datafusion.user_defined.AggregateUDF + + Look up a registered aggregate UDF by name. + + Returns the same ``AggregateUDF`` wrapper that :py:meth:`register_udaf` + accepts. Built-in aggregate functions such as ``sum`` or ``avg`` are + also discoverable through this lookup. See :py:meth:`udf` for a worked + late-binding example; the pattern is identical for aggregates. + + :param name: Name of the registered aggregate UDF. + + :raises KeyError: If no aggregate UDF is registered under ``name``. + + .. rubric:: Examples + + Look up a built-in aggregate by name and use it in + :py:meth:`~datafusion.DataFrame.aggregate`: + + >>> ctx = dfn.SessionContext() + >>> sum_fn = ctx.udaf("sum") + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> df.aggregate([], [sum_fn(col("a")).alias("total")]).to_pydict() + {'total': [6]} + + + + .. py:method:: udafs() -> list[str] + + Return the sorted names of all registered aggregate UDFs. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> "sum" in ctx.udafs() + True + + + + .. py:method:: udf(name: str) -> datafusion.user_defined.ScalarUDF + + Look up a registered scalar UDF by name. + + Returns the same ``ScalarUDF`` wrapper that :py:meth:`register_udf` + accepts, so it can be invoked as an expression in the DataFrame API + or re-registered into a different :py:class:`SessionContext`. + Built-in scalar functions from the session's function registry are + also looked up. + + :param name: Name of the registered scalar UDF. + + :raises KeyError: If no scalar UDF is registered under ``name``. + + .. rubric:: Examples + + Register a UDF, then look it up by name and use it in the + DataFrame API: + + >>> ctx = dfn.SessionContext() + >>> nullcheck = dfn.udf( + ... lambda x: x.is_null(), + ... [pa.int64()], + ... pa.bool_(), + ... volatility="immutable", + ... name="nullcheck", + ... ) + >>> ctx.register_udf(nullcheck) + >>> fn = ctx.udf("nullcheck") + >>> df = ctx.from_pydict({"a": [1, None, 3]}) + >>> df.select(fn(col("a")).alias("is_null")).to_pydict() + {'is_null': [False, True, False]} + + Late-binding: the function name can come from configuration + rather than an imported symbol, which is useful when the set + of UDFs is plugin-driven or chosen at runtime: + + >>> config = {"null_check": "nullcheck"} + >>> fn = ctx.udf(config["null_check"]) + >>> df.select(fn(col("a")).alias("is_null")).to_pydict() + {'is_null': [False, True, False]} + + + + .. py:method:: udfs() -> list[str] + + Return the sorted names of all registered scalar UDFs. + + Includes both user-registered and built-in scalar functions. Pair + with :py:meth:`udf` to drive discovery, validation, or config-based + dispatch. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> "abs" in ctx.udfs() + True + + + + .. py:method:: udwf(name: str) -> datafusion.user_defined.WindowUDF + + Look up a registered window UDF by name. + + Returns the same ``WindowUDF`` wrapper that :py:meth:`register_udwf` + accepts. Built-in window functions such as ``row_number`` or ``rank`` + are also discoverable through this lookup. See :py:meth:`udf` for a + worked late-binding example; the pattern is identical for window + functions. + + :param name: Name of the registered window UDF. + + :raises KeyError: If no window UDF is registered under ``name``. + + .. rubric:: Examples + + Look up a built-in window function by name and use it in + ``select``: + + >>> ctx = dfn.SessionContext() + >>> rn = ctx.udwf("row_number") + >>> df = ctx.from_pydict({"a": [10, 20, 30]}) + >>> df.select(col("a"), rn().alias("rn")).to_pydict() + {'a': [10, 20, 30], 'rn': [1, 2, 3]} + + + + .. py:method:: udwfs() -> list[str] + + Return the sorted names of all registered window UDFs. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> "row_number" in ctx.udwfs() + True + + + + .. py:method:: with_logical_extension_codec(codec: datafusion.user_defined.LogicalExtensionCodecExportable | _typeshed.CapsuleType) -> SessionContext + + Create a new session context with specified codec. + + Only FFI codecs are supported. Pass any object implementing + ``__datafusion_logical_extension_codec__`` (see + :py:class:`~datafusion.user_defined.LogicalExtensionCodecExportable`). + + + + .. py:method:: with_physical_extension_codec(codec: datafusion.user_defined.PhysicalExtensionCodecExportable | _typeshed.CapsuleType) -> SessionContext + + Create a new session context with the specified physical codec. + + Only FFI codecs are supported. Pass any object implementing + ``__datafusion_physical_extension_codec__`` (see + :py:class:`~datafusion.user_defined.PhysicalExtensionCodecExportable`). + + + + .. py:method:: with_python_udf_inlining(*, enabled: bool) -> SessionContext + + Control whether Python UDFs are embedded in serialized expressions. + + ``enabled`` is keyword-only and required: callers must pick a + mode explicitly. Fresh sessions inline UDFs (``enabled=True`` + behavior) until this method overrides the toggle. + + With ``enabled=True``, serialized expressions carry the Python + code for any scalar, aggregate, or window UDFs they reference. + The receiver rebuilds the UDFs from those bytes and does not + need to register them first. + + With ``enabled=False``, serialized expressions store only the + UDF names. This has two uses: + + * **Cross-language portability.** The bytes can be decoded by a + non-Python receiver, which must already have UDFs registered + under matching names. + * **Safer deserialization.** :meth:`Expr.from_bytes` will refuse + to rebuild Python UDFs rather than call ``cloudpickle.loads`` + on untrusted input. + + The setting affects :meth:`Expr.to_bytes` and + :meth:`Expr.from_bytes` whenever this session is passed as the + ``ctx`` argument. :func:`pickle.dumps` and :func:`pickle.loads` + do not pass a context, so to apply the setting through pickle, + register this session with + :func:`datafusion.ipc.set_sender_ctx` on the sender and + :func:`datafusion.ipc.set_worker_ctx` on the receiver. + + .. warning:: Security + This setting narrows only :meth:`Expr.from_bytes`. Calling + :func:`pickle.loads` on untrusted bytes remains unsafe + regardless of the toggle. + + Returns a new :class:`SessionContext` with the toggle applied; + the original session is unchanged. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datafusion import SessionContext, Expr, col, udf + >>> ctx = SessionContext() + >>> identity = udf(lambda a: a, [pa.int64()], pa.int64(), + ... volatility="immutable", name="identity_demo") + >>> ctx.register_udf(identity) + >>> blob = identity(col("x")).to_bytes(ctx) + >>> strict = SessionContext().with_python_udf_inlining(enabled=False) + >>> try: + ... Expr.from_bytes(blob, strict) + ... except Exception as e: + ... print("Refusing to deserialize" in str(e)) + True + + + + .. py:attribute:: ctx + + +.. py:class:: TableProviderExportable + + Bases: :py:obj:`Protocol` + + + Type hint for object that has __datafusion_table_provider__ PyCapsule. + + https://datafusion.apache.org/python/user-guide/io/table_provider.html + + + .. py:method:: __datafusion_table_provider__(session: Any) -> object + + diff --git a/_sources/autoapi/datafusion/dataframe/index.rst.txt b/_sources/autoapi/datafusion/dataframe/index.rst.txt new file mode 100644 index 000000000..bdc9fd78e --- /dev/null +++ b/_sources/autoapi/datafusion/dataframe/index.rst.txt @@ -0,0 +1,1654 @@ +datafusion.dataframe +==================== + +.. py:module:: datafusion.dataframe + +.. autoapi-nested-parse:: + + :py:class:`DataFrame` — lazy, chainable query representation. + + A :py:class:`DataFrame` is a logical plan over one or more data sources. + Methods that reshape the plan (:py:meth:`DataFrame.select`, + :py:meth:`DataFrame.filter`, :py:meth:`DataFrame.aggregate`, + :py:meth:`DataFrame.sort`, :py:meth:`DataFrame.join`, + :py:meth:`DataFrame.limit`, the set-operation methods, ...) return a new + :py:class:`DataFrame` and do no work until a terminal method such as + :py:meth:`DataFrame.collect`, :py:meth:`DataFrame.to_pydict`, + :py:meth:`DataFrame.show`, or one of the ``write_*`` methods is called. + + DataFrames are produced from a + :py:class:`~datafusion.context.SessionContext`, typically via + :py:meth:`~datafusion.context.SessionContext.sql`, + :py:meth:`~datafusion.context.SessionContext.read_csv`, + :py:meth:`~datafusion.context.SessionContext.read_parquet`, or + :py:meth:`~datafusion.context.SessionContext.from_pydict`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3], "b": [10, 20, 30]}) + >>> df.filter(col("a") > 1).select("b").to_pydict() + {'b': [20, 30]} + + See :ref:`user_guide_concepts` in the online documentation for a high-level + overview of the execution model. + + + +Classes +------- + +.. autoapisummary:: + + datafusion.dataframe.Compression + datafusion.dataframe.DataFrame + datafusion.dataframe.DataFrameWriteOptions + datafusion.dataframe.ExplainFormat + datafusion.dataframe.InsertOp + datafusion.dataframe.ParquetColumnOptions + datafusion.dataframe.ParquetWriterOptions + + +Module Contents +--------------- + +.. py:class:: Compression + + Bases: :py:obj:`enum.Enum` + + + Enum representing the available compression types for Parquet files. + + + .. py:method:: from_str(value: str) -> Compression + :classmethod: + + + Convert a string to a Compression enum value. + + :param value: The string representation of the compression type. + + :returns: The Compression enum lowercase value. + + :raises ValueError: If the string does not match any Compression enum value. + + + + .. py:method:: get_default_level() -> int | None + + Get the default compression level for the compression type. + + :returns: The default compression level for the compression type. + + + + .. py:attribute:: BROTLI + :value: 'brotli' + + + + .. py:attribute:: GZIP + :value: 'gzip' + + + + .. py:attribute:: LZ4 + :value: 'lz4' + + + + .. py:attribute:: LZ4_RAW + :value: 'lz4_raw' + + + + .. py:attribute:: SNAPPY + :value: 'snappy' + + + + .. py:attribute:: UNCOMPRESSED + :value: 'uncompressed' + + + + .. py:attribute:: ZSTD + :value: 'zstd' + + + +.. py:class:: DataFrame(df: datafusion._internal.DataFrame) + + Two dimensional table representation of data. + + DataFrame objects are iterable; iterating over a DataFrame yields + :class:`datafusion.RecordBatch` instances lazily. + + See :ref:`user_guide_concepts` in the online documentation for more information. + + This constructor is not to be used by the end user. + + See :py:class:`~datafusion.context.SessionContext` for methods to + create a :py:class:`DataFrame`. + + + .. py:method:: __aiter__() -> collections.abc.AsyncIterator[datafusion.record_batch.RecordBatch] + + Return an async iterator over this DataFrame's record batches. + + We're using __aiter__ because we support Python < 3.10 where aiter() is not + available. + + + + .. py:method:: __arrow_c_stream__(requested_schema: object | None = None) -> object + + Export the DataFrame as an Arrow C Stream. + + The DataFrame is executed using DataFusion's streaming APIs and exposed via + Arrow's C Stream interface. Record batches are produced incrementally, so the + full result set is never materialized in memory. + + When ``requested_schema`` is provided, DataFusion applies only simple + projections such as selecting a subset of existing columns or reordering + them. Column renaming, computed expressions, or type coercion are not + supported through this interface. + + :param requested_schema: Either a :py:class:`pyarrow.Schema` or an Arrow C + Schema capsule (``PyCapsule``) produced by + ``schema._export_to_c_capsule()``. The DataFrame will attempt to + align its output with the fields and order specified by this schema. + + :returns: Arrow ``PyCapsule`` object representing an ``ArrowArrayStream``. + + For practical usage patterns, see the Apache Arrow streaming + documentation: https://arrow.apache.org/docs/python/ipc.html#streaming. + + For details on DataFusion's Arrow integration and DataFrame streaming, + see the user guide (user-guide/io/arrow and user-guide/dataframe/index). + + .. rubric:: Notes + + The Arrow C Data Interface PyCapsule details are documented by Apache + Arrow and can be found at: + https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html + + + + .. py:method:: __getitem__(key: str | list[str]) -> DataFrame + + Return a new :py:class:`DataFrame` with the specified column or columns. + + :param key: Column name or list of column names to select. + + :returns: DataFrame with the specified column or columns. + + + + .. py:method:: __iter__() -> collections.abc.Iterator[datafusion.record_batch.RecordBatch] + + Return an iterator over this DataFrame's record batches. + + + + .. py:method:: __repr__() -> str + + Return a string representation of the DataFrame. + + :returns: String representation of the DataFrame. + + + + .. py:method:: _repr_html_() -> str + + + .. py:method:: aggregate(group_by: collections.abc.Sequence[datafusion.expr.Expr | str] | datafusion.expr.Expr | str | None, aggs: collections.abc.Sequence[datafusion.expr.Expr] | datafusion.expr.Expr) -> DataFrame + + Aggregates the rows of the current DataFrame. + + By default each unique combination of the ``group_by`` columns + produces one row. To get multiple levels of subtotals in a + single pass, pass a + :py:class:`~datafusion.expr.GroupingSet` expression + (created via + :py:meth:`~datafusion.expr.GroupingSet.rollup`, + :py:meth:`~datafusion.expr.GroupingSet.cube`, or + :py:meth:`~datafusion.expr.GroupingSet.grouping_sets`) + as the ``group_by`` argument. See the + :ref:`aggregation` user guide for detailed examples. + + :param group_by: Sequence of expressions or column names to group + by, or ``None`` for aggregation over the whole DataFrame. + A :py:class:`~datafusion.expr.GroupingSet` expression may + be included to produce multiple grouping levels (rollup, + cube, or explicit grouping sets). + :param aggs: Sequence of expressions to aggregate. + + :returns: DataFrame after aggregation. + + .. rubric:: Examples + + Aggregate without grouping — ``None`` or an empty ``group_by`` + produces a single row: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict( + ... {"team": ["x", "x", "y"], "score": [1, 2, 5]} + ... ) + >>> df.aggregate(None, [F.sum(col("score")).alias("total")]).to_pydict() + {'total': [8]} + + Group by a column and produce one row per group: + + >>> df.aggregate( + ... ["team"], [F.sum(col("score")).alias("total")] + ... ).sort("team").to_pydict() + {'team': ['x', 'y'], 'total': [3, 5]} + + + + .. py:method:: alias(alias: str) -> DataFrame + + Assign a table alias to this :py:class:`DataFrame`. + + Replaces the qualifiers of the output columns with ``alias``. Useful for + self-joins and any situation that needs an unambiguous table-style + qualifier (``alias.col``) for downstream references. + + :param alias: Table alias to apply to the DataFrame's columns. + + :returns: DataFrame with columns re-qualified under ``alias``. + + .. rubric:: Example + + >>> from datafusion import col + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"id": [1, 2], "val": [10, 20]}) + >>> left = df.alias("l") + >>> right = df.alias("r") + >>> left.join(right, left_on="id", right_on="id").select( + ... "id", col("l.val").alias("lval"), col("r.val").alias("rval") + ... ).sort("id").to_pydict() + {'id': [1, 2], 'lval': [10, 20], 'rval': [10, 20]} + + + + .. py:method:: cache() -> DataFrame + + Cache the DataFrame as a memory table. + + :returns: Cached DataFrame. + + + + .. py:method:: cast(mapping: dict[str, pyarrow.DataType[Any]]) -> DataFrame + + Cast one or more columns to a different data type. + + :param mapping: Mapped with column as key and column dtype as value. + + :returns: DataFrame after casting columns + + + + .. py:method:: col(name: str) -> datafusion.expr.Expr + + Alias for :py:meth:`column`. + + .. seealso:: :py:meth:`column` + + + + .. py:method:: collect() -> list[pyarrow.RecordBatch] + + Execute this :py:class:`DataFrame` and collect results into memory. + + Prior to calling ``collect``, modifying a DataFrame simply updates a plan + (no actual computation is performed). Calling ``collect`` triggers the + computation. + + :returns: List of :py:class:`pyarrow.RecordBatch` collected from the DataFrame. + + + + .. py:method:: collect_column(column_name: str) -> pyarrow.Array | pyarrow.ChunkedArray + + Executes this :py:class:`DataFrame` for a single column. + + + + .. py:method:: collect_partitioned() -> list[list[pyarrow.RecordBatch]] + + Execute this DataFrame and collect all partitioned results. + + This operation returns :py:class:`pyarrow.RecordBatch` maintaining the input + partitioning. + + :returns: + + List of list of :py:class:`RecordBatch` collected from the + DataFrame. + + + + .. py:method:: column(name: str) -> datafusion.expr.Expr + + Return a fully qualified column expression for ``name``. + + Resolves an unqualified column name against this DataFrame's schema + and returns an :py:class:`Expr` whose underlying column reference + includes the table qualifier. This is especially useful after joins, + where the same column name may appear in multiple relations. + + :param name: Unqualified column name to look up. + + :returns: A fully qualified column expression. + + :raises Exception: If the column is not found or is ambiguous (exists in + multiple relations). + + .. rubric:: Examples + + Resolve a column from a simple DataFrame: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2], "b": [3, 4]}) + >>> expr = df.column("a") + >>> df.select(expr).to_pydict() + {'a': [1, 2]} + + Resolve qualified columns after a join: + + >>> left = ctx.from_pydict({"id": [1, 2], "x": [10, 20]}) + >>> right = ctx.from_pydict({"id": [1, 2], "y": [30, 40]}) + >>> joined = left.join(right, on="id", how="inner") + >>> expr = joined.column("y") + >>> joined.select("id", expr).sort("id").to_pydict() + {'id': [1, 2], 'y': [30, 40]} + + + + .. py:method:: count() -> int + + Return the total number of rows in this :py:class:`DataFrame`. + + Note that this method will actually run a plan to calculate the + count, which may be slow for large or complicated DataFrames. + + :returns: Number of rows in the DataFrame. + + + + .. py:method:: default_str_repr(batches: list[pyarrow.RecordBatch], schema: pyarrow.Schema, has_more: bool, table_uuid: str | None = None) -> str + :staticmethod: + + + Return the default string representation of a DataFrame. + + This method is used by the default formatter and implemented in Rust for + performance reasons. + + + + .. py:method:: describe() -> DataFrame + + Return the statistics for this DataFrame. + + Only summarized numeric datatypes at the moments and returns nulls + for non-numeric datatypes. + + The output format is modeled after pandas. + + :returns: A summary DataFrame containing statistics. + + + + .. py:method:: distinct() -> DataFrame + + Return a new :py:class:`DataFrame` with all duplicated rows removed. + + :returns: DataFrame after removing duplicates. + + + + .. py:method:: distinct_on(on_expr: list[datafusion.expr.Expr], select_expr: list[datafusion.expr.Expr], sort_expr: list[datafusion.expr.SortKey] | None = None) -> DataFrame + + Deduplicate rows based on specific columns. + + Returns a new DataFrame with one row per unique combination of the + ``on_expr`` columns, keeping the first row per group as determined by + ``sort_expr``. + + :param on_expr: Expressions that determine uniqueness. + :param select_expr: Expressions to include in the output. + :param sort_expr: Optional sort expressions to determine which row to keep. + + :returns: DataFrame after deduplication. + + .. rubric:: Examples + + Keep the row with the smallest ``b`` for each unique ``a``: + + >>> from datafusion import col + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 1, 2, 2], "b": [10, 20, 30, 40]}) + >>> df.distinct_on( + ... [col("a")], + ... [col("a"), col("b")], + ... [col("a").sort(ascending=True), col("b").sort(ascending=True)], + ... ).sort("a").to_pydict() + {'a': [1, 2], 'b': [10, 30]} + + + + .. py:method:: drop(*columns: str) -> DataFrame + + Drop arbitrary amount of columns. + + Column names are case-sensitive and require double quotes to be dropped + if the original name is not strictly lower case. + + :param columns: Column names to drop from the dataframe. + + :returns: DataFrame with those columns removed in the projection. + + .. rubric:: Examples + + To drop a lower-cased column 'a' + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2], "b": [3, 4]}) + >>> df.drop("a").schema().names + ['b'] + + Or to drop an upper-cased column 'A' + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"A": [1, 2], "b": [3, 4]}) + >>> df.drop('"A"').schema().names + ['b'] + + + + .. py:method:: except_all(other: DataFrame, distinct: bool = False) -> DataFrame + + Calculate the set difference of two :py:class:`DataFrame`. + + Returns rows that are in this DataFrame but not in ``other``. + + The two :py:class:`DataFrame` must have exactly the same schema. + + :param other: DataFrame to calculate exception with. + :param distinct: If ``True``, duplicate rows are removed from the result. + + :returns: DataFrame after set difference. + + .. rubric:: Examples + + Remove rows present in ``df2``: + + >>> ctx = dfn.SessionContext() + >>> df1 = ctx.from_pydict({"a": [1, 2, 3], "b": [10, 20, 30]}) + >>> df2 = ctx.from_pydict({"a": [1, 2], "b": [10, 20]}) + >>> df1.except_all(df2).sort("a").to_pydict() + {'a': [3], 'b': [30]} + + Remove rows present in ``df2`` and deduplicate: + + >>> df1.except_all(df2, distinct=True).sort("a").to_pydict() + {'a': [3], 'b': [30]} + + + + .. py:method:: execute_stream() -> datafusion.record_batch.RecordBatchStream + + Executes this DataFrame and returns a stream over a single partition. + + :returns: Record Batch Stream over a single partition. + + + + .. py:method:: execute_stream_partitioned() -> list[datafusion.record_batch.RecordBatchStream] + + Executes this DataFrame and returns a stream for each partition. + + :returns: One record batch stream per partition. + + + + .. py:method:: execution_plan() -> datafusion.plan.ExecutionPlan + + Return the execution/physical plan. + + :returns: Execution plan. + + + + .. py:method:: explain(verbose: bool = False, analyze: bool = False, format: ExplainFormat | None = None) -> None + + Print an explanation of the DataFrame's plan so far. + + If ``analyze`` is specified, runs the plan and reports metrics. + + :param verbose: If ``True``, more details will be included. + :param analyze: If ``True``, the plan will run and metrics reported. + :param format: Output format for the plan. Defaults to + :py:attr:`ExplainFormat.INDENT`. + + .. rubric:: Examples + + Show the plan in tree format: + + >>> from datafusion import ExplainFormat + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> df.explain(format=ExplainFormat.TREE) # doctest: +SKIP + + Show plan with runtime metrics: + + >>> df.explain(analyze=True) # doctest: +SKIP + + + + .. py:method:: fill_null(value: Any, subset: list[str] | None = None) -> DataFrame + + Fill null values in specified columns with a value. + + :param value: Value to replace nulls with. Will be cast to match column type. + :param subset: Optional list of column names to fill. If None, fills all columns. + + :returns: DataFrame with null values replaced where type casting is possible + + .. rubric:: Examples + + >>> from datafusion import SessionContext, col + >>> ctx = SessionContext() + >>> df = ctx.from_pydict({"a": [1, None, 3], "b": [None, 5, 6]}) + >>> filled = df.fill_null(0) + >>> filled.sort(col("a")).collect()[0].column("a").to_pylist() + [0, 1, 3] + + .. rubric:: Notes + + - Only fills nulls in columns where the value can be cast to the column type + - For columns where casting fails, the original column is kept unchanged + - For columns not in subset, the original column is kept unchanged + + + + .. py:method:: filter(*predicates: datafusion.expr.Expr | str) -> DataFrame + + Return a DataFrame for which ``predicate`` evaluates to ``True``. + + Rows for which ``predicate`` evaluates to ``False`` or ``None`` are filtered + out. If more than one predicate is provided, these predicates will be + combined as a logical AND. Each ``predicate`` can be an + :class:`~datafusion.expr.Expr` created using helper functions such as + :func:`datafusion.col` or :func:`datafusion.lit`, or a SQL expression string + that will be parsed against the DataFrame schema. If more complex logic is + required, see the logical operations in :py:mod:`~datafusion.functions`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> df.filter(col("a") > lit(1)).to_pydict() + {'a': [2, 3]} + >>> df.filter("a > 1").to_pydict() + {'a': [2, 3]} + + :param predicates: Predicate expression(s) or SQL strings to filter the DataFrame. + + :returns: DataFrame after filtering. + + + + .. py:method:: find_qualified_columns(*names: str) -> list[datafusion.expr.Expr] + + Return fully qualified column expressions for the given names. + + This is a batch version of :py:meth:`column` — it resolves each + unqualified name against the DataFrame's schema and returns a list + of qualified column expressions. + + :param names: Unqualified column names to look up. + + :returns: List of fully qualified column expressions, one per name. + + :raises Exception: If any column is not found or is ambiguous. + + .. rubric:: Examples + + Resolve multiple columns at once: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2], "b": [3, 4], "c": [5, 6]}) + >>> exprs = df.find_qualified_columns("a", "c") + >>> df.select(*exprs).to_pydict() + {'a': [1, 2], 'c': [5, 6]} + + + + .. py:method:: head(n: int = 5) -> DataFrame + + Return a new :py:class:`DataFrame` with a limited number of rows. + + :param n: Number of rows to take from the head of the DataFrame. + + :returns: DataFrame after limiting. + + + + .. py:method:: intersect(other: DataFrame, distinct: bool = False) -> DataFrame + + Calculate the intersection of two :py:class:`DataFrame`. + + The two :py:class:`DataFrame` must have exactly the same schema. + + :param other: DataFrame to intersect with. + :param distinct: If ``True``, duplicate rows are removed from the result. + + :returns: DataFrame after intersection. + + .. rubric:: Examples + + Find rows common to both DataFrames: + + >>> ctx = dfn.SessionContext() + >>> df1 = ctx.from_pydict({"a": [1, 2, 3], "b": [10, 20, 30]}) + >>> df2 = ctx.from_pydict({"a": [1, 4], "b": [10, 40]}) + >>> df1.intersect(df2).to_pydict() + {'a': [1], 'b': [10]} + + Intersect with deduplication: + + >>> df1 = ctx.from_pydict({"a": [1, 1, 2], "b": [10, 10, 20]}) + >>> df2 = ctx.from_pydict({"a": [1, 1], "b": [10, 10]}) + >>> df1.intersect(df2, distinct=True).to_pydict() + {'a': [1], 'b': [10]} + + + + .. py:method:: into_view(temporary: bool = False) -> datafusion.catalog.Table + + Convert ``DataFrame`` into a :class:`~datafusion.Table`. + + .. rubric:: Examples + + >>> from datafusion import SessionContext + >>> ctx = SessionContext() + >>> df = ctx.sql("SELECT 1 AS value") + >>> view = df.into_view() + >>> ctx.register_table("values_view", view) + >>> result = ctx.sql("SELECT value FROM values_view").collect() + >>> result[0].column("value").to_pylist() + [1] + + + + .. py:method:: join(right: DataFrame, on: str | collections.abc.Sequence[str], how: Literal['inner', 'left', 'right', 'full', 'semi', 'anti'] = 'inner', *, left_on: None = None, right_on: None = None, join_keys: None = None, coalesce_duplicate_keys: bool = True) -> DataFrame + join(right: DataFrame, on: None = None, how: Literal['inner', 'left', 'right', 'full', 'semi', 'anti'] = 'inner', *, left_on: str | collections.abc.Sequence[str], right_on: str | collections.abc.Sequence[str], join_keys: tuple[list[str], list[str]] | None = None, coalesce_duplicate_keys: bool = True) -> DataFrame + join(right: DataFrame, on: None = None, how: Literal['inner', 'left', 'right', 'full', 'semi', 'anti'] = 'inner', *, join_keys: tuple[list[str], list[str]], left_on: None = None, right_on: None = None, coalesce_duplicate_keys: bool = True) -> DataFrame + + Join this :py:class:`DataFrame` with another :py:class:`DataFrame`. + + ``on`` has to be provided or both ``left_on`` and ``right_on`` in + conjunction. + + When non-key columns share the same name in both DataFrames, use + :py:meth:`DataFrame.col` on each DataFrame **before** the join to + obtain fully qualified column references that can disambiguate them. + See :py:meth:`join_on` for an example. + + :param right: Other DataFrame to join with. + :param on: Column names to join on in both dataframes. + :param how: Type of join to perform. Supported types are "inner", "left", + "right", "full", "semi", "anti". + :param left_on: Join column of the left dataframe. + :param right_on: Join column of the right dataframe. + :param coalesce_duplicate_keys: When True, coalesce the columns + from the right DataFrame and left DataFrame + that have identical names in the ``on`` fields. + :param join_keys: Tuple of two lists of column names to join on. [Deprecated] + + :returns: DataFrame after join. + + .. rubric:: Examples + + Inner-join two DataFrames on a shared column: + + >>> ctx = dfn.SessionContext() + >>> left = ctx.from_pydict({"id": [1, 2, 3], "val": [10, 20, 30]}) + >>> right = ctx.from_pydict({"id": [2, 3, 4], "label": ["b", "c", "d"]}) + >>> left.join(right, on="id").sort("id").to_pydict() + {'id': [2, 3], 'val': [20, 30], 'label': ['b', 'c']} + + Left join to keep all rows from the left side: + + >>> left.join(right, on="id", how="left").sort("id").to_pydict() + {'id': [1, 2, 3], 'val': [10, 20, 30], 'label': [None, 'b', 'c']} + + Use ``left_on`` / ``right_on`` when the key columns differ in name: + + >>> right2 = ctx.from_pydict({"rid": [2, 3], "label": ["b", "c"]}) + >>> left.join( + ... right2, left_on="id", right_on="rid" + ... ).sort("id").to_pydict() + {'id': [2, 3], 'val': [20, 30], 'rid': [2, 3], 'label': ['b', 'c']} + + + + .. py:method:: join_on(right: DataFrame, *on_exprs: datafusion.expr.Expr, how: Literal['inner', 'left', 'right', 'full', 'semi', 'anti'] = 'inner') -> DataFrame + + Join two :py:class:`DataFrame` using the specified expressions. + + Join predicates must be :class:`~datafusion.expr.Expr` objects, typically + built with :func:`datafusion.col`. On expressions are used to support + in-equality predicates. Equality predicates are correctly optimized. + + Use :py:meth:`DataFrame.col` on each DataFrame **before** the join to + obtain fully qualified column references. These qualified references + can then be used in the join predicate and to disambiguate columns + with the same name when selecting from the result. + + .. rubric:: Examples + + Join with unique column names: + + >>> ctx = dfn.SessionContext() + >>> left = ctx.from_pydict({"a": [1, 2], "x": ["a", "b"]}) + >>> right = ctx.from_pydict({"b": [1, 2], "y": ["c", "d"]}) + >>> left.join_on( + ... right, col("a") == col("b") + ... ).sort(col("x")).to_pydict() + {'a': [1, 2], 'x': ['a', 'b'], 'b': [1, 2], 'y': ['c', 'd']} + + Use :py:meth:`col` to disambiguate shared column names: + + >>> left = ctx.from_pydict({"id": [1, 2], "val": [10, 20]}) + >>> right = ctx.from_pydict({"id": [1, 2], "val": [30, 40]}) + >>> joined = left.join_on( + ... right, left.col("id") == right.col("id"), how="inner" + ... ) + >>> joined.select( + ... left.col("id"), left.col("val"), right.col("val").alias("rval") + ... ).sort(left.col("id")).to_pydict() + {'id': [1, 2], 'val': [10, 20], 'rval': [30, 40]} + + :param right: Other DataFrame to join with. + :param on_exprs: single or multiple (in)-equality predicates. + :param how: Type of join to perform. Supported types are "inner", "left", + "right", "full", "semi", "anti". + + :returns: DataFrame after join. + + + + .. py:method:: limit(count: int, offset: int = 0) -> DataFrame + + Return a new :py:class:`DataFrame` with a limited number of rows. + + Results are returned in unspecified order unless the DataFrame is + explicitly sorted first via :py:meth:`sort` or :py:meth:`sort_by`. + + :param count: Number of rows to limit the DataFrame to. + :param offset: Number of rows to skip. + + :returns: DataFrame after limiting. + + .. rubric:: Examples + + Take the first two rows: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3, 4]}).sort("a") + >>> df.limit(2).to_pydict() + {'a': [1, 2]} + + Skip the first row then take two (paging): + + >>> df.limit(2, offset=1).to_pydict() + {'a': [2, 3]} + + + + .. py:method:: logical_plan() -> datafusion.plan.LogicalPlan + + Return the unoptimized ``LogicalPlan``. + + :returns: Unoptimized logical plan. + + + + .. py:method:: optimized_logical_plan() -> datafusion.plan.LogicalPlan + + Return the optimized ``LogicalPlan``. + + :returns: Optimized logical plan. + + + + .. py:method:: parse_sql_expr(expr: str) -> datafusion.expr.Expr + + Creates logical expression from a SQL query text. + + The expression is created and processed against the current schema. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> expr = df.parse_sql_expr("a > 1") + >>> df.filter(expr).to_pydict() + {'a': [2, 3]} + + :param expr: Expression string to be converted to datafusion expression + + :returns: Logical expression . + + + + .. py:method:: repartition(num: int) -> DataFrame + + Repartition a DataFrame into ``num`` partitions. + + The batches allocation uses a round-robin algorithm. + + :param num: Number of partitions to repartition the DataFrame into. + + :returns: Repartitioned DataFrame. + + + + .. py:method:: repartition_by_hash(*exprs: datafusion.expr.Expr | str, num: int) -> DataFrame + + Repartition a DataFrame using a hash partitioning scheme. + + :param exprs: Expressions or a SQL expression string to evaluate + and perform hashing on. + :param num: Number of partitions to repartition the DataFrame into. + + :returns: Repartitioned DataFrame. + + + + .. py:method:: schema() -> pyarrow.Schema + + Return the :py:class:`pyarrow.Schema` of this DataFrame. + + The output schema contains information on the name, data type, and + nullability for each column. + + :returns: Describing schema of the DataFrame + + + + .. py:method:: select(*exprs: datafusion.expr.Expr | str) -> DataFrame + + Project arbitrary expressions into a new :py:class:`DataFrame`. + + String arguments are treated as column names; :py:class:`~datafusion.expr.Expr` + arguments can reshape, rename, or compute new columns. + + :param exprs: Either column names or :py:class:`~datafusion.expr.Expr` to select. + + :returns: DataFrame after projection. It has one column for each expression. + + .. rubric:: Examples + + Select columns by name: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3], "b": [10, 20, 30]}) + >>> df.select("a").to_pydict() + {'a': [1, 2, 3]} + + Mix column names, expressions, and aliases. The string ``"a"`` selects + column ``a`` directly; ``col("a").alias("alternate_a")`` returns a + duplicate under a new name: + + >>> df.select("a", col("b"), col("a").alias("alternate_a")).to_pydict() + {'a': [1, 2, 3], 'b': [10, 20, 30], 'alternate_a': [1, 2, 3]} + + + + .. py:method:: select_exprs(*args: str) -> DataFrame + + Project arbitrary list of expression strings into a new DataFrame. + + This method will parse string expressions into logical plan expressions. + The output DataFrame has one column for each expression. + + :returns: DataFrame only containing the specified columns. + + + + .. py:method:: show(num: int = 20) -> None + + Execute the DataFrame and print the result to the console. + + :param num: Number of lines to show. + + + + .. py:method:: sort(*exprs: datafusion.expr.SortKey) -> DataFrame + + Sort the DataFrame by the specified sorting expressions or column names. + + Note that any expression can be turned into a sort expression by + calling its ``sort`` method. For ascending-only sorts, the shorter + :py:meth:`sort_by` is usually more convenient. + + :param exprs: Sort expressions or column names, applied in order. + + :returns: DataFrame after sorting. + + .. rubric:: Examples + + Sort ascending by a column name: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [3, 1, 2], "b": [10, 20, 30]}) + >>> df.sort("a").to_pydict() + {'a': [1, 2, 3], 'b': [20, 30, 10]} + + Sort descending using :py:meth:`Expr.sort`: + + >>> df.sort(col("a").sort(ascending=False)).to_pydict() + {'a': [3, 2, 1], 'b': [10, 30, 20]} + + + + .. py:method:: sort_by(*exprs: datafusion.expr.Expr | str) -> DataFrame + + Sort the DataFrame by column expressions in ascending order. + + This is a convenience method that sorts the DataFrame by the given + expressions in ascending order with nulls last. For more control over + sort direction and null ordering, use :py:meth:`sort` instead. + + :param exprs: Expressions or column names to sort by. + + :returns: DataFrame after sorting. + + .. rubric:: Examples + + Sort by a single column: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [3, 1, 2]}) + >>> df.sort_by("a").to_pydict() + {'a': [1, 2, 3]} + + + + .. py:method:: tail(n: int = 5) -> DataFrame + + Return a new :py:class:`DataFrame` with a limited number of rows. + + Be aware this could be potentially expensive since the row size needs to be + determined of the dataframe. This is done by collecting it. + + :param n: Number of rows to take from the tail of the DataFrame. + + :returns: DataFrame after limiting. + + + + .. py:method:: to_arrow_table() -> pyarrow.Table + + Execute the :py:class:`DataFrame` and convert it into an Arrow Table. + + :returns: Arrow Table. + + + + .. py:method:: to_pandas() -> pandas.DataFrame + + Execute the :py:class:`DataFrame` and convert it into a Pandas DataFrame. + + :returns: Pandas DataFrame. + + + + .. py:method:: to_polars() -> polars.DataFrame + + Execute the :py:class:`DataFrame` and convert it into a Polars DataFrame. + + :returns: Polars DataFrame. + + + + .. py:method:: to_pydict() -> dict[str, list[Any]] + + Execute the :py:class:`DataFrame` and convert it into a dictionary of lists. + + :returns: Dictionary of lists. + + + + .. py:method:: to_pylist() -> list[dict[str, Any]] + + Execute the :py:class:`DataFrame` and convert it into a list of dictionaries. + + :returns: List of dictionaries. + + + + .. py:method:: transform(func: collections.abc.Callable[Ellipsis, DataFrame], *args: Any) -> DataFrame + + Apply a function to the current DataFrame which returns another DataFrame. + + This is useful for chaining together multiple functions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> def add_3(df): + ... return df.with_column("modified", dfn.lit(3)) + >>> def within_limit(df: DataFrame, limit: int) -> DataFrame: + ... return df.filter(col("a") < lit(limit)).distinct() + >>> df.transform(add_3).transform(within_limit, 4).sort("a").to_pydict() + {'a': [1, 2, 3], 'modified': [3, 3, 3]} + + :param func: A callable function that takes a DataFrame as it's first argument + :param args: Zero or more arguments to pass to `func` + + :returns: After applying func to the original dataframe. + :rtype: DataFrame + + + + .. py:method:: union(other: DataFrame, distinct: bool = False) -> DataFrame + + Calculate the union of two :py:class:`DataFrame`. + + The two :py:class:`DataFrame` must have exactly the same schema. + + :param other: DataFrame to union with. + :param distinct: If ``True``, duplicate rows will be removed. + + :returns: DataFrame after union. + + .. rubric:: Examples + + Stack rows from both DataFrames, preserving duplicates: + + >>> ctx = dfn.SessionContext() + >>> df1 = ctx.from_pydict({"a": [1, 2]}) + >>> df2 = ctx.from_pydict({"a": [2, 3]}) + >>> df1.union(df2).sort("a").to_pydict() + {'a': [1, 2, 2, 3]} + + Deduplicate the combined result with ``distinct=True``: + + >>> df1.union(df2, distinct=True).sort("a").to_pydict() + {'a': [1, 2, 3]} + + + + .. py:method:: union_by_name(other: DataFrame, distinct: bool = False) -> DataFrame + + Union two :py:class:`DataFrame` matching columns by name. + + Unlike :py:meth:`union` which matches columns by position, this method + matches columns by their names, allowing DataFrames with different + column orders to be combined. + + :param other: DataFrame to union with. + :param distinct: If ``True``, duplicate rows are removed from the result. + + :returns: DataFrame after union by name. + + .. rubric:: Examples + + Combine DataFrames with different column orders: + + >>> ctx = dfn.SessionContext() + >>> df1 = ctx.from_pydict({"a": [1], "b": [10]}) + >>> df2 = ctx.from_pydict({"b": [20], "a": [2]}) + >>> df1.union_by_name(df2).sort("a").to_pydict() + {'a': [1, 2], 'b': [10, 20]} + + Union by name with deduplication: + + >>> df1 = ctx.from_pydict({"a": [1, 1], "b": [10, 10]}) + >>> df2 = ctx.from_pydict({"b": [10], "a": [1]}) + >>> df1.union_by_name(df2, distinct=True).to_pydict() + {'a': [1], 'b': [10]} + + + + .. py:method:: union_distinct(other: DataFrame) -> DataFrame + + Calculate the distinct union of two :py:class:`DataFrame`. + + .. seealso:: :py:meth:`union` + + + + .. py:method:: unnest_columns(*columns: str, preserve_nulls: bool = True, recursions: list[tuple[str, str, int]] | None = None) -> DataFrame + + Expand columns of arrays into a single row per array element. + + :param columns: Column names to perform unnest operation on. + :param preserve_nulls: If False, rows with null entries will not be + returned. + :param recursions: Optional list of ``(input_column, output_column, depth)`` + tuples that control how deeply nested columns are unnested. Any + column not mentioned here is unnested with depth 1. + + :returns: A DataFrame with the columns expanded. + + .. rubric:: Examples + + Unnest an array column: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2], [3]], "b": ["x", "y"]}) + >>> df.unnest_columns("a").to_pydict() + {'a': [1, 2, 3], 'b': ['x', 'x', 'y']} + + With explicit recursion depth: + + >>> df.unnest_columns("a", recursions=[("a", "a", 1)]).to_pydict() + {'a': [1, 2, 3], 'b': ['x', 'x', 'y']} + + + + .. py:method:: window(*exprs: datafusion.expr.Expr) -> DataFrame + + Add window function columns to the DataFrame. + + Applies the given window function expressions and appends the results + as new columns. + + :param exprs: Window function expressions to evaluate. + + :returns: DataFrame with new window function columns appended. + + .. rubric:: Examples + + Add a row number within each group: + + >>> import datafusion.functions as f + >>> from datafusion import col + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3], "b": ["x", "x", "y"]}) + >>> df = df.window( + ... f.row_number( + ... partition_by=[col("b")], order_by=[col("a")] + ... ).alias("rn") + ... ) + >>> "rn" in df.schema().names + True + + + + .. py:method:: with_column(name: str, expr: datafusion.expr.Expr | str) -> DataFrame + + Add an additional column to the DataFrame. + + The ``expr`` must be an :class:`~datafusion.expr.Expr` constructed with + :func:`datafusion.col` or :func:`datafusion.lit`, or a SQL expression + string that will be parsed against the DataFrame schema. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2]}) + >>> df.with_column("b", col("a") + lit(10)).to_pydict() + {'a': [1, 2], 'b': [11, 12]} + + :param name: Name of the column to add. + :param expr: Expression to compute the column. + + :returns: DataFrame with the new column. + + + + .. py:method:: with_column_renamed(old_name: str, new_name: str) -> DataFrame + + Rename one column by applying a new projection. + + This is a no-op if the column to be renamed does not exist. + + The method supports case sensitive rename with wrapping column name + into one the following symbols (" or ' or \`). + + :param old_name: Old column name. + :param new_name: New column name. + + :returns: DataFrame with the column renamed. + + + + .. py:method:: with_columns(*exprs: datafusion.expr.Expr | str | collections.abc.Iterable[datafusion.expr.Expr | str], **named_exprs: datafusion.expr.Expr | str) -> DataFrame + + Add columns to the DataFrame. + + By passing expressions, iterables of expressions, string SQL expressions, + or named expressions. + All expressions must be :class:`~datafusion.expr.Expr` objects created via + :func:`datafusion.col` or :func:`datafusion.lit`, or SQL expression strings. + To pass named expressions use the form ``name=Expr``. + + Example usage: The following will add 4 columns labeled ``a``, ``b``, ``c``, + and ``d``:: + + from datafusion import col, lit + df = df.with_columns( + col("x").alias("a"), + [lit(1).alias("b"), col("y").alias("c")], + d=lit(3) + ) + + Equivalent example using just SQL strings: + + df = df.with_columns( + "x as a", + ["1 as b", "y as c"], + d="3" + ) + + :param exprs: Either a single expression, an iterable of expressions to add or + SQL expression strings. + :param named_exprs: Named expressions in the form of ``name=expr`` + + :returns: DataFrame with the new columns added. + + + + .. py:method:: write_csv(path: str | pathlib.Path, with_header: bool = False, write_options: DataFrameWriteOptions | None = None) -> None + + Execute the :py:class:`DataFrame` and write the results to a CSV file. + + :param path: Path of the CSV file to write. + :param with_header: If true, output the CSV header row. + :param write_options: Options that impact how the DataFrame is written. + + + + .. py:method:: write_json(path: str | pathlib.Path, write_options: DataFrameWriteOptions | None = None) -> None + + Execute the :py:class:`DataFrame` and write the results to a JSON file. + + :param path: Path of the JSON file to write. + :param write_options: Options that impact how the DataFrame is written. + + + + .. py:method:: write_parquet(path: str | pathlib.Path, compression: str, compression_level: int | None = None, write_options: DataFrameWriteOptions | None = None) -> None + write_parquet(path: str | pathlib.Path, compression: Compression = Compression.ZSTD, compression_level: int | None = None, write_options: DataFrameWriteOptions | None = None) -> None + write_parquet(path: str | pathlib.Path, compression: ParquetWriterOptions, compression_level: None = None, write_options: DataFrameWriteOptions | None = None) -> None + + Execute the :py:class:`DataFrame` and write the results to a Parquet file. + + Available compression types are: + + - "uncompressed": No compression. + - "snappy": Snappy compression. + - "gzip": Gzip compression. + - "brotli": Brotli compression. + - "lz4": LZ4 compression. + - "lz4_raw": LZ4_RAW compression. + - "zstd": Zstandard compression. + + LZO compression is not yet implemented in arrow-rs and is therefore + excluded. + + :param path: Path of the Parquet file to write. + :param compression: Compression type to use. Default is "ZSTD". + :param compression_level: Compression level to use. For ZSTD, the + recommended range is 1 to 22, with the default being 4. Higher levels + provide better compression but slower speed. + :param write_options: Options that impact how the DataFrame is written. + + + + .. py:method:: write_parquet_with_options(path: str | pathlib.Path, options: ParquetWriterOptions, write_options: DataFrameWriteOptions | None = None) -> None + + Execute the :py:class:`DataFrame` and write the results to a Parquet file. + + Allows advanced writer options to be set with `ParquetWriterOptions`. + + :param path: Path of the Parquet file to write. + :param options: Sets the writer parquet options (see `ParquetWriterOptions`). + :param write_options: Options that impact how the DataFrame is written. + + + + .. py:method:: write_table(table_name: str, write_options: DataFrameWriteOptions | None = None) -> None + + Execute the :py:class:`DataFrame` and write the results to a table. + + The table must be registered with the session to perform this operation. + Not all table providers support writing operations. See the individual + implementations for details. + + + + .. py:attribute:: df + + +.. py:class:: DataFrameWriteOptions(insert_operation: InsertOp | None = None, single_file_output: bool = False, partition_by: str | collections.abc.Sequence[str] | None = None, sort_by: datafusion.expr.Expr | datafusion.expr.SortExpr | collections.abc.Sequence[datafusion.expr.Expr] | collections.abc.Sequence[datafusion.expr.SortExpr] | None = None) + + Writer options for DataFrame. + + There is no guarantee the table provider supports all writer options. + See the individual implementation and documentation for details. + + Instantiate writer options for DataFrame. + + + .. py:attribute:: _raw_write_options + + +.. py:class:: ExplainFormat + + Bases: :py:obj:`enum.Enum` + + + Output format for explain plans. + + Controls how the query plan is rendered in :py:meth:`DataFrame.explain`. + + + .. py:attribute:: GRAPHVIZ + :value: 'graphviz' + + + Graphviz DOT format for graph rendering. + + + .. py:attribute:: INDENT + :value: 'indent' + + + Default indented text format. + + + .. py:attribute:: PGJSON + :value: 'pgjson' + + + PostgreSQL-compatible JSON format for use with visualization tools. + + + .. py:attribute:: TREE + :value: 'tree' + + + Tree-style visual format with box-drawing characters. + + +.. py:class:: InsertOp + + Bases: :py:obj:`enum.Enum` + + + Insert operation mode. + + These modes are used by the table writing feature to define how record + batches should be written to a table. + + + .. py:attribute:: APPEND + + Appends new rows to the existing table without modifying any existing rows. + + + .. py:attribute:: OVERWRITE + + Overwrites all existing rows in the table with the new rows. + + + .. py:attribute:: REPLACE + + Replace existing rows that collide with the inserted rows. + + Replacement is typically based on a unique key or primary key. + + +.. py:class:: ParquetColumnOptions(encoding: str | None = None, dictionary_enabled: bool | None = None, compression: str | None = None, statistics_enabled: str | None = None, bloom_filter_enabled: bool | None = None, bloom_filter_fpp: float | None = None, bloom_filter_ndv: int | None = None) + + Parquet options for individual columns. + + Contains the available options that can be applied for an individual Parquet column, + replacing the global options in ``ParquetWriterOptions``. + + Initialize the ParquetColumnOptions. + + :param encoding: Sets encoding for the column path. Valid values are: ``plain``, + ``plain_dictionary``, ``rle``, ``bit_packed``, ``delta_binary_packed``, + ``delta_length_byte_array``, ``delta_byte_array``, ``rle_dictionary``, + and ``byte_stream_split``. These values are not case-sensitive. If + ``None``, uses the default parquet options + :param dictionary_enabled: Sets if dictionary encoding is enabled for the column + path. If `None`, uses the default parquet options + :param compression: Sets default parquet compression codec for the column path. + Valid values are ``uncompressed``, ``snappy``, ``gzip(level)``, ``lzo``, + ``brotli(level)``, ``lz4``, ``zstd(level)``, and ``lz4_raw``. These + values are not case-sensitive. If ``None``, uses the default parquet + options. + :param statistics_enabled: Sets if statistics are enabled for the column Valid + values are: ``none``, ``chunk``, and ``page`` These values are not case + sensitive. If ``None``, uses the default parquet options. + :param bloom_filter_enabled: Sets if bloom filter is enabled for the column path. + If ``None``, uses the default parquet options. + :param bloom_filter_fpp: Sets bloom filter false positive probability for the + column path. If ``None``, uses the default parquet options. + :param bloom_filter_ndv: Sets bloom filter number of distinct values. If ``None``, + uses the default parquet options. + + + .. py:attribute:: bloom_filter_enabled + :value: None + + + + .. py:attribute:: bloom_filter_fpp + :value: None + + + + .. py:attribute:: bloom_filter_ndv + :value: None + + + + .. py:attribute:: compression + :value: None + + + + .. py:attribute:: dictionary_enabled + :value: None + + + + .. py:attribute:: encoding + :value: None + + + + .. py:attribute:: statistics_enabled + :value: None + + + +.. py:class:: ParquetWriterOptions(data_pagesize_limit: int = 1024 * 1024, write_batch_size: int = 1024, writer_version: str = '1.0', skip_arrow_metadata: bool = False, compression: str | None = 'zstd(3)', compression_level: int | None = None, dictionary_enabled: bool | None = True, dictionary_page_size_limit: int = 1024 * 1024, statistics_enabled: str | None = 'page', max_row_group_size: int = 1024 * 1024, created_by: str = 'datafusion-python', column_index_truncate_length: int | None = 64, statistics_truncate_length: int | None = None, data_page_row_count_limit: int = 20000, encoding: str | None = None, bloom_filter_on_write: bool = False, bloom_filter_fpp: float | None = None, bloom_filter_ndv: int | None = None, allow_single_file_parallelism: bool = True, maximum_parallel_row_group_writers: int = 1, maximum_buffered_record_batches_per_stream: int = 2, column_specific_options: dict[str, ParquetColumnOptions] | None = None) + + Advanced parquet writer options. + + Allows settings the writer options that apply to the entire file. Some options can + also be set on a column by column basis, with the field ``column_specific_options`` + (see ``ParquetColumnOptions``). + + Initialize the ParquetWriterOptions. + + :param data_pagesize_limit: Sets best effort maximum size of data page in bytes. + :param write_batch_size: Sets write_batch_size in bytes. + :param writer_version: Sets parquet writer version. Valid values are ``1.0`` and + ``2.0``. + :param skip_arrow_metadata: Skip encoding the embedded arrow metadata in the + KV_meta. + :param compression: Compression type to use. Default is ``zstd(3)``. + Available compression types are + + - ``uncompressed``: No compression. + - ``snappy``: Snappy compression. + - ``gzip(n)``: Gzip compression with level n. + - ``brotli(n)``: Brotli compression with level n. + - ``lz4``: LZ4 compression. + - ``lz4_raw``: LZ4_RAW compression. + - ``zstd(n)``: Zstandard compression with level n. + :param compression_level: Compression level to set. + :param dictionary_enabled: Sets if dictionary encoding is enabled. If ``None``, + uses the default parquet writer setting. + :param dictionary_page_size_limit: Sets best effort maximum dictionary page size, + in bytes. + :param statistics_enabled: Sets if statistics are enabled for any column Valid + values are ``none``, ``chunk``, and ``page``. If ``None``, uses the + default parquet writer setting. + :param max_row_group_size: Target maximum number of rows in each row group + (defaults to 1M rows). Writing larger row groups requires more memory + to write, but can get better compression and be faster to read. + :param created_by: Sets "created by" property. + :param column_index_truncate_length: Sets column index truncate length. + :param statistics_truncate_length: Sets statistics truncate length. If ``None``, + uses the default parquet writer setting. + :param data_page_row_count_limit: Sets best effort maximum number of rows in a data + page. + :param encoding: Sets default encoding for any column. Valid values are ``plain``, + ``plain_dictionary``, ``rle``, ``bit_packed``, ``delta_binary_packed``, + ``delta_length_byte_array``, ``delta_byte_array``, ``rle_dictionary``, + and ``byte_stream_split``. If ``None``, uses the default parquet writer + setting. + :param bloom_filter_on_write: Write bloom filters for all columns when creating + parquet files. + :param bloom_filter_fpp: Sets bloom filter false positive probability. If ``None``, + uses the default parquet writer setting + :param bloom_filter_ndv: Sets bloom filter number of distinct values. If ``None``, + uses the default parquet writer setting. + :param allow_single_file_parallelism: Controls whether DataFusion will attempt to + speed up writing parquet files by serializing them in parallel. Each + column in each row group in each output file are serialized in parallel + leveraging a maximum possible core count of + ``n_files * n_row_groups * n_columns``. + :param maximum_parallel_row_group_writers: By default parallel parquet writer is + tuned for minimum memory usage in a streaming execution plan. You may + see a performance benefit when writing large parquet files by increasing + ``maximum_parallel_row_group_writers`` and + ``maximum_buffered_record_batches_per_stream`` if your system has idle + cores and can tolerate additional memory usage. Boosting these values is + likely worthwhile when writing out already in-memory data, such as from + a cached data frame. + :param maximum_buffered_record_batches_per_stream: See + ``maximum_parallel_row_group_writers``. + :param column_specific_options: Overrides options for specific columns. If a column + is not a part of this dictionary, it will use the parameters provided + here. + + + .. py:attribute:: allow_single_file_parallelism + :value: True + + + + .. py:attribute:: bloom_filter_fpp + :value: None + + + + .. py:attribute:: bloom_filter_ndv + :value: None + + + + .. py:attribute:: bloom_filter_on_write + :value: False + + + + .. py:attribute:: column_index_truncate_length + :value: 64 + + + + .. py:attribute:: column_specific_options + :value: None + + + + .. py:attribute:: created_by + :value: 'datafusion-python' + + + + .. py:attribute:: data_page_row_count_limit + :value: 20000 + + + + .. py:attribute:: data_pagesize_limit + :value: 1048576 + + + + .. py:attribute:: dictionary_enabled + :value: True + + + + .. py:attribute:: dictionary_page_size_limit + :value: 1048576 + + + + .. py:attribute:: encoding + :value: None + + + + .. py:attribute:: max_row_group_size + :value: 1048576 + + + + .. py:attribute:: maximum_buffered_record_batches_per_stream + :value: 2 + + + + .. py:attribute:: maximum_parallel_row_group_writers + :value: 1 + + + + .. py:attribute:: skip_arrow_metadata + :value: False + + + + .. py:attribute:: statistics_enabled + :value: 'page' + + + + .. py:attribute:: statistics_truncate_length + :value: None + + + + .. py:attribute:: write_batch_size + :value: 1024 + + + + .. py:attribute:: writer_version + :value: '1.0' + + + diff --git a/_sources/autoapi/datafusion/dataframe_formatter/index.rst.txt b/_sources/autoapi/datafusion/dataframe_formatter/index.rst.txt new file mode 100644 index 000000000..7d0f406a8 --- /dev/null +++ b/_sources/autoapi/datafusion/dataframe_formatter/index.rst.txt @@ -0,0 +1,530 @@ +datafusion.dataframe_formatter +============================== + +.. py:module:: datafusion.dataframe_formatter + +.. autoapi-nested-parse:: + + HTML formatting utilities for DataFusion DataFrames. + + + +Classes +------- + +.. autoapisummary:: + + datafusion.dataframe_formatter.CellFormatter + datafusion.dataframe_formatter.DataFrameHtmlFormatter + datafusion.dataframe_formatter.DefaultStyleProvider + datafusion.dataframe_formatter.FormatterManager + datafusion.dataframe_formatter.StyleProvider + + +Functions +--------- + +.. autoapisummary:: + + datafusion.dataframe_formatter._refresh_formatter_reference + datafusion.dataframe_formatter._validate_bool + datafusion.dataframe_formatter._validate_formatter_parameters + datafusion.dataframe_formatter._validate_positive_int + datafusion.dataframe_formatter.configure_formatter + datafusion.dataframe_formatter.get_formatter + datafusion.dataframe_formatter.reset_formatter + datafusion.dataframe_formatter.set_formatter + + +Module Contents +--------------- + +.. py:class:: CellFormatter + + Bases: :py:obj:`Protocol` + + + Protocol for cell value formatters. + + + .. py:method:: __call__(value: Any) -> str + + Format a cell value to string representation. + + + +.. py:class:: DataFrameHtmlFormatter(max_cell_length: int = 25, max_width: int = 1000, max_height: int = 300, max_memory_bytes: int = 2 * 1024 * 1024, min_rows: int = 10, max_rows: int | None = None, repr_rows: int | None = None, enable_cell_expansion: bool = True, custom_css: str | None = None, show_truncation_message: bool = True, style_provider: StyleProvider | None = None, use_shared_styles: bool = True) + + Configurable HTML formatter for DataFusion DataFrames. + + This class handles the HTML rendering of DataFrames for display in + Jupyter notebooks and other rich display contexts. + + This class supports extension through composition. Key extension points: + - Provide a custom StyleProvider for styling cells and headers + - Register custom formatters for specific types + - Provide custom cell builders for specialized cell rendering + + :param max_cell_length: Maximum characters to display in a cell before truncation + :param max_width: Maximum width of the HTML table in pixels + :param max_height: Maximum height of the HTML table in pixels + :param max_memory_bytes: Maximum memory in bytes for rendered data (default: 2MB) + :param min_rows: Minimum number of rows to display (must be <= max_rows) + :param max_rows: Maximum number of rows to display in repr output + :param repr_rows: Deprecated alias for max_rows + :param enable_cell_expansion: Whether to add expand/collapse buttons for long cell + values + :param custom_css: Additional CSS to include in the HTML output + :param show_truncation_message: Whether to display a message when data is truncated + :param style_provider: Custom provider for cell and header styles + :param use_shared_styles: Whether to load styles and scripts only once per notebook + session + + Initialize the HTML formatter. + + :param max_cell_length: Maximum length of cell content before truncation. + :param max_width: Maximum width of the displayed table in pixels. + :param max_height: Maximum height of the displayed table in pixels. + :param max_memory_bytes: Maximum memory in bytes for rendered data. Helps prevent performance + issues with large datasets. + :param min_rows: Minimum number of rows to display even if memory limit is reached. + Must not exceed ``max_rows``. + :param max_rows: Maximum number of rows to display. Takes precedence over memory limits + when fewer rows are requested. + :param repr_rows: Deprecated alias for ``max_rows``. Use ``max_rows`` instead. + :param enable_cell_expansion: Whether to allow cells to expand when clicked. + :param custom_css: Custom CSS to apply to the HTML table. + :param show_truncation_message: Whether to show a message indicating that content has been truncated. + :param style_provider: Provider of CSS styles for the HTML table. If None, DefaultStyleProvider + is used. + :param use_shared_styles: Whether to use shared styles across multiple tables. This improves + performance when displaying many DataFrames in a single notebook. + :param Raises: + :param ------: + :param ValueError: If max_cell_length, max_width, max_height, max_memory_bytes, + min_rows or max_rows is not a positive integer, or if min_rows + exceeds max_rows. + :param TypeError: If enable_cell_expansion, show_truncation_message, or use_shared_styles is + not a boolean, or if custom_css is provided but is not a string, or if + style_provider is provided but does not implement the StyleProvider + protocol. + + + .. py:method:: _build_expandable_cell(formatted_value: str, row_count: int, col_idx: int, table_uuid: str) -> str + + Build an expandable cell for long content. + + + + .. py:method:: _build_html_footer(has_more: bool) -> list[str] + + Build the HTML footer with JavaScript and messages. + + + + .. py:method:: _build_html_header() -> list[str] + + Build the HTML header with CSS styles. + + + + .. py:method:: _build_regular_cell(formatted_value: str) -> str + + Build a regular table cell. + + + + .. py:method:: _build_table_body(batches: list, table_uuid: str) -> list[str] + + Build the HTML table body with data rows. + + + + .. py:method:: _build_table_container_start() -> list[str] + + Build the opening tags for the table container. + + + + .. py:method:: _build_table_header(schema: Any) -> list[str] + + Build the HTML table header with column names. + + + + .. py:method:: _format_cell_value(value: Any) -> str + + Format a cell value for display. + + Uses registered type formatters if available. + + :param value: The cell value to format + + :returns: Formatted cell value as string + + + + .. py:method:: _get_cell_value(column: Any, row_idx: int) -> Any + + Extract a cell value from a column. + + :param column: Arrow array + :param row_idx: Row index + + :returns: The raw cell value + + + + .. py:method:: _get_default_css() -> str + + Get default CSS styles for the HTML table. + + + + .. py:method:: _get_javascript() -> str + + Get JavaScript code for interactive elements. + + + + .. py:method:: format_html(batches: list, schema: Any, has_more: bool = False, table_uuid: str | None = None) -> str + + Format record batches as HTML. + + This method is used by DataFrame's _repr_html_ implementation and can be + called directly when custom HTML rendering is needed. + + :param batches: List of Arrow RecordBatch objects + :param schema: Arrow Schema object + :param has_more: Whether there are more batches not shown + :param table_uuid: Unique ID for the table, used for JavaScript interactions + + :returns: HTML string representation of the data + + :raises TypeError: If schema is invalid and no batches are provided + + + + .. py:method:: format_str(batches: list, schema: Any, has_more: bool = False, table_uuid: str | None = None) -> str + + Format record batches as a string. + + This method is used by DataFrame's __repr__ implementation and can be + called directly when string rendering is needed. + + :param batches: List of Arrow RecordBatch objects + :param schema: Arrow Schema object + :param has_more: Whether there are more batches not shown + :param table_uuid: Unique ID for the table, used for JavaScript interactions + + :returns: String representation of the data + + :raises TypeError: If schema is invalid and no batches are provided + + + + .. py:method:: register_formatter(type_class: type, formatter: CellFormatter) -> None + + Register a custom formatter for a specific data type. + + :param type_class: The type to register a formatter for + :param formatter: Function that takes a value of the given type and returns + a formatted string + + + + .. py:method:: set_custom_cell_builder(builder: collections.abc.Callable[[Any, int, int, str], str]) -> None + + Set a custom cell builder function. + + :param builder: Function that takes (value, row, col, table_id) and returns HTML + + + + .. py:method:: set_custom_header_builder(builder: collections.abc.Callable[[Any], str]) -> None + + Set a custom header builder function. + + :param builder: Function that takes a field and returns HTML + + + + .. py:attribute:: _custom_cell_builder + :type: collections.abc.Callable[[Any, int, int, str], str] | None + :value: None + + + + .. py:attribute:: _custom_header_builder + :type: collections.abc.Callable[[Any], str] | None + :value: None + + + + .. py:attribute:: _max_rows + :value: None + + + + .. py:attribute:: _type_formatters + :type: dict[type, CellFormatter] + + + .. py:attribute:: custom_css + :value: None + + + + .. py:attribute:: enable_cell_expansion + :value: True + + + + .. py:attribute:: max_cell_length + :value: 25 + + + + .. py:attribute:: max_height + :value: 300 + + + + .. py:attribute:: max_memory_bytes + :value: 2097152 + + + + .. py:property:: max_rows + :type: int + + + Get the maximum number of rows to display. + + :returns: The maximum number of rows to display in repr output + + + .. py:attribute:: max_width + :value: 1000 + + + + .. py:attribute:: min_rows + :value: 10 + + + + .. py:property:: repr_rows + :type: int + + + Get the maximum number of rows (deprecated name). + + .. deprecated:: + Use :attr:`max_rows` instead. This property is provided for + backward compatibility. + + :returns: The maximum number of rows to display + + + .. py:attribute:: show_truncation_message + :value: True + + + + .. py:attribute:: style_provider + + + .. py:attribute:: use_shared_styles + :value: True + + + +.. py:class:: DefaultStyleProvider + + Default implementation of StyleProvider. + + + .. py:method:: get_cell_style() -> str + + Get the CSS style for table cells. + + :returns: CSS style string + + + + .. py:method:: get_header_style() -> str + + Get the CSS style for header cells. + + :returns: CSS style string + + + +.. py:class:: FormatterManager + + Manager class for the global DataFrame HTML formatter instance. + + + .. py:method:: get_formatter() -> DataFrameHtmlFormatter + :classmethod: + + + Get the current global DataFrame HTML formatter. + + :returns: The global HTML formatter instance + + + + .. py:method:: set_formatter(formatter: DataFrameHtmlFormatter) -> None + :classmethod: + + + Set the global DataFrame HTML formatter. + + :param formatter: The formatter instance to use globally + + + + .. py:attribute:: _default_formatter + :type: DataFrameHtmlFormatter + + +.. py:class:: StyleProvider + + Bases: :py:obj:`Protocol` + + + Protocol for HTML style providers. + + + .. py:method:: get_cell_style() -> str + + Get the CSS style for table cells. + + + + .. py:method:: get_header_style() -> str + + Get the CSS style for header cells. + + + +.. py:function:: _refresh_formatter_reference() -> None + + Refresh formatter reference in any modules using it. + + This helps ensure that changes to the formatter are reflected in existing + DataFrames that might be caching the formatter reference. + + +.. py:function:: _validate_bool(value: Any, param_name: str) -> None + + Validate that a parameter is a boolean. + + :param value: The value to validate + :param param_name: Name of the parameter (used in error message) + + :raises TypeError: If the value is not a boolean + + +.. py:function:: _validate_formatter_parameters(max_cell_length: int, max_width: int, max_height: int, max_memory_bytes: int, min_rows: int, max_rows: int | None, repr_rows: int | None, enable_cell_expansion: bool, show_truncation_message: bool, use_shared_styles: bool, custom_css: str | None, style_provider: Any) -> int + + Validate all formatter parameters and return resolved max_rows value. + + :param max_cell_length: Maximum cell length value to validate + :param max_width: Maximum width value to validate + :param max_height: Maximum height value to validate + :param max_memory_bytes: Maximum memory bytes value to validate + :param min_rows: Minimum rows to display value to validate + :param max_rows: Maximum rows value to validate (None means use default) + :param repr_rows: Deprecated repr_rows value to validate + :param enable_cell_expansion: Boolean expansion flag to validate + :param show_truncation_message: Boolean message flag to validate + :param use_shared_styles: Boolean styles flag to validate + :param custom_css: Custom CSS string to validate + :param style_provider: Style provider object to validate + + :returns: The resolved max_rows value after handling repr_rows deprecation + + :raises ValueError: If any numeric parameter is invalid or constraints are violated + :raises TypeError: If any parameter has invalid type + :raises DeprecationWarning: If repr_rows parameter is used + + +.. py:function:: _validate_positive_int(value: Any, param_name: str) -> None + + Validate that a parameter is a positive integer. + + :param value: The value to validate + :param param_name: Name of the parameter (used in error message) + + :raises ValueError: If the value is not a positive integer + + +.. py:function:: configure_formatter(**kwargs: Any) -> None + + Configure the global DataFrame HTML formatter. + + This function creates a new formatter with the provided configuration + and sets it as the global formatter for all DataFrames. + + :param \*\*kwargs: Formatter configuration parameters like max_cell_length, + max_width, max_height, enable_cell_expansion, etc. + + :raises ValueError: If any invalid parameters are provided + + .. rubric:: Example + + >>> from datafusion.dataframe_formatter import configure_formatter + >>> configure_formatter( + ... max_cell_length=50, + ... max_height=500, + ... enable_cell_expansion=True, + ... use_shared_styles=True + ... ) + + +.. py:function:: get_formatter() -> DataFrameHtmlFormatter + + Get the current global DataFrame HTML formatter. + + This function is used by the DataFrame._repr_html_ implementation to access + the shared formatter instance. It can also be used directly when custom + HTML rendering is needed. + + :returns: The global HTML formatter instance + + .. rubric:: Example + + >>> from datafusion.dataframe_formatter import get_formatter + >>> formatter = get_formatter() + >>> formatter.max_cell_length = 50 # Increase cell length + + +.. py:function:: reset_formatter() -> None + + Reset the global DataFrame HTML formatter to default settings. + + This function creates a new formatter with default configuration + and sets it as the global formatter for all DataFrames. + + .. rubric:: Example + + >>> from datafusion.dataframe_formatter import reset_formatter + >>> reset_formatter() # Reset formatter to default settings + + +.. py:function:: set_formatter(formatter: DataFrameHtmlFormatter) -> None + + Set the global DataFrame HTML formatter. + + :param formatter: The formatter instance to use globally + + .. rubric:: Example + + >>> from datafusion.dataframe_formatter import get_formatter, set_formatter + >>> custom_formatter = DataFrameHtmlFormatter(max_cell_length=100) + >>> set_formatter(custom_formatter) + + diff --git a/_sources/autoapi/datafusion/expr/index.rst.txt b/_sources/autoapi/datafusion/expr/index.rst.txt new file mode 100644 index 000000000..6d938f6d2 --- /dev/null +++ b/_sources/autoapi/datafusion/expr/index.rst.txt @@ -0,0 +1,1760 @@ +datafusion.expr +=============== + +.. py:module:: datafusion.expr + +.. autoapi-nested-parse:: + + :py:class:`Expr` — the logical expression type used to build DataFusion queries. + + An :py:class:`Expr` represents a computation over columns or literals: a + column reference (``col("a")``), a literal (``lit(5)``), an operator + combination (``col("a") + lit(1)``), or the output of a function from + :py:mod:`datafusion.functions`. Expressions are passed to + :py:class:`~datafusion.dataframe.DataFrame` methods such as + :py:meth:`~datafusion.dataframe.DataFrame.select`, + :py:meth:`~datafusion.dataframe.DataFrame.filter`, + :py:meth:`~datafusion.dataframe.DataFrame.aggregate`, and + :py:meth:`~datafusion.dataframe.DataFrame.sort`. + + Convenience constructors are re-exported at the package level: + :py:func:`datafusion.col` / :py:func:`datafusion.column` for column references + and :py:func:`datafusion.lit` / :py:func:`datafusion.literal` for scalar + literals. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> df.select((col("a") * lit(10)).alias("ten_a")).to_pydict() + {'ten_a': [10, 20, 30]} + + See :ref:`expressions` in the online documentation for details on available + operators and helpers. + + + +Attributes +---------- + +.. autoapisummary:: + + datafusion.expr.Aggregate + datafusion.expr.AggregateFunction + datafusion.expr.Alias + datafusion.expr.Analyze + datafusion.expr.Between + datafusion.expr.BinaryExpr + datafusion.expr.Case + datafusion.expr.Cast + datafusion.expr.Column + datafusion.expr.CopyTo + datafusion.expr.CreateCatalog + datafusion.expr.CreateCatalogSchema + datafusion.expr.CreateExternalTable + datafusion.expr.CreateFunction + datafusion.expr.CreateFunctionBody + datafusion.expr.CreateIndex + datafusion.expr.CreateMemoryTable + datafusion.expr.CreateView + datafusion.expr.Deallocate + datafusion.expr.DescribeTable + datafusion.expr.Distinct + datafusion.expr.DmlStatement + datafusion.expr.DropCatalogSchema + datafusion.expr.DropFunction + datafusion.expr.DropTable + datafusion.expr.DropView + datafusion.expr.EXPR_TYPE_ERROR + datafusion.expr.EmptyRelation + datafusion.expr.Execute + datafusion.expr.Exists + datafusion.expr.Explain + datafusion.expr.Extension + datafusion.expr.FileType + datafusion.expr.Filter + datafusion.expr.HigherOrderFunction + datafusion.expr.ILike + datafusion.expr.InList + datafusion.expr.InSubquery + datafusion.expr.IsFalse + datafusion.expr.IsNotFalse + datafusion.expr.IsNotNull + datafusion.expr.IsNotTrue + datafusion.expr.IsNotUnknown + datafusion.expr.IsNull + datafusion.expr.IsTrue + datafusion.expr.IsUnknown + datafusion.expr.Join + datafusion.expr.JoinConstraint + datafusion.expr.JoinType + datafusion.expr.Lambda + datafusion.expr.LambdaVariable + datafusion.expr.Like + datafusion.expr.Limit + datafusion.expr.Literal + datafusion.expr.Negative + datafusion.expr.Not + datafusion.expr.OperateFunctionArg + datafusion.expr.Partitioning + datafusion.expr.Placeholder + datafusion.expr.Prepare + datafusion.expr.Projection + datafusion.expr.RecursiveQuery + datafusion.expr.Repartition + datafusion.expr.ScalarSubquery + datafusion.expr.ScalarVariable + datafusion.expr.SetVariable + datafusion.expr.SimilarTo + datafusion.expr.Sort + datafusion.expr.SortKey + datafusion.expr.Subquery + datafusion.expr.SubqueryAlias + datafusion.expr.TableScan + datafusion.expr.TransactionAccessMode + datafusion.expr.TransactionConclusion + datafusion.expr.TransactionEnd + datafusion.expr.TransactionIsolationLevel + datafusion.expr.TransactionStart + datafusion.expr.TryCast + datafusion.expr.Union + datafusion.expr.Unnest + datafusion.expr.UnnestExpr + datafusion.expr.Values + datafusion.expr.WindowExpr + + +Classes +------- + +.. autoapisummary:: + + datafusion.expr.CaseBuilder + datafusion.expr.Expr + datafusion.expr.GroupingSet + datafusion.expr.SortExpr + datafusion.expr.Window + datafusion.expr.WindowFrame + datafusion.expr.WindowFrameBound + + +Functions +--------- + +.. autoapisummary:: + + datafusion.expr.coerce_to_expr + datafusion.expr.coerce_to_expr_list + datafusion.expr.coerce_to_expr_or_none + datafusion.expr.ensure_expr + datafusion.expr.ensure_expr_list + + +Module Contents +--------------- + +.. py:class:: CaseBuilder(case_builder: datafusion._internal.expr.CaseBuilder) + + Builder class for constructing case statements. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.select( + ... dfn.functions.case(dfn.col("a")) + ... .when(dfn.lit(1), dfn.lit("One")) + ... .when(dfn.lit(2), dfn.lit("Two")) + ... .otherwise(dfn.lit("Other")) + ... .alias("label") + ... ) + >>> result.to_pydict() + {'label': ['One', 'Two', 'Other']} + + Constructs a case builder. + + This is not typically called by the end user directly. See + :py:func:`datafusion.functions.case` instead. + + + .. py:method:: end() -> Expr + + Finish building a case statement. + + Any non-matching cases will end in a `null` value. + + + + .. py:method:: otherwise(else_expr: Expr) -> Expr + + Set a default value for the case statement. + + + + .. py:method:: when(when_expr: Expr, then_expr: Expr) -> CaseBuilder + + Add a case to match against. + + + + .. py:attribute:: case_builder + + +.. py:class:: Expr(expr: datafusion._internal.expr.RawExpr) + + Expression object. + + Expressions are one of the core concepts in DataFusion. See + :ref:`Expressions` in the online documentation for more information. + + This constructor should not be called by the end user. + + + .. py:method:: __add__(rhs: Any) -> Expr + + Addition operator. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __and__(rhs: Expr) -> Expr + + Logical AND. + + + + .. py:method:: __eq__(rhs: object) -> Expr + + Equal to. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __ge__(rhs: Any) -> Expr + + Greater than or equal to. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __getitem__(key: str | int) -> Expr + + Retrieve sub-object. + + If ``key`` is a string, returns the subfield of the struct. + If ``key`` is an integer, retrieves the element in the array. Note that the + element index begins at ``0``, unlike + :py:func:`~datafusion.functions.array_element` which begins at ``1``. + If ``key`` is a slice, returns an array that contains a slice of the + original array. Similar to integer indexing, this follows Python convention + where the index begins at ``0`` unlike + :py:func:`~datafusion.functions.array_slice` which begins at ``1``. + + + + .. py:method:: __gt__(rhs: Any) -> Expr + + Greater than. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __invert__() -> Expr + + Binary not (~). + + + + .. py:method:: __le__(rhs: Any) -> Expr + + Less than or equal to. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __lt__(rhs: Any) -> Expr + + Less than. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __mod__(rhs: Any) -> Expr + + Modulo operator (%). + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __mul__(rhs: Any) -> Expr + + Multiplication operator. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __ne__(rhs: object) -> Expr + + Not equal to. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __or__(rhs: Expr) -> Expr + + Logical OR. + + + + .. py:method:: __reduce__() -> tuple[collections.abc.Callable[[bytes], Expr], tuple[bytes]] + + Pickle protocol hook. + + Lets expressions be shipped to worker processes via + :func:`pickle.dumps` / :func:`pickle.loads`. Built-in functions + and Python UDFs (scalar, aggregate, window) travel inside the + pickle bytes; only FFI-capsule UDFs require pre-registration on + the worker. The worker's :class:`SessionContext` for resolving + those references is looked up via + :func:`datafusion.ipc.set_worker_ctx`, falling back to the + global :class:`SessionContext` if none has been installed on + the worker. + + .. warning:: Security + :func:`pickle.loads` on the returned tuple executes + arbitrary Python on the receiver, including any + cloudpickled UDF callable embedded in the payload. Only + unpickle expressions from trusted sources. + + .. warning:: Portability + Sender and receiver must run the same Python + ``(major, minor)`` version; cloudpickle bytecode is not + portable across minor versions. See :meth:`to_bytes` for + details on what travels by value vs. by reference. + + .. rubric:: Examples + + >>> import pickle + >>> from datafusion import col, lit + >>> e = col("a") * lit(2) + >>> pickle.loads(pickle.dumps(e)).canonical_name() + 'a * Int64(2)' + + The encoding side honors a driver-side sender context installed + via :func:`datafusion.ipc.set_sender_ctx` — that is how + :meth:`SessionContext.with_python_udf_inlining` propagates + through ``pickle.dumps``. The sender context is read by + ``__reduce__``, so :func:`copy.copy` and :func:`copy.deepcopy` + — which also go through ``__reduce__`` — pick it up too. + + + + .. py:method:: __repr__() -> str + + Generate a string representation of this expression. + + + + .. py:method:: __richcmp__(other: Expr, op: int) -> Expr + + Comparison operator. + + + + .. py:method:: __sub__(rhs: Any) -> Expr + + Subtraction operator. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __truediv__(rhs: Any) -> Expr + + Division operator. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: _reconstruct(proto_bytes: bytes) -> Expr + :classmethod: + + + Internal entry point used by :meth:`__reduce__` on unpickle. + + .. rubric:: Examples + + >>> from datafusion import Expr, col, lit + >>> blob = (col("a") + lit(1)).to_bytes() + >>> Expr._reconstruct(blob).canonical_name() + 'a + Int64(1)' + + + + .. py:method:: abs() -> Expr + + Return the absolute value of a given number. + + Returns: + -------- + Expr + A new expression representing the absolute value of the input expression. + + + + .. py:method:: acos() -> Expr + + Returns the arc cosine or inverse cosine of a number. + + Returns: + -------- + Expr + A new expression representing the arc cosine of the input expression. + + + + .. py:method:: acosh() -> Expr + + Returns inverse hyperbolic cosine. + + + + .. py:method:: alias(name: str, metadata: dict[str, str] | None = None) -> Expr + + Assign a name to the expression. + + :param name: The name to assign to the expression. + :param metadata: Optional metadata to attach to the expression. + + :returns: A new expression with the assigned name. + + + + .. py:method:: array_dims() -> Expr + + Returns an array of the array's dimensions. + + + + .. py:method:: array_distinct() -> Expr + + Returns distinct values from the array after removing duplicates. + + + + .. py:method:: array_empty() -> Expr + + Returns a boolean indicating whether the array is empty. + + + + .. py:method:: array_length() -> Expr + + Returns the length of the array. + + + + .. py:method:: array_ndims() -> Expr + + Returns the number of dimensions of the array. + + + + .. py:method:: array_pop_back() -> Expr + + Returns the array without the last element. + + + + .. py:method:: array_pop_front() -> Expr + + Returns the array without the first element. + + + + .. py:method:: arrow_typeof() -> Expr + + Returns the Arrow type of the expression. + + + + .. py:method:: ascii() -> Expr + + Returns the numeric code of the first character of the argument. + + + + .. py:method:: asin() -> Expr + + Returns the arc sine or inverse sine of a number. + + + + .. py:method:: asinh() -> Expr + + Returns inverse hyperbolic sine. + + + + .. py:method:: atan() -> Expr + + Returns inverse tangent of a number. + + + + .. py:method:: atanh() -> Expr + + Returns inverse hyperbolic tangent. + + + + .. py:method:: between(low: Any, high: Any, negated: bool = False) -> Expr + + Returns ``True`` if this expression is between a given range. + + :param low: lower bound of the range (inclusive). + :param high: higher bound of the range (inclusive). + :param negated: negates whether the expression is between a given range + + + + .. py:method:: bit_length() -> Expr + + Returns the number of bits in the string argument. + + + + .. py:method:: btrim() -> Expr + + Removes all characters, spaces by default, from both sides of a string. + + + + .. py:method:: canonical_name() -> str + + Returns a complete string representation of this expression. + + + + .. py:method:: cardinality() -> Expr + + Returns the total number of elements in the array. + + + + .. py:method:: cast(to: pyarrow.DataType[Any] | type) -> Expr + + Cast to a new data type. + + + + .. py:method:: cbrt() -> Expr + + Returns the cube root of a number. + + + + .. py:method:: ceil() -> Expr + + Returns the nearest integer greater than or equal to argument. + + + + .. py:method:: char_length() -> Expr + + The number of characters in the ``string``. + + + + .. py:method:: character_length() -> Expr + + Returns the number of characters in the argument. + + + + .. py:method:: chr() -> Expr + + Converts the Unicode code point to a UTF8 character. + + + + .. py:method:: column(value: str) -> Expr + :staticmethod: + + + Creates a new expression representing a column. + + + + .. py:method:: column_name(plan: datafusion.plan.LogicalPlan) -> str + + Compute the output column name based on the provided logical plan. + + + + .. py:method:: cos() -> Expr + + Returns the cosine of the argument. + + + + .. py:method:: cosh() -> Expr + + Returns the hyperbolic cosine of the argument. + + + + .. py:method:: cot() -> Expr + + Returns the cotangent of the argument. + + + + .. py:method:: degrees() -> Expr + + Converts the argument from radians to degrees. + + + + .. py:method:: distinct() -> ExprFuncBuilder + + Only evaluate distinct values for an aggregate function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:method:: empty() -> Expr + + This is an alias for :py:func:`array_empty`. + + + + .. py:method:: exp() -> Expr + + Returns the exponential of the argument. + + + + .. py:method:: factorial() -> Expr + + Returns the factorial of the argument. + + + + .. py:method:: fill_nan(value: Any | Expr | None = None) -> Expr + + Fill NaN values with a provided value. + + + + .. py:method:: fill_null(value: Any | Expr | None = None) -> Expr + + Fill NULL values with a provided value. + + + + .. py:method:: filter(filter: Expr) -> ExprFuncBuilder + + Filter an aggregate function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:method:: flatten() -> Expr + + Flattens an array of arrays into a single array. + + + + .. py:method:: floor() -> Expr + + Returns the nearest integer less than or equal to the argument. + + + + .. py:method:: from_bytes(buf: bytes, ctx: datafusion.context.SessionContext | None = None) -> Expr + :classmethod: + + + Reconstruct an expression from serialized bytes. + + Accepts output of :meth:`to_bytes` or :func:`pickle.dumps`. + ``ctx`` is the :class:`SessionContext` used to resolve any + function references that travel by name (e.g. FFI UDFs, or + Python UDFs sent with inlining disabled via + :meth:`SessionContext.with_python_udf_inlining`). When + ``ctx`` is ``None`` the worker context installed via + :func:`datafusion.ipc.set_worker_ctx` is consulted; if no worker + context is installed, the global :class:`SessionContext` is used + (sufficient for built-ins and Python UDFs, plus any UDFs + registered on the global context). + + .. warning:: Security + Decoding may invoke ``cloudpickle.loads`` on bytes embedded + in the payload, which executes arbitrary Python code. Treat + ``buf`` as code, not data — only decode bytes you produced + yourself or received from a trusted sender. + + .. warning:: Portability + cloudpickle payloads are **not portable across Python + minor versions**. The wire format stamps the sender's + ``(major, minor)``; if it does not match the current + interpreter, this method raises :class:`ValueError` + naming both versions. Modules the UDF imports must also + be importable on the receiver — see :meth:`to_bytes` for + by-value vs. by-reference details. + + .. rubric:: Examples + + >>> from datafusion import Expr, col, lit + >>> blob = (col("a") + lit(1)).to_bytes() + >>> Expr.from_bytes(blob).canonical_name() + 'a + Int64(1)' + + + + .. py:method:: from_unixtime() -> Expr + + Converts an integer to RFC3339 timestamp format string. + + + + .. py:method:: initcap() -> Expr + + Set the initial letter of each word to capital. + + Converts the first letter of each word in ``string`` to uppercase and the + remaining characters to lowercase. + + + + .. py:method:: is_nan() -> Expr + + Returns true if a given number is +NaN or -NaN otherwise returns false. + + + + .. py:method:: is_not_null() -> Expr + + Returns ``True`` if this expression is not null. + + + + .. py:method:: is_null() -> Expr + + Returns ``True`` if this expression is null. + + + + .. py:method:: isnan() -> Expr + + Returns true if a given number is +NaN or -NaN otherwise returns false. + + + + .. py:method:: iszero() -> Expr + + Returns true if a given number is +0.0 or -0.0 otherwise returns false. + + + + .. py:method:: length() -> Expr + + The number of characters in the ``string``. + + + + .. py:method:: list_dims() -> Expr + + Returns an array of the array's dimensions. + + This is an alias for :py:func:`array_dims`. + + + + .. py:method:: list_distinct() -> Expr + + Returns distinct values from the array after removing duplicates. + + This is an alias for :py:func:`array_distinct`. + + + + .. py:method:: list_length() -> Expr + + Returns the length of the array. + + This is an alias for :py:func:`array_length`. + + + + .. py:method:: list_ndims() -> Expr + + Returns the number of dimensions of the array. + + This is an alias for :py:func:`array_ndims`. + + + + .. py:method:: literal(value: Any) -> Expr + :staticmethod: + + + Creates a new expression representing a scalar value. + + ``value`` must be a valid PyArrow scalar value or easily castable to one. + + + + .. py:method:: literal_with_metadata(value: Any, metadata: dict[str, str]) -> Expr + :staticmethod: + + + Creates a new expression representing a scalar value with metadata. + + :param value: A valid PyArrow scalar value or easily castable to one. + :param metadata: Metadata to attach to the expression. + + + + .. py:method:: ln() -> Expr + + Returns the natural logarithm (base e) of the argument. + + + + .. py:method:: log10() -> Expr + + Base 10 logarithm of the argument. + + + + .. py:method:: log2() -> Expr + + Base 2 logarithm of the argument. + + + + .. py:method:: lower() -> Expr + + Converts a string to lowercase. + + + + .. py:method:: ltrim() -> Expr + + Removes all characters, spaces by default, from the beginning of a string. + + + + .. py:method:: md5() -> Expr + + Computes an MD5 128-bit checksum for a string expression. + + + + .. py:method:: null_treatment(null_treatment: datafusion.common.NullTreatment) -> ExprFuncBuilder + + Set the treatment for ``null`` values for a window or aggregate function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:method:: octet_length() -> Expr + + Returns the number of bytes of a string. + + + + .. py:method:: order_by(*exprs: Expr | SortExpr) -> ExprFuncBuilder + + Set the ordering for a window or aggregate function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:method:: over(window: Window) -> Expr + + Turn an aggregate function into a window function. + + This function turns any aggregate function into a window function. With the + exception of ``partition_by``, how each of the parameters is used is determined + by the underlying aggregate function. + + :param window: Window definition + + + + .. py:method:: partition_by(*partition_by: Expr) -> ExprFuncBuilder + + Set the partitioning for a window function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:method:: python_value() -> Any + + Extracts the Expr value into `Any`. + + This is only valid for literal expressions. + + :returns: Python object representing literal value of the expression. + + + + .. py:method:: radians() -> Expr + + Converts the argument from degrees to radians. + + + + .. py:method:: reverse() -> Expr + + Reverse the string argument. + + + + .. py:method:: rex_call_operands() -> list[Expr] + + Return the operands of the expression based on it's variant type. + + Row expressions, Rex(s), operate on the concept of operands. Different + variants of Expressions, Expr(s), store those operands in different + datastructures. This function examines the Expr variant and returns + the operands to the calling logic. + + + + .. py:method:: rex_call_operator() -> str + + Extracts the operator associated with a row expression type call. + + + + .. py:method:: rex_type() -> datafusion.common.RexType + + Return the Rex Type of this expression. + + A Rex (Row Expression) specifies a single row of data.That specification + could include user defined functions or types. RexType identifies the + row as one of the possible valid ``RexType``. + + + + .. py:method:: rtrim() -> Expr + + Removes all characters, spaces by default, from the end of a string. + + + + .. py:method:: schema_name() -> str + + Returns the name of this expression as it should appear in a schema. + + This name will not include any CAST expressions. + + + + .. py:method:: sha224() -> Expr + + Computes the SHA-224 hash of a binary string. + + + + .. py:method:: sha256() -> Expr + + Computes the SHA-256 hash of a binary string. + + + + .. py:method:: sha384() -> Expr + + Computes the SHA-384 hash of a binary string. + + + + .. py:method:: sha512() -> Expr + + Computes the SHA-512 hash of a binary string. + + + + .. py:method:: signum() -> Expr + + Returns the sign of the argument (-1, 0, +1). + + + + .. py:method:: sin() -> Expr + + Returns the sine of the argument. + + + + .. py:method:: sinh() -> Expr + + Returns the hyperbolic sine of the argument. + + + + .. py:method:: sort(ascending: bool = True, nulls_first: bool = True) -> SortExpr + + Creates a sort :py:class:`Expr` from an existing :py:class:`Expr`. + + :param ascending: If true, sort in ascending order. + :param nulls_first: Return null values first. + + + + .. py:method:: sqrt() -> Expr + + Returns the square root of the argument. + + + + .. py:method:: string_literal(value: str) -> Expr + :staticmethod: + + + Creates a new expression representing a UTF8 literal value. + + It is different from `literal` because it is pa.string() instead of + pa.string_view() + + This is needed for cases where DataFusion is expecting a UTF8 instead of + UTF8View literal, like in: + https://github.com/apache/datafusion/blob/86740bfd3d9831d6b7c1d0e1bf4a21d91598a0ac/datafusion/functions/src/core/arrow_cast.rs#L179 + + + + .. py:method:: tan() -> Expr + + Returns the tangent of the argument. + + + + .. py:method:: tanh() -> Expr + + Returns the hyperbolic tangent of the argument. + + + + .. py:method:: to_bytes(ctx: datafusion.context.SessionContext | None = None) -> bytes + + Serialize this expression to bytes for shipping to another process. + + Use this — or :func:`pickle.dumps` — to send an expression to a + worker process for distributed evaluation. + + When ``ctx`` is supplied, encoding routes through that session's + installed :class:`LogicalExtensionCodec` (so settings like + :meth:`SessionContext.with_python_udf_inlining` take effect). + When ``ctx`` is ``None``, the default codec is used (Python UDF + inlining on, no user-installed extension codec). + + Built-in functions travel inside the returned bytes. Python UDFs + (scalar, aggregate, window) also inline by default, so the worker + does not need to pre-register them; when the encoding session has + :meth:`SessionContext.with_python_udf_inlining` set to ``False``, + Python UDFs travel by name only and must be registered on the + worker. UDFs imported via the FFI capsule protocol always travel + by name only and must be registered on the worker. + + .. warning:: Security + Bytes returned here may embed a cloudpickled Python + callable (when the expression carries a Python UDF). + Reconstructing them via :meth:`from_bytes` or + :func:`pickle.loads` executes arbitrary Python on the + receiver. Only accept payloads from trusted sources. + + .. warning:: Portability + cloudpickle serializes Python bytecode, which is **not + stable across Python minor versions**. A payload produced + on Python 3.11 will fail to load on Python 3.12. The + wire format stamps the sender's ``(major, minor)``; + :meth:`from_bytes` raises a :class:`ValueError` naming + both versions on mismatch. + + cloudpickle captures the UDF callable **by value** — + bytecode and closure cells inlined — but names the + callable resolves via ``import`` are captured **by + reference** (module path only) and must be importable on + the receiver. + + **Self-contained — works anywhere:** + + .. code-block:: python + + # Lambda: bytecode captured inline + udf(lambda x: x * 2, [pa.int64()], pa.int64(), + volatility="immutable") + + # Locally-defined function: bytecode captured inline + def double(x): + return x * 2 + udf(double, [pa.int64()], pa.int64(), volatility="immutable") + + # Closure over a local variable: value captured inline + factor = 3 + udf(lambda x: x * factor, [pa.int64()], pa.int64(), + volatility="immutable") + + **Requires matching environment on receiver:** + + .. code-block:: python + + # Top-level import: `foo` must be installed on receiver + from foo import double + udf(double, [pa.int64()], pa.int64(), volatility="immutable") + + # Bound method of an imported class: same caveat + from mylib import Transformer + t = Transformer() + udf(t.transform, [pa.int64()], pa.int64(), + volatility="immutable") + + .. rubric:: Examples + + >>> from datafusion import col, lit + >>> blob = (col("a") + lit(1)).to_bytes() + >>> isinstance(blob, bytes) + True + + + + .. py:method:: to_hex() -> Expr + + Converts an integer to a hexadecimal string. + + + + .. py:method:: to_variant() -> Any + + Convert this expression into a python object if possible. + + + + .. py:method:: trim() -> Expr + + Removes all characters, spaces by default, from both sides of a string. + + + + .. py:method:: try_cast(to: pyarrow.DataType[Any] | type) -> Expr + + Cast to a new data type, returning NULL on failure. + + Like :py:meth:`cast` but produces NULL instead of erroring when the + cast cannot be performed for a given row. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["oops"]}) + >>> result = df.select(col("a").try_cast(pa.float64()).alias("c")) + >>> result.collect_column("c")[0].as_py() is None + True + + + + .. py:method:: types() -> datafusion.common.DataTypeMap + + Return the ``DataTypeMap``. + + :returns: DataTypeMap which represents the PythonType, Arrow DataType, and + SqlType Enum which this expression represents. + + + + .. py:method:: upper() -> Expr + + Converts a string to uppercase. + + + + .. py:method:: variant_name() -> str + + Returns the name of the Expr variant. + + Ex: ``IsNotNull``, ``Literal``, ``BinaryExpr``, etc + + + + .. py:method:: window_frame(window_frame: WindowFrame) -> ExprFuncBuilder + + Set the frame fora window function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:attribute:: __radd__ + + + .. py:attribute:: __rand__ + + + .. py:attribute:: __rmod__ + + + .. py:attribute:: __rmul__ + + + .. py:attribute:: __ror__ + + + .. py:attribute:: __rsub__ + + + .. py:attribute:: __rtruediv__ + + + .. py:attribute:: _to_pyarrow_types + :type: ClassVar[dict[type, pyarrow.DataType]] + + + .. py:attribute:: expr + + +.. py:class:: GroupingSet + + Factory for creating grouping set expressions. + + Grouping sets control how + :py:meth:`~datafusion.dataframe.DataFrame.aggregate` groups rows. + Instead of a single ``GROUP BY``, they produce multiple grouping + levels in one pass — subtotals, cross-tabulations, or arbitrary + column subsets. + + Use :py:func:`~datafusion.functions.grouping` in the aggregate list + to tell which columns are aggregated across in each result row. + + + .. py:method:: cube(*exprs: Expr | str) -> Expr + :staticmethod: + + + Create a ``CUBE`` grouping set for use with ``aggregate()``. + + ``CUBE`` generates all possible subsets of the given column list + as grouping sets. For example, ``cube(a, b)`` produces grouping + sets ``(a, b)``, ``(a)``, ``(b)``, and ``()`` (grand total). + + This is equivalent to ``GROUP BY CUBE(a, b)`` in SQL. + + :param \*exprs: Column expressions or column name strings to + include in the cube. + + .. rubric:: Examples + + With a single column, ``cube`` behaves identically to + :py:meth:`rollup`: + + >>> from datafusion.expr import GroupingSet + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 1, 2], "b": [10, 20, 30]}) + >>> result = df.aggregate( + ... [GroupingSet.cube(dfn.col("a"))], + ... [dfn.functions.sum(dfn.col("b")).alias("s"), + ... dfn.functions.grouping(dfn.col("a"))], + ... ).sort(dfn.col("a").sort(nulls_first=False)) + >>> result.collect_column("s").to_pylist() + [30, 30, 60] + + .. seealso:: + + :py:meth:`rollup`, :py:meth:`grouping_sets`, + :py:func:`~datafusion.functions.grouping` + + + + .. py:method:: grouping_sets(*expr_lists: list[Expr | str]) -> Expr + :staticmethod: + + + Create explicit grouping sets for use with ``aggregate()``. + + Each argument is a list of column expressions or column name + strings representing one grouping set. For example, + ``grouping_sets([a], [b])`` groups by ``a`` alone and by ``b`` + alone in a single query. + + This is equivalent to ``GROUP BY GROUPING SETS ((a), (b))`` in + SQL. + + :param \*expr_lists: Each positional argument is a list of + expressions or column name strings forming one + grouping set. + + .. rubric:: Examples + + >>> from datafusion.expr import GroupingSet + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict( + ... {"a": ["x", "x", "y"], "b": ["m", "n", "m"], + ... "c": [1, 2, 3]}) + >>> result = df.aggregate( + ... [GroupingSet.grouping_sets( + ... [dfn.col("a")], [dfn.col("b")])], + ... [dfn.functions.sum(dfn.col("c")).alias("s"), + ... dfn.functions.grouping(dfn.col("a")), + ... dfn.functions.grouping(dfn.col("b"))], + ... ).sort( + ... dfn.col("a").sort(nulls_first=False), + ... dfn.col("b").sort(nulls_first=False), + ... ) + >>> result.collect_column("s").to_pylist() + [3, 3, 4, 2] + + .. seealso:: + + :py:meth:`rollup`, :py:meth:`cube`, + :py:func:`~datafusion.functions.grouping` + + + + .. py:method:: rollup(*exprs: Expr | str) -> Expr + :staticmethod: + + + Create a ``ROLLUP`` grouping set for use with ``aggregate()``. + + ``ROLLUP`` generates all prefixes of the given column list as + grouping sets. For example, ``rollup(a, b)`` produces grouping + sets ``(a, b)``, ``(a)``, and ``()`` (grand total). + + This is equivalent to ``GROUP BY ROLLUP(a, b)`` in SQL. + + :param \*exprs: Column expressions or column name strings to + include in the rollup. + + .. rubric:: Examples + + >>> from datafusion.expr import GroupingSet + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 1, 2], "b": [10, 20, 30]}) + >>> result = df.aggregate( + ... [GroupingSet.rollup(dfn.col("a"))], + ... [dfn.functions.sum(dfn.col("b")).alias("s"), + ... dfn.functions.grouping(dfn.col("a"))], + ... ).sort(dfn.col("a").sort(nulls_first=False)) + >>> result.collect_column("s").to_pylist() + [30, 30, 60] + + .. seealso:: + + :py:meth:`cube`, :py:meth:`grouping_sets`, + :py:func:`~datafusion.functions.grouping` + + + +.. py:class:: SortExpr(expr: Expr, ascending: bool, nulls_first: bool) + + Used to specify sorting on either a DataFrame or function. + + This constructor should not be called by the end user. + + + .. py:method:: __repr__() -> str + + Generate a string representation of this expression. + + + + .. py:method:: ascending() -> bool + + Return ascending property. + + + + .. py:method:: expr() -> Expr + + Return the raw expr backing the SortExpr. + + + + .. py:method:: nulls_first() -> bool + + Return nulls_first property. + + + + .. py:attribute:: raw_sort + + +.. py:class:: Window(partition_by: list[Expr] | Expr | None = None, window_frame: WindowFrame | None = None, order_by: list[SortExpr | Expr | str] | Expr | SortExpr | str | None = None, null_treatment: datafusion.common.NullTreatment | None = None) + + Define reusable window parameters. + + Construct a window definition. + + :param partition_by: Partitions for window operation + :param window_frame: Define the start and end bounds of the window frame + :param order_by: Set ordering + :param null_treatment: Indicate how nulls are to be treated + + + .. py:attribute:: _null_treatment + :value: None + + + + .. py:attribute:: _order_by + :value: None + + + + .. py:attribute:: _partition_by + :value: None + + + + .. py:attribute:: _window_frame + :value: None + + + +.. py:class:: WindowFrame(units: str, start_bound: Any | None, end_bound: Any | None) + + Defines a window frame for performing window operations. + + Construct a window frame using the given parameters. + + :param units: Should be one of ``rows``, ``range``, or ``groups``. + :param start_bound: Sets the preceding bound. Must be >= 0. If none, this + will be set to unbounded. If unit type is ``groups``, this + parameter must be set. + :param end_bound: Sets the following bound. Must be >= 0. If none, this + will be set to unbounded. If unit type is ``groups``, this + parameter must be set. + + + .. py:method:: __repr__() -> str + + Print a string representation of the window frame. + + + + .. py:method:: get_frame_units() -> str + + Returns the window frame units for the bounds. + + + + .. py:method:: get_lower_bound() -> WindowFrameBound + + Returns starting bound. + + + + .. py:method:: get_upper_bound() -> WindowFrameBound + + Returns end bound. + + + + .. py:attribute:: window_frame + + +.. py:class:: WindowFrameBound(frame_bound: datafusion._internal.expr.WindowFrameBound) + + Defines a single window frame bound. + + :py:class:`WindowFrame` typically requires a start and end bound. + + Constructs a window frame bound. + + + .. py:method:: get_offset() -> int | None + + Returns the offset of the window frame. + + + + .. py:method:: is_current_row() -> bool + + Returns if the frame bound is current row. + + + + .. py:method:: is_following() -> bool + + Returns if the frame bound is following. + + + + .. py:method:: is_preceding() -> bool + + Returns if the frame bound is preceding. + + + + .. py:method:: is_unbounded() -> bool + + Returns if the frame bound is unbounded. + + + + .. py:attribute:: frame_bound + + +.. py:function:: coerce_to_expr(value: Any) -> Expr + + Coerce a native Python value to an ``Expr`` literal, passing ``Expr`` through. + + This is the complement of :func:`ensure_expr`: where ``ensure_expr`` + *rejects* non-``Expr`` values, ``coerce_to_expr`` *wraps* them via + :meth:`Expr.literal` so that functions can accept native Python types + (``int``, ``float``, ``str``, ``bool``, etc.) alongside ``Expr``. + + :param value: An ``Expr`` instance (returned as-is) or a Python literal to wrap. + + :returns: An ``Expr`` representing the value. + + +.. py:function:: coerce_to_expr_list(values: collections.abc.Iterable[Any]) -> list[Expr] + + Coerce each item in an iterable to ``Expr`` via :func:`coerce_to_expr`. + + :param values: Iterable of ``Expr`` instances or Python literals to wrap. + + :returns: A list of ``Expr`` instances. + + +.. py:function:: coerce_to_expr_or_none(value: Any | None) -> Expr | None + + Coerce a value to ``Expr`` or pass ``None`` through unchanged. + + Same as :func:`coerce_to_expr` but accepts ``None`` for optional parameters. + + :param value: An ``Expr`` instance, a Python literal to wrap, or ``None``. + + :returns: An ``Expr`` representing the value, or ``None``. + + +.. py:function:: ensure_expr(value: Expr | Any) -> datafusion._internal.expr.Expr + + Return the internal expression from ``Expr`` or raise ``TypeError``. + + This helper rejects plain strings and other non-:class:`Expr` values so + higher level APIs consistently require explicit :func:`~datafusion.col` or + :func:`~datafusion.lit` expressions. + + .. seealso:: + + :func:`coerce_to_expr` — the opposite behavior: *wraps* non-``Expr`` + values as literals instead of rejecting them. + + :param value: Candidate expression or other object. + + :returns: The internal expression representation. + + :raises TypeError: If ``value`` is not an instance of :class:`Expr`. + + +.. py:function:: ensure_expr_list(exprs: collections.abc.Iterable[Expr | collections.abc.Iterable[Expr]]) -> list[datafusion._internal.expr.Expr] + + Flatten an iterable of expressions, validating each via ``ensure_expr``. + + :param exprs: Possibly nested iterable containing expressions. + + :returns: A flat list of raw expressions. + + :raises TypeError: If any item is not an instance of :class:`Expr`. + + +.. py:data:: Aggregate + +.. py:data:: AggregateFunction + +.. py:data:: Alias + +.. py:data:: Analyze + +.. py:data:: Between + +.. py:data:: BinaryExpr + +.. py:data:: Case + +.. py:data:: Cast + +.. py:data:: Column + +.. py:data:: CopyTo + +.. py:data:: CreateCatalog + +.. py:data:: CreateCatalogSchema + +.. py:data:: CreateExternalTable + +.. py:data:: CreateFunction + +.. py:data:: CreateFunctionBody + +.. py:data:: CreateIndex + +.. py:data:: CreateMemoryTable + +.. py:data:: CreateView + +.. py:data:: Deallocate + +.. py:data:: DescribeTable + +.. py:data:: Distinct + +.. py:data:: DmlStatement + +.. py:data:: DropCatalogSchema + +.. py:data:: DropFunction + +.. py:data:: DropTable + +.. py:data:: DropView + +.. py:data:: EXPR_TYPE_ERROR + :value: 'Use col()/column() or lit()/literal() to construct expressions' + + +.. py:data:: EmptyRelation + +.. py:data:: Execute + +.. py:data:: Exists + +.. py:data:: Explain + +.. py:data:: Extension + +.. py:data:: FileType + +.. py:data:: Filter + +.. py:data:: HigherOrderFunction + +.. py:data:: ILike + +.. py:data:: InList + +.. py:data:: InSubquery + +.. py:data:: IsFalse + +.. py:data:: IsNotFalse + +.. py:data:: IsNotNull + +.. py:data:: IsNotTrue + +.. py:data:: IsNotUnknown + +.. py:data:: IsNull + +.. py:data:: IsTrue + +.. py:data:: IsUnknown + +.. py:data:: Join + +.. py:data:: JoinConstraint + +.. py:data:: JoinType + +.. py:data:: Lambda + +.. py:data:: LambdaVariable + +.. py:data:: Like + +.. py:data:: Limit + +.. py:data:: Literal + +.. py:data:: Negative + +.. py:data:: Not + +.. py:data:: OperateFunctionArg + +.. py:data:: Partitioning + +.. py:data:: Placeholder + +.. py:data:: Prepare + +.. py:data:: Projection + +.. py:data:: RecursiveQuery + +.. py:data:: Repartition + +.. py:data:: ScalarSubquery + +.. py:data:: ScalarVariable + +.. py:data:: SetVariable + +.. py:data:: SimilarTo + +.. py:data:: Sort + +.. py:data:: SortKey + +.. py:data:: Subquery + +.. py:data:: SubqueryAlias + +.. py:data:: TableScan + +.. py:data:: TransactionAccessMode + +.. py:data:: TransactionConclusion + +.. py:data:: TransactionEnd + +.. py:data:: TransactionIsolationLevel + +.. py:data:: TransactionStart + +.. py:data:: TryCast + +.. py:data:: Union + +.. py:data:: Unnest + +.. py:data:: UnnestExpr + +.. py:data:: Values + +.. py:data:: WindowExpr + diff --git a/_sources/autoapi/datafusion/functions/index.rst.txt b/_sources/autoapi/datafusion/functions/index.rst.txt new file mode 100644 index 000000000..7fedb6564 --- /dev/null +++ b/_sources/autoapi/datafusion/functions/index.rst.txt @@ -0,0 +1,6222 @@ +datafusion.functions +==================== + +.. py:module:: datafusion.functions + +.. autoapi-nested-parse:: + + Scalar, aggregate, and window functions for :py:class:`~datafusion.expr.Expr`. + + Each function returns an :py:class:`~datafusion.expr.Expr` that can be combined + with other expressions and passed to + :py:class:`~datafusion.dataframe.DataFrame` methods such as + :py:meth:`~datafusion.dataframe.DataFrame.select`, + :py:meth:`~datafusion.dataframe.DataFrame.filter`, + :py:meth:`~datafusion.dataframe.DataFrame.aggregate`, and + :py:meth:`~datafusion.dataframe.DataFrame.window`. The module is conventionally + imported as ``F`` so calls read like ``F.sum(col("price"))``. + + .. rubric:: Examples + + >>> from datafusion import functions as F + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3, 4]}) + >>> df.aggregate([], [F.sum(col("a")).alias("total")]).to_pydict() + {'total': [10]} + + See :ref:`aggregation` and :ref:`window_functions` in the online documentation + for categorized catalogs of aggregate and window functions. + + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/datafusion/functions/spark/index + + +Attributes +---------- + +.. autoapisummary:: + + datafusion.functions.today + + +Functions +--------- + +.. autoapisummary:: + + datafusion.functions.abs + datafusion.functions.acos + datafusion.functions.acosh + datafusion.functions.alias + datafusion.functions.any_match + datafusion.functions.approx_distinct + datafusion.functions.approx_median + datafusion.functions.approx_percentile_cont + datafusion.functions.approx_percentile_cont_with_weight + datafusion.functions.array + datafusion.functions.array_agg + datafusion.functions.array_any_match + datafusion.functions.array_any_value + datafusion.functions.array_append + datafusion.functions.array_cat + datafusion.functions.array_compact + datafusion.functions.array_concat + datafusion.functions.array_contains + datafusion.functions.array_dims + datafusion.functions.array_distance + datafusion.functions.array_distinct + datafusion.functions.array_element + datafusion.functions.array_empty + datafusion.functions.array_except + datafusion.functions.array_extract + datafusion.functions.array_filter + datafusion.functions.array_has + datafusion.functions.array_has_all + datafusion.functions.array_has_any + datafusion.functions.array_indexof + datafusion.functions.array_intersect + datafusion.functions.array_join + datafusion.functions.array_length + datafusion.functions.array_max + datafusion.functions.array_min + datafusion.functions.array_ndims + datafusion.functions.array_normalize + datafusion.functions.array_pop_back + datafusion.functions.array_pop_front + datafusion.functions.array_position + datafusion.functions.array_positions + datafusion.functions.array_prepend + datafusion.functions.array_push_back + datafusion.functions.array_push_front + datafusion.functions.array_remove + datafusion.functions.array_remove_all + datafusion.functions.array_remove_n + datafusion.functions.array_repeat + datafusion.functions.array_replace + datafusion.functions.array_replace_all + datafusion.functions.array_replace_n + datafusion.functions.array_resize + datafusion.functions.array_reverse + datafusion.functions.array_slice + datafusion.functions.array_sort + datafusion.functions.array_to_string + datafusion.functions.array_transform + datafusion.functions.array_union + datafusion.functions.arrays_overlap + datafusion.functions.arrays_zip + datafusion.functions.arrow_cast + datafusion.functions.arrow_field + datafusion.functions.arrow_metadata + datafusion.functions.arrow_try_cast + datafusion.functions.arrow_typeof + datafusion.functions.ascii + datafusion.functions.asin + datafusion.functions.asinh + datafusion.functions.atan + datafusion.functions.atan2 + datafusion.functions.atanh + datafusion.functions.avg + datafusion.functions.bit_and + datafusion.functions.bit_length + datafusion.functions.bit_or + datafusion.functions.bit_xor + datafusion.functions.bool_and + datafusion.functions.bool_or + datafusion.functions.btrim + datafusion.functions.cardinality + datafusion.functions.case + datafusion.functions.cast_to_type + datafusion.functions.cbrt + datafusion.functions.ceil + datafusion.functions.char_length + datafusion.functions.character_length + datafusion.functions.chr + datafusion.functions.coalesce + datafusion.functions.col + datafusion.functions.concat + datafusion.functions.concat_ws + datafusion.functions.contains + datafusion.functions.corr + datafusion.functions.cos + datafusion.functions.cosh + datafusion.functions.cosine_distance + datafusion.functions.cot + datafusion.functions.count + datafusion.functions.count_star + datafusion.functions.covar + datafusion.functions.covar_pop + datafusion.functions.covar_samp + datafusion.functions.cume_dist + datafusion.functions.current_date + datafusion.functions.current_time + datafusion.functions.current_timestamp + datafusion.functions.date_bin + datafusion.functions.date_format + datafusion.functions.date_part + datafusion.functions.date_trunc + datafusion.functions.datepart + datafusion.functions.datetrunc + datafusion.functions.decode + datafusion.functions.degrees + datafusion.functions.dense_rank + datafusion.functions.digest + datafusion.functions.dot_product + datafusion.functions.element_at + datafusion.functions.empty + datafusion.functions.encode + datafusion.functions.ends_with + datafusion.functions.exp + datafusion.functions.extract + datafusion.functions.factorial + datafusion.functions.find_in_set + datafusion.functions.first_value + datafusion.functions.flatten + datafusion.functions.floor + datafusion.functions.from_unixtime + datafusion.functions.gcd + datafusion.functions.gen_series + datafusion.functions.generate_series + datafusion.functions.get_field + datafusion.functions.greatest + datafusion.functions.grouping + datafusion.functions.ifnull + datafusion.functions.in_list + datafusion.functions.initcap + datafusion.functions.inner_product + datafusion.functions.instr + datafusion.functions.is_nan + datafusion.functions.isnan + datafusion.functions.iszero + datafusion.functions.lag + datafusion.functions.lambda_ + datafusion.functions.lambda_var + datafusion.functions.last_value + datafusion.functions.lcm + datafusion.functions.lead + datafusion.functions.least + datafusion.functions.left + datafusion.functions.length + datafusion.functions.levenshtein + datafusion.functions.list_any_match + datafusion.functions.list_any_value + datafusion.functions.list_append + datafusion.functions.list_cat + datafusion.functions.list_compact + datafusion.functions.list_concat + datafusion.functions.list_contains + datafusion.functions.list_dims + datafusion.functions.list_distance + datafusion.functions.list_distinct + datafusion.functions.list_element + datafusion.functions.list_empty + datafusion.functions.list_except + datafusion.functions.list_extract + datafusion.functions.list_filter + datafusion.functions.list_has + datafusion.functions.list_has_all + datafusion.functions.list_has_any + datafusion.functions.list_indexof + datafusion.functions.list_intersect + datafusion.functions.list_join + datafusion.functions.list_length + datafusion.functions.list_max + datafusion.functions.list_min + datafusion.functions.list_ndims + datafusion.functions.list_normalize + datafusion.functions.list_overlap + datafusion.functions.list_pop_back + datafusion.functions.list_pop_front + datafusion.functions.list_position + datafusion.functions.list_positions + datafusion.functions.list_prepend + datafusion.functions.list_push_back + datafusion.functions.list_push_front + datafusion.functions.list_remove + datafusion.functions.list_remove_all + datafusion.functions.list_remove_n + datafusion.functions.list_repeat + datafusion.functions.list_replace + datafusion.functions.list_replace_all + datafusion.functions.list_replace_n + datafusion.functions.list_resize + datafusion.functions.list_reverse + datafusion.functions.list_slice + datafusion.functions.list_sort + datafusion.functions.list_to_string + datafusion.functions.list_transform + datafusion.functions.list_union + datafusion.functions.list_zip + datafusion.functions.ln + datafusion.functions.log + datafusion.functions.log10 + datafusion.functions.log2 + datafusion.functions.lower + datafusion.functions.lpad + datafusion.functions.ltrim + datafusion.functions.make_array + datafusion.functions.make_date + datafusion.functions.make_list + datafusion.functions.make_map + datafusion.functions.make_time + datafusion.functions.map_entries + datafusion.functions.map_extract + datafusion.functions.map_keys + datafusion.functions.map_values + datafusion.functions.max + datafusion.functions.md5 + datafusion.functions.mean + datafusion.functions.median + datafusion.functions.min + datafusion.functions.named_struct + datafusion.functions.nanvl + datafusion.functions.now + datafusion.functions.nth_value + datafusion.functions.ntile + datafusion.functions.nullif + datafusion.functions.nvl + datafusion.functions.nvl2 + datafusion.functions.octet_length + datafusion.functions.order_by + datafusion.functions.overlay + datafusion.functions.percent_rank + datafusion.functions.percentile_cont + datafusion.functions.pi + datafusion.functions.position + datafusion.functions.pow + datafusion.functions.power + datafusion.functions.quantile_cont + datafusion.functions.radians + datafusion.functions.random + datafusion.functions.range + datafusion.functions.rank + datafusion.functions.regexp_count + datafusion.functions.regexp_instr + datafusion.functions.regexp_like + datafusion.functions.regexp_match + datafusion.functions.regexp_replace + datafusion.functions.regr_avgx + datafusion.functions.regr_avgy + datafusion.functions.regr_count + datafusion.functions.regr_intercept + datafusion.functions.regr_r2 + datafusion.functions.regr_slope + datafusion.functions.regr_sxx + datafusion.functions.regr_sxy + datafusion.functions.regr_syy + datafusion.functions.repeat + datafusion.functions.replace + datafusion.functions.reverse + datafusion.functions.right + datafusion.functions.round + datafusion.functions.row + datafusion.functions.row_number + datafusion.functions.rpad + datafusion.functions.rtrim + datafusion.functions.sha224 + datafusion.functions.sha256 + datafusion.functions.sha384 + datafusion.functions.sha512 + datafusion.functions.signum + datafusion.functions.sin + datafusion.functions.sinh + datafusion.functions.split_part + datafusion.functions.sqrt + datafusion.functions.starts_with + datafusion.functions.stddev + datafusion.functions.stddev_pop + datafusion.functions.stddev_samp + datafusion.functions.string_agg + datafusion.functions.string_to_array + datafusion.functions.string_to_list + datafusion.functions.strpos + datafusion.functions.struct + datafusion.functions.substr + datafusion.functions.substr_index + datafusion.functions.substring + datafusion.functions.sum + datafusion.functions.tan + datafusion.functions.tanh + datafusion.functions.to_char + datafusion.functions.to_date + datafusion.functions.to_hex + datafusion.functions.to_local_time + datafusion.functions.to_time + datafusion.functions.to_timestamp + datafusion.functions.to_timestamp_micros + datafusion.functions.to_timestamp_millis + datafusion.functions.to_timestamp_nanos + datafusion.functions.to_timestamp_seconds + datafusion.functions.to_unixtime + datafusion.functions.translate + datafusion.functions.trim + datafusion.functions.trunc + datafusion.functions.try_cast_to_type + datafusion.functions.union_extract + datafusion.functions.union_tag + datafusion.functions.upper + datafusion.functions.uuid + datafusion.functions.var + datafusion.functions.var_pop + datafusion.functions.var_population + datafusion.functions.var_samp + datafusion.functions.var_sample + datafusion.functions.version + datafusion.functions.when + datafusion.functions.with_metadata + + +Package Contents +---------------- + +.. py:function:: abs(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Return the absolute value of a given number. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [-1, 0, 1]}) + >>> result = df.select(dfn.functions.abs(dfn.col("a")).alias("abs")) + >>> result.collect_column("abs")[0].as_py() + 1 + + +.. py:function:: acos(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the arc cosine or inverse cosine of a number. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0]}) + >>> result = df.select(dfn.functions.acos(dfn.col("a")).alias("acos")) + >>> result.collect_column("acos")[0].as_py() + 0.0 + + +.. py:function:: acosh(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns inverse hyperbolic cosine. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0]}) + >>> result = df.select(dfn.functions.acosh(dfn.col("a")).alias("acosh")) + >>> result.collect_column("acosh")[0].as_py() + 0.0 + + +.. py:function:: alias(expr: datafusion.expr.Expr, name: str, metadata: dict[str, str] | None = None) -> datafusion.expr.Expr + + Creates an alias expression with an optional metadata dictionary. + + :param expr: The expression to alias + :param name: The alias name + :param metadata: Optional metadata to attach to the column + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2]}) + >>> result = df.select( + ... dfn.functions.alias( + ... dfn.col("a"), "b" + ... ) + ... ) + >>> result.collect_column("b")[0].as_py() + 1 + + >>> result = df.select( + ... dfn.functions.alias( + ... dfn.col("a"), "b", metadata={"info": "test"} + ... ) + ... ) + >>> result.schema() + b: int64 + -- field metadata -- + info: 'test' + + +.. py:function:: any_match(array: datafusion.expr.Expr, predicate: datafusion.expr.Expr | collections.abc.Callable[Ellipsis, Any]) -> datafusion.expr.Expr + + Return ``True`` if any element of an array satisfies a predicate. + + .. seealso:: This is an alias for :py:func:`array_any_match`. + + +.. py:function:: approx_distinct(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Returns the approximate number of distinct values. + + This aggregate function is similar to :py:func:`count` with distinct set, but it + will approximate the number of distinct entries. It may return significantly faster + than :py:func:`count` for some DataFrames. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param expression: Values to check for distinct entries + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 1, 2, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.approx_distinct( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() == 3 + True + + >>> result = df.aggregate( + ... [], [dfn.functions.approx_distinct( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(1) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() == 2 + True + + +.. py:function:: approx_median(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Returns the approximate median value. + + This aggregate function is similar to :py:func:`median`, but it will only + approximate the median. It may return significantly faster for some DataFrames. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by`` and ``null_treatment``, and ``distinct``. + + :param expression: Values to find the median for + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.approx_median( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.approx_median( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.5 + + +.. py:function:: approx_percentile_cont(sort_expression: datafusion.expr.Expr | datafusion.expr.SortExpr, percentile: float, num_centroids: int | None = None, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Returns the value that is approximately at a given percentile of ``expr``. + + This aggregate function assumes the input values form a continuous distribution. + Suppose you have a DataFrame which consists of 100 different test scores. If you + called this function with a percentile of 0.9, it would return the value of the + test score that is above 90% of the other test scores. The returned value may be + between two of the values. + + This function uses the [t-digest](https://arxiv.org/abs/1902.04023) algorithm to + compute the percentile. You can limit the number of bins used in this algorithm by + setting the ``num_centroids`` parameter. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param sort_expression: Values for which to find the approximate percentile + :param percentile: This must be between 0.0 and 1.0, inclusive + :param num_centroids: Max bin size for the t-digest algorithm + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0, 4.0, 5.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.approx_percentile_cont( + ... dfn.col("a"), 0.5 + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 3.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.approx_percentile_cont( + ... dfn.col("a"), 0.5, + ... num_centroids=10, + ... filter=dfn.col("a") > dfn.lit(1.0), + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 3.5 + + +.. py:function:: approx_percentile_cont_with_weight(sort_expression: datafusion.expr.Expr | datafusion.expr.SortExpr, weight: datafusion.expr.Expr, percentile: float, num_centroids: int | None = None, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Returns the value of the weighted approximate percentile. + + This aggregate function is similar to :py:func:`approx_percentile_cont` except that + it uses the associated associated weights. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param sort_expression: Values for which to find the approximate percentile + :param weight: Relative weight for each of the values in ``expression`` + :param percentile: This must be between 0.0 and 1.0, inclusive + :param num_centroids: Max bin size for the t-digest algorithm + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0], "w": [1.0, 1.0, 1.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.approx_percentile_cont_with_weight( + ... dfn.col("a"), dfn.col("w"), 0.5 + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.approx_percentile_cont_with_weight( + ... dfn.col("a"), dfn.col("w"), 0.5, + ... num_centroids=10, + ... filter=dfn.col("a") > dfn.lit(1.0), + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.5 + + +.. py:function:: array(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns an array using the specified input expressions. + + .. seealso:: This is an alias for :py:func:`make_array`. + + +.. py:function:: array_agg(expression: datafusion.expr.Expr, distinct: bool = False, filter: datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None) -> datafusion.expr.Expr + + Aggregate values into an array. + + Currently ``distinct`` and ``order_by`` cannot be used together. As a work around, + consider :py:func:`array_sort` after aggregation. + [Issue Tracker](https://github.com/apache/datafusion/issues/12371) + + If using the builder functions described in ref:`_aggregation` this function ignores + the option ``null_treatment``. + + :param expression: Values to combine into an array + :param distinct: If True, a single entry for each distinct value will be in the result + :param filter: If provided, only compute against rows for which the filter is True + :param order_by: Order the resultant array values. Accepts column names or expressions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.array_agg( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + [1, 2, 3] + + >>> df = ctx.from_pydict({"a": [3, 1, 2, 1]}) + >>> result = df.aggregate( + ... [], [dfn.functions.array_agg( + ... dfn.col("a"), distinct=True, + ... ).alias("v")]) + >>> sorted(result.collect_column("v")[0].as_py()) + [1, 2, 3] + + >>> result = df.aggregate( + ... [], [dfn.functions.array_agg( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(1), + ... order_by="a", + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + [2, 3] + + +.. py:function:: array_any_match(array: datafusion.expr.Expr, predicate: datafusion.expr.Expr | collections.abc.Callable[Ellipsis, Any]) -> datafusion.expr.Expr + + Return ``True`` if any element of ``array`` satisfies ``predicate``. + + ``predicate`` may be a Python callable, converted to a lambda + automatically, or an explicit lambda built with :py:func:`lambda_`. It must + return a boolean expression. + + .. rubric:: Examples + + Using a Python callable: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> df.select( + ... F.array_any_match(col("a"), lambda v: v > 2).alias("m") + ... ).collect_column("m")[0].as_py() + True + + Using an explicit lambda built with :py:func:`lambda_`: + + >>> predicate = F.lambda_(["v"], F.lambda_var("v") > lit(2)) + >>> df.select( + ... F.array_any_match(col("a"), predicate).alias("m") + ... ).collect_column("m")[0].as_py() + True + + .. seealso:: :py:func:`array_transform`, :py:func:`lambda_`. + + +.. py:function:: array_any_value(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the first non-null element in the array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[None, 2, 3]]}) + >>> result = df.select( + ... dfn.functions.array_any_value(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + 2 + + +.. py:function:: array_append(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Appends an element to the end of an array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select( + ... dfn.functions.array_append(dfn.col("a"), dfn.lit(4)).alias("result")) + >>> result.collect_column("result")[0].as_py() + [1, 2, 3, 4] + + +.. py:function:: array_cat(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Concatenates the input arrays. + + .. seealso:: This is an alias for :py:func:`array_concat`. + + +.. py:function:: array_compact(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Removes NULL values from the array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, None, 2, None, 3]]}) + >>> result = df.select( + ... dfn.functions.array_compact(dfn.col("a")).alias("result") + ... ) + >>> result.collect_column("result")[0].as_py() + [1, 2, 3] + + +.. py:function:: array_concat(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Concatenates the input arrays. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2]], "b": [[3, 4]]}) + >>> result = df.select( + ... dfn.functions.array_concat(dfn.col("a"), dfn.col("b")).alias("result")) + >>> result.collect_column("result")[0].as_py() + [1, 2, 3, 4] + + +.. py:function:: array_contains(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns true if the element appears in the array, otherwise false. + + .. seealso:: This is an alias for :py:func:`array_has`. + + +.. py:function:: array_dims(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns an array of the array's dimensions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select(dfn.functions.array_dims(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + [3] + + +.. py:function:: array_distance(array1: datafusion.expr.Expr, array2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the Euclidean distance between two numeric arrays. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1.0, 2.0]], "b": [[1.0, 4.0]]}) + >>> result = df.select( + ... dfn.functions.array_distance( + ... dfn.col("a"), dfn.col("b"), + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + 2.0 + + +.. py:function:: array_distinct(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns distinct values from the array after removing duplicates. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 1, 2, 3]]}) + >>> result = df.select( + ... dfn.functions.array_distinct( + ... dfn.col("a") + ... ).alias("result") + ... ) + >>> sorted( + ... result.collect_column("result")[0].as_py() + ... ) + [1, 2, 3] + + +.. py:function:: array_element(array: datafusion.expr.Expr, n: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Extracts the element with the index n from the array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[10, 20, 30]]}) + >>> result = df.select( + ... dfn.functions.array_element(dfn.col("a"), 2).alias("result")) + >>> result.collect_column("result")[0].as_py() + 20 + + +.. py:function:: array_empty(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns a boolean indicating whether the array is empty. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2]]}) + >>> result = df.select(dfn.functions.array_empty(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + False + + +.. py:function:: array_except(array1: datafusion.expr.Expr, array2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the elements that appear in ``array1`` but not in ``array2``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]], "b": [[2, 3, 4]]}) + >>> result = df.select( + ... dfn.functions.array_except(dfn.col("a"), dfn.col("b")).alias("result")) + >>> result.collect_column("result")[0].as_py() + [1] + + +.. py:function:: array_extract(array: datafusion.expr.Expr, n: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Extracts the element with the index n from the array. + + .. seealso:: This is an alias for :py:func:`array_element`. + + +.. py:function:: array_filter(array: datafusion.expr.Expr, predicate: datafusion.expr.Expr | collections.abc.Callable[Ellipsis, Any]) -> datafusion.expr.Expr + + Keep the elements of ``array`` for which ``predicate`` is ``True``. + + ``predicate`` may be a Python callable, converted to a lambda + automatically, or an explicit lambda built with :py:func:`lambda_`. It must + return a boolean expression. The result is a new array containing only the + matching elements. + + .. rubric:: Examples + + Using a Python callable: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3, 4, 5]]}) + >>> df.select( + ... F.array_filter(col("a"), lambda v: v > 2).alias("f") + ... ).collect_column("f")[0].as_py() + [3, 4, 5] + + Using an explicit lambda built with :py:func:`lambda_`: + + >>> predicate = F.lambda_(["v"], F.lambda_var("v") > lit(2)) + >>> df.select( + ... F.array_filter(col("a"), predicate).alias("f") + ... ).collect_column("f")[0].as_py() + [3, 4, 5] + + .. seealso:: :py:func:`array_transform`, :py:func:`array_any_match`, :py:func:`lambda_`. + + +.. py:function:: array_has(first_array: datafusion.expr.Expr, second_array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns true if the element appears in the first array, otherwise false. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select( + ... dfn.functions.array_has(dfn.col("a"), dfn.lit(2)).alias("result")) + >>> result.collect_column("result")[0].as_py() + True + + +.. py:function:: array_has_all(first_array: datafusion.expr.Expr, second_array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Determines if there is complete overlap ``second_array`` in ``first_array``. + + Returns true if each element of the second array appears in the first array. + Otherwise, it returns false. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]], "b": [[1, 2]]}) + >>> result = df.select( + ... dfn.functions.array_has_all(dfn.col("a"), dfn.col("b")).alias("result")) + >>> result.collect_column("result")[0].as_py() + True + + +.. py:function:: array_has_any(first_array: datafusion.expr.Expr, second_array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Determine if there is an overlap between ``first_array`` and ``second_array``. + + Returns true if at least one element of the second array appears in the first + array. Otherwise, it returns false. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]], "b": [[2, 5]]}) + >>> result = df.select( + ... dfn.functions.array_has_any(dfn.col("a"), dfn.col("b")).alias("result")) + >>> result.collect_column("result")[0].as_py() + True + + +.. py:function:: array_indexof(array: datafusion.expr.Expr, element: datafusion.expr.Expr, index: int | None = 1) -> datafusion.expr.Expr + + Return the position of the first occurrence of ``element`` in ``array``. + + .. seealso:: This is an alias for :py:func:`array_position`. + + +.. py:function:: array_intersect(array1: datafusion.expr.Expr, array2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the intersection of ``array1`` and ``array2``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]], "b": [[2, 3, 4]]}) + >>> result = df.select( + ... dfn.functions.array_intersect( + ... dfn.col("a"), dfn.col("b") + ... ).alias("result") + ... ) + >>> sorted( + ... result.collect_column("result")[0].as_py() + ... ) + [2, 3] + + +.. py:function:: array_join(expr: datafusion.expr.Expr, delimiter: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts each element to its text representation. + + .. seealso:: This is an alias for :py:func:`array_to_string`. + + +.. py:function:: array_length(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the length of the array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select(dfn.functions.array_length(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + 3 + + +.. py:function:: array_max(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the maximum value in the array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select( + ... dfn.functions.array_max(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + 3 + + +.. py:function:: array_min(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the minimum value in the array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select( + ... dfn.functions.array_min(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + 1 + + +.. py:function:: array_ndims(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the number of dimensions of the array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select(dfn.functions.array_ndims(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + 1 + + +.. py:function:: array_normalize(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Scales a numeric array so it has Euclidean length 1. + + Treats the array as a vector and divides every element by the vector's + Euclidean (L2) norm — the square root of the sum of the squared + elements. The returned array points in the same direction as the input + but has a magnitude of 1, which makes it suitable for cosine-similarity + comparisons and other operations that expect unit vectors. + + For the input ``[3.0, 4.0]`` the L2 norm is ``sqrt(3**2 + 4**2) = 5``, + so each element is divided by 5 to produce ``[0.6, 0.8]``. + + Normalizing the zero vector is undefined (it would divide by zero), so + the function returns NULL for an all-zero input. NULL is also returned + if any element of the input array is NULL. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[3.0, 4.0]]}) + >>> result = df.select( + ... dfn.functions.array_normalize(dfn.col("a")).alias("result") + ... ) + >>> result.collect_column("result")[0].as_py() + [0.6, 0.8] + + The zero vector has no direction to preserve, so the result is NULL: + + >>> df_zero = ctx.from_pydict({"a": [[0.0, 0.0]]}) + >>> result = df_zero.select( + ... dfn.functions.array_normalize(dfn.col("a")).alias("result") + ... ) + >>> result.collect_column("result")[0].as_py() is None + True + + +.. py:function:: array_pop_back(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the array without the last element. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select( + ... dfn.functions.array_pop_back(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + [1, 2] + + +.. py:function:: array_pop_front(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the array without the first element. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select( + ... dfn.functions.array_pop_front(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + [2, 3] + + +.. py:function:: array_position(array: datafusion.expr.Expr, element: datafusion.expr.Expr, index: int | None = 1) -> datafusion.expr.Expr + + Return the position of the first occurrence of ``element`` in ``array``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[10, 20, 30]]}) + >>> result = df.select( + ... dfn.functions.array_position( + ... dfn.col("a"), dfn.lit(20) + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + 2 + + Use ``index`` to start searching from a given position: + + >>> df = ctx.from_pydict({"a": [[10, 20, 10, 20]]}) + >>> result = df.select( + ... dfn.functions.array_position( + ... dfn.col("a"), dfn.lit(20), index=3, + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + 4 + + +.. py:function:: array_positions(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Searches for an element in the array and returns all occurrences. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 1]]}) + >>> result = df.select( + ... dfn.functions.array_positions(dfn.col("a"), dfn.lit(1)).alias("result")) + >>> result.collect_column("result")[0].as_py() + [1, 3] + + +.. py:function:: array_prepend(element: datafusion.expr.Expr, array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Prepends an element to the beginning of an array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2]]}) + >>> result = df.select( + ... dfn.functions.array_prepend(dfn.lit(0), dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + [0, 1, 2] + + +.. py:function:: array_push_back(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Appends an element to the end of an array. + + .. seealso:: This is an alias for :py:func:`array_append`. + + +.. py:function:: array_push_front(element: datafusion.expr.Expr, array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Prepends an element to the beginning of an array. + + .. seealso:: This is an alias for :py:func:`array_prepend`. + + +.. py:function:: array_remove(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Removes the first element from the array equal to the given value. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 1]]}) + >>> result = df.select( + ... dfn.functions.array_remove(dfn.col("a"), dfn.lit(1)).alias("result")) + >>> result.collect_column("result")[0].as_py() + [2, 1] + + +.. py:function:: array_remove_all(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Removes all elements from the array equal to the given value. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 1]]}) + >>> result = df.select( + ... dfn.functions.array_remove_all( + ... dfn.col("a"), dfn.lit(1) + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + [2] + + +.. py:function:: array_remove_n(array: datafusion.expr.Expr, element: datafusion.expr.Expr, max: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Removes the first ``max`` elements from the array equal to the given value. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 1, 1]]}) + >>> result = df.select( + ... dfn.functions.array_remove_n( + ... dfn.col("a"), dfn.lit(1), 2 + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + [2, 1] + + +.. py:function:: array_repeat(element: datafusion.expr.Expr, count: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Returns an array containing ``element`` ``count`` times. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.array_repeat(dfn.lit(3), 3).alias("result")) + >>> result.collect_column("result")[0].as_py() + [3, 3, 3] + + +.. py:function:: array_replace(array: datafusion.expr.Expr, from_val: datafusion.expr.Expr, to_val: datafusion.expr.Expr) -> datafusion.expr.Expr + + Replaces the first occurrence of ``from_val`` with ``to_val``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 1]]}) + >>> result = df.select( + ... dfn.functions.array_replace(dfn.col("a"), dfn.lit(1), + ... dfn.lit(9)).alias("result")) + >>> result.collect_column("result")[0].as_py() + [9, 2, 1] + + +.. py:function:: array_replace_all(array: datafusion.expr.Expr, from_val: datafusion.expr.Expr, to_val: datafusion.expr.Expr) -> datafusion.expr.Expr + + Replaces all occurrences of ``from_val`` with ``to_val``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 1]]}) + >>> result = df.select( + ... dfn.functions.array_replace_all(dfn.col("a"), dfn.lit(1), + ... dfn.lit(9)).alias("result")) + >>> result.collect_column("result")[0].as_py() + [9, 2, 9] + + +.. py:function:: array_replace_n(array: datafusion.expr.Expr, from_val: datafusion.expr.Expr, to_val: datafusion.expr.Expr, max: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Replace ``n`` occurrences of ``from_val`` with ``to_val``. + + Replaces the first ``max`` occurrences of the specified element with another + specified element. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 1, 1]]}) + >>> result = df.select( + ... dfn.functions.array_replace_n( + ... dfn.col("a"), dfn.lit(1), dfn.lit(9), 2 + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + [9, 2, 9, 1] + + +.. py:function:: array_resize(array: datafusion.expr.Expr, size: datafusion.expr.Expr | int, value: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns an array with the specified size filled. + + If ``size`` is greater than the ``array`` length, the additional entries will + be filled with the given ``value``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2]]}) + >>> result = df.select( + ... dfn.functions.array_resize(dfn.col("a"), 4, dfn.lit(0)).alias("result")) + >>> result.collect_column("result")[0].as_py() + [1, 2, 0, 0] + + +.. py:function:: array_reverse(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Reverses the order of elements in the array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select( + ... dfn.functions.array_reverse(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + [3, 2, 1] + + +.. py:function:: array_slice(array: datafusion.expr.Expr, begin: datafusion.expr.Expr | int, end: datafusion.expr.Expr | int, stride: datafusion.expr.Expr | int | None = None) -> datafusion.expr.Expr + + Returns a slice of the array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3, 4]]}) + >>> result = df.select( + ... dfn.functions.array_slice(dfn.col("a"), 2, 3).alias("result")) + >>> result.collect_column("result")[0].as_py() + [2, 3] + + Use ``stride`` to skip elements: + + >>> result = df.select( + ... dfn.functions.array_slice( + ... dfn.col("a"), 1, 4, stride=2, + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + [1, 3] + + +.. py:function:: array_sort(array: datafusion.expr.Expr, descending: bool = False, null_first: bool = False) -> datafusion.expr.Expr + + Sort an array. + + :param array: The input array to sort. + :param descending: If True, sorts in descending order. + :param null_first: If True, nulls will be returned at the beginning of the array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[3, 1, 2]]}) + >>> result = df.select( + ... dfn.functions.array_sort( + ... dfn.col("a") + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + [1, 2, 3] + + >>> df = ctx.from_pydict({"a": [[3, None, 1]]}) + >>> result = df.select( + ... dfn.functions.array_sort( + ... dfn.col("a"), descending=True, null_first=True, + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + [None, 3, 1] + + +.. py:function:: array_to_string(expr: datafusion.expr.Expr, delimiter: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts each element to its text representation. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select( + ... dfn.functions.array_to_string(dfn.col("a"), ",").alias("s")) + >>> result.collect_column("s")[0].as_py() + '1,2,3' + + +.. py:function:: array_transform(array: datafusion.expr.Expr, transform: datafusion.expr.Expr | collections.abc.Callable[Ellipsis, Any]) -> datafusion.expr.Expr + + Transform each element of ``array`` with a lambda. + + ``transform`` may be a Python callable, which is converted to a lambda + automatically (its parameter names become the lambda parameters), or an + explicit lambda built with :py:func:`lambda_`. + + .. rubric:: Examples + + Using a Python callable: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> df.select( + ... F.array_transform(col("a"), lambda v: v * 2).alias("d") + ... ).collect_column("d")[0].as_py() + [2, 4, 6] + + Using an explicit lambda built with :py:func:`lambda_`: + + >>> double_fn = F.lambda_(["v"], F.lambda_var("v") * lit(2)) + >>> df.select( + ... F.array_transform(col("a"), double_fn).alias("d") + ... ).collect_column("d")[0].as_py() + [2, 4, 6] + + .. seealso:: :py:func:`array_any_match`, :py:func:`lambda_`. + + +.. py:function:: array_union(array1: datafusion.expr.Expr, array2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns an array of the elements in the union of array1 and array2. + + Duplicate rows will not be returned. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]], "b": [[2, 3, 4]]}) + >>> result = df.select( + ... dfn.functions.array_union( + ... dfn.col("a"), dfn.col("b") + ... ).alias("result") + ... ) + >>> sorted( + ... result.collect_column("result")[0].as_py() + ... ) + [1, 2, 3, 4] + + +.. py:function:: arrays_overlap(first_array: datafusion.expr.Expr, second_array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns true if any element appears in both arrays. + + .. seealso:: This is an alias for :py:func:`array_has_any`. + + +.. py:function:: arrays_zip(*arrays: datafusion.expr.Expr) -> datafusion.expr.Expr + + Combines multiple arrays into a single array of structs. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2]], "b": [[3, 4]]}) + >>> result = df.select( + ... dfn.functions.arrays_zip(dfn.col("a"), dfn.col("b")).alias("result")) + >>> result.collect_column("result")[0].as_py() + [{'1': 1, '2': 3}, {'1': 2, '2': 4}] + + +.. py:function:: arrow_cast(expr: datafusion.expr.Expr, data_type: datafusion.expr.Expr | str | pyarrow.DataType) -> datafusion.expr.Expr + + Casts an expression to a specified data type. + + The ``data_type`` can be a string, a ``pyarrow.DataType``, or an + ``Expr``. For simple types, :py:meth:`Expr.cast() + ` is more concise + (e.g., ``col("a").cast(pa.float64())``). Use ``arrow_cast`` when + you want to specify the target type as a string using DataFusion's + type syntax, which can be more readable for complex types like + ``"Timestamp(Nanosecond, None)"``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.arrow_cast(dfn.col("a"), "Float64").alias("c") + ... ) + >>> result.collect_column("c")[0].as_py() + 1.0 + + >>> result = df.select( + ... dfn.functions.arrow_cast( + ... dfn.col("a"), data_type=pa.float64() + ... ).alias("c") + ... ) + >>> result.collect_column("c")[0].as_py() + 1.0 + + +.. py:function:: arrow_field(expr: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the Arrow field information of an expression as a struct. + + The returned struct contains the field's name, data type, nullability, + and metadata. + + .. rubric:: Examples + + >>> field = pa.field("val", pa.int64(), metadata={"k": "v"}) + >>> schema = pa.schema([field]) + >>> batch = pa.RecordBatch.from_arrays([pa.array([1])], schema=schema) + >>> ctx = dfn.SessionContext() + >>> df = ctx.create_dataframe([[batch]]) + >>> result = df.select( + ... dfn.functions.arrow_field(dfn.col("val")).alias("f") + ... ) + >>> out = result.collect_column("f")[0].as_py() + >>> out["name"], out["data_type"], out["nullable"], out["metadata"] + ('val', 'Int64', True, [('k', 'v')]) + + +.. py:function:: arrow_metadata(expr: datafusion.expr.Expr, key: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Returns the metadata of the input expression. + + If called with one argument, returns a Map of all metadata key-value pairs. + If called with two arguments, returns the value for the specified metadata key. + + .. rubric:: Examples + + >>> field = pa.field("val", pa.int64(), metadata={"k": "v"}) + >>> schema = pa.schema([field]) + >>> batch = pa.RecordBatch.from_arrays([pa.array([1])], schema=schema) + >>> ctx = dfn.SessionContext() + >>> df = ctx.create_dataframe([[batch]]) + >>> result = df.select( + ... dfn.functions.arrow_metadata(dfn.col("val")).alias("meta") + ... ) + >>> ("k", "v") in result.collect_column("meta")[0].as_py() + True + + >>> result = df.select( + ... dfn.functions.arrow_metadata( + ... dfn.col("val"), key="k" + ... ).alias("meta_val") + ... ) + >>> result.collect_column("meta_val")[0].as_py() + 'v' + + +.. py:function:: arrow_try_cast(expr: datafusion.expr.Expr, data_type: datafusion.expr.Expr | str | pyarrow.DataType) -> datafusion.expr.Expr + + Casts an expression to a specified data type, returning NULL on failure. + + Like :py:func:`arrow_cast` but produces NULL instead of erroring when the + cast cannot be performed. The ``data_type`` may be a string in DataFusion + type syntax (for example ``"Float64"``), a ``pyarrow.DataType``, or an + ``Expr`` of string type. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["oops"]}) + >>> result = df.select( + ... dfn.functions.arrow_try_cast(dfn.col("a"), "Float64").alias("c") + ... ) + >>> result.collect_column("c")[0].as_py() is None + True + + >>> result = df.select( + ... dfn.functions.arrow_try_cast( + ... dfn.col("a"), data_type=pa.float64() + ... ).alias("c") + ... ) + >>> result.collect_column("c")[0].as_py() is None + True + + +.. py:function:: arrow_typeof(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the Arrow type of the expression. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select(dfn.functions.arrow_typeof(dfn.col("a")).alias("t")) + >>> result.collect_column("t")[0].as_py() + 'Int64' + + +.. py:function:: ascii(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the numeric code of the first character of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["a","b","c"]}) + >>> ascii_df = df.select(dfn.functions.ascii(dfn.col("a")).alias("ascii")) + >>> ascii_df.collect_column("ascii")[0].as_py() + 97 + + +.. py:function:: asin(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the arc sine or inverse sine of a number. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0.0]}) + >>> result = df.select(dfn.functions.asin(dfn.col("a")).alias("asin")) + >>> result.collect_column("asin")[0].as_py() + 0.0 + + +.. py:function:: asinh(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns inverse hyperbolic sine. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0.0]}) + >>> result = df.select(dfn.functions.asinh(dfn.col("a")).alias("asinh")) + >>> result.collect_column("asinh")[0].as_py() + 0.0 + + +.. py:function:: atan(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns inverse tangent of a number. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0.0]}) + >>> result = df.select(dfn.functions.atan(dfn.col("a")).alias("atan")) + >>> result.collect_column("atan")[0].as_py() + 0.0 + + +.. py:function:: atan2(y: datafusion.expr.Expr, x: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns inverse tangent of a division given in the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"y": [0.0], "x": [1.0]}) + >>> result = df.select( + ... dfn.functions.atan2(dfn.col("y"), dfn.col("x")).alias("atan2")) + >>> result.collect_column("atan2")[0].as_py() + 0.0 + + +.. py:function:: atanh(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns inverse hyperbolic tangent. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0.0]}) + >>> result = df.select(dfn.functions.atanh(dfn.col("a")).alias("atanh")) + >>> result.collect_column("atanh")[0].as_py() + 0.0 + + +.. py:function:: avg(expression: datafusion.expr.Expr, distinct: bool = False, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Returns the average value. + + This aggregate function expects a numeric expression and will return a float. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by`` and ``null_treatment``. + + :param expression: Values to combine into an array + :param distinct: If True, duplicate values are removed before averaging. + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.avg( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.avg( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.5 + + >>> df = ctx.from_pydict({"a": [1.0, 1.0, 2.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.avg( + ... dfn.col("a"), distinct=True, + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.0 + + +.. py:function:: bit_and(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the bitwise AND of the argument. + + This aggregate function will bitwise compare every value in the input partition. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param expression: Argument to perform bitwise calculation on + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [7, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.bit_and( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 3 + + >>> df = ctx.from_pydict({"a": [7, 5, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.bit_and( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(3) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 5 + + +.. py:function:: bit_length(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the number of bits in the string argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["a","b","c"]}) + >>> bit_df = df.select(dfn.functions.bit_length(dfn.col("a")).alias("bit_len")) + >>> bit_df.collect_column("bit_len")[0].as_py() + 8 + + +.. py:function:: bit_or(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the bitwise OR of the argument. + + This aggregate function will bitwise compare every value in the input partition. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param expression: Argument to perform bitwise calculation on + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2]}) + >>> result = df.aggregate( + ... [], [dfn.functions.bit_or( + ... dfn.col("a") + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 3 + + >>> df = ctx.from_pydict({"a": [1, 2, 4]}) + >>> result = df.aggregate( + ... [], [dfn.functions.bit_or( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(1) + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 6 + + +.. py:function:: bit_xor(expression: datafusion.expr.Expr, distinct: bool = False, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the bitwise XOR of the argument. + + This aggregate function will bitwise compare every value in the input partition. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by`` and ``null_treatment``. + + :param expression: Argument to perform bitwise calculation on + :param distinct: If True, evaluate each unique value of expression only once + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [5, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.bit_xor( + ... dfn.col("a") + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 6 + + >>> df = ctx.from_pydict({"a": [5, 5, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.bit_xor( + ... dfn.col("a"), distinct=True, + ... filter=dfn.col("a") > dfn.lit(3), + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 5 + + +.. py:function:: bool_and(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the boolean AND of the argument. + + This aggregate function will compare every value in the input partition. These are + expected to be boolean values. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param expression: Argument to perform calculation on + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [True, True, False]}) + >>> result = df.aggregate( + ... [], [dfn.functions.bool_and( + ... dfn.col("a") + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + False + + >>> df = ctx.from_pydict( + ... {"a": [True, True, False], "b": [1, 2, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.bool_and( + ... dfn.col("a"), + ... filter=dfn.col("b") < dfn.lit(3) + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + True + + +.. py:function:: bool_or(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the boolean OR of the argument. + + This aggregate function will compare every value in the input partition. These are + expected to be boolean values. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param expression: Argument to perform calculation on + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [False, False, True]}) + >>> result = df.aggregate( + ... [], [dfn.functions.bool_or( + ... dfn.col("a") + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + True + + >>> df = ctx.from_pydict( + ... {"a": [False, False, True], "b": [1, 2, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.bool_or( + ... dfn.col("a"), + ... filter=dfn.col("b") < dfn.lit(3) + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + False + + +.. py:function:: btrim(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Removes all characters, spaces by default, from both sides of a string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [" a "]}) + >>> trim_df = df.select(dfn.functions.btrim(dfn.col("a")).alias("trimmed")) + >>> trim_df.collect_column("trimmed")[0].as_py() + 'a' + + +.. py:function:: cardinality(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the total number of elements in the array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> result = df.select(dfn.functions.cardinality(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + 3 + + +.. py:function:: case(expr: datafusion.expr.Expr) -> datafusion.expr.CaseBuilder + + Create a case expression. + + Create a :py:class:`~datafusion.expr.CaseBuilder` to match cases for the + expression ``expr``. See :py:class:`~datafusion.expr.CaseBuilder` for + detailed usage. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.select( + ... dfn.functions.case(dfn.col("a")).when(dfn.lit(1), + ... dfn.lit("one")).otherwise(dfn.lit("other")).alias("c")) + >>> result.collect_column("c")[0].as_py() + 'one' + + +.. py:function:: cast_to_type(value: datafusion.expr.Expr, type_ref: datafusion.expr.Expr) -> datafusion.expr.Expr + + Casts ``value`` to the data type of ``type_ref``. + + Only the *type* of ``type_ref`` is used; its value is ignored. This is + useful when the target type comes from another column or expression + rather than being known up-front. Casts that fail produce an error; use + :py:func:`try_cast_to_type` for the NULL-on-failure variant. + + If the target type is known statically, prefer :py:func:`arrow_cast` + (or :py:func:`arrow_try_cast` for the NULL-on-failure variant) and + pass a type string or ``pyarrow.DataType`` directly. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1], "b": [1.0]}) + >>> result = df.select( + ... dfn.functions.cast_to_type( + ... dfn.col("a"), dfn.col("b") + ... ).alias("c") + ... ) + >>> result.collect_column("c")[0].as_py() + 1.0 + + +.. py:function:: cbrt(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the cube root of a number. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [27]}) + >>> cbrt_df = df.select(dfn.functions.cbrt(dfn.col("a")).alias("cbrt")) + >>> cbrt_df.collect_column("cbrt")[0].as_py() + 3.0 + + +.. py:function:: ceil(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the nearest integer greater than or equal to argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.9]}) + >>> ceil_df = df.select(dfn.functions.ceil(dfn.col("a")).alias("ceil")) + >>> ceil_df.collect_column("ceil")[0].as_py() + 2.0 + + +.. py:function:: char_length(string: datafusion.expr.Expr) -> datafusion.expr.Expr + + The number of characters in the ``string``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select(dfn.functions.char_length(dfn.col("a")).alias("len")) + >>> result.collect_column("len")[0].as_py() + 5 + + +.. py:function:: character_length(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the number of characters in the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["abc","b","c"]}) + >>> char_len_df = df.select( + ... dfn.functions.character_length(dfn.col("a")).alias("char_len")) + >>> char_len_df.collect_column("char_len")[0].as_py() + 3 + + +.. py:function:: chr(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Converts the Unicode code point to a UTF8 character. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [65]}) + >>> result = df.select(dfn.functions.chr(dfn.col("a")).alias("chr")) + >>> result.collect_column("chr")[0].as_py() + 'A' + + +.. py:function:: coalesce(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the value of the first expr in ``args`` which is not NULL. + + :param \*args: Expressions to evaluate in order. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [None, 1], "b": [2, 3]}) + >>> result = df.select( + ... dfn.functions.coalesce(dfn.col("a"), dfn.col("b")).alias("c")) + >>> result.collect_column("c")[0].as_py() + 2 + + +.. py:function:: col(name: str) -> datafusion.expr.Expr + + Creates a column reference expression. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> df.select(dfn.functions.col("a")).collect_column("a")[0].as_py() + 1 + + +.. py:function:: concat(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Concatenates the text representations of all the arguments. + + NULL arguments are ignored. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"], "b": [" world"]}) + >>> result = df.select( + ... dfn.functions.concat(dfn.col("a"), dfn.col("b")).alias("c") + ... ) + >>> result.collect_column("c")[0].as_py() + 'hello world' + + +.. py:function:: concat_ws(separator: str, *args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Concatenates the list ``args`` with the separator. + + ``NULL`` arguments are ignored. ``separator`` should not be ``NULL``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"], "b": ["world"]}) + >>> result = df.select( + ... dfn.functions.concat_ws("-", dfn.col("a"), dfn.col("b")).alias("c")) + >>> result.collect_column("c")[0].as_py() + 'hello-world' + + +.. py:function:: contains(string: datafusion.expr.Expr, search_str: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Returns true if ``search_str`` is found within ``string`` (case-sensitive). + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["the quick brown fox"]}) + >>> result = df.select( + ... dfn.functions.contains(dfn.col("a"), "brown").alias("c")) + >>> result.collect_column("c")[0].as_py() + True + + +.. py:function:: corr(value_y: datafusion.expr.Expr, value_x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Returns the correlation coefficient between ``value1`` and ``value2``. + + This aggregate function expects both values to be numeric and will return a float. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param value_y: The dependent variable for correlation + :param value_x: The independent variable for correlation + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0], "b": [1.0, 2.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.corr( + ... dfn.col("a"), dfn.col("b") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 1.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.corr( + ... dfn.col("a"), dfn.col("b"), + ... filter=dfn.col("a") > dfn.lit(1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 1.0 + + +.. py:function:: cos(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the cosine of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0,-1,1]}) + >>> cos_df = df.select(dfn.functions.cos(dfn.col("a")).alias("cos")) + >>> cos_df.collect_column("cos")[0].as_py() + 1.0 + + +.. py:function:: cosh(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the hyperbolic cosine of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0,-1,1]}) + >>> cosh_df = df.select(dfn.functions.cosh(dfn.col("a")).alias("cosh")) + >>> cosh_df.collect_column("cosh")[0].as_py() + 1.0 + + +.. py:function:: cosine_distance(array1: datafusion.expr.Expr, array2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Measures how much two numeric arrays differ in direction. + + Treats each input as a vector and compares the angle between them, + ignoring their magnitudes. The result is ``1 - cosine_similarity``, + where cosine similarity is the dot product of the two vectors divided + by the product of their Euclidean (L2) norms. + + The returned value ranges from 0 to 2: + + * ``0`` — vectors point in the same direction (any positive scaling + of one yields the other). + * ``1`` — vectors are orthogonal (no shared direction). + * ``2`` — vectors point in exactly opposite directions. + + This is the standard distance metric for comparing embedding vectors + (text, image, audio) where direction carries the meaning and overall + magnitude does not. + + Both arrays must have the same length; otherwise execution fails. If + either input is the zero vector the cosine is undefined and the + function returns NULL. + + .. rubric:: Examples + + Identical vectors have distance ``0``: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict( + ... {"a": [[1.0, 2.0, 3.0]], "b": [[1.0, 2.0, 3.0]]} + ... ) + >>> result = df.select( + ... dfn.functions.cosine_distance( + ... dfn.col("a"), dfn.col("b") + ... ).alias("result") + ... ) + >>> result.collect_column("result")[0].as_py() + 0.0 + + Orthogonal vectors have distance ``1``: + + >>> df_orth = ctx.from_pydict( + ... {"a": [[1.0, 0.0]], "b": [[0.0, 1.0]]} + ... ) + >>> result = df_orth.select( + ... dfn.functions.cosine_distance( + ... dfn.col("a"), dfn.col("b") + ... ).alias("result") + ... ) + >>> result.collect_column("result")[0].as_py() + 1.0 + + +.. py:function:: cot(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the cotangent of the argument. + + .. rubric:: Examples + + >>> from math import pi + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [pi / 4]}) + >>> result = df.select( + ... dfn.functions.cot(dfn.col("a")).alias("cot") + ... ) + >>> result.collect_column("cot")[0].as_py() + 1.0... + + +.. py:function:: count(expressions: datafusion.expr.Expr | list[datafusion.expr.Expr] | None = None, distinct: bool = False, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Returns the number of rows that match the given arguments. + + This aggregate function will count the non-null rows provided in the expression. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by`` and ``null_treatment``. + + :param expressions: Argument to perform bitwise calculation on + :param distinct: If True, a single entry for each distinct value will be in the result + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.count( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 3 + + >>> df = ctx.from_pydict({"a": [1, 1, 2, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.count( + ... dfn.col("a"), distinct=True, + ... filter=dfn.col("a") > dfn.lit(1), + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2 + + +.. py:function:: count_star(filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Create a COUNT(1) aggregate expression. + + This aggregate function will count all of the rows in the partition. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``distinct``, and ``null_treatment``. + + :param filter: If provided, only count rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.count_star( + ... ).alias("cnt")]) + >>> result.collect_column("cnt")[0].as_py() + 3 + + >>> result = df.aggregate( + ... [], [dfn.functions.count_star( + ... filter=dfn.col("a") > dfn.lit(1) + ... ).alias("cnt")]) + >>> result.collect_column("cnt")[0].as_py() + 2 + + +.. py:function:: covar(value_y: datafusion.expr.Expr, value_x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the sample covariance. + + .. seealso:: This is an alias for :py:func:`covar_samp`. + + +.. py:function:: covar_pop(value_y: datafusion.expr.Expr, value_x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the population covariance. + + This aggregate function expects both values to be numeric and will return a float. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param value_y: The dependent variable for covariance + :param value_x: The independent variable for covariance + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 5.0, 10.0], "b": [1.0, 2.0, 3.0]}) + >>> result = df.aggregate( + ... [], + ... [dfn.functions.covar_pop( + ... dfn.col("a"), dfn.col("b") + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 3.0 + + >>> df = ctx.from_pydict( + ... {"a": [0.0, 1.0, 3.0], "b": [0.0, 1.0, 3.0]}) + >>> result = df.aggregate( + ... [], + ... [dfn.functions.covar_pop( + ... dfn.col("a"), dfn.col("b"), + ... filter=dfn.col("a") > dfn.lit(0.0) + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 1.0 + + +.. py:function:: covar_samp(value_y: datafusion.expr.Expr, value_x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the sample covariance. + + This aggregate function expects both values to be numeric and will return a float. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param value_y: The dependent variable for covariance + :param value_x: The independent variable for covariance + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0], "b": [4.0, 5.0, 6.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.covar_samp( + ... dfn.col("a"), dfn.col("b") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 1.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.covar_samp( + ... dfn.col("a"), dfn.col("b"), + ... filter=dfn.col("a") > dfn.lit(1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 0.5 + + +.. py:function:: cume_dist(partition_by: list[datafusion.expr.Expr] | datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None) -> datafusion.expr.Expr + + Create a cumulative distribution window function. + + This window function is similar to :py:func:`rank` except that the returned values + are the ratio of the row number to the total number of rows. Here is an example of a + dataframe with a window ordered by descending ``points`` and the associated + cumulative distribution:: + + +--------+-----------+ + | points | cume_dist | + +--------+-----------+ + | 100 | 0.5 | + | 100 | 0.5 | + | 50 | 0.75 | + | 25 | 1.0 | + +--------+-----------+ + + :param partition_by: Expressions to partition the window frame on. + :param order_by: Set ordering within the window frame. Accepts + column names or expressions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1., 2., 2., 3.]}) + >>> result = df.select( + ... dfn.col("a"), + ... dfn.functions.cume_dist( + ... order_by="a" + ... ).alias("cd") + ... ) + >>> result.collect_column("cd").to_pylist() + [0.25..., 0.75..., 0.75..., 1.0...] + + >>> df = ctx.from_pydict( + ... {"g": ["a", "a", "b", "b"], "v": [1, 2, 3, 4]}) + >>> result = df.select( + ... dfn.col("g"), dfn.col("v"), + ... dfn.functions.cume_dist( + ... partition_by=dfn.col("g"), order_by="v", + ... ).alias("cd")) + >>> result.sort(dfn.col("g"), dfn.col("v")).collect_column("cd").to_pylist() + [0.5, 1.0, 0.5, 1.0] + + +.. py:function:: current_date() -> datafusion.expr.Expr + + Returns current UTC date as a Date32 value. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.current_date().alias("d") + ... ) + >>> result.collect_column("d")[0].as_py() is not None + True + + +.. py:function:: current_time() -> datafusion.expr.Expr + + Returns current UTC time as a Time64 value. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.current_time().alias("t") + ... ) + + Use .value instead of .as_py() because nanosecond timestamps + require pandas to convert to Python datetime objects. + + >>> result.collect_column("t")[0].value > 0 + True + + +.. py:function:: current_timestamp() -> datafusion.expr.Expr + + Returns the current timestamp in nanoseconds. + + .. seealso:: This is an alias for :py:func:`now`. + + +.. py:function:: date_bin(stride: datafusion.expr.Expr | str, source: datafusion.expr.Expr | str, origin: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Coerces an arbitrary timestamp to the start of the nearest specified interval. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"timestamp": ['2021-07-15 12:34:56', '2021-01-01']}) + >>> result = df.select( + ... dfn.functions.date_bin( + ... "15 minutes", + ... dfn.col("timestamp"), + ... "2001-01-01 00:00:00", + ... ).alias("b") + ... ) + >>> str(result.collect_column("b")[0].as_py()) + '2021-07-15 12:30:00' + >>> str(result.collect_column("b")[1].as_py()) + '2021-01-01 00:00:00' + + ``source`` may also be a bare literal: + + >>> result = df.select( + ... dfn.functions.date_bin( + ... "15 minutes", "2021-07-15 12:34:56", "2001-01-01 00:00:00" + ... ).alias("b") + ... ) + >>> str(result.collect_column("b")[0].as_py()) + '2021-07-15 12:30:00' + + +.. py:function:: date_format(arg: datafusion.expr.Expr, formatter: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Returns a string representation of a date, time, timestamp or duration. + + .. seealso:: This is an alias for :py:func:`to_char`. + + +.. py:function:: date_part(part: datafusion.expr.Expr | str, date: datafusion.expr.Expr) -> datafusion.expr.Expr + + Extracts a subfield from the date. + + :param part: The part of the date to extract. Must be one of ``"year"``, + ``"month"``, ``"day"``, ``"hour"``, ``"minute"``, ``"second"``, etc. + :param date: The date expression to extract from. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["2021-07-15T00:00:00"]}) + >>> df = df.select(dfn.functions.to_timestamp(dfn.col("a")).alias("a")) + >>> result = df.select( + ... dfn.functions.date_part("year", dfn.col("a")).alias("y")) + >>> result.collect_column("y")[0].as_py() + 2021 + + +.. py:function:: date_trunc(part: datafusion.expr.Expr | str, date: datafusion.expr.Expr) -> datafusion.expr.Expr + + Truncates the date to a specified level of precision. + + :param part: The precision to truncate to. Must be one of ``"year"``, + ``"month"``, ``"day"``, ``"hour"``, ``"minute"``, ``"second"``, etc. + :param date: The date expression to truncate. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["2021-07-15T12:34:56"]}) + >>> df = df.select(dfn.functions.to_timestamp(dfn.col("a")).alias("a")) + >>> result = df.select( + ... dfn.functions.date_trunc("month", dfn.col("a")).alias("t") + ... ) + >>> str(result.collect_column("t")[0].as_py()) + '2021-07-01 00:00:00' + + +.. py:function:: datepart(part: datafusion.expr.Expr | str, date: datafusion.expr.Expr) -> datafusion.expr.Expr + + Return a specified part of a date. + + .. seealso:: This is an alias for :py:func:`date_part`. + + +.. py:function:: datetrunc(part: datafusion.expr.Expr | str, date: datafusion.expr.Expr) -> datafusion.expr.Expr + + Truncates the date to a specified level of precision. + + .. seealso:: This is an alias for :py:func:`date_trunc`. + + +.. py:function:: decode(expr: datafusion.expr.Expr, encoding: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Decode the ``input``, using the ``encoding``. encoding can be base64 or hex. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["aGVsbG8="]}) + >>> result = df.select( + ... dfn.functions.decode(dfn.col("a"), "base64").alias("dec")) + >>> result.collect_column("dec")[0].as_py() + b'hello' + + +.. py:function:: degrees(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Converts the argument from radians to degrees. + + .. rubric:: Examples + + >>> from math import pi + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0,pi,2*pi]}) + >>> deg_df = df.select(dfn.functions.degrees(dfn.col("a")).alias("deg")) + >>> deg_df.collect_column("deg")[2].as_py() + 360.0 + + +.. py:function:: dense_rank(partition_by: list[datafusion.expr.Expr] | datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None) -> datafusion.expr.Expr + + Create a dense_rank window function. + + This window function is similar to :py:func:`rank` except that the returned values + will be consecutive. Here is an example of a dataframe with a window ordered by + descending ``points`` and the associated dense rank:: + + +--------+------------+ + | points | dense_rank | + +--------+------------+ + | 100 | 1 | + | 100 | 1 | + | 50 | 2 | + | 25 | 3 | + +--------+------------+ + + :param partition_by: Expressions to partition the window frame on. + :param order_by: Set ordering within the window frame. Accepts + column names or expressions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [10, 10, 20]}) + >>> result = df.select( + ... dfn.col("a"), + ... dfn.functions.dense_rank( + ... order_by="a" + ... ).alias("dr")) + >>> result.sort(dfn.col("a")).collect_column("dr").to_pylist() + [1, 1, 2] + + >>> df = ctx.from_pydict( + ... {"g": ["a", "a", "b", "b"], "v": [1, 1, 2, 3]}) + >>> result = df.select( + ... dfn.col("g"), dfn.col("v"), + ... dfn.functions.dense_rank( + ... partition_by=dfn.col("g"), order_by="v", + ... ).alias("dr")) + >>> result.sort(dfn.col("g"), dfn.col("v")).collect_column("dr").to_pylist() + [1, 1, 1, 2] + + +.. py:function:: digest(value: datafusion.expr.Expr, method: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Computes the binary hash of an expression using the specified algorithm. + + Standard algorithms are md5, sha224, sha256, sha384, sha512, blake2s, + blake2b, and blake3. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select( + ... dfn.functions.digest(dfn.col("a"), "md5").alias("d")) + >>> len(result.collect_column("d")[0].as_py()) > 0 + True + + +.. py:function:: dot_product(array1: datafusion.expr.Expr, array2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the inner (dot) product of two numeric arrays. + + .. seealso:: This is an alias for :py:func:`inner_product`. + + +.. py:function:: element_at(map: datafusion.expr.Expr, key: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the value for a given key in the map. + + Returns ``[None]`` if the key is absent. + + .. seealso:: This is an alias for :py:func:`map_extract`. + + +.. py:function:: empty(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns true if the array is empty. + + .. seealso:: This is an alias for :py:func:`array_empty`. + + +.. py:function:: encode(expr: datafusion.expr.Expr, encoding: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Encode the ``input``, using the ``encoding``. encoding can be base64 or hex. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select( + ... dfn.functions.encode(dfn.col("a"), "base64").alias("enc")) + >>> result.collect_column("enc")[0].as_py() + 'aGVsbG8' + + +.. py:function:: ends_with(arg: datafusion.expr.Expr, suffix: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Returns true if the ``string`` ends with the ``suffix``, false otherwise. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["abc","b","c"]}) + >>> ends_with_df = df.select( + ... dfn.functions.ends_with(dfn.col("a"), "c").alias("ends_with")) + >>> ends_with_df.collect_column("ends_with")[0].as_py() + True + + +.. py:function:: exp(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the exponential of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0.0]}) + >>> result = df.select(dfn.functions.exp(dfn.col("a")).alias("exp")) + >>> result.collect_column("exp")[0].as_py() + 1.0 + + +.. py:function:: extract(part: datafusion.expr.Expr | str, date: datafusion.expr.Expr) -> datafusion.expr.Expr + + Extracts a subfield from the date. + + .. seealso:: This is an alias for :py:func:`date_part`. + + +.. py:function:: factorial(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the factorial of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [3]}) + >>> result = df.select( + ... dfn.functions.factorial(dfn.col("a")).alias("factorial") + ... ) + >>> result.collect_column("factorial")[0].as_py() + 6 + + +.. py:function:: find_in_set(string: datafusion.expr.Expr, string_list: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Find a string in a list of strings. + + Returns a value in the range of 1 to N if the string is in the string list + ``string_list`` consisting of N substrings. + + The string list is a string composed of substrings separated by ``,`` characters. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["b"]}) + >>> result = df.select( + ... dfn.functions.find_in_set(dfn.col("a"), "a,b,c").alias("pos")) + >>> result.collect_column("pos")[0].as_py() + 2 + + +.. py:function:: first_value(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None, null_treatment: datafusion.common.NullTreatment = NullTreatment.RESPECT_NULLS) -> datafusion.expr.Expr + + Returns the first value in a group of values. + + This aggregate function will return the first value in the partition. + + If using the builder functions described in ref:`_aggregation` this function ignores + the option ``distinct``. + + :param expression: Argument to perform bitwise calculation on + :param filter: If provided, only compute against rows for which the filter is True + :param order_by: Set the ordering of the expression to evaluate. Accepts + column names or expressions. + :param null_treatment: Assign whether to respect or ignore null values. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [10, 20, 30]}) + >>> result = df.aggregate( + ... [], [dfn.functions.first_value( + ... dfn.col("a") + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 10 + + >>> df = ctx.from_pydict({"a": [None, 20, 10]}) + >>> result = df.aggregate( + ... [], [dfn.functions.first_value( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(10), + ... order_by="a", + ... null_treatment=dfn.common.NullTreatment.IGNORE_NULLS, + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 20 + + +.. py:function:: flatten(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Flattens an array of arrays into a single array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[[1, 2], [3, 4]]]}) + >>> result = df.select(dfn.functions.flatten(dfn.col("a")).alias("result")) + >>> result.collect_column("result")[0].as_py() + [1, 2, 3, 4] + + +.. py:function:: floor(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the nearest integer less than or equal to the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.9]}) + >>> floor_df = df.select(dfn.functions.floor(dfn.col("a")).alias("floor")) + >>> floor_df.collect_column("floor")[0].as_py() + 1.0 + + +.. py:function:: from_unixtime(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Converts an integer to RFC3339 timestamp format string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0]}) + >>> result = df.select( + ... dfn.functions.from_unixtime( + ... dfn.col("a") + ... ).alias("ts") + ... ) + >>> str(result.collect_column("ts")[0].as_py()) + '1970-01-01 00:00:00' + + +.. py:function:: gcd(x: datafusion.expr.Expr, y: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the greatest common divisor. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [12], "b": [8]}) + >>> result = df.select( + ... dfn.functions.gcd(dfn.col("a"), dfn.col("b")).alias("gcd") + ... ) + >>> result.collect_column("gcd")[0].as_py() + 4 + + +.. py:function:: gen_series(start: datafusion.expr.Expr, stop: datafusion.expr.Expr, step: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Creates a list of values in the range between start and stop. + + Unlike :py:func:`range`, this includes the upper bound. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0]}) + >>> result = df.select( + ... dfn.functions.gen_series( + ... dfn.lit(1), dfn.lit(5), + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + [1, 2, 3, 4, 5] + + Specify a custom ``step``: + + >>> result = df.select( + ... dfn.functions.gen_series( + ... dfn.lit(1), dfn.lit(10), step=dfn.lit(3), + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + [1, 4, 7, 10] + + +.. py:function:: generate_series(start: datafusion.expr.Expr, stop: datafusion.expr.Expr, step: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Creates a list of values in the range between start and stop. + + Unlike :py:func:`range`, this includes the upper bound. + + .. seealso:: This is an alias for :py:func:`gen_series`. + + +.. py:function:: get_field(expr: datafusion.expr.Expr, *names: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Extracts a (possibly nested) field from a struct or map by name. + + Pass one name for a single-level lookup, or several names to walk a path + of nested struct/map fields in a single ``get_field`` call. For a single + static-string name, ``expr["field"]`` is a convenient shorthand; use + ``get_field`` when the field name is a dynamic + :py:class:`~datafusion.expr.Expr` or when traversing multiple levels at + once. + + :param expr: The struct or map expression to read from. + :param \*names: One or more field names (``str``) or expressions + (:py:class:`~datafusion.expr.Expr`). + + .. rubric:: Examples + + Single-level lookup: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1], "b": [2]}) + >>> df = df.with_column( + ... "s", + ... F.named_struct([("x", dfn.col("a")), ("y", dfn.col("b"))]), + ... ) + >>> result = df.select( + ... F.get_field(dfn.col("s"), "x").alias("x_val") + ... ) + >>> result.collect_column("x_val")[0].as_py() + 1 + + Equivalent using bracket syntax: + + >>> result = df.select( + ... dfn.col("s")["x"].alias("x_val") + ... ) + >>> result.collect_column("x_val")[0].as_py() + 1 + + Multi-level lookup: + + >>> df = df.with_column( + ... "outer", + ... F.named_struct([("inner", dfn.col("s"))]), + ... ) + >>> result = df.select( + ... F.get_field(dfn.col("outer"), "inner", "x").alias("x_val") + ... ) + >>> result.collect_column("x_val")[0].as_py() + 1 + + +.. py:function:: greatest(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the greatest value from a list of expressions. + + Returns NULL if all expressions are NULL. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 3], "b": [2, 1]}) + >>> result = df.select( + ... dfn.functions.greatest(dfn.col("a"), dfn.col("b")).alias("greatest")) + >>> result.collect_column("greatest")[0].as_py() + 2 + >>> result.collect_column("greatest")[1].as_py() + 3 + + +.. py:function:: grouping(expression: datafusion.expr.Expr, distinct: bool = False, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Indicates whether a column is aggregated across in the current row. + + Returns 0 when the column is part of the grouping key for that row + (i.e., the row contains per-group results for that column). Returns 1 + when the column is *not* part of the grouping key (i.e., the row's + aggregate spans all values of that column). + + This function is meaningful with + :py:meth:`GroupingSet.rollup `, + :py:meth:`GroupingSet.cube `, or + :py:meth:`GroupingSet.grouping_sets `, + where different rows are grouped by different subsets of columns. In a + default aggregation without grouping sets every column is always part + of the key, so ``grouping()`` always returns 0. + + .. warning:: + + Due to an upstream DataFusion limitation + (`#21411 `_), + ``.alias()`` cannot be applied directly to a ``grouping()`` + expression. Doing so will raise an error at execution time. To + rename the column, use + :py:meth:`~datafusion.dataframe.DataFrame.with_column_renamed` + on the result DataFrame instead. + + :param expression: The column to check grouping status for + :param distinct: If True, compute on distinct values only + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + With :py:meth:`~datafusion.expr.GroupingSet.rollup`, the result + includes both per-group rows (``grouping(a) = 0``) and a + grand-total row where ``a`` is aggregated across + (``grouping(a) = 1``): + + >>> from datafusion.expr import GroupingSet + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 1, 2], "b": [10, 20, 30]}) + >>> result = df.aggregate( + ... [GroupingSet.rollup(dfn.col("a"))], + ... [dfn.functions.sum(dfn.col("b")).alias("s"), + ... dfn.functions.grouping(dfn.col("a"))], + ... ).sort(dfn.col("a").sort(nulls_first=False)) + >>> result.collect_column("s").to_pylist() + [30, 30, 60] + + .. seealso:: :py:class:`~datafusion.expr.GroupingSet` + + +.. py:function:: ifnull(x: datafusion.expr.Expr, y: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns ``x`` if ``x`` is not NULL. Otherwise returns ``y``. + + :param x: Expression to return when it is not NULL. + :param y: Fallback expression to return when ``x`` is NULL. + + .. seealso:: This is an alias for :py:func:`nvl`. + + +.. py:function:: in_list(arg: datafusion.expr.Expr, values: list[datafusion.expr.Expr], negated: bool = False) -> datafusion.expr.Expr + + Returns whether the argument is contained within the list ``values``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.select( + ... dfn.functions.in_list( + ... dfn.col("a"), [dfn.lit(1), dfn.lit(3)] + ... ).alias("in") + ... ) + >>> result.collect_column("in").to_pylist() + [True, False, True] + + >>> result = df.select( + ... dfn.functions.in_list( + ... dfn.col("a"), [dfn.lit(1), dfn.lit(3)], + ... negated=True, + ... ).alias("not_in") + ... ) + >>> result.collect_column("not_in").to_pylist() + [False, True, False] + + +.. py:function:: initcap(string: datafusion.expr.Expr) -> datafusion.expr.Expr + + Set the initial letter of each word to capital. + + Converts the first letter of each word in ``string`` to uppercase and the remaining + characters to lowercase. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["the cat"]}) + >>> cap_df = df.select(dfn.functions.initcap(dfn.col("a")).alias("cap")) + >>> cap_df.collect_column("cap")[0].as_py() + 'The Cat' + + +.. py:function:: inner_product(array1: datafusion.expr.Expr, array2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the inner (dot) product of two numeric arrays. + + Treats each input as a vector and returns the sum of the element-wise + products: ``sum(array1[i] * array2[i])``. For ``[1, 2, 3]`` and + ``[4, 5, 6]`` the result is ``1*4 + 2*5 + 3*6 = 32``. + + Also available as :py:func:`dot_product` (and as ``dot_product`` in + raw SQL). + + Both arrays must have the same length; otherwise execution fails. NULL + is returned when either input array is NULL or when any element of + either array is NULL. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict( + ... {"a": [[1.0, 2.0, 3.0]], "b": [[4.0, 5.0, 6.0]]} + ... ) + >>> result = df.select( + ... dfn.functions.inner_product( + ... dfn.col("a"), dfn.col("b") + ... ).alias("result") + ... ) + >>> result.collect_column("result")[0].as_py() + 32.0 + + NULL elements propagate to NULL output: + + >>> df_null = ctx.from_pydict( + ... {"a": [[1.0, None, 3.0]], "b": [[4.0, 5.0, 6.0]]} + ... ) + >>> result = df_null.select( + ... dfn.functions.inner_product( + ... dfn.col("a"), dfn.col("b") + ... ).alias("result") + ... ) + >>> result.collect_column("result")[0].as_py() is None + True + + +.. py:function:: instr(string: datafusion.expr.Expr, substring: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Finds the position from where the ``substring`` matches the ``string``. + + .. seealso:: This is an alias for :py:func:`strpos`. + + +.. py:function:: is_nan(expr: datafusion.expr.Expr) -> datafusion.expr.Expr + + Alias for :func:`isnan`. + + +.. py:function:: isnan(expr: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns true if a given number is +NaN or -NaN otherwise returns false. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, np.nan]}) + >>> result = df.select(dfn.functions.isnan(dfn.col("a")).alias("isnan")) + >>> result.collect_column("isnan")[1].as_py() + True + + +.. py:function:: iszero(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns true if a given number is +0.0 or -0.0 otherwise returns false. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0.0, 1.0]}) + >>> result = df.select(dfn.functions.iszero(dfn.col("a")).alias("iz")) + >>> result.collect_column("iz")[0].as_py() + True + + +.. py:function:: lag(arg: datafusion.expr.Expr, shift_offset: int = 1, default_value: Any | None = None, partition_by: list[datafusion.expr.Expr] | datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None) -> datafusion.expr.Expr + + Create a lag window function. + + Lag operation will return the argument that is in the previous shift_offset-th row + in the partition. For example ``lag(col("b"), shift_offset=3, default_value=5)`` + will return the 3rd previous value in column ``b``. At the beginning of the + partition, where no values can be returned it will return the default value of 5. + + Here is an example of both the ``lag`` and :py:func:`datafusion.functions.lead` + functions on a simple DataFrame:: + + +--------+------+-----+ + | points | lead | lag | + +--------+------+-----+ + | 100 | 100 | | + | 100 | 50 | 100 | + | 50 | 25 | 100 | + | 25 | | 50 | + +--------+------+-----+ + + :param arg: Value to return + :param shift_offset: Number of rows before the current row. + :param default_value: Value to return if shift_offet row does not exist. + :param partition_by: Expressions to partition the window frame on. + :param order_by: Set ordering within the window frame. Accepts + column names or expressions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.select( + ... dfn.col("a"), + ... dfn.functions.lag( + ... dfn.col("a"), shift_offset=1, + ... default_value=0, order_by="a" + ... ).alias("lag")) + >>> result.sort(dfn.col("a")).collect_column("lag").to_pylist() + [0, 1, 2] + + >>> df = ctx.from_pydict({"g": ["a", "a", "b"], "v": [1, 2, 3]}) + >>> result = df.select( + ... dfn.col("g"), dfn.col("v"), + ... dfn.functions.lag( + ... dfn.col("v"), shift_offset=1, default_value=0, + ... partition_by=dfn.col("g"), order_by="v", + ... ).alias("lag")) + >>> result.sort(dfn.col("g"), dfn.col("v")).collect_column("lag").to_pylist() + [0, 1, 0] + + +.. py:function:: lambda_(params: list[str], body: datafusion.expr.Expr) -> datafusion.expr.Expr + + Create a lambda expression from parameter names and a body expression. + + This is the explicit form of building a lambda. Most callers can instead + pass a Python callable directly to a higher-order function such as + :py:func:`array_transform`, which builds the lambda automatically. Reach for + ``lambda_`` when you want explicit control over the parameter names. + + :param params: Ordered lambda parameter names. + :param body: Body expression that references the parameters via + :py:func:`lambda_var`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> double_fn = F.lambda_(["v"], F.lambda_var("v") * lit(2)) + >>> df.select( + ... F.array_transform(col("a"), double_fn).alias("d") + ... ).collect_column("d")[0].as_py() + [2, 4, 6] + + .. seealso:: :py:func:`lambda_var`, :py:func:`array_transform`, :py:func:`array_any_match`. + + +.. py:function:: lambda_var(name: str) -> datafusion.expr.Expr + + Create an unresolved reference to a lambda parameter by ``name``. + + Use this inside the body passed to :py:func:`lambda_` to refer to one of the + lambda's parameters. The owning higher-order function (such as + :py:func:`array_transform`) binds the variable to a concrete element type + during query planning. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [[1, 2, 3]]}) + >>> double_fn = F.lambda_(["v"], F.lambda_var("v") * lit(2)) + >>> df.select( + ... F.array_transform(col("a"), double_fn).alias("d") + ... ).collect_column("d")[0].as_py() + [2, 4, 6] + + .. seealso:: :py:func:`lambda_`, :py:func:`array_transform`, :py:func:`array_any_match`. + + +.. py:function:: last_value(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None, null_treatment: datafusion.common.NullTreatment = NullTreatment.RESPECT_NULLS) -> datafusion.expr.Expr + + Returns the last value in a group of values. + + This aggregate function will return the last value in the partition. + + If using the builder functions described in ref:`_aggregation` this function ignores + the option ``distinct``. + + :param expression: Argument to perform bitwise calculation on + :param filter: If provided, only compute against rows for which the filter is True + :param order_by: Set the ordering of the expression to evaluate. Accepts + column names or expressions. + :param null_treatment: Assign whether to respect or ignore null values. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [10, 20, 30]}) + >>> result = df.aggregate( + ... [], [dfn.functions.last_value( + ... dfn.col("a") + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 30 + + >>> df = ctx.from_pydict({"a": [None, 20, 10]}) + >>> result = df.aggregate( + ... [], [dfn.functions.last_value( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(10), + ... order_by="a", + ... null_treatment=dfn.common.NullTreatment.IGNORE_NULLS, + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 20 + + +.. py:function:: lcm(x: datafusion.expr.Expr, y: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the least common multiple. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [4], "b": [6]}) + >>> result = df.select( + ... dfn.functions.lcm(dfn.col("a"), dfn.col("b")).alias("lcm") + ... ) + >>> result.collect_column("lcm")[0].as_py() + 12 + + +.. py:function:: lead(arg: datafusion.expr.Expr, shift_offset: int = 1, default_value: Any | None = None, partition_by: list[datafusion.expr.Expr] | datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None) -> datafusion.expr.Expr + + Create a lead window function. + + Lead operation will return the argument that is in the next shift_offset-th row in + the partition. For example ``lead(col("b"), shift_offset=3, default_value=5)`` will + return the 3rd following value in column ``b``. At the end of the partition, where + no further values can be returned it will return the default value of 5. + + Here is an example of both the ``lead`` and :py:func:`datafusion.functions.lag` + functions on a simple DataFrame:: + + +--------+------+-----+ + | points | lead | lag | + +--------+------+-----+ + | 100 | 100 | | + | 100 | 50 | 100 | + | 50 | 25 | 100 | + | 25 | | 50 | + +--------+------+-----+ + + To set window function parameters use the window builder approach described in the + ref:`_window_functions` online documentation. + + :param arg: Value to return + :param shift_offset: Number of rows following the current row. + :param default_value: Value to return if shift_offet row does not exist. + :param partition_by: Expressions to partition the window frame on. + :param order_by: Set ordering within the window frame. Accepts + column names or expressions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.select( + ... dfn.col("a"), + ... dfn.functions.lead( + ... dfn.col("a"), shift_offset=1, + ... default_value=0, order_by="a" + ... ).alias("lead")) + >>> result.sort(dfn.col("a")).collect_column("lead").to_pylist() + [2, 3, 0] + + >>> df = ctx.from_pydict({"g": ["a", "a", "b"], "v": [1, 2, 3]}) + >>> result = df.select( + ... dfn.col("g"), dfn.col("v"), + ... dfn.functions.lead( + ... dfn.col("v"), shift_offset=1, default_value=0, + ... partition_by=dfn.col("g"), order_by="v", + ... ).alias("lead")) + >>> result.sort(dfn.col("g"), dfn.col("v")).collect_column("lead").to_pylist() + [2, 0, 0] + + +.. py:function:: least(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the least value from a list of expressions. + + Returns NULL if all expressions are NULL. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 3], "b": [2, 1]}) + >>> result = df.select( + ... dfn.functions.least(dfn.col("a"), dfn.col("b")).alias("least")) + >>> result.collect_column("least")[0].as_py() + 1 + >>> result.collect_column("least")[1].as_py() + 1 + + +.. py:function:: left(string: datafusion.expr.Expr, n: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Returns the first ``n`` characters in the ``string``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["the cat"]}) + >>> left_df = df.select( + ... dfn.functions.left(dfn.col("a"), 3).alias("left")) + >>> left_df.collect_column("left")[0].as_py() + 'the' + + +.. py:function:: length(string: datafusion.expr.Expr) -> datafusion.expr.Expr + + The number of characters in the ``string``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select(dfn.functions.length(dfn.col("a")).alias("len")) + >>> result.collect_column("len")[0].as_py() + 5 + + +.. py:function:: levenshtein(string1: datafusion.expr.Expr, string2: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Returns the Levenshtein distance between the two given strings. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["kitten"]}) + >>> result = df.select( + ... dfn.functions.levenshtein(dfn.col("a"), "sitting").alias("d")) + >>> result.collect_column("d")[0].as_py() + 3 + + +.. py:function:: list_any_match(array: datafusion.expr.Expr, predicate: datafusion.expr.Expr | collections.abc.Callable[Ellipsis, Any]) -> datafusion.expr.Expr + + Return ``True`` if any element of a list satisfies a predicate. + + .. seealso:: This is an alias for :py:func:`array_any_match`. + + +.. py:function:: list_any_value(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the first non-null element in the array. + + .. seealso:: This is an alias for :py:func:`array_any_value`. + + +.. py:function:: list_append(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Appends an element to the end of an array. + + .. seealso:: This is an alias for :py:func:`array_append`. + + +.. py:function:: list_cat(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Concatenates the input arrays. + + .. seealso:: This is an alias for :py:func:`array_concat`, :py:func:`array_cat`. + + +.. py:function:: list_compact(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Removes NULL values from the array. + + .. seealso:: This is an alias for :py:func:`array_compact`. + + +.. py:function:: list_concat(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Concatenates the input arrays. + + .. seealso:: This is an alias for :py:func:`array_concat`, :py:func:`array_cat`. + + +.. py:function:: list_contains(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns true if the element appears in the array, otherwise false. + + .. seealso:: This is an alias for :py:func:`array_has`. + + +.. py:function:: list_dims(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns an array of the array's dimensions. + + .. seealso:: This is an alias for :py:func:`array_dims`. + + +.. py:function:: list_distance(array1: datafusion.expr.Expr, array2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the Euclidean distance between two numeric arrays. + + .. seealso:: This is an alias for :py:func:`array_distance`. + + +.. py:function:: list_distinct(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns distinct values from the array after removing duplicates. + + .. seealso:: This is an alias for :py:func:`array_distinct`. + + +.. py:function:: list_element(array: datafusion.expr.Expr, n: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Extracts the element with the index n from the array. + + .. seealso:: This is an alias for :py:func:`array_element`. + + +.. py:function:: list_empty(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns a boolean indicating whether the array is empty. + + .. seealso:: This is an alias for :py:func:`array_empty`. + + +.. py:function:: list_except(array1: datafusion.expr.Expr, array2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the elements that appear in ``array1`` but not in the ``array2``. + + .. seealso:: This is an alias for :py:func:`array_except`. + + +.. py:function:: list_extract(array: datafusion.expr.Expr, n: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Extracts the element with the index n from the array. + + .. seealso:: This is an alias for :py:func:`array_element`. + + +.. py:function:: list_filter(array: datafusion.expr.Expr, predicate: datafusion.expr.Expr | collections.abc.Callable[Ellipsis, Any]) -> datafusion.expr.Expr + + Keep the elements of a list for which a predicate is ``True``. + + .. seealso:: This is an alias for :py:func:`array_filter`. + + +.. py:function:: list_has(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns true if the element appears in the array, otherwise false. + + .. seealso:: This is an alias for :py:func:`array_has`. + + +.. py:function:: list_has_all(first_array: datafusion.expr.Expr, second_array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Determines if there is complete overlap ``second_array`` in ``first_array``. + + .. seealso:: This is an alias for :py:func:`array_has_all`. + + +.. py:function:: list_has_any(first_array: datafusion.expr.Expr, second_array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Determine if there is an overlap between ``first_array`` and ``second_array``. + + .. seealso:: This is an alias for :py:func:`array_has_any`. + + +.. py:function:: list_indexof(array: datafusion.expr.Expr, element: datafusion.expr.Expr, index: int | None = 1) -> datafusion.expr.Expr + + Return the position of the first occurrence of ``element`` in ``array``. + + .. seealso:: This is an alias for :py:func:`array_position`. + + +.. py:function:: list_intersect(array1: datafusion.expr.Expr, array2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns an the intersection of ``array1`` and ``array2``. + + .. seealso:: This is an alias for :py:func:`array_intersect`. + + +.. py:function:: list_join(expr: datafusion.expr.Expr, delimiter: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts each element to its text representation. + + .. seealso:: This is an alias for :py:func:`array_to_string`. + + +.. py:function:: list_length(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the length of the array. + + .. seealso:: This is an alias for :py:func:`array_length`. + + +.. py:function:: list_max(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the maximum value in the array. + + .. seealso:: This is an alias for :py:func:`array_max`. + + +.. py:function:: list_min(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the minimum value in the array. + + .. seealso:: This is an alias for :py:func:`array_min`. + + +.. py:function:: list_ndims(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the number of dimensions of the array. + + .. seealso:: This is an alias for :py:func:`array_ndims`. + + +.. py:function:: list_normalize(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Scales a numeric array so it has Euclidean length 1. + + .. seealso:: This is an alias for :py:func:`array_normalize`. + + +.. py:function:: list_overlap(first_array: datafusion.expr.Expr, second_array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns true if any element appears in both arrays. + + .. seealso:: This is an alias for :py:func:`array_has_any`. + + +.. py:function:: list_pop_back(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the array without the last element. + + .. seealso:: This is an alias for :py:func:`array_pop_back`. + + +.. py:function:: list_pop_front(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the array without the first element. + + .. seealso:: This is an alias for :py:func:`array_pop_front`. + + +.. py:function:: list_position(array: datafusion.expr.Expr, element: datafusion.expr.Expr, index: int | None = 1) -> datafusion.expr.Expr + + Return the position of the first occurrence of ``element`` in ``array``. + + .. seealso:: This is an alias for :py:func:`array_position`. + + +.. py:function:: list_positions(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Searches for an element in the array and returns all occurrences. + + .. seealso:: This is an alias for :py:func:`array_positions`. + + +.. py:function:: list_prepend(element: datafusion.expr.Expr, array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Prepends an element to the beginning of an array. + + .. seealso:: This is an alias for :py:func:`array_prepend`. + + +.. py:function:: list_push_back(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Appends an element to the end of an array. + + .. seealso:: This is an alias for :py:func:`array_append`. + + +.. py:function:: list_push_front(element: datafusion.expr.Expr, array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Prepends an element to the beginning of an array. + + .. seealso:: This is an alias for :py:func:`array_prepend`. + + +.. py:function:: list_remove(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Removes the first element from the array equal to the given value. + + .. seealso:: This is an alias for :py:func:`array_remove`. + + +.. py:function:: list_remove_all(array: datafusion.expr.Expr, element: datafusion.expr.Expr) -> datafusion.expr.Expr + + Removes all elements from the array equal to the given value. + + .. seealso:: This is an alias for :py:func:`array_remove_all`. + + +.. py:function:: list_remove_n(array: datafusion.expr.Expr, element: datafusion.expr.Expr, max: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Removes the first ``max`` elements from the array equal to the given value. + + .. seealso:: This is an alias for :py:func:`array_remove_n`. + + +.. py:function:: list_repeat(element: datafusion.expr.Expr, count: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Returns an array containing ``element`` ``count`` times. + + .. seealso:: This is an alias for :py:func:`array_repeat`. + + +.. py:function:: list_replace(array: datafusion.expr.Expr, from_val: datafusion.expr.Expr, to_val: datafusion.expr.Expr) -> datafusion.expr.Expr + + Replaces the first occurrence of ``from_val`` with ``to_val``. + + .. seealso:: This is an alias for :py:func:`array_replace`. + + +.. py:function:: list_replace_all(array: datafusion.expr.Expr, from_val: datafusion.expr.Expr, to_val: datafusion.expr.Expr) -> datafusion.expr.Expr + + Replaces all occurrences of ``from_val`` with ``to_val``. + + .. seealso:: This is an alias for :py:func:`array_replace_all`. + + +.. py:function:: list_replace_n(array: datafusion.expr.Expr, from_val: datafusion.expr.Expr, to_val: datafusion.expr.Expr, max: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Replace ``n`` occurrences of ``from_val`` with ``to_val``. + + Replaces the first ``max`` occurrences of the specified element with another + specified element. + + .. seealso:: This is an alias for :py:func:`array_replace_n`. + + +.. py:function:: list_resize(array: datafusion.expr.Expr, size: datafusion.expr.Expr | int, value: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns an array with the specified size filled. + + If ``size`` is greater than the ``array`` length, the additional entries will be + filled with the given ``value``. + + .. seealso:: This is an alias for :py:func:`array_resize`. + + +.. py:function:: list_reverse(array: datafusion.expr.Expr) -> datafusion.expr.Expr + + Reverses the order of elements in the array. + + .. seealso:: This is an alias for :py:func:`array_reverse`. + + +.. py:function:: list_slice(array: datafusion.expr.Expr, begin: datafusion.expr.Expr | int, end: datafusion.expr.Expr | int, stride: datafusion.expr.Expr | int | None = None) -> datafusion.expr.Expr + + Returns a slice of the array. + + .. seealso:: This is an alias for :py:func:`array_slice`. + + +.. py:function:: list_sort(array: datafusion.expr.Expr, descending: bool = False, null_first: bool = False) -> datafusion.expr.Expr + + Sorts the array. + + .. seealso:: This is an alias for :py:func:`array_sort`. + + +.. py:function:: list_to_string(expr: datafusion.expr.Expr, delimiter: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts each element to its text representation. + + .. seealso:: This is an alias for :py:func:`array_to_string`. + + +.. py:function:: list_transform(array: datafusion.expr.Expr, transform: datafusion.expr.Expr | collections.abc.Callable[Ellipsis, Any]) -> datafusion.expr.Expr + + Transform each element of a list with a lambda. + + .. seealso:: This is an alias for :py:func:`array_transform`. + + +.. py:function:: list_union(array1: datafusion.expr.Expr, array2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns an array of the elements in the union of array1 and array2. + + Duplicate rows will not be returned. + + .. seealso:: This is an alias for :py:func:`array_union`. + + +.. py:function:: list_zip(*arrays: datafusion.expr.Expr) -> datafusion.expr.Expr + + Combines multiple arrays into a single array of structs. + + .. seealso:: This is an alias for :py:func:`arrays_zip`. + + +.. py:function:: ln(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the natural logarithm (base e) of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0]}) + >>> result = df.select(dfn.functions.ln(dfn.col("a")).alias("ln")) + >>> result.collect_column("ln")[0].as_py() + 0.0 + + +.. py:function:: log(base: datafusion.expr.Expr | int | float, num: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the logarithm of a number for a particular ``base``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [100.0]}) + >>> result = df.select( + ... dfn.functions.log(10.0, dfn.col("a")).alias("log") + ... ) + >>> result.collect_column("log")[0].as_py() + 2.0 + + +.. py:function:: log10(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Base 10 logarithm of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [100.0]}) + >>> result = df.select(dfn.functions.log10(dfn.col("a")).alias("log10")) + >>> result.collect_column("log10")[0].as_py() + 2.0 + + +.. py:function:: log2(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Base 2 logarithm of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [8.0]}) + >>> result = df.select(dfn.functions.log2(dfn.col("a")).alias("log2")) + >>> result.collect_column("log2")[0].as_py() + 3.0 + + +.. py:function:: lower(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Converts a string to lowercase. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["THE CaT"]}) + >>> lower_df = df.select(dfn.functions.lower(dfn.col("a")).alias("lower")) + >>> lower_df.collect_column("lower")[0].as_py() + 'the cat' + + +.. py:function:: lpad(string: datafusion.expr.Expr, count: datafusion.expr.Expr | int, characters: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Add left padding to a string. + + Extends the string to length length by prepending the characters fill (a + space by default). If the string is already longer than length then it is + truncated (on the right). + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["the cat", "a hat"]}) + >>> lpad_df = df.select( + ... dfn.functions.lpad(dfn.col("a"), 6).alias("lpad")) + >>> lpad_df.collect_column("lpad")[0].as_py() + 'the ca' + >>> lpad_df.collect_column("lpad")[1].as_py() + ' a hat' + + >>> result = df.select( + ... dfn.functions.lpad( + ... dfn.col("a"), 10, characters="." + ... ).alias("lpad")) + >>> result.collect_column("lpad")[0].as_py() + '...the cat' + + +.. py:function:: ltrim(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Removes all characters, spaces by default, from the beginning of a string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [" a "]}) + >>> trim_df = df.select(dfn.functions.ltrim(dfn.col("a")).alias("trimmed")) + >>> trim_df.collect_column("trimmed")[0].as_py() + 'a ' + + +.. py:function:: make_array(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns an array using the specified input expressions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.make_array( + ... dfn.lit(1), dfn.lit(2), dfn.lit(3) + ... ).alias("arr")) + >>> result.collect_column("arr")[0].as_py() + [1, 2, 3] + + +.. py:function:: make_date(year: datafusion.expr.Expr | int, month: datafusion.expr.Expr | int, day: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Make a date from year, month and day component parts. + + .. rubric:: Examples + + >>> from datetime import date + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"y": [2024], "m": [1], "d": [15]}) + >>> result = df.select( + ... dfn.functions.make_date(dfn.col("y"), dfn.col("m"), + ... dfn.col("d")).alias("dt")) + >>> result.collect_column("dt")[0].as_py() + datetime.date(2024, 1, 15) + + Pass bare ints for any component: + + >>> df = ctx.from_pydict({"y": [2024]}) + >>> result = df.select( + ... dfn.functions.make_date(dfn.col("y"), 1, 15).alias("dt")) + >>> result.collect_column("dt")[0].as_py() + datetime.date(2024, 1, 15) + + +.. py:function:: make_list(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns an array using the specified input expressions. + + .. seealso:: This is an alias for :py:func:`make_array`. + + +.. py:function:: make_map(*args: Any) -> datafusion.expr.Expr + + Returns a map expression. + + Supports three calling conventions: + + - ``make_map({"a": 1, "b": 2})`` — from a Python dictionary. + - ``make_map([keys], [values])`` — from a list of keys and a list of + their associated values. Both lists must be the same length. + - ``make_map(k1, v1, k2, v2, ...)`` — from alternating keys and their + associated values. + + Keys and values that are not already :py:class:`~datafusion.expr.Expr` + are automatically converted to literal expressions. + + .. rubric:: Examples + + From a dictionary: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.make_map({"a": 1, "b": 2}).alias("m")) + >>> result.collect_column("m")[0].as_py() + [('a', 1), ('b', 2)] + + From two lists: + + >>> df = ctx.from_pydict({"key": ["x", "y"], "val": [10, 20]}) + >>> df = df.select( + ... dfn.functions.make_map( + ... [dfn.col("key")], [dfn.col("val")] + ... ).alias("m")) + >>> df.collect_column("m")[0].as_py() + [('x', 10)] + + From alternating keys and values: + + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.make_map("x", 1, "y", 2).alias("m")) + >>> result.collect_column("m")[0].as_py() + [('x', 1), ('y', 2)] + + +.. py:function:: make_time(hour: datafusion.expr.Expr | int, minute: datafusion.expr.Expr | int, second: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Make a time from hour, minute and second component parts. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"h": [12], "m": [30], "s": [0]}) + >>> result = df.select( + ... dfn.functions.make_time(dfn.col("h"), dfn.col("m"), + ... dfn.col("s")).alias("t")) + >>> result.collect_column("t")[0].as_py() + datetime.time(12, 30) + + Pass bare ints for any component: + + >>> df = ctx.from_pydict({"h": [12]}) + >>> result = df.select( + ... dfn.functions.make_time(dfn.col("h"), 30, 0).alias("t")) + >>> result.collect_column("t")[0].as_py() + datetime.time(12, 30) + + +.. py:function:: map_entries(map: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns a list of all entries (key-value struct pairs) in the map. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> df = df.select( + ... dfn.functions.make_map({"x": 1, "y": 2}).alias("m")) + >>> result = df.select( + ... dfn.functions.map_entries(dfn.col("m")).alias("entries")) + >>> result.collect_column("entries")[0].as_py() + [{'key': 'x', 'value': 1}, {'key': 'y', 'value': 2}] + + +.. py:function:: map_extract(map: datafusion.expr.Expr, key: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the value for a given key in the map. + + Returns ``[None]`` if the key is absent. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> df = df.select( + ... dfn.functions.make_map({"x": 1, "y": 2}).alias("m")) + >>> result = df.select( + ... dfn.functions.map_extract( + ... dfn.col("m"), dfn.lit("x") + ... ).alias("val")) + >>> result.collect_column("val")[0].as_py() + [1] + + +.. py:function:: map_keys(map: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns a list of all keys in the map. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> df = df.select( + ... dfn.functions.make_map({"x": 1, "y": 2}).alias("m")) + >>> result = df.select( + ... dfn.functions.map_keys(dfn.col("m")).alias("keys")) + >>> result.collect_column("keys")[0].as_py() + ['x', 'y'] + + +.. py:function:: map_values(map: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns a list of all values in the map. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> df = df.select( + ... dfn.functions.make_map({"x": 1, "y": 2}).alias("m")) + >>> result = df.select( + ... dfn.functions.map_values(dfn.col("m")).alias("vals")) + >>> result.collect_column("vals")[0].as_py() + [1, 2] + + +.. py:function:: max(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Aggregate function that returns the maximum value of the argument. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param expression: The value to find the maximum of + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.max( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 3 + + >>> result = df.aggregate( + ... [], [dfn.functions.max( + ... dfn.col("a"), + ... filter=dfn.col("a") < dfn.lit(3) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2 + + +.. py:function:: md5(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Computes an MD5 128-bit checksum for a string expression. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select(dfn.functions.md5(dfn.col("a")).alias("md5")) + >>> result.collect_column("md5")[0].as_py() + '5d41402abc4b2a76b9719d911017c592' + + +.. py:function:: mean(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Returns the average (mean) value of the argument. + + .. seealso:: This is an alias for :py:func:`avg`. + + +.. py:function:: median(expression: datafusion.expr.Expr, distinct: bool = False, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the median of a set of numbers. + + This aggregate function returns the median value of the expression for the given + aggregate function. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by`` and ``null_treatment``. + + :param expression: The value to compute the median of + :param distinct: If True, a single entry for each distinct value will be in the result + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.median( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.0 + + >>> df = ctx.from_pydict({"a": [1.0, 1.0, 2.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.median( + ... dfn.col("a"), distinct=True, + ... filter=dfn.col("a") < dfn.lit(3.0), + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 1.5 + + +.. py:function:: min(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Aggregate function that returns the minimum value of the argument. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param expression: The value to find the minimum of + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.min( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 1 + + >>> result = df.aggregate( + ... [], [dfn.functions.min( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(1) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2 + + +.. py:function:: named_struct(name_pairs: list[tuple[str, datafusion.expr.Expr]]) -> datafusion.expr.Expr + + Returns a struct with the given names and arguments pairs. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.named_struct( + ... [("x", dfn.lit(10)), ("y", dfn.lit(20))] + ... ).alias("s") + ... ) + >>> result.collect_column("s")[0].as_py() == {"x": 10, "y": 20} + True + + +.. py:function:: nanvl(x: datafusion.expr.Expr, y: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns ``x`` if ``x`` is not ``NaN``. Otherwise returns ``y``. + + :param x: Expression to return when it is not NaN. + :param y: Fallback expression to return when ``x`` is NaN. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [np.nan, 1.0], "b": [0.0, 0.0]}) + >>> nanvl_df = df.select( + ... dfn.functions.nanvl(dfn.col("a"), dfn.col("b")).alias("nanvl")) + >>> nanvl_df.collect_column("nanvl")[0].as_py() + 0.0 + >>> nanvl_df.collect_column("nanvl")[1].as_py() + 1.0 + + +.. py:function:: now() -> datafusion.expr.Expr + + Returns the current timestamp in nanoseconds. + + This will use the same value for all instances of now() in same statement. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.now().alias("now") + ... ) + + Use .value instead of .as_py() because nanosecond timestamps + require pandas to convert to Python datetime objects. + + >>> result.collect_column("now")[0].value > 0 + True + + +.. py:function:: nth_value(expression: datafusion.expr.Expr, n: int, filter: datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None, null_treatment: datafusion.common.NullTreatment = NullTreatment.RESPECT_NULLS) -> datafusion.expr.Expr + + Returns the n-th value in a group of values. + + This aggregate function will return the n-th value in the partition. + + If using the builder functions described in ref:`_aggregation` this function ignores + the option ``distinct``. + + :param expression: Argument to perform bitwise calculation on + :param n: Index of value to return. Starts at 1. + :param filter: If provided, only compute against rows for which the filter is True + :param order_by: Set the ordering of the expression to evaluate. Accepts + column names or expressions. + :param null_treatment: Assign whether to respect or ignore null values. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [10, 20, 30]}) + >>> result = df.aggregate( + ... [], [dfn.functions.nth_value( + ... dfn.col("a"), 1 + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 10 + + >>> result = df.aggregate( + ... [], [dfn.functions.nth_value( + ... dfn.col("a"), 1, + ... filter=dfn.col("a") > dfn.lit(10), + ... order_by="a", + ... null_treatment=dfn.common.NullTreatment.IGNORE_NULLS, + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 20 + + +.. py:function:: ntile(groups: int, partition_by: list[datafusion.expr.Expr] | datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None) -> datafusion.expr.Expr + + Create a n-tile window function. + + This window function orders the window frame into a give number of groups based on + the ordering criteria. It then returns which group the current row is assigned to. + Here is an example of a dataframe with a window ordered by descending ``points`` + and the associated n-tile function:: + + +--------+-------+ + | points | ntile | + +--------+-------+ + | 120 | 1 | + | 100 | 1 | + | 80 | 2 | + | 60 | 2 | + | 40 | 3 | + | 20 | 3 | + +--------+-------+ + + :param groups: Number of groups for the n-tile to be divided into. + :param partition_by: Expressions to partition the window frame on. + :param order_by: Set ordering within the window frame. Accepts + column names or expressions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [10, 20, 30, 40]}) + >>> result = df.select( + ... dfn.col("a"), + ... dfn.functions.ntile( + ... 2, order_by="a" + ... ).alias("nt")) + >>> result.sort(dfn.col("a")).collect_column("nt").to_pylist() + [1, 1, 2, 2] + + >>> df = ctx.from_pydict( + ... {"g": ["a", "a", "b", "b"], "v": [1, 2, 3, 4]}) + >>> result = df.select( + ... dfn.col("g"), dfn.col("v"), + ... dfn.functions.ntile( + ... 2, partition_by=dfn.col("g"), order_by="v", + ... ).alias("nt")) + >>> result.sort(dfn.col("g"), dfn.col("v")).collect_column("nt").to_pylist() + [1, 2, 1, 2] + + +.. py:function:: nullif(expr1: datafusion.expr.Expr, expr2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns NULL if expr1 equals expr2; otherwise it returns expr1. + + This can be used to perform the inverse operation of the COALESCE expression. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2], "b": [1, 3]}) + >>> result = df.select( + ... dfn.functions.nullif(dfn.col("a"), dfn.col("b")).alias("nullif")) + >>> result.collect_column("nullif").to_pylist() + [None, 2] + + +.. py:function:: nvl(x: datafusion.expr.Expr, y: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns ``x`` if ``x`` is not ``NULL``. Otherwise returns ``y``. + + :param x: Expression to return when it is not NULL. + :param y: Fallback expression to return when ``x`` is NULL. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [None, 1], "b": [0, 0]}) + >>> nvl_df = df.select( + ... dfn.functions.nvl(dfn.col("a"), dfn.col("b")).alias("nvl") + ... ) + >>> nvl_df.collect_column("nvl")[0].as_py() + 0 + >>> nvl_df.collect_column("nvl")[1].as_py() + 1 + + +.. py:function:: nvl2(x: datafusion.expr.Expr, y: datafusion.expr.Expr, z: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns ``y`` if ``x`` is not NULL. Otherwise returns ``z``. + + :param x: Expression to check for NULL. + :param y: Expression to return when ``x`` is not NULL. + :param z: Expression to return when ``x`` is NULL. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [None, 1], "b": [10, 20], "c": [30, 40]}) + >>> result = df.select( + ... dfn.functions.nvl2( + ... dfn.col("a"), dfn.col("b"), dfn.col("c")).alias("nvl2") + ... ) + >>> result.collect_column("nvl2")[0].as_py() + 30 + >>> result.collect_column("nvl2")[1].as_py() + 20 + + +.. py:function:: octet_length(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the number of bytes of a string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select(dfn.functions.octet_length(dfn.col("a")).alias("len")) + >>> result.collect_column("len")[0].as_py() + 5 + + +.. py:function:: order_by(expr: datafusion.expr.Expr, ascending: bool = True, nulls_first: bool = True) -> datafusion.expr.SortExpr + + Creates a new sort expression. + + .. rubric:: Examples + + >>> sort_expr = dfn.functions.order_by( + ... dfn.col("a"), ascending=False) + >>> sort_expr.ascending() + False + + >>> sort_expr = dfn.functions.order_by( + ... dfn.col("a"), ascending=True, nulls_first=False) + >>> sort_expr.nulls_first() + False + + +.. py:function:: overlay(string: datafusion.expr.Expr, substring: datafusion.expr.Expr | str, start: datafusion.expr.Expr | int, length: datafusion.expr.Expr | int | None = None) -> datafusion.expr.Expr + + Replace a substring with a new substring. + + Replace the substring of string that starts at the ``start``'th character and + extends for ``length`` characters with new substring. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["abcdef"]}) + >>> result = df.select( + ... dfn.functions.overlay(dfn.col("a"), "XY", 3, 2).alias("o")) + >>> result.collect_column("o")[0].as_py() + 'abXYef' + + +.. py:function:: percent_rank(partition_by: list[datafusion.expr.Expr] | datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None) -> datafusion.expr.Expr + + Create a percent_rank window function. + + This window function is similar to :py:func:`rank` except that the returned values + are the percentage from 0.0 to 1.0 from first to last. Here is an example of a + dataframe with a window ordered by descending ``points`` and the associated percent + rank:: + + +--------+--------------+ + | points | percent_rank | + +--------+--------------+ + | 100 | 0.0 | + | 100 | 0.0 | + | 50 | 0.666667 | + | 25 | 1.0 | + +--------+--------------+ + + :param partition_by: Expressions to partition the window frame on. + :param order_by: Set ordering within the window frame. Accepts + column names or expressions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [10, 20, 30]}) + >>> result = df.select( + ... dfn.col("a"), + ... dfn.functions.percent_rank( + ... order_by="a" + ... ).alias("pr")) + >>> result.sort(dfn.col("a")).collect_column("pr").to_pylist() + [0.0, 0.5, 1.0] + + >>> df = ctx.from_pydict( + ... {"g": ["a", "a", "a", "b", "b"], "v": [1, 2, 3, 4, 5]}) + >>> result = df.select( + ... dfn.col("g"), dfn.col("v"), + ... dfn.functions.percent_rank( + ... partition_by=dfn.col("g"), order_by="v", + ... ).alias("pr")) + >>> result.sort(dfn.col("g"), dfn.col("v")).collect_column("pr").to_pylist() + [0.0, 0.5, 1.0, 0.0, 1.0] + + +.. py:function:: percentile_cont(sort_expression: datafusion.expr.Expr | datafusion.expr.SortExpr, percentile: float, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the exact percentile of input values using continuous interpolation. + + Unlike :py:func:`approx_percentile_cont`, this function computes the exact + percentile value rather than an approximation. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param sort_expression: Values for which to find the percentile + :param percentile: This must be between 0.0 and 1.0, inclusive + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0, 4.0, 5.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.percentile_cont( + ... dfn.col("a"), 0.5 + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 3.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.percentile_cont( + ... dfn.col("a"), 0.5, + ... filter=dfn.col("a") > dfn.lit(1.0), + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 3.5 + + +.. py:function:: pi() -> datafusion.expr.Expr + + Returns an approximate value of π. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> from math import pi + >>> result = df.select( + ... dfn.functions.pi().alias("pi") + ... ) + >>> result.collect_column("pi")[0].as_py() == pi + True + + +.. py:function:: position(string: datafusion.expr.Expr, substring: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Finds the position from where the ``substring`` matches the ``string``. + + .. seealso:: This is an alias for :py:func:`strpos`. + + +.. py:function:: pow(base: datafusion.expr.Expr, exponent: datafusion.expr.Expr | int | float) -> datafusion.expr.Expr + + Returns ``base`` raised to the power of ``exponent``. + + .. seealso:: This is an alias of :py:func:`power`. + + +.. py:function:: power(base: datafusion.expr.Expr, exponent: datafusion.expr.Expr | int | float) -> datafusion.expr.Expr + + Returns ``base`` raised to the power of ``exponent``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [2.0]}) + >>> result = df.select( + ... dfn.functions.power(dfn.col("a"), 3.0).alias("pow") + ... ) + >>> result.collect_column("pow")[0].as_py() + 8.0 + + +.. py:function:: quantile_cont(sort_expression: datafusion.expr.Expr | datafusion.expr.SortExpr, percentile: float, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the exact percentile of input values using continuous interpolation. + + .. seealso:: This is an alias for :py:func:`percentile_cont`. + + +.. py:function:: radians(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Converts the argument from degrees to radians. + + .. rubric:: Examples + + >>> from math import pi + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [180.0]}) + >>> result = df.select( + ... dfn.functions.radians(dfn.col("a")).alias("rad") + ... ) + >>> result.collect_column("rad")[0].as_py() == pi + True + + +.. py:function:: random() -> datafusion.expr.Expr + + Returns a random value in the range ``0.0 <= x < 1.0``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.random().alias("r") + ... ) + >>> val = result.collect_column("r")[0].as_py() + >>> 0.0 <= val < 1.0 + True + + +.. py:function:: range(start: datafusion.expr.Expr, stop: datafusion.expr.Expr, step: datafusion.expr.Expr) -> datafusion.expr.Expr + + Create a list of values in the range between start and stop. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.range(dfn.lit(0), dfn.lit(5), dfn.lit(2)).alias("r")) + >>> result.collect_column("r")[0].as_py() + [0, 2, 4] + + +.. py:function:: rank(partition_by: list[datafusion.expr.Expr] | datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None) -> datafusion.expr.Expr + + Create a rank window function. + + Returns the rank based upon the window order. Consecutive equal values will receive + the same rank, but the next different value will not be consecutive but rather the + number of rows that precede it plus one. This is similar to Olympic medals. If two + people tie for gold, the next place is bronze. There would be no silver medal. Here + is an example of a dataframe with a window ordered by descending ``points`` and the + associated rank. + + You should set ``order_by`` to produce meaningful results:: + + +--------+------+ + | points | rank | + +--------+------+ + | 100 | 1 | + | 100 | 1 | + | 50 | 3 | + | 25 | 4 | + +--------+------+ + + :param partition_by: Expressions to partition the window frame on. + :param order_by: Set ordering within the window frame. Accepts + column names or expressions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [10, 10, 20]}) + >>> result = df.select( + ... dfn.col("a"), + ... dfn.functions.rank( + ... order_by="a" + ... ).alias("rnk") + ... ) + >>> result.sort(dfn.col("a")).collect_column("rnk").to_pylist() + [1, 1, 3] + + >>> df = ctx.from_pydict( + ... {"g": ["a", "a", "b", "b"], "v": [1, 1, 2, 3]}) + >>> result = df.select( + ... dfn.col("g"), dfn.col("v"), + ... dfn.functions.rank( + ... partition_by=dfn.col("g"), order_by="v", + ... ).alias("rnk")) + >>> result.sort(dfn.col("g"), dfn.col("v")).collect_column("rnk").to_pylist() + [1, 1, 1, 2] + + +.. py:function:: regexp_count(string: datafusion.expr.Expr, pattern: datafusion.expr.Expr | str, start: datafusion.expr.Expr | int | None = None, flags: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Returns the number of matches in a string. + + Optional start position (the first position is 1) to search for the regular + expression. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["abcabc"]}) + >>> result = df.select( + ... dfn.functions.regexp_count(dfn.col("a"), "abc").alias("c")) + >>> result.collect_column("c")[0].as_py() + 2 + + Use ``start`` to begin searching from a position, and + ``flags`` for case-insensitive matching: + + >>> result = df.select( + ... dfn.functions.regexp_count( + ... dfn.col("a"), "ABC", start=4, flags="i", + ... ).alias("c")) + >>> result.collect_column("c")[0].as_py() + 1 + + +.. py:function:: regexp_instr(values: datafusion.expr.Expr, regex: datafusion.expr.Expr | str, start: datafusion.expr.Expr | int | None = None, n: datafusion.expr.Expr | int | None = None, flags: datafusion.expr.Expr | str | None = None, sub_expr: datafusion.expr.Expr | int | None = None) -> datafusion.expr.Expr + + Returns the position of a regular expression match in a string. + + :param values: Data to search for the regular expression match. + :param regex: Regular expression to search for. + :param start: Optional position to start the search (the first position is 1). + :param n: Optional occurrence of the match to find (the first occurrence is 1). + :param flags: Optional regular expression flags to control regex behavior. + :param sub_expr: Optionally capture group position instead of the entire match. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello 42 world"]}) + >>> result = df.select( + ... dfn.functions.regexp_instr(dfn.col("a"), "\\d+").alias("pos") + ... ) + >>> result.collect_column("pos")[0].as_py() + 7 + + Use ``start`` to search from a position, ``n`` for the + nth occurrence, and ``flags`` for case-insensitive mode: + + >>> df = ctx.from_pydict({"a": ["abc ABC abc"]}) + >>> result = df.select( + ... dfn.functions.regexp_instr( + ... dfn.col("a"), "abc", + ... start=2, n=1, flags="i", + ... ).alias("pos") + ... ) + >>> result.collect_column("pos")[0].as_py() + 5 + + Use ``sub_expr`` to get the position of a capture group: + + >>> result = df.select( + ... dfn.functions.regexp_instr( + ... dfn.col("a"), "(abc)", sub_expr=1, + ... ).alias("pos") + ... ) + >>> result.collect_column("pos")[0].as_py() + 1 + + +.. py:function:: regexp_like(string: datafusion.expr.Expr, regex: datafusion.expr.Expr | str, flags: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Find if any regular expression (regex) matches exist. + + Tests a string using a regular expression returning true if at least one match, + false otherwise. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello123"]}) + >>> result = df.select( + ... dfn.functions.regexp_like(dfn.col("a"), "\\d+").alias("m") + ... ) + >>> result.collect_column("m")[0].as_py() + True + + Use ``flags`` for case-insensitive matching: + + >>> result = df.select( + ... dfn.functions.regexp_like( + ... dfn.col("a"), "HELLO", flags="i", + ... ).alias("m") + ... ) + >>> result.collect_column("m")[0].as_py() + True + + +.. py:function:: regexp_match(string: datafusion.expr.Expr, regex: datafusion.expr.Expr | str, flags: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Perform regular expression (regex) matching. + + Returns an array with each element containing the leftmost-first match of the + corresponding index in ``regex`` to string in ``string``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello 42 world"]}) + >>> result = df.select( + ... dfn.functions.regexp_match(dfn.col("a"), "(\\d+)").alias("m") + ... ) + >>> result.collect_column("m")[0].as_py() + ['42'] + + Use ``flags`` for case-insensitive matching: + + >>> result = df.select( + ... dfn.functions.regexp_match( + ... dfn.col("a"), "(HELLO)", flags="i", + ... ).alias("m") + ... ) + >>> result.collect_column("m")[0].as_py() + ['hello'] + + +.. py:function:: regexp_replace(string: datafusion.expr.Expr, pattern: datafusion.expr.Expr | str, replacement: datafusion.expr.Expr | str, flags: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Replaces substring(s) matching a PCRE-like regular expression. + + The full list of supported features and syntax can be found at + + + Supported flags with the addition of 'g' can be found at + + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello 42"]}) + >>> result = df.select( + ... dfn.functions.regexp_replace( + ... dfn.col("a"), "\\d+", "XX" + ... ).alias("r") + ... ) + >>> result.collect_column("r")[0].as_py() + 'hello XX' + + Use the ``g`` flag to replace all occurrences: + + >>> df = ctx.from_pydict({"a": ["a1 b2 c3"]}) + >>> result = df.select( + ... dfn.functions.regexp_replace( + ... dfn.col("a"), "\\d+", "X", flags="g", + ... ).alias("r") + ... ) + >>> result.collect_column("r")[0].as_py() + 'aX bX cX' + + +.. py:function:: regr_avgx(y: datafusion.expr.Expr, x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the average of the independent variable ``x``. + + This is a linear regression aggregate function. Only non-null pairs of the inputs + are evaluated. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param y: The linear regression dependent variable + :param x: The linear regression independent variable + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"y": [1.0, 2.0, 3.0], "x": [4.0, 5.0, 6.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.regr_avgx( + ... dfn.col("y"), dfn.col("x") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 5.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.regr_avgx( + ... dfn.col("y"), dfn.col("x"), + ... filter=dfn.col("y") > dfn.lit(1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 5.5 + + +.. py:function:: regr_avgy(y: datafusion.expr.Expr, x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the average of the dependent variable ``y``. + + This is a linear regression aggregate function. Only non-null pairs of the inputs + are evaluated. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param y: The linear regression dependent variable + :param x: The linear regression independent variable + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"y": [1.0, 2.0, 3.0], "x": [4.0, 5.0, 6.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.regr_avgy( + ... dfn.col("y"), dfn.col("x") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.regr_avgy( + ... dfn.col("y"), dfn.col("x"), + ... filter=dfn.col("y") > dfn.lit(1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.5 + + +.. py:function:: regr_count(y: datafusion.expr.Expr, x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Counts the number of rows in which both expressions are not null. + + This is a linear regression aggregate function. Only non-null pairs of the inputs + are evaluated. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param y: The linear regression dependent variable + :param x: The linear regression independent variable + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"y": [1.0, 2.0, 3.0], "x": [4.0, 5.0, 6.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.regr_count( + ... dfn.col("y"), dfn.col("x") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 3 + + >>> result = df.aggregate( + ... [], [dfn.functions.regr_count( + ... dfn.col("y"), dfn.col("x"), + ... filter=dfn.col("y") > dfn.lit(1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2 + + +.. py:function:: regr_intercept(y: datafusion.expr.Expr, x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the intercept from the linear regression. + + This is a linear regression aggregate function. Only non-null pairs of the inputs + are evaluated. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param y: The linear regression dependent variable + :param x: The linear regression independent variable + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"y": [2.0, 4.0, 6.0], "x": [4.0, 16.0, 36.0]}) + >>> result = df.aggregate( + ... [], + ... [dfn.functions.regr_intercept( + ... dfn.col("y"), dfn.col("x") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 1.714... + + >>> result = df.aggregate( + ... [], + ... [dfn.functions.regr_intercept( + ... dfn.col("y"), dfn.col("x"), + ... filter=dfn.col("y") > dfn.lit(2.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.4 + + +.. py:function:: regr_r2(y: datafusion.expr.Expr, x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the R-squared value from linear regression. + + This is a linear regression aggregate function. Only non-null pairs of the inputs + are evaluated. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param y: The linear regression dependent variable + :param x: The linear regression independent variable + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"y": [2.0, 4.0, 6.0], "x": [4.0, 16.0, 36.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.regr_r2( + ... dfn.col("y"), dfn.col("x") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 0.9795... + + >>> result = df.aggregate( + ... [], [dfn.functions.regr_r2( + ... dfn.col("y"), dfn.col("x"), + ... filter=dfn.col("y") > dfn.lit(2.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 1.0 + + +.. py:function:: regr_slope(y: datafusion.expr.Expr, x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the slope from linear regression. + + This is a linear regression aggregate function. Only non-null pairs of the inputs + are evaluated. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param y: The linear regression dependent variable + :param x: The linear regression independent variable + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"y": [2.0, 4.0, 6.0], "x": [4.0, 16.0, 36.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.regr_slope( + ... dfn.col("y"), dfn.col("x") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 0.122... + + >>> result = df.aggregate( + ... [], [dfn.functions.regr_slope( + ... dfn.col("y"), dfn.col("x"), + ... filter=dfn.col("y") > dfn.lit(2.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 0.1 + + +.. py:function:: regr_sxx(y: datafusion.expr.Expr, x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the sum of squares of the independent variable ``x``. + + This is a linear regression aggregate function. Only non-null pairs of the inputs + are evaluated. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param y: The linear regression dependent variable + :param x: The linear regression independent variable + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"y": [1.0, 2.0, 3.0], "x": [1.0, 2.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.regr_sxx( + ... dfn.col("y"), dfn.col("x") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.regr_sxx( + ... dfn.col("y"), dfn.col("x"), + ... filter=dfn.col("y") > dfn.lit(1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 0.5 + + +.. py:function:: regr_sxy(y: datafusion.expr.Expr, x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the sum of products of pairs of numbers. + + This is a linear regression aggregate function. Only non-null pairs of the inputs + are evaluated. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param y: The linear regression dependent variable + :param x: The linear regression independent variable + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"y": [1.0, 2.0, 3.0], "x": [1.0, 2.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.regr_sxy( + ... dfn.col("y"), dfn.col("x") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.regr_sxy( + ... dfn.col("y"), dfn.col("x"), + ... filter=dfn.col("y") > dfn.lit(1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 0.5 + + +.. py:function:: regr_syy(y: datafusion.expr.Expr, x: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the sum of squares of the dependent variable ``y``. + + This is a linear regression aggregate function. Only non-null pairs of the inputs + are evaluated. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param y: The linear regression dependent variable + :param x: The linear regression independent variable + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"y": [1.0, 2.0, 3.0], "x": [1.0, 2.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.regr_syy( + ... dfn.col("y"), dfn.col("x") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.regr_syy( + ... dfn.col("y"), dfn.col("x"), + ... filter=dfn.col("y") > dfn.lit(1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 0.5 + + +.. py:function:: repeat(string: datafusion.expr.Expr, n: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Repeats the ``string`` to ``n`` times. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["ha"]}) + >>> result = df.select( + ... dfn.functions.repeat(dfn.col("a"), 3).alias("r")) + >>> result.collect_column("r")[0].as_py() + 'hahaha' + + +.. py:function:: replace(string: datafusion.expr.Expr, from_val: datafusion.expr.Expr | str, to_val: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Replaces all occurrences of ``from_val`` with ``to_val`` in the ``string``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello world"]}) + >>> result = df.select( + ... dfn.functions.replace(dfn.col("a"), "world", "there").alias("r")) + >>> result.collect_column("r")[0].as_py() + 'hello there' + + +.. py:function:: reverse(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Reverse the string argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select(dfn.functions.reverse(dfn.col("a")).alias("r")) + >>> result.collect_column("r")[0].as_py() + 'olleh' + + +.. py:function:: right(string: datafusion.expr.Expr, n: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Returns the last ``n`` characters in the ``string``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select(dfn.functions.right(dfn.col("a"), 3).alias("r")) + >>> result.collect_column("r")[0].as_py() + 'llo' + + +.. py:function:: round(value: datafusion.expr.Expr, decimal_places: datafusion.expr.Expr | int | None = None) -> datafusion.expr.Expr + + Round the argument to the nearest integer. + + If the optional ``decimal_places`` is specified, round to the nearest number of + decimal places. You can specify a negative number of decimal places. For example + ``round(lit(125.2345), -2)`` would yield a value of ``100.0``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.567]}) + >>> result = df.select(dfn.functions.round(dfn.col("a"), 2).alias("r")) + >>> result.collect_column("r")[0].as_py() + 1.57 + + +.. py:function:: row(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns a struct with the given arguments. + + .. seealso:: This is an alias for :py:func:`struct`. + + +.. py:function:: row_number(partition_by: list[datafusion.expr.Expr] | datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None) -> datafusion.expr.Expr + + Create a row number window function. + + Returns the row number of the window function. + + Here is an example of the ``row_number`` on a simple DataFrame:: + + +--------+------------+ + | points | row number | + +--------+------------+ + | 100 | 1 | + | 100 | 2 | + | 50 | 3 | + | 25 | 4 | + +--------+------------+ + + :param partition_by: Expressions to partition the window frame on. + :param order_by: Set ordering within the window frame. Accepts + column names or expressions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [10, 20, 30]}) + >>> result = df.select( + ... dfn.col("a"), + ... dfn.functions.row_number( + ... order_by="a" + ... ).alias("rn")) + >>> result.sort(dfn.col("a")).collect_column("rn").to_pylist() + [1, 2, 3] + + >>> df = ctx.from_pydict( + ... {"g": ["a", "a", "b", "b"], "v": [1, 2, 3, 4]}) + >>> result = df.select( + ... dfn.col("g"), dfn.col("v"), + ... dfn.functions.row_number( + ... partition_by=dfn.col("g"), order_by="v", + ... ).alias("rn")) + >>> result.sort(dfn.col("g"), dfn.col("v")).collect_column("rn").to_pylist() + [1, 2, 1, 2] + + +.. py:function:: rpad(string: datafusion.expr.Expr, count: datafusion.expr.Expr | int, characters: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Add right padding to a string. + + Extends the string to length length by appending the characters fill (a space + by default). If the string is already longer than length then it is truncated. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hi"]}) + >>> result = df.select( + ... dfn.functions.rpad(dfn.col("a"), 5, "!").alias("r")) + >>> result.collect_column("r")[0].as_py() + 'hi!!!' + + +.. py:function:: rtrim(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Removes all characters, spaces by default, from the end of a string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [" a "]}) + >>> trim_df = df.select(dfn.functions.rtrim(dfn.col("a")).alias("trimmed")) + >>> trim_df.collect_column("trimmed")[0].as_py() + ' a' + + +.. py:function:: sha224(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Computes the SHA-224 hash of a binary string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select( + ... dfn.functions.sha224(dfn.col("a")).alias("h") + ... ) + >>> result.collect_column("h")[0].as_py().hex() + 'ea09ae9cc6768c50fcee903ed054556e5bfc8347907f12598aa24193' + + +.. py:function:: sha256(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Computes the SHA-256 hash of a binary string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select( + ... dfn.functions.sha256(dfn.col("a")).alias("h") + ... ) + >>> result.collect_column("h")[0].as_py().hex() + '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824' + + +.. py:function:: sha384(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Computes the SHA-384 hash of a binary string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select( + ... dfn.functions.sha384(dfn.col("a")).alias("h") + ... ) + >>> result.collect_column("h")[0].as_py().hex() + '59e1748777448c69de6b800d7a33bbfb9ff1b... + + +.. py:function:: sha512(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Computes the SHA-512 hash of a binary string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select( + ... dfn.functions.sha512(dfn.col("a")).alias("h") + ... ) + >>> result.collect_column("h")[0].as_py().hex() + '9b71d224bd62f3785d96d46ad3ea3d73319bfb... + + +.. py:function:: signum(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the sign of the argument (-1, 0, +1). + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [-5.0, 0.0, 5.0]}) + >>> result = df.select(dfn.functions.signum(dfn.col("a")).alias("s")) + >>> result.collect_column("s").to_pylist() + [-1.0, 0.0, 1.0] + + +.. py:function:: sin(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the sine of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0.0]}) + >>> result = df.select(dfn.functions.sin(dfn.col("a")).alias("sin")) + >>> result.collect_column("sin")[0].as_py() + 0.0 + + +.. py:function:: sinh(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the hyperbolic sine of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0.0]}) + >>> result = df.select(dfn.functions.sinh(dfn.col("a")).alias("sinh")) + >>> result.collect_column("sinh")[0].as_py() + 0.0 + + +.. py:function:: split_part(string: datafusion.expr.Expr, delimiter: datafusion.expr.Expr | str, index: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Split a string and return one part. + + Splits a string based on a delimiter and picks out the desired field based + on the index. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["a,b,c"]}) + >>> result = df.select( + ... dfn.functions.split_part(dfn.col("a"), ",", 2).alias("s")) + >>> result.collect_column("s")[0].as_py() + 'b' + + +.. py:function:: sqrt(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the square root of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [9.0]}) + >>> result = df.select(dfn.functions.sqrt(dfn.col("a")).alias("sqrt")) + >>> result.collect_column("sqrt")[0].as_py() + 3.0 + + +.. py:function:: starts_with(string: datafusion.expr.Expr, prefix: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Returns true if string starts with prefix. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello_from_datafusion"]}) + >>> result = df.select( + ... dfn.functions.starts_with(dfn.col("a"), "hello").alias("sw")) + >>> result.collect_column("sw")[0].as_py() + True + + +.. py:function:: stddev(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the standard deviation of the argument. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param expression: The value to find the minimum of + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [2.0, 4.0, 6.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.stddev( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 2.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.stddev( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(2.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 1.41... + + +.. py:function:: stddev_pop(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the population standard deviation of the argument. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param expression: The value to find the minimum of + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0.0, 1.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.stddev_pop( + ... dfn.col("a") + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 1.247... + + >>> df = ctx.from_pydict({"a": [0.0, 1.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.stddev_pop( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(0.0) + ... ).alias("v")] + ... ) + >>> result.collect_column("v")[0].as_py() + 1.0 + + +.. py:function:: stddev_samp(arg: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the sample standard deviation of the argument. + + .. seealso:: This is an alias for :py:func:`stddev`. + + +.. py:function:: string_agg(expression: datafusion.expr.Expr, delimiter: str, filter: datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None) -> datafusion.expr.Expr + + Concatenates the input strings. + + This aggregate function will concatenate input strings, ignoring null values, and + separating them with the specified delimiter. Non-string values will be converted to + their string equivalents. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``distinct`` and ``null_treatment``. + + :param expression: Argument to perform bitwise calculation on + :param delimiter: Text to place between each value of expression + :param filter: If provided, only compute against rows for which the filter is True + :param order_by: Set the ordering of the expression to evaluate. Accepts + column names or expressions. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["x", "y", "z"]}) + >>> result = df.aggregate( + ... [], [dfn.functions.string_agg( + ... dfn.col("a"), ",", order_by="a" + ... ).alias("s")]) + >>> result.collect_column("s")[0].as_py() + 'x,y,z' + + >>> result = df.aggregate( + ... [], [dfn.functions.string_agg( + ... dfn.col("a"), ",", + ... filter=dfn.col("a") > dfn.lit("x"), + ... order_by="a", + ... ).alias("s")]) + >>> result.collect_column("s")[0].as_py() + 'y,z' + + +.. py:function:: string_to_array(string: datafusion.expr.Expr, delimiter: datafusion.expr.Expr | str, null_string: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Splits a string based on a delimiter and returns an array of parts. + + Any parts matching the optional ``null_string`` will be replaced with ``NULL``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello,world"]}) + >>> result = df.select( + ... dfn.functions.string_to_array(dfn.col("a"), ",").alias("result")) + >>> result.collect_column("result")[0].as_py() + ['hello', 'world'] + + Replace parts matching a ``null_string`` with ``NULL``: + + >>> result = df.select( + ... dfn.functions.string_to_array( + ... dfn.col("a"), ",", null_string="world", + ... ).alias("result")) + >>> result.collect_column("result")[0].as_py() + ['hello', None] + + +.. py:function:: string_to_list(string: datafusion.expr.Expr, delimiter: datafusion.expr.Expr | str, null_string: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Splits a string based on a delimiter and returns an array of parts. + + .. seealso:: This is an alias for :py:func:`string_to_array`. + + +.. py:function:: strpos(string: datafusion.expr.Expr, substring: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Finds the position from where the ``substring`` matches the ``string``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select( + ... dfn.functions.strpos(dfn.col("a"), "llo").alias("pos")) + >>> result.collect_column("pos")[0].as_py() + 3 + + +.. py:function:: struct(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns a struct with the given arguments. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1], "b": [2]}) + >>> result = df.select( + ... dfn.functions.struct( + ... dfn.col("a"), dfn.col("b") + ... ).alias("s") + ... ) + + Children in the new struct will always be `c0`, ..., `cN-1` + for `N` children. + + >>> result.collect_column("s")[0].as_py() == {"c0": 1, "c1": 2} + True + + +.. py:function:: substr(string: datafusion.expr.Expr, position: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Substring from the ``position`` to the end. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select( + ... dfn.functions.substr(dfn.col("a"), 3).alias("s")) + >>> result.collect_column("s")[0].as_py() + 'llo' + + +.. py:function:: substr_index(string: datafusion.expr.Expr, delimiter: datafusion.expr.Expr | str, count: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Returns an indexed substring. + + The return will be the ``string`` from before ``count`` occurrences of + ``delimiter``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["a.b.c"]}) + >>> result = df.select( + ... dfn.functions.substr_index(dfn.col("a"), ".", 2).alias("s")) + >>> result.collect_column("s")[0].as_py() + 'a.b' + + +.. py:function:: substring(string: datafusion.expr.Expr, position: datafusion.expr.Expr | int, length: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Substring from the ``position`` with ``length`` characters. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello world"]}) + >>> result = df.select( + ... dfn.functions.substring(dfn.col("a"), 1, 5).alias("s")) + >>> result.collect_column("s")[0].as_py() + 'hello' + + +.. py:function:: sum(expression: datafusion.expr.Expr, distinct: bool = False, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the sum of a set of numbers. + + This aggregate function expects a numeric expression. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by`` and ``null_treatment``. + + :param expression: Values to combine into an array + :param distinct: If True, duplicate values are removed before summing. + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.sum( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 6 + + >>> result = df.aggregate( + ... [], [dfn.functions.sum( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(1) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 5 + + >>> df = ctx.from_pydict({"a": [1, 1, 2, 3]}) + >>> result = df.aggregate( + ... [], [dfn.functions.sum( + ... dfn.col("a"), distinct=True, + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 6 + + +.. py:function:: tan(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the tangent of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0.0]}) + >>> result = df.select(dfn.functions.tan(dfn.col("a")).alias("tan")) + >>> result.collect_column("tan")[0].as_py() + 0.0 + + +.. py:function:: tanh(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the hyperbolic tangent of the argument. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [0.0]}) + >>> result = df.select(dfn.functions.tanh(dfn.col("a")).alias("tanh")) + >>> result.collect_column("tanh")[0].as_py() + 0.0 + + +.. py:function:: to_char(arg: datafusion.expr.Expr, formatter: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Returns a string representation of a date, time, timestamp or duration. + + For usage of ``formatter`` see the rust chrono package ``strftime`` package. + + [Documentation here.](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["2021-01-01T00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_char( + ... dfn.functions.to_timestamp(dfn.col("a")), + ... "%Y/%m/%d", + ... ).alias("formatted") + ... ) + >>> result.collect_column("formatted")[0].as_py() + '2021/01/01' + + +.. py:function:: to_date(arg: datafusion.expr.Expr, *formatters: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts a value to a date (YYYY-MM-DD). + + Supports strings, numeric and timestamp types as input. + Integers and doubles are interpreted as days since the unix epoch. + Strings are parsed as YYYY-MM-DD (e.g. '2023-07-20') + if ``formatters`` are not provided. + + For usage of ``formatters`` see the rust chrono package ``strftime`` package. + + [Documentation here.](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["2021-07-20"]}) + >>> result = df.select( + ... dfn.functions.to_date(dfn.col("a")).alias("dt")) + >>> str(result.collect_column("dt")[0].as_py()) + '2021-07-20' + + Pass a format string as a bare ``str``: + + >>> df = ctx.from_pydict({"a": ["20-07-2021"]}) + >>> result = df.select( + ... dfn.functions.to_date(dfn.col("a"), "%d-%m-%Y").alias("dt")) + >>> str(result.collect_column("dt")[0].as_py()) + '2021-07-20' + + +.. py:function:: to_hex(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Converts an integer to a hexadecimal string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [255]}) + >>> result = df.select(dfn.functions.to_hex(dfn.col("a")).alias("hex")) + >>> result.collect_column("hex")[0].as_py() + 'ff' + + +.. py:function:: to_local_time(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Converts a timestamp with a timezone to a timestamp without a timezone. + + This function handles daylight saving time changes. + + +.. py:function:: to_time(arg: datafusion.expr.Expr, *formatters: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts a value to a time. Supports strings and timestamps as input. + + If ``formatters`` is not provided strings are parsed as HH:MM:SS, HH:MM or + HH:MM:SS.nnnnnnnnn; + + For usage of ``formatters`` see the rust chrono package ``strftime`` package. + + [Documentation here.](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["14:30:00"]}) + >>> result = df.select( + ... dfn.functions.to_time(dfn.col("a")).alias("t")) + >>> str(result.collect_column("t")[0].as_py()) + '14:30:00' + + Pass a format string as a bare ``str``: + + >>> df = ctx.from_pydict({"a": ["14h30m00s"]}) + >>> result = df.select( + ... dfn.functions.to_time(dfn.col("a"), "%Hh%Mm%Ss").alias("t")) + >>> str(result.collect_column("t")[0].as_py()) + '14:30:00' + + +.. py:function:: to_timestamp(arg: datafusion.expr.Expr, *formatters: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts a string and optional formats to a ``Timestamp`` in nanoseconds. + + For usage of ``formatters`` see the rust chrono package ``strftime`` package. + + [Documentation here.](https://docs.rs/chrono/latest/chrono/format/strftime/index.html) + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["2021-01-01T00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_timestamp( + ... dfn.col("a") + ... ).alias("ts") + ... ) + >>> str(result.collect_column("ts")[0].as_py()) + '2021-01-01 00:00:00' + + Pass a format string as a bare ``str``: + + >>> df = ctx.from_pydict({"a": ["01/01/2021 00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_timestamp( + ... dfn.col("a"), "%d/%m/%Y %H:%M:%S" + ... ).alias("ts") + ... ) + >>> str(result.collect_column("ts")[0].as_py()) + '2021-01-01 00:00:00' + + +.. py:function:: to_timestamp_micros(arg: datafusion.expr.Expr, *formatters: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts a string and optional formats to a ``Timestamp`` in microseconds. + + See :py:func:`to_timestamp` for a description on how to use formatters. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["2021-01-01T00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_timestamp_micros( + ... dfn.col("a") + ... ).alias("ts") + ... ) + >>> str(result.collect_column("ts")[0].as_py()) + '2021-01-01 00:00:00' + + Pass a format string as a bare ``str``: + + >>> df = ctx.from_pydict({"a": ["01/01/2021 00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_timestamp_micros( + ... dfn.col("a"), "%d/%m/%Y %H:%M:%S" + ... ).alias("ts") + ... ) + >>> str(result.collect_column("ts")[0].as_py()) + '2021-01-01 00:00:00' + + +.. py:function:: to_timestamp_millis(arg: datafusion.expr.Expr, *formatters: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts a string and optional formats to a ``Timestamp`` in milliseconds. + + See :py:func:`to_timestamp` for a description on how to use formatters. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["2021-01-01T00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_timestamp_millis( + ... dfn.col("a") + ... ).alias("ts") + ... ) + >>> str(result.collect_column("ts")[0].as_py()) + '2021-01-01 00:00:00' + + Pass a format string as a bare ``str``: + + >>> df = ctx.from_pydict({"a": ["01/01/2021 00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_timestamp_millis( + ... dfn.col("a"), "%d/%m/%Y %H:%M:%S" + ... ).alias("ts") + ... ) + >>> str(result.collect_column("ts")[0].as_py()) + '2021-01-01 00:00:00' + + +.. py:function:: to_timestamp_nanos(arg: datafusion.expr.Expr, *formatters: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts a string and optional formats to a ``Timestamp`` in nanoseconds. + + See :py:func:`to_timestamp` for a description on how to use formatters. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["2021-01-01T00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_timestamp_nanos( + ... dfn.col("a") + ... ).alias("ts") + ... ) + >>> str(result.collect_column("ts")[0].as_py()) + '2021-01-01 00:00:00' + + Pass a format string as a bare ``str``: + + >>> df = ctx.from_pydict({"a": ["01/01/2021 00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_timestamp_nanos( + ... dfn.col("a"), "%d/%m/%Y %H:%M:%S" + ... ).alias("ts") + ... ) + >>> str(result.collect_column("ts")[0].as_py()) + '2021-01-01 00:00:00' + + +.. py:function:: to_timestamp_seconds(arg: datafusion.expr.Expr, *formatters: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts a string and optional formats to a ``Timestamp`` in seconds. + + See :py:func:`to_timestamp` for a description on how to use formatters. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["2021-01-01T00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_timestamp_seconds( + ... dfn.col("a") + ... ).alias("ts") + ... ) + >>> str(result.collect_column("ts")[0].as_py()) + '2021-01-01 00:00:00' + + Pass a format string as a bare ``str``: + + >>> df = ctx.from_pydict({"a": ["01/01/2021 00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_timestamp_seconds( + ... dfn.col("a"), "%d/%m/%Y %H:%M:%S" + ... ).alias("ts") + ... ) + >>> str(result.collect_column("ts")[0].as_py()) + '2021-01-01 00:00:00' + + +.. py:function:: to_unixtime(string: datafusion.expr.Expr, *format_arguments: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Converts a string and optional formats to a Unixtime. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["1970-01-01T00:00:00"]}) + >>> result = df.select(dfn.functions.to_unixtime(dfn.col("a")).alias("u")) + >>> result.collect_column("u")[0].as_py() + 0 + + Pass a format string as a bare ``str``: + + >>> df = ctx.from_pydict({"a": ["01/01/1970 00:00:00"]}) + >>> result = df.select( + ... dfn.functions.to_unixtime( + ... dfn.col("a"), "%d/%m/%Y %H:%M:%S" + ... ).alias("u") + ... ) + >>> result.collect_column("u")[0].as_py() + 0 + + +.. py:function:: translate(string: datafusion.expr.Expr, from_val: datafusion.expr.Expr | str, to_val: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Replaces the characters in ``from_val`` with the counterpart in ``to_val``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select( + ... dfn.functions.translate(dfn.col("a"), "helo", "HELO").alias("t")) + >>> result.collect_column("t")[0].as_py() + 'HELLO' + + +.. py:function:: trim(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Removes all characters, spaces by default, from both sides of a string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [" hello "]}) + >>> result = df.select(dfn.functions.trim(dfn.col("a")).alias("t")) + >>> result.collect_column("t")[0].as_py() + 'hello' + + +.. py:function:: trunc(num: datafusion.expr.Expr, precision: datafusion.expr.Expr | int | None = None) -> datafusion.expr.Expr + + Truncate the number toward zero with optional precision. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.567]}) + >>> result = df.select( + ... dfn.functions.trunc(dfn.col("a")).alias("t")) + >>> result.collect_column("t")[0].as_py() + 1.0 + + >>> result = df.select( + ... dfn.functions.trunc(dfn.col("a"), precision=2).alias("t")) + >>> result.collect_column("t")[0].as_py() + 1.56 + + +.. py:function:: try_cast_to_type(value: datafusion.expr.Expr, type_ref: datafusion.expr.Expr) -> datafusion.expr.Expr + + Casts ``value`` to the data type of ``type_ref``, NULL on failure. + + Like :py:func:`cast_to_type`, but casts that fail produce NULL instead + of erroring. Only the *type* of ``type_ref`` is used; its value is + ignored. + + If the target type is known statically, prefer :py:func:`arrow_try_cast` + and pass a type string or ``pyarrow.DataType`` directly. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["oops"], "b": [1.0]}) + >>> result = df.select( + ... dfn.functions.try_cast_to_type( + ... dfn.col("a"), dfn.col("b") + ... ).alias("c") + ... ) + >>> result.collect_column("c")[0].as_py() is None + True + + +.. py:function:: union_extract(union_expr: datafusion.expr.Expr, field_name: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Extracts a value from a union type by field name. + + Returns the value of the named field if it is the currently selected + variant, otherwise returns NULL. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> types = pa.array([0, 1, 0], type=pa.int8()) + >>> offsets = pa.array([0, 0, 1], type=pa.int32()) + >>> arr = pa.UnionArray.from_dense( + ... types, offsets, [pa.array([1, 2]), pa.array(["hi"])], + ... ["int", "str"], [0, 1], + ... ) + >>> batch = pa.RecordBatch.from_arrays([arr], names=["u"]) + >>> df = ctx.create_dataframe([[batch]]) + >>> result = df.select( + ... dfn.functions.union_extract(dfn.col("u"), "int").alias("val") + ... ) + >>> result.collect_column("val").to_pylist() + [1, None, 2] + + +.. py:function:: union_tag(union_expr: datafusion.expr.Expr) -> datafusion.expr.Expr + + Returns the tag (active field name) of a union type. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> types = pa.array([0, 1, 0], type=pa.int8()) + >>> offsets = pa.array([0, 0, 1], type=pa.int32()) + >>> arr = pa.UnionArray.from_dense( + ... types, offsets, [pa.array([1, 2]), pa.array(["hi"])], + ... ["int", "str"], [0, 1], + ... ) + >>> batch = pa.RecordBatch.from_arrays([arr], names=["u"]) + >>> df = ctx.create_dataframe([[batch]]) + >>> result = df.select( + ... dfn.functions.union_tag(dfn.col("u")).alias("tag") + ... ) + >>> result.collect_column("tag").to_pylist() + ['int', 'str', 'int'] + + +.. py:function:: upper(arg: datafusion.expr.Expr) -> datafusion.expr.Expr + + Converts a string to uppercase. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["hello"]}) + >>> result = df.select(dfn.functions.upper(dfn.col("a")).alias("u")) + >>> result.collect_column("u")[0].as_py() + 'HELLO' + + +.. py:function:: uuid() -> datafusion.expr.Expr + + Returns uuid v4 as a string value. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.uuid().alias("u") + ... ) + >>> len(result.collect_column("u")[0].as_py()) == 36 + True + + +.. py:function:: var(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the sample variance of the argument. + + .. seealso:: This is an alias for :py:func:`var_samp`. + + +.. py:function:: var_pop(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the population variance of the argument. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param expression: The variable to compute the variance for + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [-1.0, 0.0, 2.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.var_pop( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 1.555... + + >>> result = df.aggregate( + ... [], [dfn.functions.var_pop( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(-1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 1.0 + + +.. py:function:: var_population(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the population variance of the argument. + + .. seealso:: This is an alias for :py:func:`var_pop`. + + +.. py:function:: var_samp(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the sample variance of the argument. + + If using the builder functions described in ref:`_aggregation` this function ignores + the options ``order_by``, ``null_treatment``, and ``distinct``. + + :param expression: The variable to compute the variance for + :param filter: If provided, only compute against rows for which the filter is True + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0]}) + >>> result = df.aggregate( + ... [], [dfn.functions.var_samp( + ... dfn.col("a") + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 1.0 + + >>> result = df.aggregate( + ... [], [dfn.functions.var_samp( + ... dfn.col("a"), + ... filter=dfn.col("a") > dfn.lit(1.0) + ... ).alias("v")]) + >>> result.collect_column("v")[0].as_py() + 0.5 + + +.. py:function:: var_sample(expression: datafusion.expr.Expr, filter: datafusion.expr.Expr | None = None) -> datafusion.expr.Expr + + Computes the sample variance of the argument. + + .. seealso:: This is an alias for :py:func:`var_samp`. + + +.. py:function:: version() -> datafusion.expr.Expr + + Returns the DataFusion version string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.empty_table() + >>> result = df.select(dfn.functions.version().alias("v")) + >>> "Apache DataFusion" in result.collect_column("v")[0].as_py() + True + + +.. py:function:: when(when: datafusion.expr.Expr, then: datafusion.expr.Expr) -> datafusion.expr.CaseBuilder + + Create a case expression that has no base expression. + + Create a :py:class:`~datafusion.expr.CaseBuilder` to match cases for the + expression ``expr``. See :py:class:`~datafusion.expr.CaseBuilder` for + detailed usage. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> result = df.select( + ... dfn.functions.when(dfn.col("a") > dfn.lit(2), + ... dfn.lit("big")).otherwise(dfn.lit("small")).alias("c")) + >>> result.collect_column("c")[2].as_py() + 'big' + + +.. py:function:: with_metadata(expr: datafusion.expr.Expr, metadata: dict[str, str]) -> datafusion.expr.Expr + + Attaches Arrow field metadata (key/value pairs) to the input expression. + + This is the inverse of :py:func:`arrow_metadata`. Existing metadata on the + input field is preserved; new keys overwrite on collision. Keys must be + non-empty strings; empty values are allowed. + + An empty ``metadata`` dict is a no-op and returns the input expression + unchanged. Empty keys raise :py:class:`ValueError`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1]}) + >>> result = df.select( + ... dfn.functions.with_metadata( + ... dfn.col("a"), {"unit": "ms"} + ... ).alias("a") + ... ) + >>> result.select( + ... dfn.functions.arrow_metadata(dfn.col("a"), "unit").alias("u") + ... ).collect_column("u")[0].as_py() + 'ms' + + +.. py:data:: today + diff --git a/_sources/autoapi/datafusion/functions/spark/index.rst.txt b/_sources/autoapi/datafusion/functions/spark/index.rst.txt new file mode 100644 index 000000000..a2eae89bb --- /dev/null +++ b/_sources/autoapi/datafusion/functions/spark/index.rst.txt @@ -0,0 +1,1569 @@ +datafusion.functions.spark +========================== + +.. py:module:: datafusion.functions.spark + +.. autoapi-nested-parse:: + + Spark-compatible function bindings. + + These functions mirror the semantics of their Apache Spark counterparts + exactly. Some override DataFusion built-ins (``substring`` is 1-indexed, + ``concat`` propagates NULL, ``round`` uses HALF_UP rounding, etc.), which is + why they live in a separate namespace rather than replacing the defaults. + + For DataFrame use, import this module and call functions directly. For SQL + use, call :py:meth:`datafusion.SessionContext.enable_spark_functions` to + register the Spark UDFs by name (overriding any built-ins with matching + names) before issuing SQL queries. + + + +Functions +--------- + +.. autoapisummary:: + + datafusion.functions.spark.abs + datafusion.functions.spark.add_months + datafusion.functions.spark.array + datafusion.functions.spark.array_contains + datafusion.functions.spark.array_repeat + datafusion.functions.spark.ascii + datafusion.functions.spark.avg + datafusion.functions.spark.base64 + datafusion.functions.spark.bin + datafusion.functions.spark.bit_count + datafusion.functions.spark.bit_get + datafusion.functions.spark.bitmap_bit_position + datafusion.functions.spark.bitmap_bucket_number + datafusion.functions.spark.bitmap_count + datafusion.functions.spark.bitwise_not + datafusion.functions.spark.ceil + datafusion.functions.spark.char + datafusion.functions.spark.collect_list + datafusion.functions.spark.collect_set + datafusion.functions.spark.concat + datafusion.functions.spark.crc32 + datafusion.functions.spark.csc + datafusion.functions.spark.date_add + datafusion.functions.spark.date_diff + datafusion.functions.spark.date_part + datafusion.functions.spark.date_sub + datafusion.functions.spark.date_trunc + datafusion.functions.spark.elt + datafusion.functions.spark.expm1 + datafusion.functions.spark.factorial + datafusion.functions.spark.floor + datafusion.functions.spark.format_string + datafusion.functions.spark.from_utc_timestamp + datafusion.functions.spark.hex + datafusion.functions.spark.hour + datafusion.functions.spark.if_ + datafusion.functions.spark.ilike + datafusion.functions.spark.is_valid_utf8 + datafusion.functions.spark.json_tuple + datafusion.functions.spark.last_day + datafusion.functions.spark.length + datafusion.functions.spark.like + datafusion.functions.spark.luhn_check + datafusion.functions.spark.make_dt_interval + datafusion.functions.spark.make_interval + datafusion.functions.spark.make_valid_utf8 + datafusion.functions.spark.map_from_arrays + datafusion.functions.spark.map_from_entries + datafusion.functions.spark.minute + datafusion.functions.spark.modulus + datafusion.functions.spark.negative + datafusion.functions.spark.next_day + datafusion.functions.spark.parse_url + datafusion.functions.spark.pmod + datafusion.functions.spark.rint + datafusion.functions.spark.round + datafusion.functions.spark.sec + datafusion.functions.spark.second + datafusion.functions.spark.sha1 + datafusion.functions.spark.sha2 + datafusion.functions.spark.shiftleft + datafusion.functions.spark.shiftright + datafusion.functions.spark.shiftrightunsigned + datafusion.functions.spark.shuffle + datafusion.functions.spark.size + datafusion.functions.spark.slice + datafusion.functions.spark.soundex + datafusion.functions.spark.space + datafusion.functions.spark.spark_cast + datafusion.functions.spark.str_to_map + datafusion.functions.spark.substring + datafusion.functions.spark.time_trunc + datafusion.functions.spark.to_utc_timestamp + datafusion.functions.spark.trunc + datafusion.functions.spark.try_parse_url + datafusion.functions.spark.try_sum + datafusion.functions.spark.try_url_decode + datafusion.functions.spark.unbase64 + datafusion.functions.spark.unhex + datafusion.functions.spark.unix_date + datafusion.functions.spark.unix_micros + datafusion.functions.spark.unix_millis + datafusion.functions.spark.unix_seconds + datafusion.functions.spark.url_decode + datafusion.functions.spark.url_encode + datafusion.functions.spark.width_bucket + datafusion.functions.spark.xxhash64 + + +Module Contents +--------------- + +.. py:function:: abs(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``abs``: absolute value. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.abs(dfn.lit(-5)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 5 + + +.. py:function:: add_months(start: datafusion.expr.Expr, months: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``add_months``: date + N months. + + ``months`` accepts a native ``int`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import date + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> d = dfn.lit(pa.scalar(date(2020, 1, 15), type=pa.date32())) + >>> r = df.select(dfn.functions.spark.add_months(d, 2).alias("v")) + >>> r.collect_column("v")[0].as_py() + datetime.date(2020, 3, 15) + + +.. py:function:: array(*cols: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``array``: builds an array from the given elements. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.array( + ... dfn.lit(1), dfn.lit(2), dfn.lit(3) + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + [1, 2, 3] + + +.. py:function:: array_contains(col: datafusion.expr.Expr, value: datafusion.expr.Expr | Any) -> datafusion.expr.Expr + + Spark ``array_contains``: true if the array contains the element. + + ``value`` accepts a native Python literal or an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.array_contains( + ... dfn.functions.spark.array(dfn.lit(1), dfn.lit(2)), 1 + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + True + + +.. py:function:: array_repeat(col: datafusion.expr.Expr, count: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``array_repeat``: array of ``element`` repeated ``count`` times. + + ``count`` accepts a native ``int`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.array_repeat(dfn.lit("a"), 3).alias("v")) + >>> r.collect_column("v")[0].as_py() + ['a', 'a', 'a'] + + +.. py:function:: ascii(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``ascii``: code point of the first character. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.ascii(dfn.lit("A")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 65 + + +.. py:function:: avg(col: datafusion.expr.Expr, distinct: bool | None = None, filter: datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None, null_treatment: datafusion.common.NullTreatment | None = None) -> datafusion.expr.Expr + + Spark ``avg``: returns the mean of a numeric column. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0]}) + >>> r = df.aggregate( + ... [], [dfn.functions.spark.avg(dfn.col("a")).alias("v")]) + >>> r.collect_column("v")[0].as_py() + 2.0 + + +.. py:function:: base64(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``base64``: encode binary as a base64 string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.base64(dfn.lit(b"hi")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 'aGk=' + + +.. py:function:: bin(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``bin``: binary string representation of a long. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.bin(dfn.lit(7)).alias("v")) + >>> r.collect_column("v")[0].as_py() + '111' + + +.. py:function:: bit_count(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``bit_count``: number of bits set in the integer's binary form. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.bit_count(dfn.lit(7)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 3 + + +.. py:function:: bit_get(col: datafusion.expr.Expr, pos: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Spark ``bit_get``: returns the bit (0 or 1) at ``pos``. + + A bare ``str`` ``pos`` is treated as a column name (matching pyspark), + not a literal; pass :func:`~datafusion.lit` for a literal position. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.bit_get(dfn.lit(5), dfn.lit(0)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 1 + + +.. py:function:: bitmap_bit_position(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``bitmap_bit_position``: bit position for a child expression. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.bitmap_bit_position(dfn.lit(15)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 14 + + +.. py:function:: bitmap_bucket_number(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``bitmap_bucket_number``: bucket number for a child expression. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.bitmap_bucket_number(dfn.lit(15)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 1 + + +.. py:function:: bitmap_count(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``bitmap_count``: number of set bits in a bitmap. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.bitmap_count(dfn.lit(b"\xff")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 8 + + +.. py:function:: bitwise_not(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``~``: bitwise NOT. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.bitwise_not(dfn.lit(0)).alias("v")) + >>> r.collect_column("v")[0].as_py() + -1 + + +.. py:function:: ceil(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``ceil``: smallest integer ≥ arg. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.ceil(dfn.lit(1.2)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 2 + + +.. py:function:: char(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``char``: ASCII character for a code point (mod 256). + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.char(dfn.lit(65)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 'A' + + +.. py:function:: collect_list(col: datafusion.expr.Expr, distinct: bool | None = None, filter: datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None, null_treatment: datafusion.common.NullTreatment | None = None) -> datafusion.expr.Expr + + Spark ``collect_list``: collect values into an array (preserves dups). + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 2]}) + >>> r = df.aggregate( + ... [], [dfn.functions.spark.collect_list(dfn.col("a")).alias("v")]) + >>> sorted(r.collect_column("v")[0].as_py()) + [1, 2, 2] + + +.. py:function:: collect_set(col: datafusion.expr.Expr, distinct: bool | None = None, filter: datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None, null_treatment: datafusion.common.NullTreatment | None = None) -> datafusion.expr.Expr + + Spark ``collect_set``: collect distinct values into an array. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 2, 3]}) + >>> r = df.aggregate( + ... [], [dfn.functions.spark.collect_set(dfn.col("a")).alias("v")]) + >>> sorted(r.collect_column("v")[0].as_py()) + [1, 2, 3] + + +.. py:function:: concat(*cols: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``concat``: concatenates strings; NULL if any input is NULL. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.concat(dfn.lit("a"), dfn.lit("b")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 'ab' + + +.. py:function:: crc32(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``crc32``: cyclic redundancy check value as a bigint. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"s": ["ABC"]}) + >>> r = df.select(dfn.functions.spark.crc32(dfn.col("s")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 2743272264 + + +.. py:function:: csc(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``csc``: cosecant. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.csc(dfn.lit(1.5708)).alias("v")) + >>> f"{r.collect_column('v')[0].as_py():.4f}" + '1.0000' + + +.. py:function:: date_add(start: datafusion.expr.Expr, days: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``date_add``: date + N days. + + ``days`` accepts a native ``int`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import date + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> d = dfn.lit(pa.scalar(date(2020, 1, 15), type=pa.date32())) + >>> r = df.select(dfn.functions.spark.date_add(d, 5).alias("v")) + >>> r.collect_column("v")[0].as_py() + datetime.date(2020, 1, 20) + + +.. py:function:: date_diff(end: datafusion.expr.Expr, start: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``date_diff``: number of days from ``start_date`` to ``end_date``. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import date + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> d = dfn.lit(pa.scalar(date(2020, 1, 15), type=pa.date32())) + >>> end = dfn.lit(pa.scalar(date(2020, 1, 20), type=pa.date32())) + >>> r = df.select(dfn.functions.spark.date_diff(end, d).alias("v")) + >>> r.collect_column("v")[0].as_py() + 5 + + +.. py:function:: date_part(field: datafusion.expr.Expr | str, source: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``date_part``: extract ``field`` from a date/time/timestamp. + + ``field`` accepts a native ``str`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import date + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> d = dfn.lit(pa.scalar(date(2020, 1, 15), type=pa.date32())) + >>> r = df.select( + ... dfn.functions.spark.date_part("year", d).alias("v")) + >>> r.collect_column("v")[0].as_py() + 2020 + + +.. py:function:: date_sub(start: datafusion.expr.Expr, days: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``date_sub``: date - N days. + + ``days`` accepts a native ``int`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import date + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> d = dfn.lit(pa.scalar(date(2020, 1, 15), type=pa.date32())) + >>> r = df.select(dfn.functions.spark.date_sub(d, 5).alias("v")) + >>> r.collect_column("v")[0].as_py() + datetime.date(2020, 1, 10) + + +.. py:function:: date_trunc(format: datafusion.expr.Expr | str, timestamp: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``date_trunc``: truncate timestamp to unit ``fmt``. + + ``format`` accepts a native ``str`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import datetime + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> ts = dfn.lit( + ... pa.scalar(datetime(2020, 1, 15, 14, 30, 45), + ... type=pa.timestamp('us'))) + >>> r = df.select( + ... dfn.functions.spark.date_trunc("month", ts).alias("v")) + >>> r.collect_column("v")[0].as_py() + datetime.datetime(2020, 1, 1, 0, 0) + + +.. py:function:: elt(*inputs: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``elt``: returns the n-th input (1-indexed). + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.elt( + ... dfn.lit(2), dfn.lit("a"), dfn.lit("b") + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + 'b' + + +.. py:function:: expm1(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``expm1``: exp(arg) - 1. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.expm1(dfn.lit(0.0)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 0.0 + + +.. py:function:: factorial(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``factorial``: n! for n in [0..20], else NULL. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.factorial( + ... dfn.lit(pa.scalar(5, type=pa.int32())) + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + 120 + + +.. py:function:: floor(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``floor``: largest integer ≤ arg. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.floor(dfn.lit(1.8)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 1 + + +.. py:function:: format_string(format: str | datafusion.expr.Expr, *cols: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``format_string``: printf-style format string. + + ``format`` is the printf-style template (a plain ``str`` is auto-promoted + to a literal expression); remaining args are values to substitute. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.format_string( + ... "%d-%s", dfn.lit(42), dfn.lit("hi") + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + '42-hi' + + +.. py:function:: from_utc_timestamp(timestamp: datafusion.expr.Expr, tz: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Spark ``from_utc_timestamp``: interpret ``ts`` as UTC, convert to ``tz``. + + ``tz`` accepts a native ``str`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import datetime + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> ts = dfn.lit( + ... pa.scalar(datetime(2020, 1, 15, 14, 30, 45), + ... type=pa.timestamp('us'))) + >>> r = df.select( + ... dfn.functions.spark.from_utc_timestamp(ts, "UTC").alias("v")) + >>> r.collect_column("v")[0].as_py() + datetime.datetime(2020, 1, 15, 14, 30, 45) + + +.. py:function:: hex(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``hex``: hexadecimal representation. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.hex(dfn.lit(255)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 'FF' + + +.. py:function:: hour(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``hour``: extract hour component of a timestamp. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import datetime + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> ts = dfn.lit( + ... pa.scalar(datetime(2020, 1, 15, 14, 30, 45), + ... type=pa.timestamp('us'))) + >>> r = df.select(dfn.functions.spark.hour(ts).alias("v")) + >>> r.collect_column("v")[0].as_py() + 14 + + +.. py:function:: if_(condition: datafusion.expr.Expr, if_true: datafusion.expr.Expr | Any, if_false: datafusion.expr.Expr | Any) -> datafusion.expr.Expr + + Spark ``if``: returns ``if_true`` when ``condition`` is true, else ``if_false``. + + Exposed as ``if_`` because ``if`` is a Python keyword. ``if_true`` and + ``if_false`` accept native Python literals or :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.if_( + ... dfn.lit(2) > dfn.lit(1), "big", "small" + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + 'big' + + +.. py:function:: ilike(str: datafusion.expr.Expr, pattern: datafusion.expr.Expr | ilike.str, escapeChar: ilike.str | None = None) -> datafusion.expr.Expr + + Spark ``ilike``: case-insensitive pattern match. + + A bare ``str`` ``pattern`` is treated as a column name (matching pyspark), + not a literal; pass :func:`~datafusion.lit` for a literal pattern. + ``escapeChar`` is accepted for pyspark parity but is not yet wired through + the Rust binding; passing a non-``None`` value raises ``NotImplementedError``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.ilike(dfn.lit("HELLO"), dfn.lit("h%")).alias("v")) + >>> r.collect_column("v")[0].as_py() + True + + +.. py:function:: is_valid_utf8(str: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``is_valid_utf8``: true if the string is valid UTF-8. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.is_valid_utf8(dfn.lit("hello")).alias("v")) + >>> r.collect_column("v")[0].as_py() + True + + +.. py:function:: json_tuple(col: datafusion.expr.Expr, *fields: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Spark ``json_tuple``: extract top-level fields from a JSON string. + + Each field name accepts a native ``str`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.json_tuple( + ... dfn.lit('{"a":1,"b":"x"}'), "a", "b" + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + {'c0': '1', 'c1': 'x'} + + +.. py:function:: last_day(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``last_day``: last day of the month containing the date. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import date + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> d = dfn.lit(pa.scalar(date(2020, 1, 15), type=pa.date32())) + >>> r = df.select(dfn.functions.spark.last_day(d).alias("v")) + >>> r.collect_column("v")[0].as_py() + datetime.date(2020, 1, 31) + + +.. py:function:: length(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``length``: character length of a string, or byte length of binary. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.length(dfn.lit("hello")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 5 + + +.. py:function:: like(str: datafusion.expr.Expr, pattern: datafusion.expr.Expr | like.str, escapeChar: like.str | None = None) -> datafusion.expr.Expr + + Spark ``like``: case-sensitive pattern match. + + A bare ``str`` ``pattern`` is treated as a column name (matching pyspark), + not a literal; pass :func:`~datafusion.lit` for a literal pattern. + ``escapeChar`` is accepted for pyspark parity but is not yet wired through + the Rust binding; passing a non-``None`` value raises ``NotImplementedError``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.like(dfn.lit("hello"), dfn.lit("h%")).alias("v")) + >>> r.collect_column("v")[0].as_py() + True + + +.. py:function:: luhn_check(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``luhn_check``: true if the digit string passes the Luhn check. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.luhn_check( + ... dfn.lit("4111111111111111") + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + True + + +.. py:function:: make_dt_interval(days: datafusion.expr.Expr | int | None = None, hours: datafusion.expr.Expr | int | None = None, mins: datafusion.expr.Expr | int | None = None, secs: datafusion.expr.Expr | float | None = None) -> datafusion.expr.Expr + + Spark ``make_dt_interval``: day-time interval from components. + + All parts are optional; omitted parts default to zero, matching pyspark. + Integer parts accept a native ``int`` and ``secs`` accepts a ``float``, + or any part may be an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.make_dt_interval().alias("v")) + >>> r.collect_column("v")[0].as_py() + datetime.timedelta(0) + + >>> r = df.select( + ... dfn.functions.spark.make_dt_interval( + ... days=1, hours=2, mins=3, secs=4.5 + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + datetime.timedelta(days=1, seconds=7384, microseconds=500000) + + +.. py:function:: make_interval(years: datafusion.expr.Expr | int | None = None, months: datafusion.expr.Expr | int | None = None, weeks: datafusion.expr.Expr | int | None = None, days: datafusion.expr.Expr | int | None = None, hours: datafusion.expr.Expr | int | None = None, mins: datafusion.expr.Expr | int | None = None, secs: datafusion.expr.Expr | float | None = None) -> datafusion.expr.Expr + + Spark ``make_interval``: interval from year/month/week/day/hour/min/sec parts. + + All parts are optional; omitted parts default to zero, matching pyspark. + Integer parts accept a native ``int`` and ``secs`` accepts a ``float``, + or any part may be an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.make_interval().alias("v")) + >>> r.collect_column("v")[0].as_py().months + 0 + + >>> r = df.select(dfn.functions.spark.make_interval(years=1).alias("v")) + >>> r.collect_column("v")[0].as_py().months + 12 + + +.. py:function:: make_valid_utf8(str: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``make_valid_utf8``: replace invalid UTF-8 bytes with U+FFFD. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.make_valid_utf8(dfn.lit("hello")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 'hello' + + +.. py:function:: map_from_arrays(col1: datafusion.expr.Expr, col2: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``map_from_arrays``: build a map from parallel key/value arrays. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> keys = dfn.functions.spark.array(dfn.lit("a"), dfn.lit("b")) + >>> vals = dfn.functions.spark.array(dfn.lit(1), dfn.lit(2)) + >>> r = df.select( + ... dfn.functions.spark.map_from_arrays(keys, vals).alias("v")) + >>> r.collect_column("v")[0].as_py() + [('a', 1), ('b', 2)] + + +.. py:function:: map_from_entries(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``map_from_entries``: build a map from an array of key/value structs. + + ``col`` must be an array whose elements are two-field structs; the first + field becomes the map key and the second the value. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> ctx = dfn.SessionContext() + >>> entry_type = pa.list_( + ... pa.struct([("key", pa.string()), ("value", pa.int64())])) + >>> entries = pa.array( + ... [[{"key": "a", "value": 1}, {"key": "b", "value": 2}]], + ... type=entry_type) + >>> df = ctx.from_arrow(pa.record_batch([entries], names=["e"])) + >>> r = df.select( + ... dfn.functions.spark.map_from_entries(dfn.col("e")).alias("v")) + >>> r.collect_column("v")[0].as_py() + [('a', 1), ('b', 2)] + + +.. py:function:: minute(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``minute``: extract minute component of a timestamp. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import datetime + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> ts = dfn.lit( + ... pa.scalar(datetime(2020, 1, 15, 14, 30, 45), + ... type=pa.timestamp('us'))) + >>> r = df.select(dfn.functions.spark.minute(ts).alias("v")) + >>> r.collect_column("v")[0].as_py() + 30 + + +.. py:function:: modulus(dividend: datafusion.expr.Expr | float, divisor: datafusion.expr.Expr | float) -> datafusion.expr.Expr + + Spark ``mod``: remainder of ``dividend / divisor`` (sign follows dividend). + + ``dividend`` and ``divisor`` accept native numbers or :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.modulus(dfn.lit(10), dfn.lit(3)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 1 + + +.. py:function:: negative(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``negative``: unary minus. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.negative(dfn.lit(3)).alias("v")) + >>> r.collect_column("v")[0].as_py() + -3 + + +.. py:function:: next_day(date: datafusion.expr.Expr, dayOfWeek: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Spark ``next_day``: first date after ``start_date`` named ``day_of_week``. + + ``dayOfWeek`` accepts a native ``str`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import date + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> d = dfn.lit(pa.scalar(date(2020, 1, 15), type=pa.date32())) + >>> r = df.select(dfn.functions.spark.next_day(d, "Mon").alias("v")) + >>> r.collect_column("v")[0].as_py() + datetime.date(2020, 1, 20) + + +.. py:function:: parse_url(url: datafusion.expr.Expr, partToExtract: datafusion.expr.Expr | str, key: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Spark ``parse_url``: extract a part from a URL; errors on invalid URLs. + + ``partToExtract`` is one of ``"HOST"``, ``"PATH"``, ``"QUERY"``, + ``"REF"``, ``"PROTOCOL"``, ``"FILE"``, ``"AUTHORITY"``, ``"USERINFO"``. + Pass ``key`` only with ``"QUERY"`` to extract a single parameter. Bare + ``str`` values for ``partToExtract``/``key`` are treated as column names + (matching pyspark); pass :func:`~datafusion.lit` for a literal. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.parse_url( + ... dfn.lit("http://example.com/path?q=1"), dfn.lit("HOST") + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + 'example.com' + + >>> r = df.select( + ... dfn.functions.spark.parse_url( + ... dfn.lit("http://example.com/path?q=1"), + ... dfn.lit("QUERY"), + ... key=dfn.lit("q"), + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + '1' + + +.. py:function:: pmod(dividend: datafusion.expr.Expr | float, divisor: datafusion.expr.Expr | float) -> datafusion.expr.Expr + + Spark ``pmod``: positive remainder of division. + + ``dividend`` and ``divisor`` accept native numbers or :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.pmod(dfn.lit(-1), dfn.lit(3)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 2 + + +.. py:function:: rint(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``rint``: round to nearest mathematical integer (as double). + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.rint(dfn.lit(2.5)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 2.0 + + +.. py:function:: round(col: datafusion.expr.Expr, scale: datafusion.expr.Expr | int | None = None) -> datafusion.expr.Expr + + Spark ``round``: round to ``scale`` decimal places, HALF_UP rounding. + + ``scale`` defaults to zero when omitted, matching pyspark, and accepts a + native ``int`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.round(dfn.lit(2.5)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 3.0 + + >>> r = df.select( + ... dfn.functions.spark.round(dfn.lit(2.345), scale=2).alias("v")) + >>> r.collect_column("v")[0].as_py() + 2.35 + + +.. py:function:: sec(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``sec``: secant. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.sec(dfn.lit(0.0)).alias("v")) + >>> r.collect_column("v")[0].as_py() + 1.0 + + +.. py:function:: second(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``second``: extract second component of a timestamp. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import datetime + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> ts = dfn.lit( + ... pa.scalar(datetime(2020, 1, 15, 14, 30, 45), + ... type=pa.timestamp('us'))) + >>> r = df.select(dfn.functions.spark.second(ts).alias("v")) + >>> r.collect_column("v")[0].as_py() + 45 + + +.. py:function:: sha1(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``sha1``: SHA-1 hash as a hex string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"s": ["hello"]}) + >>> r = df.select(dfn.functions.spark.sha1(dfn.col("s")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 'aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d' + + +.. py:function:: sha2(col: datafusion.expr.Expr, numBits: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``sha2``: SHA-2 family hash (224, 256, 384, 512). Bit length 0 = 256. + + ``numBits`` accepts a native ``int`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"s": ["hello"]}) + >>> r = df.select( + ... dfn.functions.spark.sha2(dfn.col("s"), 256).alias("v")) + >>> r.collect_column("v")[0].as_py() + '2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824' + + +.. py:function:: shiftleft(col: datafusion.expr.Expr, numBits: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``shiftleft``: ``value`` shifted left by ``shift`` bits. + + ``numBits`` accepts a native ``int`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.shiftleft(dfn.lit(1), 3).alias("v")) + >>> r.collect_column("v")[0].as_py() + 8 + + +.. py:function:: shiftright(col: datafusion.expr.Expr, numBits: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``shiftright``: arithmetic right shift. + + ``numBits`` accepts a native ``int`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.shiftright(dfn.lit(8), 2).alias("v")) + >>> r.collect_column("v")[0].as_py() + 2 + + +.. py:function:: shiftrightunsigned(col: datafusion.expr.Expr, numBits: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``shiftrightunsigned``: logical (unsigned) right shift. + + ``numBits`` accepts a native ``int`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.shiftrightunsigned(dfn.lit(8), 2).alias("v")) + >>> r.collect_column("v")[0].as_py() + 2 + + +.. py:function:: shuffle(col: datafusion.expr.Expr, seed: int | None = None) -> datafusion.expr.Expr + + Spark ``shuffle``: returns a random permutation of the input array. + + ``seed`` is accepted for pyspark parity but is not yet wired through the + Rust binding; passing a non-``None`` value raises ``NotImplementedError``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.shuffle( + ... dfn.functions.spark.array(dfn.lit(1), dfn.lit(2), dfn.lit(3)) + ... ).alias("v") + ... ) + >>> sorted(r.collect_column("v")[0].as_py()) + [1, 2, 3] + + +.. py:function:: size(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``size``: length of an array or map. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.size( + ... dfn.functions.spark.array(dfn.lit(1), dfn.lit(2), dfn.lit(3)) + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + 3 + + +.. py:function:: slice(x: datafusion.expr.Expr, start: datafusion.expr.Expr | int, length: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``slice``: subset of the array from 1-indexed ``start`` with ``length``. + + Negative ``start`` counts from the end. ``start`` and ``length`` accept + native ``int`` values or :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [[1, 2, 3, 4]]}) + >>> r = df.select( + ... dfn.functions.spark.slice( + ... dfn.col("x"), 2, 2, + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + [2, 3] + + +.. py:function:: soundex(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``soundex``: Soundex phonetic code. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.soundex(dfn.lit("Robert")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 'R163' + + +.. py:function:: space(col: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``space``: string of n spaces. + + ``col`` accepts a native ``int`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.space(3).alias("v")) + >>> r.collect_column("v")[0].as_py() + ' ' + + +.. py:function:: spark_cast(arg: datafusion.expr.Expr, type_str: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Spark ``cast``: cast ``arg`` to the type named by ``type_str``. + + Uses Spark cast semantics (e.g. overflow returns NULL, not error). + ``type_str`` accepts a native ``str`` or an :class:`Expr`. + + Currently only supports casting numeric values to ``"timestamp"``. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.spark_cast( + ... dfn.lit(1579098645), "timestamp" + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py().isoformat() + '2020-01-15T14:30:45+00:00' + + +.. py:function:: str_to_map(text: datafusion.expr.Expr, pairDelim: datafusion.expr.Expr | str | None = None, keyValueDelim: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Spark ``str_to_map``: split text into key/value pairs using delimiters. + + Delimiters default to ``","`` and ``":"`` when omitted, matching pyspark. + Parameter names match ``pyspark.sql.functions.str_to_map``; pyspark types + the delimiters as column-or-name, so a bare ``str`` is treated as a column + name. Pass :func:`~datafusion.lit` for a literal delimiter. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.str_to_map(dfn.lit("a:1,b:2")).alias("v")) + >>> r.collect_column("v")[0].as_py() + [('a', '1'), ('b', '2')] + + >>> r = df.select( + ... dfn.functions.spark.str_to_map( + ... dfn.lit("a=1;b=2"), + ... pairDelim=dfn.lit(";"), + ... keyValueDelim=dfn.lit("="), + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + [('a', '1'), ('b', '2')] + + +.. py:function:: substring(str: datafusion.expr.Expr, pos: datafusion.expr.Expr | int, len: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``substring``: 1-indexed substring starting at ``pos`` of given ``length``. + + Negative ``pos`` counts from the end. ``pos`` and ``len`` accept native + ``int`` values or :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.substring(dfn.lit("hello"), 1, 3).alias("v")) + >>> r.collect_column("v")[0].as_py() + 'hel' + + +.. py:function:: time_trunc(unit: datafusion.expr.Expr | str, time: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``time_trunc``: truncate time value to unit ``fmt``. + + A bare ``str`` ``unit`` is treated as a column name (matching pyspark), + not a literal; pass :func:`~datafusion.lit` for a literal unit. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import time + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> t = dfn.lit(pa.scalar(time(14, 30, 45), type=pa.time64('us'))) + >>> r = df.select( + ... dfn.functions.spark.time_trunc(dfn.lit("hour"), t).alias("v")) + >>> r.collect_column("v")[0].as_py() + datetime.time(14, 0) + + +.. py:function:: to_utc_timestamp(timestamp: datafusion.expr.Expr, tz: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Spark ``to_utc_timestamp``: interpret ``ts`` as ``tz``, convert to UTC. + + ``tz`` accepts a native ``str`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import datetime + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> ts = dfn.lit( + ... pa.scalar(datetime(2020, 1, 15, 14, 30, 45), + ... type=pa.timestamp('us'))) + >>> r = df.select( + ... dfn.functions.spark.to_utc_timestamp(ts, "UTC").alias("v")) + >>> r.collect_column("v")[0].as_py() + datetime.datetime(2020, 1, 15, 14, 30, 45) + + +.. py:function:: trunc(date: datafusion.expr.Expr, format: datafusion.expr.Expr | str) -> datafusion.expr.Expr + + Spark ``trunc``: truncate date to unit ``fmt``. + + ``format`` accepts a native ``str`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import date + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> d = dfn.lit(pa.scalar(date(2020, 1, 15), type=pa.date32())) + >>> r = df.select(dfn.functions.spark.trunc(d, "YEAR").alias("v")) + >>> r.collect_column("v")[0].as_py() + datetime.date(2020, 1, 1) + + +.. py:function:: try_parse_url(url: datafusion.expr.Expr, partToExtract: datafusion.expr.Expr | str, key: datafusion.expr.Expr | str | None = None) -> datafusion.expr.Expr + + Spark ``try_parse_url``: like ``parse_url`` but returns NULL on invalid URLs. + + Bare ``str`` values for ``partToExtract``/``key`` are treated as column + names (matching pyspark); pass :func:`~datafusion.lit` for a literal. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.try_parse_url( + ... dfn.lit("http://example.com/"), dfn.lit("HOST") + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + 'example.com' + + +.. py:function:: try_sum(col: datafusion.expr.Expr, distinct: bool | None = None, filter: datafusion.expr.Expr | None = None, order_by: list[datafusion.expr.SortKey] | datafusion.expr.SortKey | None = None, null_treatment: datafusion.common.NullTreatment | None = None) -> datafusion.expr.Expr + + Spark ``try_sum``: sum that returns NULL on overflow. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3]}) + >>> r = df.aggregate( + ... [], [dfn.functions.spark.try_sum(dfn.col("a")).alias("v")]) + >>> r.collect_column("v")[0].as_py() + 6 + + +.. py:function:: try_url_decode(str: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``try_url_decode``: like ``url_decode``; returns NULL on invalid input. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.try_url_decode(dfn.lit("a%20b")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 'a b' + + +.. py:function:: unbase64(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``unbase64``: decode a base64 string to binary. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.unbase64(dfn.lit("aGk=")).alias("v")) + >>> r.collect_column("v")[0].as_py() + b'hi' + + +.. py:function:: unhex(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``unhex``: convert hexadecimal string to binary. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select(dfn.functions.spark.unhex(dfn.lit("FF")).alias("v")) + >>> r.collect_column("v")[0].as_py() + b'\xff' + + +.. py:function:: unix_date(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``unix_date``: days since 1970-01-01 for ``dt``. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import date + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> d = dfn.lit(pa.scalar(date(2020, 1, 15), type=pa.date32())) + >>> r = df.select(dfn.functions.spark.unix_date(d).alias("v")) + >>> r.collect_column("v")[0].as_py() + 18276 + + +.. py:function:: unix_micros(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``unix_micros``: microseconds since epoch for ``ts``. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import datetime + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> ts = dfn.lit( + ... pa.scalar(datetime(2020, 1, 15, 14, 30, 45), + ... type=pa.timestamp('us'))) + >>> r = df.select(dfn.functions.spark.unix_micros(ts).alias("v")) + >>> r.collect_column("v")[0].as_py() + 1579098645000000 + + +.. py:function:: unix_millis(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``unix_millis``: milliseconds since epoch for ``ts``. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import datetime + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> ts = dfn.lit( + ... pa.scalar(datetime(2020, 1, 15, 14, 30, 45), + ... type=pa.timestamp('us'))) + >>> r = df.select(dfn.functions.spark.unix_millis(ts).alias("v")) + >>> r.collect_column("v")[0].as_py() + 1579098645000 + + +.. py:function:: unix_seconds(col: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``unix_seconds``: seconds since epoch for ``ts``. + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datetime import datetime + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> ts = dfn.lit( + ... pa.scalar(datetime(2020, 1, 15, 14, 30, 45), + ... type=pa.timestamp('us'))) + >>> r = df.select(dfn.functions.spark.unix_seconds(ts).alias("v")) + >>> r.collect_column("v")[0].as_py() + 1579098645 + + +.. py:function:: url_decode(str: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``url_decode``: decode an application/x-www-form-urlencoded string. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.url_decode(dfn.lit("a%20b")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 'a b' + + +.. py:function:: url_encode(str: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``url_encode``: encode a string in application/x-www-form-urlencoded. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.url_encode(dfn.lit("a b")).alias("v")) + >>> r.collect_column("v")[0].as_py() + 'a+b' + + +.. py:function:: width_bucket(v: datafusion.expr.Expr, min: datafusion.expr.Expr, max: datafusion.expr.Expr, numBucket: datafusion.expr.Expr | int) -> datafusion.expr.Expr + + Spark ``width_bucket``: bucket number for ``value`` in equi-width histogram. + + ``numBucket`` accepts a native ``int`` or an :class:`Expr`. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.width_bucket( + ... dfn.lit(5.0), dfn.lit(0.0), dfn.lit(10.0), 5 + ... ).alias("v") + ... ) + >>> r.collect_column("v")[0].as_py() + 3 + + +.. py:function:: xxhash64(*cols: datafusion.expr.Expr) -> datafusion.expr.Expr + + Spark ``xxhash64``: 64-bit xxHash of the arguments. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1]}) + >>> r = df.select( + ... dfn.functions.spark.xxhash64(dfn.lit("hello")).alias("v")) + >>> r.collect_column("v")[0].as_py() + -4367754540140381902 + + diff --git a/_sources/autoapi/datafusion/index.rst.txt b/_sources/autoapi/datafusion/index.rst.txt new file mode 100644 index 000000000..2d06639f1 --- /dev/null +++ b/_sources/autoapi/datafusion/index.rst.txt @@ -0,0 +1,3429 @@ +datafusion +========== + +.. py:module:: datafusion + +.. autoapi-nested-parse:: + + DataFusion: an in-process query engine built on Apache Arrow. + + DataFusion is not a database -- it has no server and no external dependencies. + You create a :py:class:`SessionContext`, point it at data sources (Parquet, CSV, + JSON, Arrow IPC, Pandas, Polars, or raw Python dicts/lists), and run queries + using either SQL or the DataFrame API. + + Core abstractions + ----------------- + - **SessionContext** -- entry point for loading data, running SQL, and creating + DataFrames. + - **DataFrame** -- lazy query builder. Every method returns a new DataFrame; + call :py:meth:`~datafusion.dataframe.DataFrame.collect` or a ``to_*`` + method to execute. + - **Expr** -- expression tree node for column references, literals, and function + calls. Build with :py:func:`col` and :py:func:`lit`. + - **functions** -- 290+ built-in scalar, aggregate, and window functions. + + Quick start + ----------- + + >>> from datafusion import SessionContext, col + >>> from datafusion import functions as F + >>> ctx = SessionContext() + >>> df = ctx.from_pydict({"a": [1, 2, 3], "b": [4, 5, 6]}) + >>> result = ( + ... df.filter(col("a") > 1) + ... .with_column("total", col("a") + col("b")) + ... .aggregate([], [F.sum(col("total")).alias("grand_total")]) + ... ) + >>> result.to_pydict() + {'grand_total': [16]} + + User guide and full documentation: https://datafusion.apache.org/python + + AI agent reference (SQL-to-DataFrame mappings, expression-building patterns, + common pitfalls), written in a dense, skill-oriented format: + https://github.com/apache/datafusion-python/blob/main/skills/datafusion_python/SKILL.md + + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/datafusion/catalog/index + /autoapi/datafusion/context/index + /autoapi/datafusion/dataframe/index + /autoapi/datafusion/dataframe_formatter/index + /autoapi/datafusion/expr/index + /autoapi/datafusion/functions/index + /autoapi/datafusion/input/index + /autoapi/datafusion/io/index + /autoapi/datafusion/ipc/index + /autoapi/datafusion/object_store/index + /autoapi/datafusion/options/index + /autoapi/datafusion/plan/index + /autoapi/datafusion/record_batch/index + /autoapi/datafusion/substrait/index + /autoapi/datafusion/unparser/index + /autoapi/datafusion/user_defined/index + + +Attributes +---------- + +.. autoapisummary:: + + datafusion.DFSchema + datafusion.col + datafusion.column + datafusion.udaf + datafusion.udf + datafusion.udtf + datafusion.udwf + + +Classes +------- + +.. autoapisummary:: + + datafusion.Accumulator + datafusion.AggregateUDF + datafusion.Catalog + datafusion.CsvReadOptions + datafusion.DataFrameWriteOptions + datafusion.ExecutionPlan + datafusion.ExplainFormat + datafusion.Expr + datafusion.InsertOp + datafusion.LogicalPlan + datafusion.Metric + datafusion.MetricsSet + datafusion.ParquetColumnOptions + datafusion.ParquetWriterOptions + datafusion.RecordBatch + datafusion.RecordBatchStream + datafusion.RuntimeEnvBuilder + datafusion.SQLOptions + datafusion.ScalarUDF + datafusion.SessionConfig + datafusion.Table + datafusion.TableFunction + datafusion.TableProviderFactory + datafusion.TableProviderFactoryExportable + datafusion.WindowFrame + datafusion.WindowUDF + + +Functions +--------- + +.. autoapisummary:: + + datafusion.configure_formatter + datafusion.lit + datafusion.literal + datafusion.read_avro + datafusion.read_csv + datafusion.read_json + datafusion.read_parquet + + +Package Contents +---------------- + +.. py:class:: Accumulator + + Defines how an :py:class:`AggregateUDF` accumulates values. + + + .. py:method:: evaluate() -> pyarrow.Scalar + :abstractmethod: + + + Return the resultant value. + + While this function template expects a PyArrow Scalar value return type, + you can return any value that can be converted into a Scalar. This + includes basic Python data types such as integers and strings. In + addition to primitive types, we currently support PyArrow, nanoarrow, + and arro3 objects in addition to primitive data types. Other objects + that support the Arrow FFI standard will be given a "best attempt" at + conversion to scalar objects. + + + + .. py:method:: merge(states: list[pyarrow.Array]) -> None + :abstractmethod: + + + Merge a set of states. + + + + .. py:method:: state() -> list[pyarrow.Scalar] + :abstractmethod: + + + Return the current state. + + While this function template expects PyArrow Scalar values return type, + you can return any value that can be converted into a Scalar. This + includes basic Python data types such as integers and strings. In + addition to primitive types, we currently support PyArrow, nanoarrow, + and arro3 objects in addition to primitive data types. Other objects + that support the Arrow FFI standard will be given a "best attempt" at + conversion to scalar objects. + + + + .. py:method:: update(*values: pyarrow.Array) -> None + :abstractmethod: + + + Evaluate an array of values and update state. + + + +.. py:class:: AggregateUDF(name: str, accumulator: collections.abc.Callable[[], Accumulator], input_types: list[pyarrow.DataType], return_type: pyarrow.DataType, state_type: list[pyarrow.DataType], volatility: Volatility | str) + AggregateUDF(name: str, accumulator: AggregateUDFExportable, input_types: None = ..., return_type: None = ..., state_type: None = ..., volatility: None = ...) + + Class for performing scalar user-defined functions (UDF). + + Aggregate UDFs operate on a group of rows and return a single value. See + also :py:class:`ScalarUDF` for operating on a row by row basis. + + Instantiate a user-defined aggregate function (UDAF). + + See :py:func:`udaf` for a convenience function and argument + descriptions. + + + .. py:method:: __call__(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Execute the UDAF. + + This function is not typically called by an end user. These calls will + occur during the evaluation of the dataframe. + + + + .. py:method:: __repr__() -> str + + Print a string representation of the Aggregate UDF. + + + + .. py:method:: _from_internal(internal: datafusion._internal.AggregateUDF) -> AggregateUDF + :classmethod: + + + Wrap an already-constructed internal ``AggregateUDF`` handle. + + Used by :py:meth:`SessionContext.udaf` to surface a function looked + up from the session's function registry without re-running + :py:meth:`__init__`. + + + + .. py:method:: from_pycapsule(func: AggregateUDFExportable | _typeshed.CapsuleType) -> AggregateUDF + :staticmethod: + + + Create an Aggregate UDF from AggregateUDF PyCapsule object. + + This function will instantiate a Aggregate UDF that uses a DataFusion + AggregateUDF that is exported via the FFI bindings. + + + + .. py:method:: udaf(input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, state_type: list[pyarrow.DataType], volatility: Volatility | str, name: str | None = None) -> collections.abc.Callable[Ellipsis, AggregateUDF] + udaf(accum: collections.abc.Callable[[], Accumulator], input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, state_type: list[pyarrow.DataType], volatility: Volatility | str, name: str | None = None) -> AggregateUDF + udaf(accum: AggregateUDFExportable) -> AggregateUDF + udaf(accum: _typeshed.CapsuleType) -> AggregateUDF + :staticmethod: + + + Create a new User-Defined Aggregate Function (UDAF). + + This class allows you to define an aggregate function that can be used in + data aggregation or window function calls. + + Usage: + - As a function: ``udaf(accum, input_types, return_type, state_type, volatility, name)``. + - As a decorator: ``@udaf(input_types, return_type, state_type, volatility, name)``. + When using ``udaf`` as a decorator, do not pass ``accum`` explicitly. + + If your :py:class:`Accumulator` can be instantiated with no arguments, you + can simply pass its type as ``accum``. If you need to pass additional + arguments to its constructor, you can define a lambda or a factory method. + During runtime the :py:class:`Accumulator` will be constructed for every + instance in which this UDAF is used. + + .. rubric:: Examples + + >>> import pyarrow.compute as pc + >>> from datafusion.user_defined import AggregateUDF, Accumulator, udaf + >>> class Summarize(Accumulator): + ... def __init__(self, bias: float = 0.0): + ... self._sum = pa.scalar(bias) + ... def state(self): + ... return [self._sum] + ... def update(self, values): + ... self._sum = pa.scalar( + ... self._sum.as_py() + pc.sum(values).as_py()) + ... def merge(self, states): + ... self._sum = pa.scalar( + ... self._sum.as_py() + pc.sum(states[0]).as_py()) + ... def evaluate(self): + ... return self._sum + + Using ``udaf`` as a function: + + >>> udaf1 = AggregateUDF.udaf( + ... Summarize, pa.float64(), pa.float64(), + ... [pa.float64()], "immutable") + + Wrapping ``udaf`` with a function: + + >>> def sum_bias_10() -> Summarize: + ... return Summarize(10.0) + >>> udaf2 = udaf(sum_bias_10, pa.float64(), pa.float64(), [pa.float64()], + ... "immutable") + + Using ``udaf`` with lambda: + + >>> udaf3 = udaf(lambda: Summarize(20.0), pa.float64(), pa.float64(), + ... [pa.float64()], "immutable") + + Using ``udaf`` as a decorator: + + >>> @AggregateUDF.udaf( + ... pa.float64(), pa.float64(), + ... [pa.float64()], "immutable") + ... def udaf4(): + ... return Summarize(10.0) + + Apply to a dataframe: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0]}) + >>> df.aggregate([], [udaf1(col("a")).alias("total")]).collect_column( + ... "total")[0].as_py() + 6.0 + >>> df.aggregate([], [udaf2(col("a")).alias("total")]).collect_column( + ... "total")[0].as_py() + 16.0 + >>> df.aggregate([], [udaf3(col("a")).alias("total")]).collect_column( + ... "total")[0].as_py() + 26.0 + >>> df.aggregate([], [udaf4(col("a")).alias("total")]).collect_column( + ... "total")[0].as_py() + 16.0 + + :param accum: The accumulator python function. Only needed when calling as a + function. Skip this argument when using ``udaf`` as a decorator. + If you have a Rust backed AggregateUDF within a PyCapsule, you can + pass this parameter and ignore the rest. They will be determined + directly from the underlying function. See the online documentation + for more information. + :param input_types: The data types of the arguments to ``accum``. + :param return_type: The data type of the return value. + :param state_type: The data types of the intermediate accumulation. + :param volatility: See :py:class:`Volatility` for allowed values. + :param name: A descriptive name for the function. + + :returns: A user-defined aggregate function, which can be used in either data + aggregation or window function calls. + + + + .. py:attribute:: _udaf + + + .. py:property:: name + :type: str + + + Return the registered name of this UDAF. + + For UDAFs imported via the FFI capsule protocol, this is the + name the capsule itself reports — not the ``name`` argument + passed to the constructor (which is ignored on the FFI path). + + +.. py:class:: Catalog(catalog: datafusion._internal.catalog.RawCatalog) + + DataFusion data catalog. + + This constructor is not typically called by the end user. + + + .. py:method:: __repr__() -> str + + Print a string representation of the catalog. + + + + .. py:method:: deregister_schema(name: str, cascade: bool = True) -> Schema | None + + Deregister a schema from this catalog. + + + + .. py:method:: memory_catalog(ctx: datafusion.SessionContext | None = None) -> Catalog + :staticmethod: + + + Create an in-memory catalog provider. + + + + .. py:method:: names() -> set[str] + + This is an alias for `schema_names`. + + + + .. py:method:: register_schema(name: str, schema: Schema | SchemaProvider | SchemaProviderExportable) -> Schema | None + + Register a schema with this catalog. + + + + .. py:method:: schema(name: str = 'public') -> Schema + + Returns the database with the given ``name`` from this catalog. + + + + .. py:method:: schema_names() -> set[str] + + Returns the list of schemas in this catalog. + + + + .. py:attribute:: catalog + + +.. py:class:: CsvReadOptions(*, has_header: bool = True, delimiter: str = ',', quote: str = '"', terminator: str | None = None, escape: str | None = None, comment: str | None = None, newlines_in_values: bool = False, schema: pyarrow.Schema | None = None, schema_infer_max_records: int = DEFAULT_MAX_INFER_SCHEMA, file_extension: str = '.csv', table_partition_cols: list[tuple[str, pyarrow.DataType]] | None = None, file_compression_type: str = '', file_sort_order: list[list[datafusion.expr.SortExpr]] | None = None, null_regex: str | None = None, truncated_rows: bool = False) + + Options for reading CSV files. + + This class provides a builder pattern for configuring CSV reading options. + All methods starting with ``with_`` return ``self`` to allow method chaining. + + Initialize CsvReadOptions. + + :param has_header: Does the CSV file have a header row? If schema inference + is run on a file with no headers, default column names are created. + :param delimiter: Column delimiter character. Must be a single ASCII character. + :param quote: Quote character for fields containing delimiters or newlines. + Must be a single ASCII character. + :param terminator: Optional line terminator character. If ``None``, uses CRLF. + Must be a single ASCII character. + :param escape: Optional escape character for quotes. Must be a single ASCII + character. + :param comment: If specified, lines beginning with this character are ignored. + Must be a single ASCII character. + :param newlines_in_values: Whether newlines in quoted values are supported. + Parsing newlines in quoted values may be affected by execution + behavior such as parallel file scanning. Setting this to ``True`` + ensures that newlines in values are parsed successfully, which may + reduce performance. + :param schema: Optional PyArrow schema representing the CSV files. If ``None``, + the CSV reader will try to infer it based on data in the file. + :param schema_infer_max_records: Maximum number of rows to read from CSV files + for schema inference if needed. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param table_partition_cols: Partition columns as a list of tuples of + (column_name, data_type). + :param file_compression_type: File compression type. Supported values are + ``"gzip"``, ``"bz2"``, ``"xz"``, ``"zstd"``, or empty string for + uncompressed. + :param file_sort_order: Optional sort order of the files as a list of sort + expressions per file. + :param null_regex: Optional regex pattern to match null values in the CSV. + :param truncated_rows: Whether to allow truncated rows when parsing. By default + this is ``False`` and will error if the CSV rows have different + lengths. When set to ``True``, it will allow records with less than + the expected number of columns and fill the missing columns with + nulls. If the record's schema is not nullable, it will still return + an error. + + + .. py:method:: to_inner() -> datafusion._internal.options.CsvReadOptions + + Convert this object into the underlying Rust structure. + + This is intended for internal use only. + + + + .. py:method:: with_comment(comment: str | None) -> CsvReadOptions + + Configure the comment character. + + + + .. py:method:: with_delimiter(delimiter: str) -> CsvReadOptions + + Configure the column delimiter. + + + + .. py:method:: with_escape(escape: str | None) -> CsvReadOptions + + Configure the escape character. + + + + .. py:method:: with_file_compression_type(file_compression_type: str) -> CsvReadOptions + + Configure file compression type. + + + + .. py:method:: with_file_extension(file_extension: str) -> CsvReadOptions + + Configure the file extension filter. + + + + .. py:method:: with_file_sort_order(file_sort_order: list[list[datafusion.expr.SortExpr]]) -> CsvReadOptions + + Configure file sort order. + + + + .. py:method:: with_has_header(has_header: bool) -> CsvReadOptions + + Configure whether the CSV has a header row. + + + + .. py:method:: with_newlines_in_values(newlines_in_values: bool) -> CsvReadOptions + + Configure whether newlines in values are supported. + + + + .. py:method:: with_null_regex(null_regex: str | None) -> CsvReadOptions + + Configure null value regex pattern. + + + + .. py:method:: with_quote(quote: str) -> CsvReadOptions + + Configure the quote character. + + + + .. py:method:: with_schema(schema: pyarrow.Schema | None) -> CsvReadOptions + + Configure the schema. + + + + .. py:method:: with_schema_infer_max_records(schema_infer_max_records: int) -> CsvReadOptions + + Configure maximum records for schema inference. + + + + .. py:method:: with_table_partition_cols(table_partition_cols: list[tuple[str, pyarrow.DataType]]) -> CsvReadOptions + + Configure table partition columns. + + + + .. py:method:: with_terminator(terminator: str | None) -> CsvReadOptions + + Configure the line terminator character. + + + + .. py:method:: with_truncated_rows(truncated_rows: bool) -> CsvReadOptions + + Configure whether to allow truncated rows. + + + + .. py:attribute:: comment + :value: None + + + + .. py:attribute:: delimiter + :value: ',' + + + + .. py:attribute:: escape + :value: None + + + + .. py:attribute:: file_compression_type + :value: '' + + + + .. py:attribute:: file_extension + :value: '.csv' + + + + .. py:attribute:: file_sort_order + :value: [] + + + + .. py:attribute:: has_header + :value: True + + + + .. py:attribute:: newlines_in_values + :value: False + + + + .. py:attribute:: null_regex + :value: None + + + + .. py:attribute:: quote + :value: '"' + + + + .. py:attribute:: schema + :value: None + + + + .. py:attribute:: schema_infer_max_records + :value: 1000 + + + + .. py:attribute:: table_partition_cols + :value: [] + + + + .. py:attribute:: terminator + :value: None + + + + .. py:attribute:: truncated_rows + :value: False + + + +.. py:class:: DataFrameWriteOptions(insert_operation: InsertOp | None = None, single_file_output: bool = False, partition_by: str | collections.abc.Sequence[str] | None = None, sort_by: datafusion.expr.Expr | datafusion.expr.SortExpr | collections.abc.Sequence[datafusion.expr.Expr] | collections.abc.Sequence[datafusion.expr.SortExpr] | None = None) + + Writer options for DataFrame. + + There is no guarantee the table provider supports all writer options. + See the individual implementation and documentation for details. + + Instantiate writer options for DataFrame. + + + .. py:attribute:: _raw_write_options + + +.. py:class:: ExecutionPlan(plan: datafusion._internal.ExecutionPlan) + + Represent nodes in the DataFusion Physical Plan. + + This constructor should not be called by the end user. + + + .. py:method:: __repr__() -> str + + Print a string representation of the physical plan. + + + + .. py:method:: children() -> list[ExecutionPlan] + + Get a list of children `ExecutionPlan` that act as inputs to this plan. + + The returned list will be empty for leaf nodes such as scans, will contain a + single value for unary nodes, or two values for binary nodes (such as joins). + + + + .. py:method:: collect_metrics() -> list[tuple[str, MetricsSet]] + + Return runtime statistics for each step of the query execution. + + DataFusion executes a query as a pipeline of operators — for example a + data source scan, followed by a filter, followed by a projection. After + the DataFrame has been executed (via + :py:meth:`~datafusion.DataFrame.collect`, + :py:meth:`~datafusion.DataFrame.execute_stream`, etc.), each operator + records statistics such as how many rows it produced and how much CPU + time it consumed. + + Each entry in the returned list corresponds to one operator that + recorded metrics. The first element of the tuple is the operator's + description string — the same text shown by + :py:meth:`display_indent` — which identifies both the operator type + and its key parameters, for example ``"FilterExec: column1@0 > 1"`` + or ``"DataSourceExec: partitions=1"``. + + :returns: A list of ``(description, MetricsSet)`` tuples ordered from the + outermost operator (top of the execution tree) down to the + data-source leaves. Only operators that recorded at least one + metric are included. Returns an empty list if called before the + DataFrame has been executed. + + + + .. py:method:: display() -> str + + Print the physical plan. + + + + .. py:method:: display_indent() -> str + + Print an indented form of the physical plan. + + + + .. py:method:: from_bytes(ctx: datafusion.context.SessionContext, data: bytes) -> ExecutionPlan + :staticmethod: + + + Create an ExecutionPlan from serialized protobuf bytes. + + Decoding routes through the session's installed + `PhysicalExtensionCodec`. Tables created in memory from record + batches are currently not supported. + + + + .. py:method:: from_proto(ctx: datafusion.context.SessionContext, data: bytes) -> ExecutionPlan + :staticmethod: + + + Deprecated alias for :meth:`from_bytes`. + + + + .. py:method:: metrics() -> MetricsSet | None + + Return metrics for this plan node, or None if this plan has no MetricsSet. + + Some operators (e.g. DataSourceExec) eagerly initialize a MetricsSet + when the plan is created, so this may return a set even before + execution. Metric *values* (such as ``output_rows``) are only + meaningful after the DataFrame has been executed. + + + + .. py:method:: to_bytes(ctx: datafusion.context.SessionContext | None = None) -> bytes + + Convert an ExecutionPlan into serialized protobuf bytes. + + When ``ctx`` is supplied, encoding routes through the session's + installed `PhysicalExtensionCodec`. Tables created in memory + from record batches are currently not supported. + + + + .. py:method:: to_proto() -> bytes + + Deprecated alias for :meth:`to_bytes`. + + + + .. py:attribute:: _raw_plan + + + .. py:property:: partition_count + :type: int + + + Returns the number of partitions in the physical plan. + + +.. py:class:: ExplainFormat + + Bases: :py:obj:`enum.Enum` + + + Output format for explain plans. + + Controls how the query plan is rendered in :py:meth:`DataFrame.explain`. + + + .. py:attribute:: GRAPHVIZ + :value: 'graphviz' + + + Graphviz DOT format for graph rendering. + + + .. py:attribute:: INDENT + :value: 'indent' + + + Default indented text format. + + + .. py:attribute:: PGJSON + :value: 'pgjson' + + + PostgreSQL-compatible JSON format for use with visualization tools. + + + .. py:attribute:: TREE + :value: 'tree' + + + Tree-style visual format with box-drawing characters. + + +.. py:class:: Expr(expr: datafusion._internal.expr.RawExpr) + + Expression object. + + Expressions are one of the core concepts in DataFusion. See + :ref:`Expressions` in the online documentation for more information. + + This constructor should not be called by the end user. + + + .. py:method:: __add__(rhs: Any) -> Expr + + Addition operator. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __and__(rhs: Expr) -> Expr + + Logical AND. + + + + .. py:method:: __eq__(rhs: object) -> Expr + + Equal to. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __ge__(rhs: Any) -> Expr + + Greater than or equal to. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __getitem__(key: str | int) -> Expr + + Retrieve sub-object. + + If ``key`` is a string, returns the subfield of the struct. + If ``key`` is an integer, retrieves the element in the array. Note that the + element index begins at ``0``, unlike + :py:func:`~datafusion.functions.array_element` which begins at ``1``. + If ``key`` is a slice, returns an array that contains a slice of the + original array. Similar to integer indexing, this follows Python convention + where the index begins at ``0`` unlike + :py:func:`~datafusion.functions.array_slice` which begins at ``1``. + + + + .. py:method:: __gt__(rhs: Any) -> Expr + + Greater than. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __invert__() -> Expr + + Binary not (~). + + + + .. py:method:: __le__(rhs: Any) -> Expr + + Less than or equal to. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __lt__(rhs: Any) -> Expr + + Less than. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __mod__(rhs: Any) -> Expr + + Modulo operator (%). + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __mul__(rhs: Any) -> Expr + + Multiplication operator. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __ne__(rhs: object) -> Expr + + Not equal to. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __or__(rhs: Expr) -> Expr + + Logical OR. + + + + .. py:method:: __reduce__() -> tuple[collections.abc.Callable[[bytes], Expr], tuple[bytes]] + + Pickle protocol hook. + + Lets expressions be shipped to worker processes via + :func:`pickle.dumps` / :func:`pickle.loads`. Built-in functions + and Python UDFs (scalar, aggregate, window) travel inside the + pickle bytes; only FFI-capsule UDFs require pre-registration on + the worker. The worker's :class:`SessionContext` for resolving + those references is looked up via + :func:`datafusion.ipc.set_worker_ctx`, falling back to the + global :class:`SessionContext` if none has been installed on + the worker. + + .. warning:: Security + :func:`pickle.loads` on the returned tuple executes + arbitrary Python on the receiver, including any + cloudpickled UDF callable embedded in the payload. Only + unpickle expressions from trusted sources. + + .. warning:: Portability + Sender and receiver must run the same Python + ``(major, minor)`` version; cloudpickle bytecode is not + portable across minor versions. See :meth:`to_bytes` for + details on what travels by value vs. by reference. + + .. rubric:: Examples + + >>> import pickle + >>> from datafusion import col, lit + >>> e = col("a") * lit(2) + >>> pickle.loads(pickle.dumps(e)).canonical_name() + 'a * Int64(2)' + + The encoding side honors a driver-side sender context installed + via :func:`datafusion.ipc.set_sender_ctx` — that is how + :meth:`SessionContext.with_python_udf_inlining` propagates + through ``pickle.dumps``. The sender context is read by + ``__reduce__``, so :func:`copy.copy` and :func:`copy.deepcopy` + — which also go through ``__reduce__`` — pick it up too. + + + + .. py:method:: __repr__() -> str + + Generate a string representation of this expression. + + + + .. py:method:: __richcmp__(other: Expr, op: int) -> Expr + + Comparison operator. + + + + .. py:method:: __sub__(rhs: Any) -> Expr + + Subtraction operator. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: __truediv__(rhs: Any) -> Expr + + Division operator. + + Accepts either an expression or any valid PyArrow scalar literal value. + + + + .. py:method:: _reconstruct(proto_bytes: bytes) -> Expr + :classmethod: + + + Internal entry point used by :meth:`__reduce__` on unpickle. + + .. rubric:: Examples + + >>> from datafusion import Expr, col, lit + >>> blob = (col("a") + lit(1)).to_bytes() + >>> Expr._reconstruct(blob).canonical_name() + 'a + Int64(1)' + + + + .. py:method:: abs() -> Expr + + Return the absolute value of a given number. + + Returns: + -------- + Expr + A new expression representing the absolute value of the input expression. + + + + .. py:method:: acos() -> Expr + + Returns the arc cosine or inverse cosine of a number. + + Returns: + -------- + Expr + A new expression representing the arc cosine of the input expression. + + + + .. py:method:: acosh() -> Expr + + Returns inverse hyperbolic cosine. + + + + .. py:method:: alias(name: str, metadata: dict[str, str] | None = None) -> Expr + + Assign a name to the expression. + + :param name: The name to assign to the expression. + :param metadata: Optional metadata to attach to the expression. + + :returns: A new expression with the assigned name. + + + + .. py:method:: array_dims() -> Expr + + Returns an array of the array's dimensions. + + + + .. py:method:: array_distinct() -> Expr + + Returns distinct values from the array after removing duplicates. + + + + .. py:method:: array_empty() -> Expr + + Returns a boolean indicating whether the array is empty. + + + + .. py:method:: array_length() -> Expr + + Returns the length of the array. + + + + .. py:method:: array_ndims() -> Expr + + Returns the number of dimensions of the array. + + + + .. py:method:: array_pop_back() -> Expr + + Returns the array without the last element. + + + + .. py:method:: array_pop_front() -> Expr + + Returns the array without the first element. + + + + .. py:method:: arrow_typeof() -> Expr + + Returns the Arrow type of the expression. + + + + .. py:method:: ascii() -> Expr + + Returns the numeric code of the first character of the argument. + + + + .. py:method:: asin() -> Expr + + Returns the arc sine or inverse sine of a number. + + + + .. py:method:: asinh() -> Expr + + Returns inverse hyperbolic sine. + + + + .. py:method:: atan() -> Expr + + Returns inverse tangent of a number. + + + + .. py:method:: atanh() -> Expr + + Returns inverse hyperbolic tangent. + + + + .. py:method:: between(low: Any, high: Any, negated: bool = False) -> Expr + + Returns ``True`` if this expression is between a given range. + + :param low: lower bound of the range (inclusive). + :param high: higher bound of the range (inclusive). + :param negated: negates whether the expression is between a given range + + + + .. py:method:: bit_length() -> Expr + + Returns the number of bits in the string argument. + + + + .. py:method:: btrim() -> Expr + + Removes all characters, spaces by default, from both sides of a string. + + + + .. py:method:: canonical_name() -> str + + Returns a complete string representation of this expression. + + + + .. py:method:: cardinality() -> Expr + + Returns the total number of elements in the array. + + + + .. py:method:: cast(to: pyarrow.DataType[Any] | type) -> Expr + + Cast to a new data type. + + + + .. py:method:: cbrt() -> Expr + + Returns the cube root of a number. + + + + .. py:method:: ceil() -> Expr + + Returns the nearest integer greater than or equal to argument. + + + + .. py:method:: char_length() -> Expr + + The number of characters in the ``string``. + + + + .. py:method:: character_length() -> Expr + + Returns the number of characters in the argument. + + + + .. py:method:: chr() -> Expr + + Converts the Unicode code point to a UTF8 character. + + + + .. py:method:: column(value: str) -> Expr + :staticmethod: + + + Creates a new expression representing a column. + + + + .. py:method:: column_name(plan: datafusion.plan.LogicalPlan) -> str + + Compute the output column name based on the provided logical plan. + + + + .. py:method:: cos() -> Expr + + Returns the cosine of the argument. + + + + .. py:method:: cosh() -> Expr + + Returns the hyperbolic cosine of the argument. + + + + .. py:method:: cot() -> Expr + + Returns the cotangent of the argument. + + + + .. py:method:: degrees() -> Expr + + Converts the argument from radians to degrees. + + + + .. py:method:: distinct() -> ExprFuncBuilder + + Only evaluate distinct values for an aggregate function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:method:: empty() -> Expr + + This is an alias for :py:func:`array_empty`. + + + + .. py:method:: exp() -> Expr + + Returns the exponential of the argument. + + + + .. py:method:: factorial() -> Expr + + Returns the factorial of the argument. + + + + .. py:method:: fill_nan(value: Any | Expr | None = None) -> Expr + + Fill NaN values with a provided value. + + + + .. py:method:: fill_null(value: Any | Expr | None = None) -> Expr + + Fill NULL values with a provided value. + + + + .. py:method:: filter(filter: Expr) -> ExprFuncBuilder + + Filter an aggregate function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:method:: flatten() -> Expr + + Flattens an array of arrays into a single array. + + + + .. py:method:: floor() -> Expr + + Returns the nearest integer less than or equal to the argument. + + + + .. py:method:: from_bytes(buf: bytes, ctx: datafusion.context.SessionContext | None = None) -> Expr + :classmethod: + + + Reconstruct an expression from serialized bytes. + + Accepts output of :meth:`to_bytes` or :func:`pickle.dumps`. + ``ctx`` is the :class:`SessionContext` used to resolve any + function references that travel by name (e.g. FFI UDFs, or + Python UDFs sent with inlining disabled via + :meth:`SessionContext.with_python_udf_inlining`). When + ``ctx`` is ``None`` the worker context installed via + :func:`datafusion.ipc.set_worker_ctx` is consulted; if no worker + context is installed, the global :class:`SessionContext` is used + (sufficient for built-ins and Python UDFs, plus any UDFs + registered on the global context). + + .. warning:: Security + Decoding may invoke ``cloudpickle.loads`` on bytes embedded + in the payload, which executes arbitrary Python code. Treat + ``buf`` as code, not data — only decode bytes you produced + yourself or received from a trusted sender. + + .. warning:: Portability + cloudpickle payloads are **not portable across Python + minor versions**. The wire format stamps the sender's + ``(major, minor)``; if it does not match the current + interpreter, this method raises :class:`ValueError` + naming both versions. Modules the UDF imports must also + be importable on the receiver — see :meth:`to_bytes` for + by-value vs. by-reference details. + + .. rubric:: Examples + + >>> from datafusion import Expr, col, lit + >>> blob = (col("a") + lit(1)).to_bytes() + >>> Expr.from_bytes(blob).canonical_name() + 'a + Int64(1)' + + + + .. py:method:: from_unixtime() -> Expr + + Converts an integer to RFC3339 timestamp format string. + + + + .. py:method:: initcap() -> Expr + + Set the initial letter of each word to capital. + + Converts the first letter of each word in ``string`` to uppercase and the + remaining characters to lowercase. + + + + .. py:method:: is_nan() -> Expr + + Returns true if a given number is +NaN or -NaN otherwise returns false. + + + + .. py:method:: is_not_null() -> Expr + + Returns ``True`` if this expression is not null. + + + + .. py:method:: is_null() -> Expr + + Returns ``True`` if this expression is null. + + + + .. py:method:: isnan() -> Expr + + Returns true if a given number is +NaN or -NaN otherwise returns false. + + + + .. py:method:: iszero() -> Expr + + Returns true if a given number is +0.0 or -0.0 otherwise returns false. + + + + .. py:method:: length() -> Expr + + The number of characters in the ``string``. + + + + .. py:method:: list_dims() -> Expr + + Returns an array of the array's dimensions. + + This is an alias for :py:func:`array_dims`. + + + + .. py:method:: list_distinct() -> Expr + + Returns distinct values from the array after removing duplicates. + + This is an alias for :py:func:`array_distinct`. + + + + .. py:method:: list_length() -> Expr + + Returns the length of the array. + + This is an alias for :py:func:`array_length`. + + + + .. py:method:: list_ndims() -> Expr + + Returns the number of dimensions of the array. + + This is an alias for :py:func:`array_ndims`. + + + + .. py:method:: literal(value: Any) -> Expr + :staticmethod: + + + Creates a new expression representing a scalar value. + + ``value`` must be a valid PyArrow scalar value or easily castable to one. + + + + .. py:method:: literal_with_metadata(value: Any, metadata: dict[str, str]) -> Expr + :staticmethod: + + + Creates a new expression representing a scalar value with metadata. + + :param value: A valid PyArrow scalar value or easily castable to one. + :param metadata: Metadata to attach to the expression. + + + + .. py:method:: ln() -> Expr + + Returns the natural logarithm (base e) of the argument. + + + + .. py:method:: log10() -> Expr + + Base 10 logarithm of the argument. + + + + .. py:method:: log2() -> Expr + + Base 2 logarithm of the argument. + + + + .. py:method:: lower() -> Expr + + Converts a string to lowercase. + + + + .. py:method:: ltrim() -> Expr + + Removes all characters, spaces by default, from the beginning of a string. + + + + .. py:method:: md5() -> Expr + + Computes an MD5 128-bit checksum for a string expression. + + + + .. py:method:: null_treatment(null_treatment: datafusion.common.NullTreatment) -> ExprFuncBuilder + + Set the treatment for ``null`` values for a window or aggregate function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:method:: octet_length() -> Expr + + Returns the number of bytes of a string. + + + + .. py:method:: order_by(*exprs: Expr | SortExpr) -> ExprFuncBuilder + + Set the ordering for a window or aggregate function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:method:: over(window: Window) -> Expr + + Turn an aggregate function into a window function. + + This function turns any aggregate function into a window function. With the + exception of ``partition_by``, how each of the parameters is used is determined + by the underlying aggregate function. + + :param window: Window definition + + + + .. py:method:: partition_by(*partition_by: Expr) -> ExprFuncBuilder + + Set the partitioning for a window function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:method:: python_value() -> Any + + Extracts the Expr value into `Any`. + + This is only valid for literal expressions. + + :returns: Python object representing literal value of the expression. + + + + .. py:method:: radians() -> Expr + + Converts the argument from degrees to radians. + + + + .. py:method:: reverse() -> Expr + + Reverse the string argument. + + + + .. py:method:: rex_call_operands() -> list[Expr] + + Return the operands of the expression based on it's variant type. + + Row expressions, Rex(s), operate on the concept of operands. Different + variants of Expressions, Expr(s), store those operands in different + datastructures. This function examines the Expr variant and returns + the operands to the calling logic. + + + + .. py:method:: rex_call_operator() -> str + + Extracts the operator associated with a row expression type call. + + + + .. py:method:: rex_type() -> datafusion.common.RexType + + Return the Rex Type of this expression. + + A Rex (Row Expression) specifies a single row of data.That specification + could include user defined functions or types. RexType identifies the + row as one of the possible valid ``RexType``. + + + + .. py:method:: rtrim() -> Expr + + Removes all characters, spaces by default, from the end of a string. + + + + .. py:method:: schema_name() -> str + + Returns the name of this expression as it should appear in a schema. + + This name will not include any CAST expressions. + + + + .. py:method:: sha224() -> Expr + + Computes the SHA-224 hash of a binary string. + + + + .. py:method:: sha256() -> Expr + + Computes the SHA-256 hash of a binary string. + + + + .. py:method:: sha384() -> Expr + + Computes the SHA-384 hash of a binary string. + + + + .. py:method:: sha512() -> Expr + + Computes the SHA-512 hash of a binary string. + + + + .. py:method:: signum() -> Expr + + Returns the sign of the argument (-1, 0, +1). + + + + .. py:method:: sin() -> Expr + + Returns the sine of the argument. + + + + .. py:method:: sinh() -> Expr + + Returns the hyperbolic sine of the argument. + + + + .. py:method:: sort(ascending: bool = True, nulls_first: bool = True) -> SortExpr + + Creates a sort :py:class:`Expr` from an existing :py:class:`Expr`. + + :param ascending: If true, sort in ascending order. + :param nulls_first: Return null values first. + + + + .. py:method:: sqrt() -> Expr + + Returns the square root of the argument. + + + + .. py:method:: string_literal(value: str) -> Expr + :staticmethod: + + + Creates a new expression representing a UTF8 literal value. + + It is different from `literal` because it is pa.string() instead of + pa.string_view() + + This is needed for cases where DataFusion is expecting a UTF8 instead of + UTF8View literal, like in: + https://github.com/apache/datafusion/blob/86740bfd3d9831d6b7c1d0e1bf4a21d91598a0ac/datafusion/functions/src/core/arrow_cast.rs#L179 + + + + .. py:method:: tan() -> Expr + + Returns the tangent of the argument. + + + + .. py:method:: tanh() -> Expr + + Returns the hyperbolic tangent of the argument. + + + + .. py:method:: to_bytes(ctx: datafusion.context.SessionContext | None = None) -> bytes + + Serialize this expression to bytes for shipping to another process. + + Use this — or :func:`pickle.dumps` — to send an expression to a + worker process for distributed evaluation. + + When ``ctx`` is supplied, encoding routes through that session's + installed :class:`LogicalExtensionCodec` (so settings like + :meth:`SessionContext.with_python_udf_inlining` take effect). + When ``ctx`` is ``None``, the default codec is used (Python UDF + inlining on, no user-installed extension codec). + + Built-in functions travel inside the returned bytes. Python UDFs + (scalar, aggregate, window) also inline by default, so the worker + does not need to pre-register them; when the encoding session has + :meth:`SessionContext.with_python_udf_inlining` set to ``False``, + Python UDFs travel by name only and must be registered on the + worker. UDFs imported via the FFI capsule protocol always travel + by name only and must be registered on the worker. + + .. warning:: Security + Bytes returned here may embed a cloudpickled Python + callable (when the expression carries a Python UDF). + Reconstructing them via :meth:`from_bytes` or + :func:`pickle.loads` executes arbitrary Python on the + receiver. Only accept payloads from trusted sources. + + .. warning:: Portability + cloudpickle serializes Python bytecode, which is **not + stable across Python minor versions**. A payload produced + on Python 3.11 will fail to load on Python 3.12. The + wire format stamps the sender's ``(major, minor)``; + :meth:`from_bytes` raises a :class:`ValueError` naming + both versions on mismatch. + + cloudpickle captures the UDF callable **by value** — + bytecode and closure cells inlined — but names the + callable resolves via ``import`` are captured **by + reference** (module path only) and must be importable on + the receiver. + + **Self-contained — works anywhere:** + + .. code-block:: python + + # Lambda: bytecode captured inline + udf(lambda x: x * 2, [pa.int64()], pa.int64(), + volatility="immutable") + + # Locally-defined function: bytecode captured inline + def double(x): + return x * 2 + udf(double, [pa.int64()], pa.int64(), volatility="immutable") + + # Closure over a local variable: value captured inline + factor = 3 + udf(lambda x: x * factor, [pa.int64()], pa.int64(), + volatility="immutable") + + **Requires matching environment on receiver:** + + .. code-block:: python + + # Top-level import: `foo` must be installed on receiver + from foo import double + udf(double, [pa.int64()], pa.int64(), volatility="immutable") + + # Bound method of an imported class: same caveat + from mylib import Transformer + t = Transformer() + udf(t.transform, [pa.int64()], pa.int64(), + volatility="immutable") + + .. rubric:: Examples + + >>> from datafusion import col, lit + >>> blob = (col("a") + lit(1)).to_bytes() + >>> isinstance(blob, bytes) + True + + + + .. py:method:: to_hex() -> Expr + + Converts an integer to a hexadecimal string. + + + + .. py:method:: to_variant() -> Any + + Convert this expression into a python object if possible. + + + + .. py:method:: trim() -> Expr + + Removes all characters, spaces by default, from both sides of a string. + + + + .. py:method:: try_cast(to: pyarrow.DataType[Any] | type) -> Expr + + Cast to a new data type, returning NULL on failure. + + Like :py:meth:`cast` but produces NULL instead of erroring when the + cast cannot be performed for a given row. + + .. rubric:: Examples + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": ["oops"]}) + >>> result = df.select(col("a").try_cast(pa.float64()).alias("c")) + >>> result.collect_column("c")[0].as_py() is None + True + + + + .. py:method:: types() -> datafusion.common.DataTypeMap + + Return the ``DataTypeMap``. + + :returns: DataTypeMap which represents the PythonType, Arrow DataType, and + SqlType Enum which this expression represents. + + + + .. py:method:: upper() -> Expr + + Converts a string to uppercase. + + + + .. py:method:: variant_name() -> str + + Returns the name of the Expr variant. + + Ex: ``IsNotNull``, ``Literal``, ``BinaryExpr``, etc + + + + .. py:method:: window_frame(window_frame: WindowFrame) -> ExprFuncBuilder + + Set the frame fora window function. + + This function will create an :py:class:`ExprFuncBuilder` that can be used to + set parameters for either window or aggregate functions. If used on any other + type of expression, an error will be generated when ``build()`` is called. + + + + .. py:attribute:: __radd__ + + + .. py:attribute:: __rand__ + + + .. py:attribute:: __rmod__ + + + .. py:attribute:: __rmul__ + + + .. py:attribute:: __ror__ + + + .. py:attribute:: __rsub__ + + + .. py:attribute:: __rtruediv__ + + + .. py:attribute:: _to_pyarrow_types + :type: ClassVar[dict[type, pyarrow.DataType]] + + + .. py:attribute:: expr + + +.. py:class:: InsertOp + + Bases: :py:obj:`enum.Enum` + + + Insert operation mode. + + These modes are used by the table writing feature to define how record + batches should be written to a table. + + + .. py:attribute:: APPEND + + Appends new rows to the existing table without modifying any existing rows. + + + .. py:attribute:: OVERWRITE + + Overwrites all existing rows in the table with the new rows. + + + .. py:attribute:: REPLACE + + Replace existing rows that collide with the inserted rows. + + Replacement is typically based on a unique key or primary key. + + +.. py:class:: LogicalPlan(plan: datafusion._internal.LogicalPlan) + + Logical Plan. + + A `LogicalPlan` is a node in a tree of relational operators (such as + Projection or Filter). + + Represents transforming an input relation (table) to an output relation + (table) with a potentially different schema. Plans form a dataflow tree + where data flows from leaves up to the root to produce the query result. + + A `LogicalPlan` can be created by the SQL query planner, the DataFrame API, + or programmatically (for example custom query languages). + + This constructor should not be called by the end user. + + + .. py:method:: __eq__(other: LogicalPlan) -> bool + + Test equality. + + + + .. py:method:: __repr__() -> str + + Generate a printable representation of the plan. + + + + .. py:method:: display() -> str + + Print the logical plan. + + + + .. py:method:: display_graphviz() -> str + + Print the graph visualization of the logical plan. + + Returns a `format`able structure that produces lines meant for graphical display + using the `DOT` language. This format can be visualized using software from + [`graphviz`](https://graphviz.org/) + + + + .. py:method:: display_indent() -> str + + Print an indented form of the logical plan. + + + + .. py:method:: display_indent_schema() -> str + + Print an indented form of the schema for the logical plan. + + + + .. py:method:: from_bytes(ctx: datafusion.context.SessionContext, data: bytes) -> LogicalPlan + :staticmethod: + + + Create a LogicalPlan from serialized protobuf bytes. + + Decoding routes through the session's installed + `LogicalExtensionCodec`. Tables created in memory from record + batches are currently not supported. + + + + .. py:method:: from_proto(ctx: datafusion.context.SessionContext, data: bytes) -> LogicalPlan + :staticmethod: + + + Deprecated alias for :meth:`from_bytes`. + + + + .. py:method:: inputs() -> list[LogicalPlan] + + Returns the list of inputs to the logical plan. + + + + .. py:method:: to_bytes(ctx: datafusion.context.SessionContext | None = None) -> bytes + + Convert a LogicalPlan to serialized protobuf bytes. + + When ``ctx`` is supplied, encoding routes through the session's + installed `LogicalExtensionCodec` so user FFI codecs (registered + via :py:meth:`SessionContext.with_logical_extension_codec`) see + the encode path. With ``ctx=None`` a default codec is used. + Tables created in memory from record batches are currently not + supported. + + + + .. py:method:: to_proto() -> bytes + + Deprecated alias for :meth:`to_bytes`. + + + + .. py:method:: to_variant() -> Any + + Convert the logical plan into its specific variant. + + + + .. py:attribute:: _raw_plan + + +.. py:class:: Metric(raw: datafusion._internal.Metric) + + A single execution metric with name, value, partition, and labels. + + This constructor should not be called by the end user. + + + .. py:method:: __repr__() -> str + + Return a string representation of the metric. + + + + .. py:method:: labels() -> dict[str, str] + + Return the labels associated with this metric. + + Labels provide additional context for a metric. For example:: + + metric.labels() + # {'output_type': 'final'} + + + + .. py:attribute:: _raw + + + .. py:property:: name + :type: str + + + The name of this metric (e.g. ``output_rows``). + + + .. py:property:: partition + :type: int | None + + + The 0-based partition index this metric applies to. + + Returns ``None`` for metrics that are not partition-specific (i.e. they + apply globally across all partitions of the operator). + + + .. py:property:: value + :type: int | datetime.datetime | None + + + The value of this metric. + + Returns an ``int`` for counters, gauges, and time-based metrics + (nanoseconds), a :py:class:`~datetime.datetime` (UTC) for + ``start_timestamp`` / ``end_timestamp`` metrics, or ``None`` + when the value has not been set or is not representable. + + + .. py:property:: value_as_datetime + :type: datetime.datetime | None + + + The value as a UTC :py:class:`~datetime.datetime` for timestamp metrics. + + Returns ``None`` for all non-timestamp metrics and for timestamp + metrics whose value has not been set (e.g. before execution). + + +.. py:class:: MetricsSet(raw: datafusion._internal.MetricsSet) + + A set of metrics for a single execution plan operator. + + A physical plan operator runs independently across one or more partitions. + :py:meth:`metrics` returns the raw per-partition :py:class:`Metric` objects. + The convenience properties (:py:attr:`output_rows`, :py:attr:`elapsed_compute`, + etc.) automatically sum the named metric across *all* partitions, giving a + single aggregate value for the operator as a whole. + + This constructor should not be called by the end user. + + + .. py:method:: __repr__() -> str + + Return a string representation of the metrics set. + + + + .. py:method:: metrics() -> list[Metric] + + Return all individual metrics in this set. + + + + .. py:method:: sum_by_name(name: str) -> int | None + + Sum the named metric across all partitions. + + Useful for accessing any metric not exposed as a first-class property. + Returns ``None`` if no metric with the given name was recorded. + + :param name: The metric name, e.g. ``"output_rows"`` or ``"elapsed_compute"``. + + + + .. py:attribute:: _raw + + + .. py:property:: elapsed_compute + :type: int | None + + + Total CPU time (in nanoseconds) spent inside this operator's execute loop. + + Summed across all partitions. Returns ``None`` if no ``elapsed_compute`` + metric was recorded. + + + .. py:property:: output_rows + :type: int | None + + + Sum of output_rows across all partitions. + + + .. py:property:: spill_count + :type: int | None + + + Number of times this operator spilled data to disk due to memory pressure. + + This is a count of spill events, not a byte count. Summed across all + partitions. Returns ``None`` if no ``spill_count`` metric was recorded. + + + .. py:property:: spilled_bytes + :type: int | None + + + Sum of spilled_bytes across all partitions. + + + .. py:property:: spilled_rows + :type: int | None + + + Sum of spilled_rows across all partitions. + + +.. py:class:: ParquetColumnOptions(encoding: str | None = None, dictionary_enabled: bool | None = None, compression: str | None = None, statistics_enabled: str | None = None, bloom_filter_enabled: bool | None = None, bloom_filter_fpp: float | None = None, bloom_filter_ndv: int | None = None) + + Parquet options for individual columns. + + Contains the available options that can be applied for an individual Parquet column, + replacing the global options in ``ParquetWriterOptions``. + + Initialize the ParquetColumnOptions. + + :param encoding: Sets encoding for the column path. Valid values are: ``plain``, + ``plain_dictionary``, ``rle``, ``bit_packed``, ``delta_binary_packed``, + ``delta_length_byte_array``, ``delta_byte_array``, ``rle_dictionary``, + and ``byte_stream_split``. These values are not case-sensitive. If + ``None``, uses the default parquet options + :param dictionary_enabled: Sets if dictionary encoding is enabled for the column + path. If `None`, uses the default parquet options + :param compression: Sets default parquet compression codec for the column path. + Valid values are ``uncompressed``, ``snappy``, ``gzip(level)``, ``lzo``, + ``brotli(level)``, ``lz4``, ``zstd(level)``, and ``lz4_raw``. These + values are not case-sensitive. If ``None``, uses the default parquet + options. + :param statistics_enabled: Sets if statistics are enabled for the column Valid + values are: ``none``, ``chunk``, and ``page`` These values are not case + sensitive. If ``None``, uses the default parquet options. + :param bloom_filter_enabled: Sets if bloom filter is enabled for the column path. + If ``None``, uses the default parquet options. + :param bloom_filter_fpp: Sets bloom filter false positive probability for the + column path. If ``None``, uses the default parquet options. + :param bloom_filter_ndv: Sets bloom filter number of distinct values. If ``None``, + uses the default parquet options. + + + .. py:attribute:: bloom_filter_enabled + :value: None + + + + .. py:attribute:: bloom_filter_fpp + :value: None + + + + .. py:attribute:: bloom_filter_ndv + :value: None + + + + .. py:attribute:: compression + :value: None + + + + .. py:attribute:: dictionary_enabled + :value: None + + + + .. py:attribute:: encoding + :value: None + + + + .. py:attribute:: statistics_enabled + :value: None + + + +.. py:class:: ParquetWriterOptions(data_pagesize_limit: int = 1024 * 1024, write_batch_size: int = 1024, writer_version: str = '1.0', skip_arrow_metadata: bool = False, compression: str | None = 'zstd(3)', compression_level: int | None = None, dictionary_enabled: bool | None = True, dictionary_page_size_limit: int = 1024 * 1024, statistics_enabled: str | None = 'page', max_row_group_size: int = 1024 * 1024, created_by: str = 'datafusion-python', column_index_truncate_length: int | None = 64, statistics_truncate_length: int | None = None, data_page_row_count_limit: int = 20000, encoding: str | None = None, bloom_filter_on_write: bool = False, bloom_filter_fpp: float | None = None, bloom_filter_ndv: int | None = None, allow_single_file_parallelism: bool = True, maximum_parallel_row_group_writers: int = 1, maximum_buffered_record_batches_per_stream: int = 2, column_specific_options: dict[str, ParquetColumnOptions] | None = None) + + Advanced parquet writer options. + + Allows settings the writer options that apply to the entire file. Some options can + also be set on a column by column basis, with the field ``column_specific_options`` + (see ``ParquetColumnOptions``). + + Initialize the ParquetWriterOptions. + + :param data_pagesize_limit: Sets best effort maximum size of data page in bytes. + :param write_batch_size: Sets write_batch_size in bytes. + :param writer_version: Sets parquet writer version. Valid values are ``1.0`` and + ``2.0``. + :param skip_arrow_metadata: Skip encoding the embedded arrow metadata in the + KV_meta. + :param compression: Compression type to use. Default is ``zstd(3)``. + Available compression types are + + - ``uncompressed``: No compression. + - ``snappy``: Snappy compression. + - ``gzip(n)``: Gzip compression with level n. + - ``brotli(n)``: Brotli compression with level n. + - ``lz4``: LZ4 compression. + - ``lz4_raw``: LZ4_RAW compression. + - ``zstd(n)``: Zstandard compression with level n. + :param compression_level: Compression level to set. + :param dictionary_enabled: Sets if dictionary encoding is enabled. If ``None``, + uses the default parquet writer setting. + :param dictionary_page_size_limit: Sets best effort maximum dictionary page size, + in bytes. + :param statistics_enabled: Sets if statistics are enabled for any column Valid + values are ``none``, ``chunk``, and ``page``. If ``None``, uses the + default parquet writer setting. + :param max_row_group_size: Target maximum number of rows in each row group + (defaults to 1M rows). Writing larger row groups requires more memory + to write, but can get better compression and be faster to read. + :param created_by: Sets "created by" property. + :param column_index_truncate_length: Sets column index truncate length. + :param statistics_truncate_length: Sets statistics truncate length. If ``None``, + uses the default parquet writer setting. + :param data_page_row_count_limit: Sets best effort maximum number of rows in a data + page. + :param encoding: Sets default encoding for any column. Valid values are ``plain``, + ``plain_dictionary``, ``rle``, ``bit_packed``, ``delta_binary_packed``, + ``delta_length_byte_array``, ``delta_byte_array``, ``rle_dictionary``, + and ``byte_stream_split``. If ``None``, uses the default parquet writer + setting. + :param bloom_filter_on_write: Write bloom filters for all columns when creating + parquet files. + :param bloom_filter_fpp: Sets bloom filter false positive probability. If ``None``, + uses the default parquet writer setting + :param bloom_filter_ndv: Sets bloom filter number of distinct values. If ``None``, + uses the default parquet writer setting. + :param allow_single_file_parallelism: Controls whether DataFusion will attempt to + speed up writing parquet files by serializing them in parallel. Each + column in each row group in each output file are serialized in parallel + leveraging a maximum possible core count of + ``n_files * n_row_groups * n_columns``. + :param maximum_parallel_row_group_writers: By default parallel parquet writer is + tuned for minimum memory usage in a streaming execution plan. You may + see a performance benefit when writing large parquet files by increasing + ``maximum_parallel_row_group_writers`` and + ``maximum_buffered_record_batches_per_stream`` if your system has idle + cores and can tolerate additional memory usage. Boosting these values is + likely worthwhile when writing out already in-memory data, such as from + a cached data frame. + :param maximum_buffered_record_batches_per_stream: See + ``maximum_parallel_row_group_writers``. + :param column_specific_options: Overrides options for specific columns. If a column + is not a part of this dictionary, it will use the parameters provided + here. + + + .. py:attribute:: allow_single_file_parallelism + :value: True + + + + .. py:attribute:: bloom_filter_fpp + :value: None + + + + .. py:attribute:: bloom_filter_ndv + :value: None + + + + .. py:attribute:: bloom_filter_on_write + :value: False + + + + .. py:attribute:: column_index_truncate_length + :value: 64 + + + + .. py:attribute:: column_specific_options + :value: None + + + + .. py:attribute:: created_by + :value: 'datafusion-python' + + + + .. py:attribute:: data_page_row_count_limit + :value: 20000 + + + + .. py:attribute:: data_pagesize_limit + :value: 1048576 + + + + .. py:attribute:: dictionary_enabled + :value: True + + + + .. py:attribute:: dictionary_page_size_limit + :value: 1048576 + + + + .. py:attribute:: encoding + :value: None + + + + .. py:attribute:: max_row_group_size + :value: 1048576 + + + + .. py:attribute:: maximum_buffered_record_batches_per_stream + :value: 2 + + + + .. py:attribute:: maximum_parallel_row_group_writers + :value: 1 + + + + .. py:attribute:: skip_arrow_metadata + :value: False + + + + .. py:attribute:: statistics_enabled + :value: 'page' + + + + .. py:attribute:: statistics_truncate_length + :value: None + + + + .. py:attribute:: write_batch_size + :value: 1024 + + + + .. py:attribute:: writer_version + :value: '1.0' + + + +.. py:class:: RecordBatch(record_batch: datafusion._internal.RecordBatch) + + This class is essentially a wrapper for :py:class:`pa.RecordBatch`. + + This constructor is generally not called by the end user. + + See the :py:class:`RecordBatchStream` iterator for generating this class. + + + .. py:method:: __arrow_c_array__(requested_schema: object | None = None) -> tuple[object, object] + + Export the record batch via the Arrow C Data Interface. + + This allows zero-copy interchange with libraries that support the + `Arrow PyCapsule interface `_. + + :param requested_schema: Attempt to provide the record batch using this + schema. Only straightforward projections such as column + selection or reordering are applied. + + :returns: Two Arrow PyCapsule objects representing the ``ArrowArray`` and + ``ArrowSchema``. + + + + .. py:method:: to_pyarrow() -> pyarrow.RecordBatch + + Convert to :py:class:`pa.RecordBatch`. + + + + .. py:attribute:: record_batch + + +.. py:class:: RecordBatchStream(record_batch_stream: datafusion._internal.RecordBatchStream) + + This class represents a stream of record batches. + + These are typically the result of a + :py:func:`~datafusion.dataframe.DataFrame.execute_stream` operation. + + This constructor is typically not called by the end user. + + + .. py:method:: __aiter__() -> Self + + Return an asynchronous iterator over record batches. + + + + .. py:method:: __anext__() -> RecordBatch + :async: + + + Return the next :py:class:`RecordBatch` in the stream asynchronously. + + + + .. py:method:: __iter__() -> Self + + Return an iterator over record batches. + + + + .. py:method:: __next__() -> RecordBatch + + Return the next :py:class:`RecordBatch` in the stream. + + + + .. py:method:: next() -> RecordBatch + + See :py:func:`__next__` for the iterator function. + + + + .. py:attribute:: rbs + + +.. py:class:: RuntimeEnvBuilder + + Runtime configuration options. + + Create a new :py:class:`RuntimeEnvBuilder` with default values. + + + .. py:method:: with_disk_manager_disabled() -> RuntimeEnvBuilder + + Disable the disk manager, attempts to create temporary files will error. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + + + .. py:method:: with_disk_manager_os() -> RuntimeEnvBuilder + + Use the operating system's temporary directory for disk manager. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + + + .. py:method:: with_disk_manager_specified(*paths: str | pathlib.Path) -> RuntimeEnvBuilder + + Use the specified paths for the disk manager's temporary files. + + :param paths: Paths to use for the disk manager's temporary files. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + + + .. py:method:: with_fair_spill_pool(size: int) -> RuntimeEnvBuilder + + Use a fair spill pool with the specified size. + + This pool works best when you know beforehand the query has multiple spillable + operators that will likely all need to spill. Sometimes it will cause spills + even when there was sufficient memory (reserved for other operators) to avoid + doing so:: + + ┌───────────────────────z──────────────────────z───────────────┐ + │ z z │ + │ z z │ + │ Spillable z Unspillable z Free │ + │ Memory z Memory z Memory │ + │ z z │ + │ z z │ + └───────────────────────z──────────────────────z───────────────┘ + + :param size: Size of the memory pool in bytes. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + .. rubric:: Examples + + >>> config = dfn.RuntimeEnvBuilder().with_fair_spill_pool(1024) + + + + .. py:method:: with_greedy_memory_pool(size: int) -> RuntimeEnvBuilder + + Use a greedy memory pool with the specified size. + + This pool works well for queries that do not need to spill or have a single + spillable operator. See :py:func:`with_fair_spill_pool` if there are + multiple spillable operators that all will spill. + + :param size: Size of the memory pool in bytes. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + .. rubric:: Examples + + >>> config = dfn.RuntimeEnvBuilder().with_greedy_memory_pool(1024) + + + + .. py:method:: with_temp_file_path(path: str | pathlib.Path) -> RuntimeEnvBuilder + + Use the specified path to create any needed temporary files. + + :param path: Path to use for temporary files. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + .. rubric:: Examples + + >>> config = dfn.RuntimeEnvBuilder().with_temp_file_path("/tmp") + + + + .. py:method:: with_unbounded_memory_pool() -> RuntimeEnvBuilder + + Use an unbounded memory pool. + + :returns: A new :py:class:`RuntimeEnvBuilder` object with the updated setting. + + + + .. py:attribute:: config_internal + + +.. py:class:: SQLOptions + + Options to be used when performing SQL queries. + + Create a new :py:class:`SQLOptions` with default values. + + The default values are: + - DDL commands are allowed + - DML commands are allowed + - Statements are allowed + + + .. py:method:: with_allow_ddl(allow: bool = True) -> SQLOptions + + Should DDL (Data Definition Language) commands be run? + + Examples of DDL commands include ``CREATE TABLE`` and ``DROP TABLE``. + + :param allow: Allow DDL commands to be run. + + :returns: A new :py:class:`SQLOptions` object with the updated setting. + + .. rubric:: Examples + + >>> options = dfn.SQLOptions().with_allow_ddl(True) + + + + .. py:method:: with_allow_dml(allow: bool = True) -> SQLOptions + + Should DML (Data Manipulation Language) commands be run? + + Examples of DML commands include ``INSERT INTO`` and ``DELETE``. + + :param allow: Allow DML commands to be run. + + :returns: A new :py:class:`SQLOptions` object with the updated setting. + + .. rubric:: Examples + + >>> options = dfn.SQLOptions().with_allow_dml(True) + + + + .. py:method:: with_allow_statements(allow: bool = True) -> SQLOptions + + Should statements such as ``SET VARIABLE`` and ``BEGIN TRANSACTION`` be run? + + :param allow: Allow statements to be run. + + :returns: py:class:SQLOptions` object with the updated setting. + :rtype: A new + + .. rubric:: Examples + + >>> options = dfn.SQLOptions().with_allow_statements(True) + + + + .. py:attribute:: options_internal + + +.. py:class:: ScalarUDF(name: str, func: collections.abc.Callable[Ellipsis, _R], input_fields: list[pyarrow.Field], return_field: pyarrow.Field, volatility: Volatility | str) + + Class for performing scalar user-defined functions (UDF). + + Scalar UDFs operate on a row by row basis. See also :py:class:`AggregateUDF` for + operating on a group of rows. + + Instantiate a scalar user-defined function (UDF). + + See helper method :py:func:`udf` for argument details. + + + .. py:method:: __call__(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Execute the UDF. + + This function is not typically called by an end user. These calls will + occur during the evaluation of the dataframe. + + + + .. py:method:: __repr__() -> str + + Print a string representation of the Scalar UDF. + + + + .. py:method:: _from_internal(internal: datafusion._internal.ScalarUDF) -> ScalarUDF + :classmethod: + + + Wrap an already-constructed internal ``ScalarUDF`` handle. + + Used by :py:meth:`SessionContext.udf` to surface a function looked + up from the session's function registry without re-running + :py:meth:`__init__`. + + + + .. py:method:: from_pycapsule(func: ScalarUDFExportable) -> ScalarUDF + :staticmethod: + + + Create a Scalar UDF from ScalarUDF PyCapsule object. + + This function will instantiate a Scalar UDF that uses a DataFusion + ScalarUDF that is exported via the FFI bindings. + + + + .. py:method:: udf(input_fields: collections.abc.Sequence[pyarrow.DataType | pyarrow.Field] | pyarrow.DataType | pyarrow.Field, return_field: pyarrow.DataType | pyarrow.Field, volatility: Volatility | str, name: str | None = None) -> collections.abc.Callable[Ellipsis, ScalarUDF] + udf(func: collections.abc.Callable[Ellipsis, _R], input_fields: collections.abc.Sequence[pyarrow.DataType | pyarrow.Field] | pyarrow.DataType | pyarrow.Field, return_field: pyarrow.DataType | pyarrow.Field, volatility: Volatility | str, name: str | None = None) -> ScalarUDF + udf(func: ScalarUDFExportable) -> ScalarUDF + :staticmethod: + + + Create a new User-Defined Function (UDF). + + This class can be used both as either a function or a decorator. + + Usage: + - As a function: ``udf(func, input_fields, return_field, + volatility, name)``. + - As a decorator: ``@udf(input_fields, return_field, volatility, name)``. + When used a decorator, do **not** pass ``func`` explicitly. + + In lieu of passing a PyArrow Field, you can pass a DataType for simplicity. + When you do so, it will be assumed that the nullability of the inputs and + output are True and that they have no metadata. + + :param func: Only needed when calling as a function. + Skip this argument when using `udf` as a decorator. If you have a Rust + backed ScalarUDF within a PyCapsule, you can pass this parameter + and ignore the rest. They will be determined directly from the + underlying function. See the online documentation for more information. + :type func: Callable, optional + :param input_fields: The data types or Fields + of the arguments to ``func``. This list must be of the same length + as the number of arguments. + :type input_fields: list[pa.Field | pa.DataType] + :param return_field: The field of the return value + from the function. + :type return_field: pa.DataType | pa.Field + :param volatility: See `Volatility` for allowed values. + :type volatility: Volatility | str + :param name: A descriptive name for the function. + :type name: Optional[str] + + :returns: A user-defined function that can be used in SQL expressions, + data aggregation, or window function calls. + + .. rubric:: Examples + + Using ``udf`` as a function: + + >>> import pyarrow.compute as pc + >>> from datafusion.user_defined import ScalarUDF + >>> def double_func(x): + ... return pc.multiply(x, 2) + >>> double_udf = ScalarUDF.udf( + ... double_func, [pa.int64()], pa.int64(), + ... "volatile", "double_it") + + Using ``udf`` as a decorator: + + >>> @ScalarUDF.udf([pa.int64()], pa.int64(), "volatile") + ... def decorator_double_udf(x): + ... return pc.multiply(x, 3) + + Apply to a dataframe: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1, 2, 3]}) + >>> df.select(double_udf(col("x")).alias("result")).to_pydict() + {'result': [2, 4, 6]} + >>> df.select(decorator_double_udf(col("x")).alias("result")).to_pydict() + {'result': [3, 6, 9]} + + + + .. py:attribute:: _udf + + + .. py:property:: name + :type: str + + + Return the registered name of this UDF. + + For UDFs imported via the FFI capsule protocol, this is the + name the capsule itself reports — not the ``name`` argument + passed to the constructor (which is ignored on the FFI path). + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datafusion import udf + >>> double = udf( + ... lambda arr: pa.array([(v.as_py() or 0) * 2 for v in arr]), + ... [pa.int64()], + ... pa.int64(), + ... volatility="immutable", + ... name="double", + ... ) + >>> double.name + 'double' + + +.. py:class:: SessionConfig(config_options: dict[str, str] | None = None) + + Session configuration options. + + Create a new :py:class:`SessionConfig` with the given configuration options. + + :param config_options: Configuration options. + + + .. py:method:: set(key: str, value: str) -> SessionConfig + + Set a configuration option. + + Args: + key: Option key. + value: Option value. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_batch_size(batch_size: int) -> SessionConfig + + Customize batch size. + + :param batch_size: Batch size. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_create_default_catalog_and_schema(enabled: bool = True) -> SessionConfig + + Control if the default catalog and schema will be automatically created. + + :param enabled: Whether the default catalog and schema will be + automatically created. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_default_catalog_and_schema(catalog: str, schema: str) -> SessionConfig + + Select a name for the default catalog and schema. + + :param catalog: Catalog name. + :param schema: Schema name. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_extension(extension: Any) -> SessionConfig + + Create a new configuration using an extension. + + :param extension: A custom configuration extension object. These are + :param shared from another DataFusion extension library.: + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_information_schema(enabled: bool = True) -> SessionConfig + + Enable or disable the inclusion of ``information_schema`` virtual tables. + + :param enabled: Whether to include ``information_schema`` virtual tables. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_parquet_pruning(enabled: bool = True) -> SessionConfig + + Enable or disable the use of pruning predicate for parquet readers. + + Pruning predicates will enable the reader to skip row groups. + + :param enabled: Whether to use pruning predicate for parquet readers. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_aggregations(enabled: bool = True) -> SessionConfig + + Enable or disable the use of repartitioning for aggregations. + + Enabling this improves parallelism. + + :param enabled: Whether to use repartitioning for aggregations. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_file_min_size(size: int) -> SessionConfig + + Set minimum file range size for repartitioning scans. + + :param size: Minimum file range size. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_file_scans(enabled: bool = True) -> SessionConfig + + Enable or disable the use of repartitioning for file scans. + + :param enabled: Whether to use repartitioning for file scans. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_joins(enabled: bool = True) -> SessionConfig + + Enable or disable the use of repartitioning for joins to improve parallelism. + + :param enabled: Whether to use repartitioning for joins. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_sorts(enabled: bool = True) -> SessionConfig + + Enable or disable the use of repartitioning for window functions. + + This may improve parallelism. + + :param enabled: Whether to use repartitioning for window functions. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_repartition_windows(enabled: bool = True) -> SessionConfig + + Enable or disable the use of repartitioning for window functions. + + This may improve parallelism. + + :param enabled: Whether to use repartitioning for window functions. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:method:: with_target_partitions(target_partitions: int) -> SessionConfig + + Customize the number of target partitions for query execution. + + Increasing partitions can increase concurrency. + + :param target_partitions: Number of target partitions. + + :returns: A new :py:class:`SessionConfig` object with the updated setting. + + + + .. py:attribute:: config_internal + + +.. py:class:: Table(table: Table | datafusion.context.TableProviderExportable | datafusion.DataFrame | pyarrow.dataset.Dataset, ctx: datafusion.SessionContext | None = None) + + A DataFusion table. + + Internally we currently support the following types of tables: + + - Tables created using built-in DataFusion methods, such as + reading from CSV or Parquet + - pyarrow datasets + - DataFusion DataFrames, which will be converted into a view + - Externally provided tables implemented with the FFI PyCapsule + interface (advanced) + + Constructor. + + + .. py:method:: __repr__() -> str + + Print a string representation of the table. + + + + .. py:method:: from_dataset(dataset: pyarrow.dataset.Dataset) -> Table + :staticmethod: + + + Turn a :mod:`pyarrow.dataset` ``Dataset`` into a :class:`Table`. + + + + .. py:attribute:: __slots__ + :value: ('_inner',) + + + + .. py:attribute:: _inner + + + .. py:property:: kind + :type: str + + + Returns the kind of table. + + + .. py:property:: schema + :type: pyarrow.Schema + + + Returns the schema associated with this table. + + +.. py:class:: TableFunction(name: str, func: collections.abc.Callable[Ellipsis, Any], ctx: datafusion.SessionContext | None = None, *, with_session: bool = False) + + Class for performing user-defined table functions (UDTF). + + Table functions generate new table providers based on the + input expressions. + + Instantiate a user-defined table function (UDTF). + + Set ``with_session=True`` to have the calling + :class:`SessionContext` passed as a ``session`` keyword argument + on each invocation. Use it inside the callback to look up + registered tables, UDFs, or session configuration. When + ``with_session`` is ``False`` (the default), ``func`` is invoked + with the positional expression arguments only. + + ``with_session=True`` is only supported for pure-Python callables. + Passing it together with an FFI-exported table function (one + exposing ``__datafusion_table_function__``) raises + :class:`TypeError`. + + Registry mutations performed through the injected session (such + as registering tables or UDFs) propagate to the caller's + :class:`SessionContext` because the registries are shared. + Configuration changes do **not** propagate; the wrapper holds + its own clone of the session config. + + See :py:func:`udtf` for a convenience function and argument + descriptions. + + + .. py:method:: __call__(*args: datafusion.expr.Expr) -> Any + + Execute the UDTF and return a table provider. + + + + .. py:method:: __repr__() -> str + + User printable representation. + + + + .. py:method:: _create_table_udf(func: collections.abc.Callable[Ellipsis, Any], name: str, *, with_session: bool = False) -> TableFunction + :staticmethod: + + + Create a TableFunction instance from function arguments. + + + + .. py:method:: _create_table_udf_decorator(name: str | None = None, *, with_session: bool = False) -> collections.abc.Callable[[collections.abc.Callable[Ellipsis, Any]], TableFunction] + :staticmethod: + + + Create a decorator for a TableFunction. + + + + .. py:method:: udtf(name: str, *, with_session: bool = False) -> collections.abc.Callable[Ellipsis, Any] + udtf(func: collections.abc.Callable[Ellipsis, Any], name: str, *, with_session: bool = False) -> TableFunction + :staticmethod: + + + Create a new User-Defined Table Function (UDTF). + + Pass ``with_session=True`` to have the calling + :class:`SessionContext` injected as a ``session`` keyword + argument on each invocation. + + + + .. py:attribute:: _udtf + + +.. py:class:: TableProviderFactory + + Bases: :py:obj:`abc.ABC` + + + Abstract class for defining a Python based Table Provider Factory. + + + .. py:method:: create(cmd: datafusion.expr.CreateExternalTable) -> Table + :abstractmethod: + + + Create a table using the :class:`CreateExternalTable`. + + + +.. py:class:: TableProviderFactoryExportable + + Bases: :py:obj:`Protocol` + + + Type hint for object that has __datafusion_table_provider_factory__ PyCapsule. + + https://docs.rs/datafusion/latest/datafusion/catalog/trait.TableProviderFactory.html + + + .. py:method:: __datafusion_table_provider_factory__(session: Any) -> object + + +.. py:class:: WindowFrame(units: str, start_bound: Any | None, end_bound: Any | None) + + Defines a window frame for performing window operations. + + Construct a window frame using the given parameters. + + :param units: Should be one of ``rows``, ``range``, or ``groups``. + :param start_bound: Sets the preceding bound. Must be >= 0. If none, this + will be set to unbounded. If unit type is ``groups``, this + parameter must be set. + :param end_bound: Sets the following bound. Must be >= 0. If none, this + will be set to unbounded. If unit type is ``groups``, this + parameter must be set. + + + .. py:method:: __repr__() -> str + + Print a string representation of the window frame. + + + + .. py:method:: get_frame_units() -> str + + Returns the window frame units for the bounds. + + + + .. py:method:: get_lower_bound() -> WindowFrameBound + + Returns starting bound. + + + + .. py:method:: get_upper_bound() -> WindowFrameBound + + Returns end bound. + + + + .. py:attribute:: window_frame + + +.. py:class:: WindowUDF(name: str, func: collections.abc.Callable[[], WindowEvaluator], input_types: list[pyarrow.DataType], return_type: pyarrow.DataType, volatility: Volatility | str) + + Class for performing window user-defined functions (UDF). + + Window UDFs operate on a partition of rows. See + also :py:class:`ScalarUDF` for operating on a row by row basis. + + Instantiate a user-defined window function (UDWF). + + See :py:func:`udwf` for a convenience function and argument + descriptions. + + + .. py:method:: __call__(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Execute the UDWF. + + This function is not typically called by an end user. These calls will + occur during the evaluation of the dataframe. + + + + .. py:method:: __repr__() -> str + + Print a string representation of the Window UDF. + + + + .. py:method:: _create_window_udf(func: collections.abc.Callable[[], WindowEvaluator], input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, volatility: Volatility | str, name: str | None = None) -> WindowUDF + :staticmethod: + + + Create a WindowUDF instance from function arguments. + + + + .. py:method:: _create_window_udf_decorator(input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, volatility: Volatility | str, name: str | None = None) -> collections.abc.Callable[[collections.abc.Callable[[], WindowEvaluator]], collections.abc.Callable[Ellipsis, datafusion.expr.Expr]] + :staticmethod: + + + Create a decorator for a WindowUDF. + + + + .. py:method:: _from_internal(internal: datafusion._internal.WindowUDF) -> WindowUDF + :classmethod: + + + Wrap an already-constructed internal ``WindowUDF`` handle. + + Used by :py:meth:`SessionContext.udwf` to surface a function looked + up from the session's function registry without re-running + :py:meth:`__init__`. + + + + .. py:method:: _get_default_name(func: collections.abc.Callable) -> str + :staticmethod: + + + Get the default name for a function based on its attributes. + + + + .. py:method:: _normalize_input_types(input_types: pyarrow.DataType | list[pyarrow.DataType]) -> list[pyarrow.DataType] + :staticmethod: + + + Convert a single DataType to a list if needed. + + + + .. py:method:: from_pycapsule(func: WindowUDFExportable) -> WindowUDF + :staticmethod: + + + Create a Window UDF from WindowUDF PyCapsule object. + + This function will instantiate a Window UDF that uses a DataFusion + WindowUDF that is exported via the FFI bindings. + + + + .. py:method:: udwf(input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, volatility: Volatility | str, name: str | None = None) -> collections.abc.Callable[Ellipsis, WindowUDF] + udwf(func: collections.abc.Callable[[], WindowEvaluator], input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, volatility: Volatility | str, name: str | None = None) -> WindowUDF + :staticmethod: + + + Create a new User-Defined Window Function (UDWF). + + This class can be used both as either a function or a decorator. + + Usage: + - As a function: ``udwf(func, input_types, return_type, volatility, name)``. + - As a decorator: ``@udwf(input_types, return_type, volatility, name)``. + When using ``udwf`` as a decorator, do not pass ``func`` explicitly. + + .. rubric:: Examples + + >>> from datafusion.user_defined import WindowUDF, WindowEvaluator, udwf + >>> class BiasedNumbers(WindowEvaluator): + ... def __init__(self, start: int = 0): + ... self.start = start + ... def evaluate_all(self, values, num_rows): + ... return pa.array( + ... [self.start + i for i in range(num_rows)]) + + Using ``udwf`` as a function: + + >>> udwf1 = WindowUDF.udwf( + ... BiasedNumbers, pa.int64(), pa.int64(), "immutable") + >>> def bias_10() -> BiasedNumbers: + ... return BiasedNumbers(10) + >>> udwf2 = udwf(bias_10, pa.int64(), pa.int64(), "immutable") + >>> udwf3 = udwf( + ... lambda: BiasedNumbers(20), pa.int64(), pa.int64(), "immutable" + ... ) + + Using ``udwf`` as a decorator: + + >>> @WindowUDF.udwf(pa.int64(), pa.int64(), "immutable") + ... def biased_numbers(): + ... return BiasedNumbers(10) + + Apply to a dataframe: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [10, 20, 30]}) + >>> df.select(udwf1(col("a")).alias("result")).to_pydict() + {'result': [0, 1, 2]} + >>> df.select(udwf2(col("a")).alias("result")).to_pydict() + {'result': [10, 11, 12]} + >>> df.select(udwf3(col("a")).alias("result")).to_pydict() + {'result': [20, 21, 22]} + >>> df.select(biased_numbers(col("a")).alias("result")).to_pydict() + {'result': [10, 11, 12]} + + :param func: Only needed when calling as a function. Skip this argument when + using ``udwf`` as a decorator. If you have a Rust backed WindowUDF + within a PyCapsule, you can pass this parameter and ignore the rest. + They will be determined directly from the underlying function. See + the online documentation for more information. + :param input_types: The data types of the arguments. + :param return_type: The data type of the return value. + :param volatility: See :py:class:`Volatility` for allowed values. + :param name: A descriptive name for the function. + + :returns: A user-defined window function that can be used in window function calls. + + + + .. py:attribute:: _udwf + + + .. py:property:: name + :type: str + + + Return the registered name of this UDWF. + + For UDWFs imported via the FFI capsule protocol, this is the + name the capsule itself reports — not the ``name`` argument + passed to the constructor (which is ignored on the FFI path). + + +.. py:function:: configure_formatter(**kwargs: Any) -> None + + Configure the global DataFrame HTML formatter. + + This function creates a new formatter with the provided configuration + and sets it as the global formatter for all DataFrames. + + :param \*\*kwargs: Formatter configuration parameters like max_cell_length, + max_width, max_height, enable_cell_expansion, etc. + + :raises ValueError: If any invalid parameters are provided + + .. rubric:: Example + + >>> from datafusion.dataframe_formatter import configure_formatter + >>> configure_formatter( + ... max_cell_length=50, + ... max_height=500, + ... enable_cell_expansion=True, + ... use_shared_styles=True + ... ) + + +.. py:function:: lit(value: Any) -> expr.Expr + + Create a literal expression. + + +.. py:function:: literal(value: Any) -> expr.Expr + + Create a literal expression. + + +.. py:function:: read_avro(path: str | pathlib.Path, schema: pyarrow.Schema | None = None, file_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, file_extension: str = '.avro') -> datafusion.dataframe.DataFrame + + Create a :py:class:`DataFrame` for reading Avro data source. + + This function will use the global context. Any functions or tables registered + with another context may not be accessible when used with a DataFrame created + using this function. + + :param path: Path to the Avro file. + :param schema: The data source schema. + :param file_partition_cols: Partition columns. + :param file_extension: File extension to select. + + :returns: DataFrame representation of the read Avro file + + +.. py:function:: read_csv(path: str | pathlib.Path | list[str] | list[pathlib.Path], schema: pyarrow.Schema | None = None, has_header: bool = True, delimiter: str = ',', schema_infer_max_records: int = 1000, file_extension: str = '.csv', table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, file_compression_type: str | None = None, options: datafusion.options.CsvReadOptions | None = None) -> datafusion.dataframe.DataFrame + + Read a CSV data source. + + This function will use the global context. Any functions or tables registered + with another context may not be accessible when used with a DataFrame created + using this function. + + :param path: Path to the CSV file + :param schema: An optional schema representing the CSV files. If None, the + CSV reader will try to infer it based on data in file. + :param has_header: Whether the CSV file have a header. If schema inference + is run on a file with no headers, default column names are + created. + :param delimiter: An optional column delimiter. + :param schema_infer_max_records: Maximum number of rows to read from CSV + files for schema inference if needed. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param table_partition_cols: Partition columns. + :param file_compression_type: File compression type. + :param options: Set advanced options for CSV reading. This cannot be + combined with any of the other options in this method. + + :returns: DataFrame representation of the read CSV files + + +.. py:function:: read_json(path: str | pathlib.Path, schema: pyarrow.Schema | None = None, schema_infer_max_records: int = 1000, file_extension: str = '.json', table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, file_compression_type: str | None = None) -> datafusion.dataframe.DataFrame + + Read a line-delimited JSON data source. + + This function will use the global context. Any functions or tables registered + with another context may not be accessible when used with a DataFrame created + using this function. + + :param path: Path to the JSON file. + :param schema: The data source schema. + :param schema_infer_max_records: Maximum number of rows to read from JSON + files for schema inference if needed. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param table_partition_cols: Partition columns. + :param file_compression_type: File compression type. + + :returns: DataFrame representation of the read JSON files. + + +.. py:function:: read_parquet(path: str | pathlib.Path, table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, parquet_pruning: bool = True, file_extension: str = '.parquet', skip_metadata: bool = True, schema: pyarrow.Schema | None = None, file_sort_order: list[list[datafusion.expr.Expr]] | None = None) -> datafusion.dataframe.DataFrame + + Read a Parquet source into a :py:class:`~datafusion.dataframe.Dataframe`. + + This function will use the global context. Any functions or tables registered + with another context may not be accessible when used with a DataFrame created + using this function. + + :param path: Path to the Parquet file. + :param table_partition_cols: Partition columns. + :param parquet_pruning: Whether the parquet reader should use the predicate + to prune row groups. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param skip_metadata: Whether the parquet reader should skip any metadata + that may be in the file schema. This can help avoid schema + conflicts due to metadata. + :param schema: An optional schema representing the parquet files. If None, + the parquet reader will try to infer it based on data in the + file. + :param file_sort_order: Sort order for the file. + + :returns: DataFrame representation of the read Parquet files + + +.. py:data:: DFSchema + +.. py:data:: col + :type: Col + +.. py:data:: column + :type: Col + +.. py:data:: udaf + +.. py:data:: udf + +.. py:data:: udtf + +.. py:data:: udwf + diff --git a/_sources/autoapi/datafusion/input/base/index.rst.txt b/_sources/autoapi/datafusion/input/base/index.rst.txt new file mode 100644 index 000000000..9b962d05c --- /dev/null +++ b/_sources/autoapi/datafusion/input/base/index.rst.txt @@ -0,0 +1,55 @@ +datafusion.input.base +===================== + +.. py:module:: datafusion.input.base + +.. autoapi-nested-parse:: + + This module provides ``BaseInputSource``. + + A user can extend this to provide a custom input source. + + + +Classes +------- + +.. autoapisummary:: + + datafusion.input.base.BaseInputSource + + +Module Contents +--------------- + +.. py:class:: BaseInputSource + + Bases: :py:obj:`abc.ABC` + + + Base Input Source class. + + If a consuming library would like to provider their own InputSource this is + the class they should extend to write their own. + + Once completed the Plugin InputSource can be registered with the + SessionContext to ensure that it will be used in order + to obtain the SqlTable information from the custom datasource. + + + .. py:method:: build_table(input_item: Any, table_name: str, **kwarg: Any) -> datafusion.common.SqlTable + :abstractmethod: + + + Create a table from the input source. + + + + .. py:method:: is_correct_input(input_item: Any, table_name: str, **kwargs: Any) -> bool + :abstractmethod: + + + Returns `True` if the input is valid. + + + diff --git a/_sources/autoapi/datafusion/input/index.rst.txt b/_sources/autoapi/datafusion/input/index.rst.txt new file mode 100644 index 000000000..9a081edfe --- /dev/null +++ b/_sources/autoapi/datafusion/input/index.rst.txt @@ -0,0 +1,56 @@ +datafusion.input +================ + +.. py:module:: datafusion.input + +.. autoapi-nested-parse:: + + This package provides for input sources. + + The primary class used within DataFusion is ``LocationInputPlugin``. + + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/datafusion/input/base/index + /autoapi/datafusion/input/location/index + + +Classes +------- + +.. autoapisummary:: + + datafusion.input.LocationInputPlugin + + +Package Contents +---------------- + +.. py:class:: LocationInputPlugin + + Bases: :py:obj:`datafusion.input.base.BaseInputSource` + + + Input Plugin for everything. + + This can be read in from a file (on disk, remote etc.). + + + .. py:method:: build_table(input_item: str, table_name: str, **kwargs: Any) -> datafusion.common.SqlTable + + Create a table from the input source. + + + + .. py:method:: is_correct_input(input_item: Any, table_name: str, **kwargs: Any) -> bool + + Returns `True` if the input is valid. + + + diff --git a/_sources/autoapi/datafusion/input/location/index.rst.txt b/_sources/autoapi/datafusion/input/location/index.rst.txt new file mode 100644 index 000000000..609a280bd --- /dev/null +++ b/_sources/autoapi/datafusion/input/location/index.rst.txt @@ -0,0 +1,44 @@ +datafusion.input.location +========================= + +.. py:module:: datafusion.input.location + +.. autoapi-nested-parse:: + + The default input source for DataFusion. + + + +Classes +------- + +.. autoapisummary:: + + datafusion.input.location.LocationInputPlugin + + +Module Contents +--------------- + +.. py:class:: LocationInputPlugin + + Bases: :py:obj:`datafusion.input.base.BaseInputSource` + + + Input Plugin for everything. + + This can be read in from a file (on disk, remote etc.). + + + .. py:method:: build_table(input_item: str, table_name: str, **kwargs: Any) -> datafusion.common.SqlTable + + Create a table from the input source. + + + + .. py:method:: is_correct_input(input_item: Any, table_name: str, **kwargs: Any) -> bool + + Returns `True` if the input is valid. + + + diff --git a/_sources/autoapi/datafusion/io/index.rst.txt b/_sources/autoapi/datafusion/io/index.rst.txt new file mode 100644 index 000000000..453d6fa04 --- /dev/null +++ b/_sources/autoapi/datafusion/io/index.rst.txt @@ -0,0 +1,113 @@ +datafusion.io +============= + +.. py:module:: datafusion.io + +.. autoapi-nested-parse:: + + IO read functions using global context. + + + +Functions +--------- + +.. autoapisummary:: + + datafusion.io.read_avro + datafusion.io.read_csv + datafusion.io.read_json + datafusion.io.read_parquet + + +Module Contents +--------------- + +.. py:function:: read_avro(path: str | pathlib.Path, schema: pyarrow.Schema | None = None, file_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, file_extension: str = '.avro') -> datafusion.dataframe.DataFrame + + Create a :py:class:`DataFrame` for reading Avro data source. + + This function will use the global context. Any functions or tables registered + with another context may not be accessible when used with a DataFrame created + using this function. + + :param path: Path to the Avro file. + :param schema: The data source schema. + :param file_partition_cols: Partition columns. + :param file_extension: File extension to select. + + :returns: DataFrame representation of the read Avro file + + +.. py:function:: read_csv(path: str | pathlib.Path | list[str] | list[pathlib.Path], schema: pyarrow.Schema | None = None, has_header: bool = True, delimiter: str = ',', schema_infer_max_records: int = 1000, file_extension: str = '.csv', table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, file_compression_type: str | None = None, options: datafusion.options.CsvReadOptions | None = None) -> datafusion.dataframe.DataFrame + + Read a CSV data source. + + This function will use the global context. Any functions or tables registered + with another context may not be accessible when used with a DataFrame created + using this function. + + :param path: Path to the CSV file + :param schema: An optional schema representing the CSV files. If None, the + CSV reader will try to infer it based on data in file. + :param has_header: Whether the CSV file have a header. If schema inference + is run on a file with no headers, default column names are + created. + :param delimiter: An optional column delimiter. + :param schema_infer_max_records: Maximum number of rows to read from CSV + files for schema inference if needed. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param table_partition_cols: Partition columns. + :param file_compression_type: File compression type. + :param options: Set advanced options for CSV reading. This cannot be + combined with any of the other options in this method. + + :returns: DataFrame representation of the read CSV files + + +.. py:function:: read_json(path: str | pathlib.Path, schema: pyarrow.Schema | None = None, schema_infer_max_records: int = 1000, file_extension: str = '.json', table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, file_compression_type: str | None = None) -> datafusion.dataframe.DataFrame + + Read a line-delimited JSON data source. + + This function will use the global context. Any functions or tables registered + with another context may not be accessible when used with a DataFrame created + using this function. + + :param path: Path to the JSON file. + :param schema: The data source schema. + :param schema_infer_max_records: Maximum number of rows to read from JSON + files for schema inference if needed. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param table_partition_cols: Partition columns. + :param file_compression_type: File compression type. + + :returns: DataFrame representation of the read JSON files. + + +.. py:function:: read_parquet(path: str | pathlib.Path, table_partition_cols: list[tuple[str, str | pyarrow.DataType]] | None = None, parquet_pruning: bool = True, file_extension: str = '.parquet', skip_metadata: bool = True, schema: pyarrow.Schema | None = None, file_sort_order: list[list[datafusion.expr.Expr]] | None = None) -> datafusion.dataframe.DataFrame + + Read a Parquet source into a :py:class:`~datafusion.dataframe.Dataframe`. + + This function will use the global context. Any functions or tables registered + with another context may not be accessible when used with a DataFrame created + using this function. + + :param path: Path to the Parquet file. + :param table_partition_cols: Partition columns. + :param parquet_pruning: Whether the parquet reader should use the predicate + to prune row groups. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param skip_metadata: Whether the parquet reader should skip any metadata + that may be in the file schema. This can help avoid schema + conflicts due to metadata. + :param schema: An optional schema representing the parquet files. If None, + the parquet reader will try to infer it based on data in the + file. + :param file_sort_order: Sort order for the file. + + :returns: DataFrame representation of the read Parquet files + + diff --git a/_sources/autoapi/datafusion/ipc/index.rst.txt b/_sources/autoapi/datafusion/ipc/index.rst.txt new file mode 100644 index 000000000..d81b34432 --- /dev/null +++ b/_sources/autoapi/datafusion/ipc/index.rst.txt @@ -0,0 +1,201 @@ +datafusion.ipc +============== + +.. py:module:: datafusion.ipc + +.. autoapi-nested-parse:: + + Driver- and worker-side setup for distributing DataFusion expressions. + + When a :class:`Expr` is shipped to a worker process (e.g. through + :func:`multiprocessing.Pool` or a Ray actor), the worker reconstructs the + expression against a :class:`SessionContext`. If the expression references + UDFs imported via the FFI capsule protocol — or any UDF the worker would + otherwise resolve from its registered functions rather than from inside + the shipped expression — install a configured :class:`SessionContext` + once per worker: + + .. code-block:: python + + from datafusion import SessionContext + from datafusion.ipc import set_worker_ctx + + def init_worker(): + ctx = SessionContext() + ctx.register_udaf(my_ffi_aggregate) + set_worker_ctx(ctx) + + Built-in functions and Python UDFs (scalar, aggregate, window) travel + inside the shipped expression itself and do not need pre-registration + on the worker. + + .. note:: Serialization model + + Expressions containing Python UDFs (scalar, aggregate, window) are + serialized using :mod:`cloudpickle`. The callable itself travels + **by value** (bytecode and closure cells inlined), but any names the + callable resolves via ``import`` are captured **by reference** and + must be importable on the receiving worker. + + The serialized payload is stamped with the sender's Python + ``(major, minor)`` version. Loading on a different minor version + raises :class:`ValueError` with an actionable message — cloudpickle + payloads are not portable across Python minor versions. See + :meth:`datafusion.Expr.to_bytes` for examples of what travels by + value vs. by reference. + + On the driver side, call :func:`set_sender_ctx` to control how + :func:`pickle.dumps` encodes expressions — for example, to apply + :meth:`SessionContext.with_python_udf_inlining` to every pickled + expression on this thread: + + >>> import pickle + >>> from datafusion import SessionContext, col, lit + >>> from datafusion.ipc import clear_sender_ctx, set_sender_ctx + >>> driver_ctx = SessionContext().with_python_udf_inlining(enabled=False) + >>> set_sender_ctx(driver_ctx) + >>> try: + ... blob = pickle.dumps(col("a") + lit(1)) + ... finally: + ... clear_sender_ctx() + >>> isinstance(blob, bytes) + True + + Without a sender context the default codec is used (Python UDF + inlining on). The sender context only affects pickle / ``to_bytes`` + encoding; explicit ``expr.to_bytes(ctx)`` calls still use the supplied + ``ctx``. + + The thread-local sender context holds a strong reference to the + installed :class:`SessionContext` until :func:`clear_sender_ctx` is + called or the thread exits. Long-running driver threads that install a sender + context once and never clear it will retain that session for the + lifetime of the thread; pair :func:`set_sender_ctx` with + :func:`clear_sender_ctx` (e.g. in a ``try``/``finally``) when the + sender context is only needed for a bounded scope. + + + +Functions +--------- + +.. autoapisummary:: + + datafusion.ipc.clear_sender_ctx + datafusion.ipc.clear_worker_ctx + datafusion.ipc.get_sender_ctx + datafusion.ipc.get_worker_ctx + datafusion.ipc.set_sender_ctx + datafusion.ipc.set_worker_ctx + + +Module Contents +--------------- + +.. py:function:: clear_sender_ctx() -> None + + Remove this driver's installed sender :class:`SessionContext`. + + After clearing, pickled expressions fall back to the default codec + (Python UDF inlining on). + + .. rubric:: Examples + + >>> from datafusion import SessionContext + >>> from datafusion.ipc import ( + ... set_sender_ctx, clear_sender_ctx, get_sender_ctx, + ... ) + >>> set_sender_ctx(SessionContext()) + >>> clear_sender_ctx() + >>> get_sender_ctx() is None + True + + +.. py:function:: clear_worker_ctx() -> None + + Remove this worker's installed :class:`SessionContext`. + + After clearing, expressions reconstructed in this worker fall back to + the global :class:`SessionContext` — adequate for built-ins and Python + UDFs (scalar, aggregate, window), but anything imported via the FFI + capsule protocol must be registered on the global context to resolve. + + .. rubric:: Examples + + >>> from datafusion import SessionContext + >>> from datafusion.ipc import set_worker_ctx, clear_worker_ctx, get_worker_ctx + >>> set_worker_ctx(SessionContext()) + >>> clear_worker_ctx() + >>> get_worker_ctx() is None + True + + +.. py:function:: get_sender_ctx() -> datafusion.context.SessionContext | None + + Return this driver's installed sender :class:`SessionContext`, or ``None``. + + .. rubric:: Examples + + >>> from datafusion.ipc import get_sender_ctx, clear_sender_ctx + >>> clear_sender_ctx() + >>> get_sender_ctx() is None + True + + +.. py:function:: get_worker_ctx() -> datafusion.context.SessionContext | None + + Return this worker's installed :class:`SessionContext`, or ``None``. + + .. rubric:: Examples + + >>> from datafusion.ipc import get_worker_ctx, clear_worker_ctx + >>> clear_worker_ctx() + >>> get_worker_ctx() is None + True + + +.. py:function:: set_sender_ctx(ctx: datafusion.context.SessionContext) -> None + + Install this driver's :class:`SessionContext` for outbound pickles. + + Controls how :func:`pickle.dumps` encodes :class:`Expr` instances on + this thread. The most useful application is propagating a session + configured with + :meth:`SessionContext.with_python_udf_inlining` so the toggle takes + effect through pickle (which otherwise calls + :meth:`Expr.to_bytes` with no context and uses the default codec). + + Idempotent: overwrites any previous value. Stored in a thread-local + slot, so worker threads on the driver may install their own contexts. + Does not affect :meth:`Expr.to_bytes` calls that pass an explicit + ``ctx`` — those continue to use the supplied context. + + .. rubric:: Examples + + >>> from datafusion import SessionContext + >>> from datafusion.ipc import set_sender_ctx, get_sender_ctx + >>> driver = SessionContext().with_python_udf_inlining(enabled=False) + >>> set_sender_ctx(driver) + >>> get_sender_ctx() is driver + True + + +.. py:function:: set_worker_ctx(ctx: datafusion.context.SessionContext) -> None + + Install this worker's :class:`SessionContext` for shipped expressions. + + Call once per worker — typically from a ``multiprocessing.Pool`` + initializer or a Ray actor ``__init__``. Idempotent: overwrites any + previous value. Stored in a thread-local slot, so each thread within a + worker may install its own context independently. + + .. rubric:: Examples + + >>> from datafusion import SessionContext + >>> from datafusion.ipc import set_worker_ctx, get_worker_ctx, clear_worker_ctx + >>> set_worker_ctx(SessionContext()) + >>> get_worker_ctx() is not None + True + >>> clear_worker_ctx() + + diff --git a/_sources/autoapi/datafusion/object_store/index.rst.txt b/_sources/autoapi/datafusion/object_store/index.rst.txt new file mode 100644 index 000000000..d38e86792 --- /dev/null +++ b/_sources/autoapi/datafusion/object_store/index.rst.txt @@ -0,0 +1,36 @@ +datafusion.object_store +======================= + +.. py:module:: datafusion.object_store + +.. autoapi-nested-parse:: + + Object store functionality. + + + +Attributes +---------- + +.. autoapisummary:: + + datafusion.object_store.AmazonS3 + datafusion.object_store.GoogleCloud + datafusion.object_store.Http + datafusion.object_store.LocalFileSystem + datafusion.object_store.MicrosoftAzure + + +Module Contents +--------------- + +.. py:data:: AmazonS3 + +.. py:data:: GoogleCloud + +.. py:data:: Http + +.. py:data:: LocalFileSystem + +.. py:data:: MicrosoftAzure + diff --git a/_sources/autoapi/datafusion/options/index.rst.txt b/_sources/autoapi/datafusion/options/index.rst.txt new file mode 100644 index 000000000..25a6d6464 --- /dev/null +++ b/_sources/autoapi/datafusion/options/index.rst.txt @@ -0,0 +1,242 @@ +datafusion.options +================== + +.. py:module:: datafusion.options + +.. autoapi-nested-parse:: + + Options for reading various file formats. + + + +Classes +------- + +.. autoapisummary:: + + datafusion.options.CsvReadOptions + + +Module Contents +--------------- + +.. py:class:: CsvReadOptions(*, has_header: bool = True, delimiter: str = ',', quote: str = '"', terminator: str | None = None, escape: str | None = None, comment: str | None = None, newlines_in_values: bool = False, schema: pyarrow.Schema | None = None, schema_infer_max_records: int = DEFAULT_MAX_INFER_SCHEMA, file_extension: str = '.csv', table_partition_cols: list[tuple[str, pyarrow.DataType]] | None = None, file_compression_type: str = '', file_sort_order: list[list[datafusion.expr.SortExpr]] | None = None, null_regex: str | None = None, truncated_rows: bool = False) + + Options for reading CSV files. + + This class provides a builder pattern for configuring CSV reading options. + All methods starting with ``with_`` return ``self`` to allow method chaining. + + Initialize CsvReadOptions. + + :param has_header: Does the CSV file have a header row? If schema inference + is run on a file with no headers, default column names are created. + :param delimiter: Column delimiter character. Must be a single ASCII character. + :param quote: Quote character for fields containing delimiters or newlines. + Must be a single ASCII character. + :param terminator: Optional line terminator character. If ``None``, uses CRLF. + Must be a single ASCII character. + :param escape: Optional escape character for quotes. Must be a single ASCII + character. + :param comment: If specified, lines beginning with this character are ignored. + Must be a single ASCII character. + :param newlines_in_values: Whether newlines in quoted values are supported. + Parsing newlines in quoted values may be affected by execution + behavior such as parallel file scanning. Setting this to ``True`` + ensures that newlines in values are parsed successfully, which may + reduce performance. + :param schema: Optional PyArrow schema representing the CSV files. If ``None``, + the CSV reader will try to infer it based on data in the file. + :param schema_infer_max_records: Maximum number of rows to read from CSV files + for schema inference if needed. + :param file_extension: File extension; only files with this extension are + selected for data input. + :param table_partition_cols: Partition columns as a list of tuples of + (column_name, data_type). + :param file_compression_type: File compression type. Supported values are + ``"gzip"``, ``"bz2"``, ``"xz"``, ``"zstd"``, or empty string for + uncompressed. + :param file_sort_order: Optional sort order of the files as a list of sort + expressions per file. + :param null_regex: Optional regex pattern to match null values in the CSV. + :param truncated_rows: Whether to allow truncated rows when parsing. By default + this is ``False`` and will error if the CSV rows have different + lengths. When set to ``True``, it will allow records with less than + the expected number of columns and fill the missing columns with + nulls. If the record's schema is not nullable, it will still return + an error. + + + .. py:method:: to_inner() -> datafusion._internal.options.CsvReadOptions + + Convert this object into the underlying Rust structure. + + This is intended for internal use only. + + + + .. py:method:: with_comment(comment: str | None) -> CsvReadOptions + + Configure the comment character. + + + + .. py:method:: with_delimiter(delimiter: str) -> CsvReadOptions + + Configure the column delimiter. + + + + .. py:method:: with_escape(escape: str | None) -> CsvReadOptions + + Configure the escape character. + + + + .. py:method:: with_file_compression_type(file_compression_type: str) -> CsvReadOptions + + Configure file compression type. + + + + .. py:method:: with_file_extension(file_extension: str) -> CsvReadOptions + + Configure the file extension filter. + + + + .. py:method:: with_file_sort_order(file_sort_order: list[list[datafusion.expr.SortExpr]]) -> CsvReadOptions + + Configure file sort order. + + + + .. py:method:: with_has_header(has_header: bool) -> CsvReadOptions + + Configure whether the CSV has a header row. + + + + .. py:method:: with_newlines_in_values(newlines_in_values: bool) -> CsvReadOptions + + Configure whether newlines in values are supported. + + + + .. py:method:: with_null_regex(null_regex: str | None) -> CsvReadOptions + + Configure null value regex pattern. + + + + .. py:method:: with_quote(quote: str) -> CsvReadOptions + + Configure the quote character. + + + + .. py:method:: with_schema(schema: pyarrow.Schema | None) -> CsvReadOptions + + Configure the schema. + + + + .. py:method:: with_schema_infer_max_records(schema_infer_max_records: int) -> CsvReadOptions + + Configure maximum records for schema inference. + + + + .. py:method:: with_table_partition_cols(table_partition_cols: list[tuple[str, pyarrow.DataType]]) -> CsvReadOptions + + Configure table partition columns. + + + + .. py:method:: with_terminator(terminator: str | None) -> CsvReadOptions + + Configure the line terminator character. + + + + .. py:method:: with_truncated_rows(truncated_rows: bool) -> CsvReadOptions + + Configure whether to allow truncated rows. + + + + .. py:attribute:: comment + :value: None + + + + .. py:attribute:: delimiter + :value: ',' + + + + .. py:attribute:: escape + :value: None + + + + .. py:attribute:: file_compression_type + :value: '' + + + + .. py:attribute:: file_extension + :value: '.csv' + + + + .. py:attribute:: file_sort_order + :value: [] + + + + .. py:attribute:: has_header + :value: True + + + + .. py:attribute:: newlines_in_values + :value: False + + + + .. py:attribute:: null_regex + :value: None + + + + .. py:attribute:: quote + :value: '"' + + + + .. py:attribute:: schema + :value: None + + + + .. py:attribute:: schema_infer_max_records + :value: 1000 + + + + .. py:attribute:: table_partition_cols + :value: [] + + + + .. py:attribute:: terminator + :value: None + + + + .. py:attribute:: truncated_rows + :value: False + + + diff --git a/_sources/autoapi/datafusion/plan/index.rst.txt b/_sources/autoapi/datafusion/plan/index.rst.txt new file mode 100644 index 000000000..2db4c17d0 --- /dev/null +++ b/_sources/autoapi/datafusion/plan/index.rst.txt @@ -0,0 +1,400 @@ +datafusion.plan +=============== + +.. py:module:: datafusion.plan + +.. autoapi-nested-parse:: + + This module supports physical and logical plans in DataFusion. + + + +Classes +------- + +.. autoapisummary:: + + datafusion.plan.ExecutionPlan + datafusion.plan.LogicalPlan + datafusion.plan.Metric + datafusion.plan.MetricsSet + + +Module Contents +--------------- + +.. py:class:: ExecutionPlan(plan: datafusion._internal.ExecutionPlan) + + Represent nodes in the DataFusion Physical Plan. + + This constructor should not be called by the end user. + + + .. py:method:: __repr__() -> str + + Print a string representation of the physical plan. + + + + .. py:method:: children() -> list[ExecutionPlan] + + Get a list of children `ExecutionPlan` that act as inputs to this plan. + + The returned list will be empty for leaf nodes such as scans, will contain a + single value for unary nodes, or two values for binary nodes (such as joins). + + + + .. py:method:: collect_metrics() -> list[tuple[str, MetricsSet]] + + Return runtime statistics for each step of the query execution. + + DataFusion executes a query as a pipeline of operators — for example a + data source scan, followed by a filter, followed by a projection. After + the DataFrame has been executed (via + :py:meth:`~datafusion.DataFrame.collect`, + :py:meth:`~datafusion.DataFrame.execute_stream`, etc.), each operator + records statistics such as how many rows it produced and how much CPU + time it consumed. + + Each entry in the returned list corresponds to one operator that + recorded metrics. The first element of the tuple is the operator's + description string — the same text shown by + :py:meth:`display_indent` — which identifies both the operator type + and its key parameters, for example ``"FilterExec: column1@0 > 1"`` + or ``"DataSourceExec: partitions=1"``. + + :returns: A list of ``(description, MetricsSet)`` tuples ordered from the + outermost operator (top of the execution tree) down to the + data-source leaves. Only operators that recorded at least one + metric are included. Returns an empty list if called before the + DataFrame has been executed. + + + + .. py:method:: display() -> str + + Print the physical plan. + + + + .. py:method:: display_indent() -> str + + Print an indented form of the physical plan. + + + + .. py:method:: from_bytes(ctx: datafusion.context.SessionContext, data: bytes) -> ExecutionPlan + :staticmethod: + + + Create an ExecutionPlan from serialized protobuf bytes. + + Decoding routes through the session's installed + `PhysicalExtensionCodec`. Tables created in memory from record + batches are currently not supported. + + + + .. py:method:: from_proto(ctx: datafusion.context.SessionContext, data: bytes) -> ExecutionPlan + :staticmethod: + + + Deprecated alias for :meth:`from_bytes`. + + + + .. py:method:: metrics() -> MetricsSet | None + + Return metrics for this plan node, or None if this plan has no MetricsSet. + + Some operators (e.g. DataSourceExec) eagerly initialize a MetricsSet + when the plan is created, so this may return a set even before + execution. Metric *values* (such as ``output_rows``) are only + meaningful after the DataFrame has been executed. + + + + .. py:method:: to_bytes(ctx: datafusion.context.SessionContext | None = None) -> bytes + + Convert an ExecutionPlan into serialized protobuf bytes. + + When ``ctx`` is supplied, encoding routes through the session's + installed `PhysicalExtensionCodec`. Tables created in memory + from record batches are currently not supported. + + + + .. py:method:: to_proto() -> bytes + + Deprecated alias for :meth:`to_bytes`. + + + + .. py:attribute:: _raw_plan + + + .. py:property:: partition_count + :type: int + + + Returns the number of partitions in the physical plan. + + +.. py:class:: LogicalPlan(plan: datafusion._internal.LogicalPlan) + + Logical Plan. + + A `LogicalPlan` is a node in a tree of relational operators (such as + Projection or Filter). + + Represents transforming an input relation (table) to an output relation + (table) with a potentially different schema. Plans form a dataflow tree + where data flows from leaves up to the root to produce the query result. + + A `LogicalPlan` can be created by the SQL query planner, the DataFrame API, + or programmatically (for example custom query languages). + + This constructor should not be called by the end user. + + + .. py:method:: __eq__(other: LogicalPlan) -> bool + + Test equality. + + + + .. py:method:: __repr__() -> str + + Generate a printable representation of the plan. + + + + .. py:method:: display() -> str + + Print the logical plan. + + + + .. py:method:: display_graphviz() -> str + + Print the graph visualization of the logical plan. + + Returns a `format`able structure that produces lines meant for graphical display + using the `DOT` language. This format can be visualized using software from + [`graphviz`](https://graphviz.org/) + + + + .. py:method:: display_indent() -> str + + Print an indented form of the logical plan. + + + + .. py:method:: display_indent_schema() -> str + + Print an indented form of the schema for the logical plan. + + + + .. py:method:: from_bytes(ctx: datafusion.context.SessionContext, data: bytes) -> LogicalPlan + :staticmethod: + + + Create a LogicalPlan from serialized protobuf bytes. + + Decoding routes through the session's installed + `LogicalExtensionCodec`. Tables created in memory from record + batches are currently not supported. + + + + .. py:method:: from_proto(ctx: datafusion.context.SessionContext, data: bytes) -> LogicalPlan + :staticmethod: + + + Deprecated alias for :meth:`from_bytes`. + + + + .. py:method:: inputs() -> list[LogicalPlan] + + Returns the list of inputs to the logical plan. + + + + .. py:method:: to_bytes(ctx: datafusion.context.SessionContext | None = None) -> bytes + + Convert a LogicalPlan to serialized protobuf bytes. + + When ``ctx`` is supplied, encoding routes through the session's + installed `LogicalExtensionCodec` so user FFI codecs (registered + via :py:meth:`SessionContext.with_logical_extension_codec`) see + the encode path. With ``ctx=None`` a default codec is used. + Tables created in memory from record batches are currently not + supported. + + + + .. py:method:: to_proto() -> bytes + + Deprecated alias for :meth:`to_bytes`. + + + + .. py:method:: to_variant() -> Any + + Convert the logical plan into its specific variant. + + + + .. py:attribute:: _raw_plan + + +.. py:class:: Metric(raw: datafusion._internal.Metric) + + A single execution metric with name, value, partition, and labels. + + This constructor should not be called by the end user. + + + .. py:method:: __repr__() -> str + + Return a string representation of the metric. + + + + .. py:method:: labels() -> dict[str, str] + + Return the labels associated with this metric. + + Labels provide additional context for a metric. For example:: + + metric.labels() + # {'output_type': 'final'} + + + + .. py:attribute:: _raw + + + .. py:property:: name + :type: str + + + The name of this metric (e.g. ``output_rows``). + + + .. py:property:: partition + :type: int | None + + + The 0-based partition index this metric applies to. + + Returns ``None`` for metrics that are not partition-specific (i.e. they + apply globally across all partitions of the operator). + + + .. py:property:: value + :type: int | datetime.datetime | None + + + The value of this metric. + + Returns an ``int`` for counters, gauges, and time-based metrics + (nanoseconds), a :py:class:`~datetime.datetime` (UTC) for + ``start_timestamp`` / ``end_timestamp`` metrics, or ``None`` + when the value has not been set or is not representable. + + + .. py:property:: value_as_datetime + :type: datetime.datetime | None + + + The value as a UTC :py:class:`~datetime.datetime` for timestamp metrics. + + Returns ``None`` for all non-timestamp metrics and for timestamp + metrics whose value has not been set (e.g. before execution). + + +.. py:class:: MetricsSet(raw: datafusion._internal.MetricsSet) + + A set of metrics for a single execution plan operator. + + A physical plan operator runs independently across one or more partitions. + :py:meth:`metrics` returns the raw per-partition :py:class:`Metric` objects. + The convenience properties (:py:attr:`output_rows`, :py:attr:`elapsed_compute`, + etc.) automatically sum the named metric across *all* partitions, giving a + single aggregate value for the operator as a whole. + + This constructor should not be called by the end user. + + + .. py:method:: __repr__() -> str + + Return a string representation of the metrics set. + + + + .. py:method:: metrics() -> list[Metric] + + Return all individual metrics in this set. + + + + .. py:method:: sum_by_name(name: str) -> int | None + + Sum the named metric across all partitions. + + Useful for accessing any metric not exposed as a first-class property. + Returns ``None`` if no metric with the given name was recorded. + + :param name: The metric name, e.g. ``"output_rows"`` or ``"elapsed_compute"``. + + + + .. py:attribute:: _raw + + + .. py:property:: elapsed_compute + :type: int | None + + + Total CPU time (in nanoseconds) spent inside this operator's execute loop. + + Summed across all partitions. Returns ``None`` if no ``elapsed_compute`` + metric was recorded. + + + .. py:property:: output_rows + :type: int | None + + + Sum of output_rows across all partitions. + + + .. py:property:: spill_count + :type: int | None + + + Number of times this operator spilled data to disk due to memory pressure. + + This is a count of spill events, not a byte count. Summed across all + partitions. Returns ``None`` if no ``spill_count`` metric was recorded. + + + .. py:property:: spilled_bytes + :type: int | None + + + Sum of spilled_bytes across all partitions. + + + .. py:property:: spilled_rows + :type: int | None + + + Sum of spilled_rows across all partitions. + + diff --git a/_sources/autoapi/datafusion/record_batch/index.rst.txt b/_sources/autoapi/datafusion/record_batch/index.rst.txt new file mode 100644 index 000000000..b55742cfe --- /dev/null +++ b/_sources/autoapi/datafusion/record_batch/index.rst.txt @@ -0,0 +1,106 @@ +datafusion.record_batch +======================= + +.. py:module:: datafusion.record_batch + +.. autoapi-nested-parse:: + + This module provides the classes for handling record batches. + + These are typically the result of dataframe + :py:func:`datafusion.dataframe.execute_stream` operations. + + + +Classes +------- + +.. autoapisummary:: + + datafusion.record_batch.RecordBatch + datafusion.record_batch.RecordBatchStream + + +Module Contents +--------------- + +.. py:class:: RecordBatch(record_batch: datafusion._internal.RecordBatch) + + This class is essentially a wrapper for :py:class:`pa.RecordBatch`. + + This constructor is generally not called by the end user. + + See the :py:class:`RecordBatchStream` iterator for generating this class. + + + .. py:method:: __arrow_c_array__(requested_schema: object | None = None) -> tuple[object, object] + + Export the record batch via the Arrow C Data Interface. + + This allows zero-copy interchange with libraries that support the + `Arrow PyCapsule interface `_. + + :param requested_schema: Attempt to provide the record batch using this + schema. Only straightforward projections such as column + selection or reordering are applied. + + :returns: Two Arrow PyCapsule objects representing the ``ArrowArray`` and + ``ArrowSchema``. + + + + .. py:method:: to_pyarrow() -> pyarrow.RecordBatch + + Convert to :py:class:`pa.RecordBatch`. + + + + .. py:attribute:: record_batch + + +.. py:class:: RecordBatchStream(record_batch_stream: datafusion._internal.RecordBatchStream) + + This class represents a stream of record batches. + + These are typically the result of a + :py:func:`~datafusion.dataframe.DataFrame.execute_stream` operation. + + This constructor is typically not called by the end user. + + + .. py:method:: __aiter__() -> Self + + Return an asynchronous iterator over record batches. + + + + .. py:method:: __anext__() -> RecordBatch + :async: + + + Return the next :py:class:`RecordBatch` in the stream asynchronously. + + + + .. py:method:: __iter__() -> Self + + Return an iterator over record batches. + + + + .. py:method:: __next__() -> RecordBatch + + Return the next :py:class:`RecordBatch` in the stream. + + + + .. py:method:: next() -> RecordBatch + + See :py:func:`__next__` for the iterator function. + + + + .. py:attribute:: rbs + + diff --git a/_sources/autoapi/datafusion/substrait/index.rst.txt b/_sources/autoapi/datafusion/substrait/index.rst.txt new file mode 100644 index 000000000..b9a1fd525 --- /dev/null +++ b/_sources/autoapi/datafusion/substrait/index.rst.txt @@ -0,0 +1,174 @@ +datafusion.substrait +==================== + +.. py:module:: datafusion.substrait + +.. autoapi-nested-parse:: + + This module provides support for using substrait with datafusion. + + For additional information about substrait, see https://substrait.io/ for more + information about substrait. + + + +Classes +------- + +.. autoapisummary:: + + datafusion.substrait.Consumer + datafusion.substrait.Plan + datafusion.substrait.Producer + datafusion.substrait.Serde + + +Module Contents +--------------- + +.. py:class:: Consumer + + Generates a logical plan from a substrait plan. + + + .. py:method:: from_substrait_plan(ctx: datafusion.context.SessionContext, plan: Plan) -> datafusion.plan.LogicalPlan + :staticmethod: + + + Convert a Substrait plan to a DataFusion LogicalPlan. + + :param ctx: SessionContext to use. + :param plan: Substrait plan to convert. + + :returns: LogicalPlan. + + + +.. py:class:: Plan(plan: datafusion._internal.substrait.Plan) + + A class representing an encodable substrait plan. + + Create a substrait plan. + + The user should not have to call this constructor directly. Rather, it + should be created via :py:class:`Serde` or py:class:`Producer` classes + in this module. + + + .. py:method:: encode() -> bytes + + Encode the plan to bytes. + + :returns: Encoded plan. + + + + .. py:method:: from_json(json: str) -> Plan + :staticmethod: + + + Parse a plan from a JSON string representation. + + :param json: JSON representation of a Substrait plan. + + :returns: Plan object representing the Substrait plan. + + + + .. py:method:: to_json() -> str + + Get the JSON representation of the Substrait plan. + + :returns: A JSON representation of the Substrait plan. + + + + .. py:attribute:: plan_internal + + +.. py:class:: Producer + + Generates substrait plans from a logical plan. + + + .. py:method:: to_substrait_plan(logical_plan: datafusion.plan.LogicalPlan, ctx: datafusion.context.SessionContext) -> Plan + :staticmethod: + + + Convert a DataFusion LogicalPlan to a Substrait plan. + + :param logical_plan: LogicalPlan to convert. + :param ctx: SessionContext to use. + + :returns: Substrait plan. + + + +.. py:class:: Serde + + Provides the ``Substrait`` serialization and deserialization. + + + .. py:method:: deserialize(path: str | pathlib.Path) -> Plan + :staticmethod: + + + Deserialize a Substrait plan from a file. + + :param path: Path to read the Substrait plan from. + + :returns: Substrait plan. + + + + .. py:method:: deserialize_bytes(proto_bytes: bytes) -> Plan + :staticmethod: + + + Deserialize a Substrait plan from bytes. + + :param proto_bytes: Bytes to read the Substrait plan from. + + :returns: Substrait plan. + + + + .. py:method:: serialize(sql: str, ctx: datafusion.context.SessionContext, path: str | pathlib.Path) -> None + :staticmethod: + + + Serialize a SQL query to a Substrait plan and write it to a file. + + :param sql: SQL query to serialize. + :param ctx: SessionContext to use. + :param path: Path to write the Substrait plan to. + + + + .. py:method:: serialize_bytes(sql: str, ctx: datafusion.context.SessionContext) -> bytes + :staticmethod: + + + Serialize a SQL query to a Substrait plan as bytes. + + :param sql: SQL query to serialize. + :param ctx: SessionContext to use. + + :returns: Substrait plan as bytes. + + + + .. py:method:: serialize_to_plan(sql: str, ctx: datafusion.context.SessionContext) -> Plan + :staticmethod: + + + Serialize a SQL query to a Substrait plan. + + Args: + sql: SQL query to serialize. + ctx: SessionContext to use. + + :returns: Substrait plan. + + + diff --git a/_sources/autoapi/datafusion/unparser/index.rst.txt b/_sources/autoapi/datafusion/unparser/index.rst.txt new file mode 100644 index 000000000..be2a35240 --- /dev/null +++ b/_sources/autoapi/datafusion/unparser/index.rst.txt @@ -0,0 +1,97 @@ +datafusion.unparser +=================== + +.. py:module:: datafusion.unparser + +.. autoapi-nested-parse:: + + This module provides support for unparsing datafusion plans to SQL. + + For additional information about unparsing, see https://docs.rs/datafusion-sql/latest/datafusion_sql/unparser/index.html + + + +Classes +------- + +.. autoapisummary:: + + datafusion.unparser.Dialect + datafusion.unparser.Unparser + + +Module Contents +--------------- + +.. py:class:: Dialect(dialect: datafusion._internal.unparser.Dialect) + + DataFusion data catalog. + + This constructor is not typically called by the end user. + + + .. py:method:: default() -> Dialect + :staticmethod: + + + Create a new default dialect. + + + + .. py:method:: duckdb() -> Dialect + :staticmethod: + + + Create a new DuckDB dialect. + + + + .. py:method:: mysql() -> Dialect + :staticmethod: + + + Create a new MySQL dialect. + + + + .. py:method:: postgres() -> Dialect + :staticmethod: + + + Create a new PostgreSQL dialect. + + + + .. py:method:: sqlite() -> Dialect + :staticmethod: + + + Create a new SQLite dialect. + + + + .. py:attribute:: dialect + + +.. py:class:: Unparser(dialect: Dialect) + + DataFusion unparser. + + This constructor is not typically called by the end user. + + + .. py:method:: plan_to_sql(plan: datafusion.plan.LogicalPlan) -> str + + Convert a logical plan to a SQL string. + + + + .. py:method:: with_pretty(pretty: bool) -> Unparser + + Set the pretty flag. + + + + .. py:attribute:: unparser + + diff --git a/_sources/autoapi/datafusion/user_defined/index.rst.txt b/_sources/autoapi/datafusion/user_defined/index.rst.txt new file mode 100644 index 000000000..6e2505995 --- /dev/null +++ b/_sources/autoapi/datafusion/user_defined/index.rst.txt @@ -0,0 +1,982 @@ +datafusion.user_defined +======================= + +.. py:module:: datafusion.user_defined + +.. autoapi-nested-parse:: + + Provides the user-defined functions for evaluation of dataframes. + + + +Attributes +---------- + +.. autoapisummary:: + + datafusion.user_defined._R + datafusion.user_defined.udaf + datafusion.user_defined.udf + datafusion.user_defined.udtf + datafusion.user_defined.udwf + + +Classes +------- + +.. autoapisummary:: + + datafusion.user_defined.Accumulator + datafusion.user_defined.AggregateUDF + datafusion.user_defined.AggregateUDFExportable + datafusion.user_defined.LogicalExtensionCodecExportable + datafusion.user_defined.PhysicalExtensionCodecExportable + datafusion.user_defined.ScalarUDF + datafusion.user_defined.ScalarUDFExportable + datafusion.user_defined.TableFunction + datafusion.user_defined.Volatility + datafusion.user_defined.WindowEvaluator + datafusion.user_defined.WindowUDF + datafusion.user_defined.WindowUDFExportable + + +Functions +--------- + +.. autoapisummary:: + + datafusion.user_defined._is_pycapsule + datafusion.user_defined._wrap_session_kwarg_for_udtf + datafusion.user_defined.data_type_or_field_to_field + datafusion.user_defined.data_types_or_fields_to_field_list + + +Module Contents +--------------- + +.. py:class:: Accumulator + + Defines how an :py:class:`AggregateUDF` accumulates values. + + + .. py:method:: evaluate() -> pyarrow.Scalar + :abstractmethod: + + + Return the resultant value. + + While this function template expects a PyArrow Scalar value return type, + you can return any value that can be converted into a Scalar. This + includes basic Python data types such as integers and strings. In + addition to primitive types, we currently support PyArrow, nanoarrow, + and arro3 objects in addition to primitive data types. Other objects + that support the Arrow FFI standard will be given a "best attempt" at + conversion to scalar objects. + + + + .. py:method:: merge(states: list[pyarrow.Array]) -> None + :abstractmethod: + + + Merge a set of states. + + + + .. py:method:: state() -> list[pyarrow.Scalar] + :abstractmethod: + + + Return the current state. + + While this function template expects PyArrow Scalar values return type, + you can return any value that can be converted into a Scalar. This + includes basic Python data types such as integers and strings. In + addition to primitive types, we currently support PyArrow, nanoarrow, + and arro3 objects in addition to primitive data types. Other objects + that support the Arrow FFI standard will be given a "best attempt" at + conversion to scalar objects. + + + + .. py:method:: update(*values: pyarrow.Array) -> None + :abstractmethod: + + + Evaluate an array of values and update state. + + + +.. py:class:: AggregateUDF(name: str, accumulator: collections.abc.Callable[[], Accumulator], input_types: list[pyarrow.DataType], return_type: pyarrow.DataType, state_type: list[pyarrow.DataType], volatility: Volatility | str) + AggregateUDF(name: str, accumulator: AggregateUDFExportable, input_types: None = ..., return_type: None = ..., state_type: None = ..., volatility: None = ...) + + Class for performing scalar user-defined functions (UDF). + + Aggregate UDFs operate on a group of rows and return a single value. See + also :py:class:`ScalarUDF` for operating on a row by row basis. + + Instantiate a user-defined aggregate function (UDAF). + + See :py:func:`udaf` for a convenience function and argument + descriptions. + + + .. py:method:: __call__(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Execute the UDAF. + + This function is not typically called by an end user. These calls will + occur during the evaluation of the dataframe. + + + + .. py:method:: __repr__() -> str + + Print a string representation of the Aggregate UDF. + + + + .. py:method:: _from_internal(internal: datafusion._internal.AggregateUDF) -> AggregateUDF + :classmethod: + + + Wrap an already-constructed internal ``AggregateUDF`` handle. + + Used by :py:meth:`SessionContext.udaf` to surface a function looked + up from the session's function registry without re-running + :py:meth:`__init__`. + + + + .. py:method:: from_pycapsule(func: AggregateUDFExportable | _typeshed.CapsuleType) -> AggregateUDF + :staticmethod: + + + Create an Aggregate UDF from AggregateUDF PyCapsule object. + + This function will instantiate a Aggregate UDF that uses a DataFusion + AggregateUDF that is exported via the FFI bindings. + + + + .. py:method:: udaf(input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, state_type: list[pyarrow.DataType], volatility: Volatility | str, name: str | None = None) -> collections.abc.Callable[Ellipsis, AggregateUDF] + udaf(accum: collections.abc.Callable[[], Accumulator], input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, state_type: list[pyarrow.DataType], volatility: Volatility | str, name: str | None = None) -> AggregateUDF + udaf(accum: AggregateUDFExportable) -> AggregateUDF + udaf(accum: _typeshed.CapsuleType) -> AggregateUDF + :staticmethod: + + + Create a new User-Defined Aggregate Function (UDAF). + + This class allows you to define an aggregate function that can be used in + data aggregation or window function calls. + + Usage: + - As a function: ``udaf(accum, input_types, return_type, state_type, volatility, name)``. + - As a decorator: ``@udaf(input_types, return_type, state_type, volatility, name)``. + When using ``udaf`` as a decorator, do not pass ``accum`` explicitly. + + If your :py:class:`Accumulator` can be instantiated with no arguments, you + can simply pass its type as ``accum``. If you need to pass additional + arguments to its constructor, you can define a lambda or a factory method. + During runtime the :py:class:`Accumulator` will be constructed for every + instance in which this UDAF is used. + + .. rubric:: Examples + + >>> import pyarrow.compute as pc + >>> from datafusion.user_defined import AggregateUDF, Accumulator, udaf + >>> class Summarize(Accumulator): + ... def __init__(self, bias: float = 0.0): + ... self._sum = pa.scalar(bias) + ... def state(self): + ... return [self._sum] + ... def update(self, values): + ... self._sum = pa.scalar( + ... self._sum.as_py() + pc.sum(values).as_py()) + ... def merge(self, states): + ... self._sum = pa.scalar( + ... self._sum.as_py() + pc.sum(states[0]).as_py()) + ... def evaluate(self): + ... return self._sum + + Using ``udaf`` as a function: + + >>> udaf1 = AggregateUDF.udaf( + ... Summarize, pa.float64(), pa.float64(), + ... [pa.float64()], "immutable") + + Wrapping ``udaf`` with a function: + + >>> def sum_bias_10() -> Summarize: + ... return Summarize(10.0) + >>> udaf2 = udaf(sum_bias_10, pa.float64(), pa.float64(), [pa.float64()], + ... "immutable") + + Using ``udaf`` with lambda: + + >>> udaf3 = udaf(lambda: Summarize(20.0), pa.float64(), pa.float64(), + ... [pa.float64()], "immutable") + + Using ``udaf`` as a decorator: + + >>> @AggregateUDF.udaf( + ... pa.float64(), pa.float64(), + ... [pa.float64()], "immutable") + ... def udaf4(): + ... return Summarize(10.0) + + Apply to a dataframe: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [1.0, 2.0, 3.0]}) + >>> df.aggregate([], [udaf1(col("a")).alias("total")]).collect_column( + ... "total")[0].as_py() + 6.0 + >>> df.aggregate([], [udaf2(col("a")).alias("total")]).collect_column( + ... "total")[0].as_py() + 16.0 + >>> df.aggregate([], [udaf3(col("a")).alias("total")]).collect_column( + ... "total")[0].as_py() + 26.0 + >>> df.aggregate([], [udaf4(col("a")).alias("total")]).collect_column( + ... "total")[0].as_py() + 16.0 + + :param accum: The accumulator python function. Only needed when calling as a + function. Skip this argument when using ``udaf`` as a decorator. + If you have a Rust backed AggregateUDF within a PyCapsule, you can + pass this parameter and ignore the rest. They will be determined + directly from the underlying function. See the online documentation + for more information. + :param input_types: The data types of the arguments to ``accum``. + :param return_type: The data type of the return value. + :param state_type: The data types of the intermediate accumulation. + :param volatility: See :py:class:`Volatility` for allowed values. + :param name: A descriptive name for the function. + + :returns: A user-defined aggregate function, which can be used in either data + aggregation or window function calls. + + + + .. py:attribute:: _udaf + + + .. py:property:: name + :type: str + + + Return the registered name of this UDAF. + + For UDAFs imported via the FFI capsule protocol, this is the + name the capsule itself reports — not the ``name`` argument + passed to the constructor (which is ignored on the FFI path). + + +.. py:class:: AggregateUDFExportable + + Bases: :py:obj:`Protocol` + + + Type hint for object that has __datafusion_aggregate_udf__ PyCapsule. + + + .. py:method:: __datafusion_aggregate_udf__() -> object + + +.. py:class:: LogicalExtensionCodecExportable + + Bases: :py:obj:`Protocol` + + + Type hint for objects exposing ``__datafusion_logical_extension_codec__``. + + + .. py:method:: __datafusion_logical_extension_codec__() -> object + + +.. py:class:: PhysicalExtensionCodecExportable + + Bases: :py:obj:`Protocol` + + + Type hint for objects exposing ``__datafusion_physical_extension_codec__``. + + + .. py:method:: __datafusion_physical_extension_codec__() -> object + + +.. py:class:: ScalarUDF(name: str, func: collections.abc.Callable[Ellipsis, _R], input_fields: list[pyarrow.Field], return_field: pyarrow.Field, volatility: Volatility | str) + + Class for performing scalar user-defined functions (UDF). + + Scalar UDFs operate on a row by row basis. See also :py:class:`AggregateUDF` for + operating on a group of rows. + + Instantiate a scalar user-defined function (UDF). + + See helper method :py:func:`udf` for argument details. + + + .. py:method:: __call__(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Execute the UDF. + + This function is not typically called by an end user. These calls will + occur during the evaluation of the dataframe. + + + + .. py:method:: __repr__() -> str + + Print a string representation of the Scalar UDF. + + + + .. py:method:: _from_internal(internal: datafusion._internal.ScalarUDF) -> ScalarUDF + :classmethod: + + + Wrap an already-constructed internal ``ScalarUDF`` handle. + + Used by :py:meth:`SessionContext.udf` to surface a function looked + up from the session's function registry without re-running + :py:meth:`__init__`. + + + + .. py:method:: from_pycapsule(func: ScalarUDFExportable) -> ScalarUDF + :staticmethod: + + + Create a Scalar UDF from ScalarUDF PyCapsule object. + + This function will instantiate a Scalar UDF that uses a DataFusion + ScalarUDF that is exported via the FFI bindings. + + + + .. py:method:: udf(input_fields: collections.abc.Sequence[pyarrow.DataType | pyarrow.Field] | pyarrow.DataType | pyarrow.Field, return_field: pyarrow.DataType | pyarrow.Field, volatility: Volatility | str, name: str | None = None) -> collections.abc.Callable[Ellipsis, ScalarUDF] + udf(func: collections.abc.Callable[Ellipsis, _R], input_fields: collections.abc.Sequence[pyarrow.DataType | pyarrow.Field] | pyarrow.DataType | pyarrow.Field, return_field: pyarrow.DataType | pyarrow.Field, volatility: Volatility | str, name: str | None = None) -> ScalarUDF + udf(func: ScalarUDFExportable) -> ScalarUDF + :staticmethod: + + + Create a new User-Defined Function (UDF). + + This class can be used both as either a function or a decorator. + + Usage: + - As a function: ``udf(func, input_fields, return_field, + volatility, name)``. + - As a decorator: ``@udf(input_fields, return_field, volatility, name)``. + When used a decorator, do **not** pass ``func`` explicitly. + + In lieu of passing a PyArrow Field, you can pass a DataType for simplicity. + When you do so, it will be assumed that the nullability of the inputs and + output are True and that they have no metadata. + + :param func: Only needed when calling as a function. + Skip this argument when using `udf` as a decorator. If you have a Rust + backed ScalarUDF within a PyCapsule, you can pass this parameter + and ignore the rest. They will be determined directly from the + underlying function. See the online documentation for more information. + :type func: Callable, optional + :param input_fields: The data types or Fields + of the arguments to ``func``. This list must be of the same length + as the number of arguments. + :type input_fields: list[pa.Field | pa.DataType] + :param return_field: The field of the return value + from the function. + :type return_field: pa.DataType | pa.Field + :param volatility: See `Volatility` for allowed values. + :type volatility: Volatility | str + :param name: A descriptive name for the function. + :type name: Optional[str] + + :returns: A user-defined function that can be used in SQL expressions, + data aggregation, or window function calls. + + .. rubric:: Examples + + Using ``udf`` as a function: + + >>> import pyarrow.compute as pc + >>> from datafusion.user_defined import ScalarUDF + >>> def double_func(x): + ... return pc.multiply(x, 2) + >>> double_udf = ScalarUDF.udf( + ... double_func, [pa.int64()], pa.int64(), + ... "volatile", "double_it") + + Using ``udf`` as a decorator: + + >>> @ScalarUDF.udf([pa.int64()], pa.int64(), "volatile") + ... def decorator_double_udf(x): + ... return pc.multiply(x, 3) + + Apply to a dataframe: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"x": [1, 2, 3]}) + >>> df.select(double_udf(col("x")).alias("result")).to_pydict() + {'result': [2, 4, 6]} + >>> df.select(decorator_double_udf(col("x")).alias("result")).to_pydict() + {'result': [3, 6, 9]} + + + + .. py:attribute:: _udf + + + .. py:property:: name + :type: str + + + Return the registered name of this UDF. + + For UDFs imported via the FFI capsule protocol, this is the + name the capsule itself reports — not the ``name`` argument + passed to the constructor (which is ignored on the FFI path). + + .. rubric:: Examples + + >>> import pyarrow as pa + >>> from datafusion import udf + >>> double = udf( + ... lambda arr: pa.array([(v.as_py() or 0) * 2 for v in arr]), + ... [pa.int64()], + ... pa.int64(), + ... volatility="immutable", + ... name="double", + ... ) + >>> double.name + 'double' + + +.. py:class:: ScalarUDFExportable + + Bases: :py:obj:`Protocol` + + + Type hint for object that has __datafusion_scalar_udf__ PyCapsule. + + + .. py:method:: __datafusion_scalar_udf__() -> object + + +.. py:class:: TableFunction(name: str, func: collections.abc.Callable[Ellipsis, Any], ctx: datafusion.SessionContext | None = None, *, with_session: bool = False) + + Class for performing user-defined table functions (UDTF). + + Table functions generate new table providers based on the + input expressions. + + Instantiate a user-defined table function (UDTF). + + Set ``with_session=True`` to have the calling + :class:`SessionContext` passed as a ``session`` keyword argument + on each invocation. Use it inside the callback to look up + registered tables, UDFs, or session configuration. When + ``with_session`` is ``False`` (the default), ``func`` is invoked + with the positional expression arguments only. + + ``with_session=True`` is only supported for pure-Python callables. + Passing it together with an FFI-exported table function (one + exposing ``__datafusion_table_function__``) raises + :class:`TypeError`. + + Registry mutations performed through the injected session (such + as registering tables or UDFs) propagate to the caller's + :class:`SessionContext` because the registries are shared. + Configuration changes do **not** propagate; the wrapper holds + its own clone of the session config. + + See :py:func:`udtf` for a convenience function and argument + descriptions. + + + .. py:method:: __call__(*args: datafusion.expr.Expr) -> Any + + Execute the UDTF and return a table provider. + + + + .. py:method:: __repr__() -> str + + User printable representation. + + + + .. py:method:: _create_table_udf(func: collections.abc.Callable[Ellipsis, Any], name: str, *, with_session: bool = False) -> TableFunction + :staticmethod: + + + Create a TableFunction instance from function arguments. + + + + .. py:method:: _create_table_udf_decorator(name: str | None = None, *, with_session: bool = False) -> collections.abc.Callable[[collections.abc.Callable[Ellipsis, Any]], TableFunction] + :staticmethod: + + + Create a decorator for a TableFunction. + + + + .. py:method:: udtf(name: str, *, with_session: bool = False) -> collections.abc.Callable[Ellipsis, Any] + udtf(func: collections.abc.Callable[Ellipsis, Any], name: str, *, with_session: bool = False) -> TableFunction + :staticmethod: + + + Create a new User-Defined Table Function (UDTF). + + Pass ``with_session=True`` to have the calling + :class:`SessionContext` injected as a ``session`` keyword + argument on each invocation. + + + + .. py:attribute:: _udtf + + +.. py:class:: Volatility + + Bases: :py:obj:`enum.Enum` + + + Defines how stable or volatile a function is. + + When setting the volatility of a function, you can either pass this + enumeration or a ``str``. The ``str`` equivalent is the lower case value of the + name (`"immutable"`, `"stable"`, or `"volatile"`). + + + .. py:method:: __str__() -> str + + Returns the string equivalent. + + + + .. py:attribute:: Immutable + :value: 1 + + + An immutable function will always return the same output when given the + same input. + + DataFusion will attempt to inline immutable functions during planning. + + + .. py:attribute:: Stable + :value: 2 + + + Returns the same value for a given input within a single queries. + + A stable function may return different values given the same input across + different queries but must return the same value for a given input within a + query. An example of this is the ``Now`` function. DataFusion will attempt to + inline ``Stable`` functions during planning, when possible. For query + ``select col1, now() from t1``, it might take a while to execute but ``now()`` + column will be the same for each output row, which is evaluated during + planning. + + + .. py:attribute:: Volatile + :value: 3 + + + A volatile function may change the return value from evaluation to + evaluation. + + Multiple invocations of a volatile function may return different results + when used in the same query. An example of this is the random() function. + DataFusion can not evaluate such functions during planning. In the query + ``select col1, random() from t1``, ``random()`` function will be evaluated + for each output row, resulting in a unique random value for each row. + + +.. py:class:: WindowEvaluator + + Evaluator class for user-defined window functions (UDWF). + + It is up to the user to decide which evaluate function is appropriate. + + +------------------------+--------------------------------+------------------+---------------------------+ + | ``uses_window_frame`` | ``supports_bounded_execution`` | ``include_rank`` | function_to_implement | + +========================+================================+==================+===========================+ + | False (default) | False (default) | False (default) | ``evaluate_all`` | + +------------------------+--------------------------------+------------------+---------------------------+ + | False | True | False | ``evaluate`` | + +------------------------+--------------------------------+------------------+---------------------------+ + | False | True/False | True | ``evaluate_all_with_rank``| + +------------------------+--------------------------------+------------------+---------------------------+ + | True | True/False | True/False | ``evaluate`` | + +------------------------+--------------------------------+------------------+---------------------------+ + + + .. py:method:: evaluate(values: list[pyarrow.Array], eval_range: tuple[int, int]) -> pyarrow.Scalar + + Evaluate window function on a range of rows in an input partition. + + This is the simplest and most general function to implement + but also the least performant as it creates output one row at + a time. It is typically much faster to implement stateful + evaluation using one of the other specialized methods on this + trait. + + Returns a [`ScalarValue`] that is the value of the window + function within `range` for the entire partition. Argument + `values` contains the evaluation result of function arguments + and evaluation results of ORDER BY expressions. If function has a + single argument, `values[1..]` will contain ORDER BY expression results. + + + + .. py:method:: evaluate_all(values: list[pyarrow.Array], num_rows: int) -> pyarrow.Array + + Evaluate a window function on an entire input partition. + + This function is called once per input *partition* for window functions that + *do not use* values from the window frame, such as + :py:func:`~datafusion.functions.row_number`, + :py:func:`~datafusion.functions.rank`, + :py:func:`~datafusion.functions.dense_rank`, + :py:func:`~datafusion.functions.percent_rank`, + :py:func:`~datafusion.functions.cume_dist`, + :py:func:`~datafusion.functions.lead`, + and :py:func:`~datafusion.functions.lag`. + + It produces the result of all rows in a single pass. It + expects to receive the entire partition as the ``value`` and + must produce an output column with one output row for every + input row. + + ``num_rows`` is required to correctly compute the output in case + ``len(values) == 0`` + + Implementing this function is an optimization. Certain window + functions are not affected by the window frame definition or + the query doesn't have a frame, and ``evaluate`` skips the + (costly) window frame boundary calculation and the overhead of + calling ``evaluate`` for each output row. + + For example, the `LAG` built in window function does not use + the values of its window frame (it can be computed in one shot + on the entire partition with ``Self::evaluate_all`` regardless of the + window defined in the ``OVER`` clause) + + .. code-block:: text + + lag(x, 1) OVER (ORDER BY z ROWS BETWEEN 2 PRECEDING AND 3 FOLLOWING) + + However, ``avg()`` computes the average in the window and thus + does use its window frame. + + .. code-block:: text + + avg(x) OVER (PARTITION BY y ORDER BY z ROWS BETWEEN 2 PRECEDING AND 3 FOLLOWING) + + + + .. py:method:: evaluate_all_with_rank(num_rows: int, ranks_in_partition: list[tuple[int, int]]) -> pyarrow.Array + + Called for window functions that only need the rank of a row. + + Evaluate the partition evaluator against the partition using + the row ranks. For example, ``rank(col("a"))`` produces + + .. code-block:: text + + a | rank + - + ---- + A | 1 + A | 1 + C | 3 + D | 4 + D | 4 + + For this case, `num_rows` would be `5` and the + `ranks_in_partition` would be called with + + .. code-block:: text + + [ + (0,1), + (2,2), + (3,4), + ] + + The user must implement this method if ``include_rank`` returns True. + + + + .. py:method:: get_range(idx: int, num_rows: int) -> tuple[int, int] + + Return the range for the window function. + + If `uses_window_frame` flag is `false`. This method is used to + calculate required range for the window function during + stateful execution. + + Generally there is no required range, hence by default this + returns smallest range(current row). e.g seeing current row is + enough to calculate window result (such as row_number, rank, + etc) + + :param idx:: Current index: + :param num_rows: Number of rows. + + + + .. py:method:: include_rank() -> bool + + Can this function be evaluated with (only) rank? + + + + .. py:method:: is_causal() -> bool + + Get whether evaluator needs future data for its result. + + + + .. py:method:: memoize() -> None + + Perform a memoize operation to improve performance. + + When the window frame has a fixed beginning (e.g UNBOUNDED + PRECEDING), some functions such as FIRST_VALUE and + NTH_VALUE do not need the (unbounded) input once they have + seen a certain amount of input. + + `memoize` is called after each input batch is processed, and + such functions can save whatever they need + + + + .. py:method:: supports_bounded_execution() -> bool + + Can the window function be incrementally computed using bounded memory? + + + + .. py:method:: uses_window_frame() -> bool + + Does the window function use the values from the window frame? + + + +.. py:class:: WindowUDF(name: str, func: collections.abc.Callable[[], WindowEvaluator], input_types: list[pyarrow.DataType], return_type: pyarrow.DataType, volatility: Volatility | str) + + Class for performing window user-defined functions (UDF). + + Window UDFs operate on a partition of rows. See + also :py:class:`ScalarUDF` for operating on a row by row basis. + + Instantiate a user-defined window function (UDWF). + + See :py:func:`udwf` for a convenience function and argument + descriptions. + + + .. py:method:: __call__(*args: datafusion.expr.Expr) -> datafusion.expr.Expr + + Execute the UDWF. + + This function is not typically called by an end user. These calls will + occur during the evaluation of the dataframe. + + + + .. py:method:: __repr__() -> str + + Print a string representation of the Window UDF. + + + + .. py:method:: _create_window_udf(func: collections.abc.Callable[[], WindowEvaluator], input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, volatility: Volatility | str, name: str | None = None) -> WindowUDF + :staticmethod: + + + Create a WindowUDF instance from function arguments. + + + + .. py:method:: _create_window_udf_decorator(input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, volatility: Volatility | str, name: str | None = None) -> collections.abc.Callable[[collections.abc.Callable[[], WindowEvaluator]], collections.abc.Callable[Ellipsis, datafusion.expr.Expr]] + :staticmethod: + + + Create a decorator for a WindowUDF. + + + + .. py:method:: _from_internal(internal: datafusion._internal.WindowUDF) -> WindowUDF + :classmethod: + + + Wrap an already-constructed internal ``WindowUDF`` handle. + + Used by :py:meth:`SessionContext.udwf` to surface a function looked + up from the session's function registry without re-running + :py:meth:`__init__`. + + + + .. py:method:: _get_default_name(func: collections.abc.Callable) -> str + :staticmethod: + + + Get the default name for a function based on its attributes. + + + + .. py:method:: _normalize_input_types(input_types: pyarrow.DataType | list[pyarrow.DataType]) -> list[pyarrow.DataType] + :staticmethod: + + + Convert a single DataType to a list if needed. + + + + .. py:method:: from_pycapsule(func: WindowUDFExportable) -> WindowUDF + :staticmethod: + + + Create a Window UDF from WindowUDF PyCapsule object. + + This function will instantiate a Window UDF that uses a DataFusion + WindowUDF that is exported via the FFI bindings. + + + + .. py:method:: udwf(input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, volatility: Volatility | str, name: str | None = None) -> collections.abc.Callable[Ellipsis, WindowUDF] + udwf(func: collections.abc.Callable[[], WindowEvaluator], input_types: pyarrow.DataType | list[pyarrow.DataType], return_type: pyarrow.DataType, volatility: Volatility | str, name: str | None = None) -> WindowUDF + :staticmethod: + + + Create a new User-Defined Window Function (UDWF). + + This class can be used both as either a function or a decorator. + + Usage: + - As a function: ``udwf(func, input_types, return_type, volatility, name)``. + - As a decorator: ``@udwf(input_types, return_type, volatility, name)``. + When using ``udwf`` as a decorator, do not pass ``func`` explicitly. + + .. rubric:: Examples + + >>> from datafusion.user_defined import WindowUDF, WindowEvaluator, udwf + >>> class BiasedNumbers(WindowEvaluator): + ... def __init__(self, start: int = 0): + ... self.start = start + ... def evaluate_all(self, values, num_rows): + ... return pa.array( + ... [self.start + i for i in range(num_rows)]) + + Using ``udwf`` as a function: + + >>> udwf1 = WindowUDF.udwf( + ... BiasedNumbers, pa.int64(), pa.int64(), "immutable") + >>> def bias_10() -> BiasedNumbers: + ... return BiasedNumbers(10) + >>> udwf2 = udwf(bias_10, pa.int64(), pa.int64(), "immutable") + >>> udwf3 = udwf( + ... lambda: BiasedNumbers(20), pa.int64(), pa.int64(), "immutable" + ... ) + + Using ``udwf`` as a decorator: + + >>> @WindowUDF.udwf(pa.int64(), pa.int64(), "immutable") + ... def biased_numbers(): + ... return BiasedNumbers(10) + + Apply to a dataframe: + + >>> ctx = dfn.SessionContext() + >>> df = ctx.from_pydict({"a": [10, 20, 30]}) + >>> df.select(udwf1(col("a")).alias("result")).to_pydict() + {'result': [0, 1, 2]} + >>> df.select(udwf2(col("a")).alias("result")).to_pydict() + {'result': [10, 11, 12]} + >>> df.select(udwf3(col("a")).alias("result")).to_pydict() + {'result': [20, 21, 22]} + >>> df.select(biased_numbers(col("a")).alias("result")).to_pydict() + {'result': [10, 11, 12]} + + :param func: Only needed when calling as a function. Skip this argument when + using ``udwf`` as a decorator. If you have a Rust backed WindowUDF + within a PyCapsule, you can pass this parameter and ignore the rest. + They will be determined directly from the underlying function. See + the online documentation for more information. + :param input_types: The data types of the arguments. + :param return_type: The data type of the return value. + :param volatility: See :py:class:`Volatility` for allowed values. + :param name: A descriptive name for the function. + + :returns: A user-defined window function that can be used in window function calls. + + + + .. py:attribute:: _udwf + + + .. py:property:: name + :type: str + + + Return the registered name of this UDWF. + + For UDWFs imported via the FFI capsule protocol, this is the + name the capsule itself reports — not the ``name`` argument + passed to the constructor (which is ignored on the FFI path). + + +.. py:class:: WindowUDFExportable + + Bases: :py:obj:`Protocol` + + + Type hint for object that has __datafusion_window_udf__ PyCapsule. + + + .. py:method:: __datafusion_window_udf__() -> object + + +.. py:function:: _is_pycapsule(value: object) -> TypeGuard[_typeshed.CapsuleType] + + Return ``True`` when ``value`` is a CPython ``PyCapsule``. + + +.. py:function:: _wrap_session_kwarg_for_udtf(func: collections.abc.Callable[Ellipsis, Any]) -> collections.abc.Callable[Ellipsis, Any] + + Adapt the raw internal session pyo3 object back to a Python wrapper. + + The Rust call site forwards a ``datafusion._internal.SessionContext``, + but UDTF authors expect to interact with the public + :class:`datafusion.SessionContext` wrapper. This closure wraps the + internal object once per call before delegating to ``func``. + + +.. py:function:: data_type_or_field_to_field(value: pyarrow.DataType | pyarrow.Field, name: str) -> pyarrow.Field + + Helper function to return a Field from either a Field or DataType. + + +.. py:function:: data_types_or_fields_to_field_list(inputs: collections.abc.Sequence[pyarrow.Field | pyarrow.DataType] | pyarrow.Field | pyarrow.DataType) -> list[pyarrow.Field] + + Helper function to return a list of Fields. + + +.. py:data:: _R + +.. py:data:: udaf + +.. py:data:: udf + +.. py:data:: udtf + +.. py:data:: udwf + diff --git a/_sources/autoapi/index.rst.txt b/_sources/autoapi/index.rst.txt new file mode 100644 index 000000000..5c5423444 --- /dev/null +++ b/_sources/autoapi/index.rst.txt @@ -0,0 +1,11 @@ +API Reference +============= + +This page contains auto-generated API reference documentation [#f1]_. + +.. toctree:: + :titlesonly: + + /autoapi/datafusion/index + +.. [#f1] Created with `sphinx-autoapi `_ \ No newline at end of file diff --git a/docs/source/contributor-guide/ffi.md b/_sources/contributor-guide/ffi.md.txt similarity index 100% rename from docs/source/contributor-guide/ffi.md rename to _sources/contributor-guide/ffi.md.txt diff --git a/docs/source/contributor-guide/index.md b/_sources/contributor-guide/index.md.txt similarity index 100% rename from docs/source/contributor-guide/index.md rename to _sources/contributor-guide/index.md.txt diff --git a/docs/source/contributor-guide/introduction.md b/_sources/contributor-guide/introduction.md.txt similarity index 100% rename from docs/source/contributor-guide/introduction.md rename to _sources/contributor-guide/introduction.md.txt diff --git a/docs/source/index.md b/_sources/index.md.txt similarity index 100% rename from docs/source/index.md rename to _sources/index.md.txt diff --git a/docs/source/links.md b/_sources/links.md.txt similarity index 100% rename from docs/source/links.md rename to _sources/links.md.txt diff --git a/docs/source/user-guide/ai-coding-assistants.md b/_sources/user-guide/ai-coding-assistants.md.txt similarity index 100% rename from docs/source/user-guide/ai-coding-assistants.md rename to _sources/user-guide/ai-coding-assistants.md.txt diff --git a/docs/source/user-guide/basics.md b/_sources/user-guide/basics.md.txt similarity index 100% rename from docs/source/user-guide/basics.md rename to _sources/user-guide/basics.md.txt diff --git a/docs/source/user-guide/common-operations/aggregations.md b/_sources/user-guide/common-operations/aggregations.md.txt similarity index 100% rename from docs/source/user-guide/common-operations/aggregations.md rename to _sources/user-guide/common-operations/aggregations.md.txt diff --git a/docs/source/user-guide/common-operations/basic-info.md b/_sources/user-guide/common-operations/basic-info.md.txt similarity index 100% rename from docs/source/user-guide/common-operations/basic-info.md rename to _sources/user-guide/common-operations/basic-info.md.txt diff --git a/docs/source/user-guide/common-operations/expressions.md b/_sources/user-guide/common-operations/expressions.md.txt similarity index 100% rename from docs/source/user-guide/common-operations/expressions.md rename to _sources/user-guide/common-operations/expressions.md.txt diff --git a/docs/source/user-guide/common-operations/functions.md b/_sources/user-guide/common-operations/functions.md.txt similarity index 100% rename from docs/source/user-guide/common-operations/functions.md rename to _sources/user-guide/common-operations/functions.md.txt diff --git a/docs/source/user-guide/common-operations/index.md b/_sources/user-guide/common-operations/index.md.txt similarity index 100% rename from docs/source/user-guide/common-operations/index.md rename to _sources/user-guide/common-operations/index.md.txt diff --git a/docs/source/user-guide/common-operations/joins.md b/_sources/user-guide/common-operations/joins.md.txt similarity index 100% rename from docs/source/user-guide/common-operations/joins.md rename to _sources/user-guide/common-operations/joins.md.txt diff --git a/docs/source/user-guide/common-operations/select-and-filter.md b/_sources/user-guide/common-operations/select-and-filter.md.txt similarity index 100% rename from docs/source/user-guide/common-operations/select-and-filter.md rename to _sources/user-guide/common-operations/select-and-filter.md.txt diff --git a/docs/source/user-guide/common-operations/spark-functions.md b/_sources/user-guide/common-operations/spark-functions.md.txt similarity index 100% rename from docs/source/user-guide/common-operations/spark-functions.md rename to _sources/user-guide/common-operations/spark-functions.md.txt diff --git a/docs/source/user-guide/common-operations/udf-and-udfa.md b/_sources/user-guide/common-operations/udf-and-udfa.md.txt similarity index 100% rename from docs/source/user-guide/common-operations/udf-and-udfa.md rename to _sources/user-guide/common-operations/udf-and-udfa.md.txt diff --git a/docs/source/user-guide/common-operations/views.md b/_sources/user-guide/common-operations/views.md.txt similarity index 100% rename from docs/source/user-guide/common-operations/views.md rename to _sources/user-guide/common-operations/views.md.txt diff --git a/docs/source/user-guide/common-operations/windows.md b/_sources/user-guide/common-operations/windows.md.txt similarity index 100% rename from docs/source/user-guide/common-operations/windows.md rename to _sources/user-guide/common-operations/windows.md.txt diff --git a/docs/source/user-guide/configuration.md b/_sources/user-guide/configuration.md.txt similarity index 100% rename from docs/source/user-guide/configuration.md rename to _sources/user-guide/configuration.md.txt diff --git a/docs/source/user-guide/data-sources.md b/_sources/user-guide/data-sources.md.txt similarity index 100% rename from docs/source/user-guide/data-sources.md rename to _sources/user-guide/data-sources.md.txt diff --git a/docs/source/user-guide/dataframe/execution-metrics.md b/_sources/user-guide/dataframe/execution-metrics.md.txt similarity index 100% rename from docs/source/user-guide/dataframe/execution-metrics.md rename to _sources/user-guide/dataframe/execution-metrics.md.txt diff --git a/docs/source/user-guide/dataframe/index.md b/_sources/user-guide/dataframe/index.md.txt similarity index 100% rename from docs/source/user-guide/dataframe/index.md rename to _sources/user-guide/dataframe/index.md.txt diff --git a/docs/source/user-guide/dataframe/rendering.md b/_sources/user-guide/dataframe/rendering.md.txt similarity index 100% rename from docs/source/user-guide/dataframe/rendering.md rename to _sources/user-guide/dataframe/rendering.md.txt diff --git a/docs/source/user-guide/distributing-work.md b/_sources/user-guide/distributing-work.md.txt similarity index 100% rename from docs/source/user-guide/distributing-work.md rename to _sources/user-guide/distributing-work.md.txt diff --git a/docs/source/user-guide/index.md b/_sources/user-guide/index.md.txt similarity index 100% rename from docs/source/user-guide/index.md rename to _sources/user-guide/index.md.txt diff --git a/docs/source/user-guide/introduction.md b/_sources/user-guide/introduction.md.txt similarity index 100% rename from docs/source/user-guide/introduction.md rename to _sources/user-guide/introduction.md.txt diff --git a/docs/source/user-guide/io/arrow.md b/_sources/user-guide/io/arrow.md.txt similarity index 100% rename from docs/source/user-guide/io/arrow.md rename to _sources/user-guide/io/arrow.md.txt diff --git a/docs/source/user-guide/io/avro.md b/_sources/user-guide/io/avro.md.txt similarity index 100% rename from docs/source/user-guide/io/avro.md rename to _sources/user-guide/io/avro.md.txt diff --git a/docs/source/user-guide/io/csv.md b/_sources/user-guide/io/csv.md.txt similarity index 100% rename from docs/source/user-guide/io/csv.md rename to _sources/user-guide/io/csv.md.txt diff --git a/docs/source/user-guide/io/index.md b/_sources/user-guide/io/index.md.txt similarity index 100% rename from docs/source/user-guide/io/index.md rename to _sources/user-guide/io/index.md.txt diff --git a/docs/source/user-guide/io/json.md b/_sources/user-guide/io/json.md.txt similarity index 100% rename from docs/source/user-guide/io/json.md rename to _sources/user-guide/io/json.md.txt diff --git a/docs/source/user-guide/io/parquet.md b/_sources/user-guide/io/parquet.md.txt similarity index 100% rename from docs/source/user-guide/io/parquet.md rename to _sources/user-guide/io/parquet.md.txt diff --git a/docs/source/user-guide/io/table_provider.md b/_sources/user-guide/io/table_provider.md.txt similarity index 100% rename from docs/source/user-guide/io/table_provider.md rename to _sources/user-guide/io/table_provider.md.txt diff --git a/docs/source/user-guide/sql.md b/_sources/user-guide/sql.md.txt similarity index 100% rename from docs/source/user-guide/sql.md rename to _sources/user-guide/sql.md.txt diff --git a/docs/source/user-guide/upgrade-guides.md b/_sources/user-guide/upgrade-guides.md.txt similarity index 100% rename from docs/source/user-guide/upgrade-guides.md rename to _sources/user-guide/upgrade-guides.md.txt diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 000000000..7ebbd6d07 --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,914 @@ +/* + * Sphinx stylesheet -- basic theme. + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin-top: 10px; +} + +ul.search li { + padding: 5px 0; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 360px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 000000000..0398ebb9f --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,149 @@ +/* + * Base JavaScript utilities for all Sphinx HTML documentation. + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 000000000..7e4c114f2 --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/docs/source/_static/favicon.svg b/_static/favicon.svg similarity index 100% rename from docs/source/_static/favicon.svg rename to _static/favicon.svg diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 000000000..a858a410e Binary files /dev/null and b/_static/file.png differ diff --git a/_static/graphviz.css b/_static/graphviz.css new file mode 100644 index 000000000..30f3837b6 --- /dev/null +++ b/_static/graphviz.css @@ -0,0 +1,12 @@ +/* + * Sphinx stylesheet -- graphviz extension. + */ + +img.graphviz { + border: 0; + max-width: 100%; +} + +object.graphviz { + max-width: 100%; +} diff --git a/docs/source/_static/images/2x_bgwhite_original.png b/_static/images/2x_bgwhite_original.png similarity index 100% rename from docs/source/_static/images/2x_bgwhite_original.png rename to _static/images/2x_bgwhite_original.png diff --git a/docs/source/_static/images/original.png b/_static/images/original.png similarity index 100% rename from docs/source/_static/images/original.png rename to _static/images/original.png diff --git a/docs/source/_static/images/original.svg b/_static/images/original.svg similarity index 100% rename from docs/source/_static/images/original.svg rename to _static/images/original.svg diff --git a/docs/source/_static/images/original2x.png b/_static/images/original2x.png similarity index 100% rename from docs/source/_static/images/original2x.png rename to _static/images/original2x.png diff --git a/docs/source/_static/images/original_dark.svg b/_static/images/original_dark.svg similarity index 100% rename from docs/source/_static/images/original_dark.svg rename to _static/images/original_dark.svg diff --git a/_static/language_data.js b/_static/language_data.js new file mode 100644 index 000000000..c7fe6c6fa --- /dev/null +++ b/_static/language_data.js @@ -0,0 +1,192 @@ +/* + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, if available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/_static/minus.png b/_static/minus.png new file mode 100644 index 000000000..d96755fda Binary files /dev/null and b/_static/minus.png differ diff --git a/_static/mystnb.11b39860a7a0cbfd473a3ad8a317855267ff0bd372690045ca344a6b62be495e.css b/_static/mystnb.11b39860a7a0cbfd473a3ad8a317855267ff0bd372690045ca344a6b62be495e.css new file mode 100644 index 000000000..9f3d7646b --- /dev/null +++ b/_static/mystnb.11b39860a7a0cbfd473a3ad8a317855267ff0bd372690045ca344a6b62be495e.css @@ -0,0 +1,2449 @@ +/* Dark mode support: + * if (e.g. Furo theme) or (e.g. PyData theme) has `data-theme` set, respect it. + * else default to the system color scheme + */ +@media (prefers-color-scheme: dark) { + :root { + --light: ; + --dark: initial; + } +} + +@media (prefers-color-scheme: light) { + :root { + --dark: ; + --light: initial; + } +} + +:is(html, body)[data-theme="dark"] { + --light: ; + --dark: initial; +} + +:is(html, body)[data-theme="light"] { + --dark: ; + --light: initial; +} + +/* Variables */ +:root { + /* + Following palettes are generated by using https://m2.material.io/design/color/the-color-system.html#tools-for-picking-colors + - neutral palette with #fcfcfc and danger palette with #ffdddd as base colors. + 50 means lightest, 900 means darkest; less used intermediate shades are omitted + but can be added when needed by accessing full palette from the above link. + */ + --mystnb-neutral-palette-50: #fcfcfc; + --mystnb-neutral-palette-100: #f7f7f7; + --mystnb-neutral-palette-400: #cccccc; + --mystnb-neutral-palette-500: #afafaf; + --mystnb-neutral-palette-800: #505050; + --mystnb-neutral-palette-900: #2d2d2d; + + --mystnb-danger-palette-50: #ffdddd; + --mystnb-danger-palette-100: #f5acad; + --mystnb-danger-palette-400: #c42029; + --mystnb-danger-palette-500: #b40008; + --mystnb-danger-palette-800: #850010; + --mystnb-danger-palette-900: #680010; + + /* MyST-NB specific variables; colors should be logically picked from palettes */ + --mystnb-source-bg-color: var(--light, var(--mystnb-neutral-palette-100)) var(--dark, var(--mystnb-neutral-palette-800)); + --mystnb-stdout-bg-color: var(--light, var(--mystnb-neutral-palette-50)) var(--dark, var(--mystnb-neutral-palette-900)); + --mystnb-stderr-bg-color: var(--light, var(--mystnb-danger-palette-50)) var(--dark, var(--mystnb-danger-palette-900)); + --mystnb-traceback-bg-color: var(--light, var(--mystnb-neutral-palette-50)) var(--dark, var(--mystnb-neutral-palette-900)); + --mystnb-source-border-color: var(--light, var(--mystnb-neutral-palette-400)) var(--dark, var(--mystnb-neutral-palette-500)); + --mystnb-source-margin-color: green; + --mystnb-stdout-border-color: var(--light, var(--mystnb-neutral-palette-100)) var(--dark, var(--mystnb-neutral-palette-800)); + --mystnb-stderr-border-color: var(--light, var(--mystnb-neutral-palette-100)) var(--dark, var(--mystnb-neutral-palette-800)); + --mystnb-traceback-border-color: var(--light, var(--mystnb-danger-palette-100)) var(--dark, var(--mystnb-danger-palette-800)); + --mystnb-hide-prompt-opacity: 70%; + --mystnb-source-border-radius: .4em; + --mystnb-source-border-width: 1px; + --mystnb-scrollbar-width: 0.3rem; + --mystnb-scrollbar-height: 0.3rem; + --mystnb-scrollbar-thumb-color: var(--light, var(--mystnb-neutral-palette-400)) var(--dark, var(--mystnb-neutral-palette-500)); + --mystnb-scrollbar-thumb-hover-color: var(--light, var(--mystnb-neutral-palette-500)) var(--dark, var(--mystnb-neutral-palette-400)); + --mystnb-scrollbar-thumb-border-radius: 0.25rem; +} + + +/* Whole cell */ +div.container.cell { + padding-left: 0; + margin-bottom: 1em; +} + +/* Removing all background formatting so we can control at the div level */ +.cell_input div.highlight, +.cell_output pre, +.cell_input pre, +.cell_output .output { + border: none; + box-shadow: none; +} + +.cell_output .output pre, +.cell_input pre { + margin: 0px; +} + +/* Input cells */ +div.cell > div.cell_input { + padding-left: 0em; + padding-right: 0em; + border: var(--mystnb-source-border-width) var(--mystnb-source-border-color) solid; + background-color: var(--mystnb-source-bg-color); + border-left-color: var(--mystnb-source-margin-color); + border-left-width: medium; + border-radius: var(--mystnb-source-border-radius); +} + +div.cell_input>div, +div.cell_output div.output>div.highlight { + margin: 0em !important; + border: none !important; +} + +/* All cell outputs */ +.cell_output { + padding-left: 1em; + padding-right: 0em; + margin-top: 1em; +} + +/* Text outputs from cells */ +.cell_output .output.text_plain, +.cell_output .output.traceback, +.cell_output .output.stream, +.cell_output .output.stderr { + margin-top: 1em; + margin-bottom: 0em; + box-shadow: none; +} + +.cell_output .output.text_plain:not(:has(.highlight)), +.cell_output .output.stream:not(:has(.highlight)) { + /* plain (or stream of) output, not containing a pygments-highlighted block */ + background: var(--mystnb-stdout-bg-color); + border: 1px solid var(--mystnb-stdout-border-color); +} + +.cell_output .output.stderr { + background: var(--mystnb-stderr-bg-color); + border: 1px solid var(--mystnb-stderr-border-color); +} + +.cell_output .output.traceback { + background: var(--mystnb-traceback-bg-color); + border: 1px solid var(--mystnb-traceback-border-color); +} + +/* --- Collapsible cell content --- */ + +/* +encourage summary container to blend in with its parent. +p.admonition-title should hold the title styles. +*/ +div.cell details.hide summary { + border-left: unset; + padding: inherit; + margin: inherit; + background-color: inherit; +} + +/* Neighboring input/output elements - spacing, borders */ +div.cell details.hide.above-input + details.below-input, +div.cell div.cell_input + details.below-input +{ + margin-top: 0; +} + +div.cell details.hide.above-input:has(+ details.below-input), +div.cell div.cell_input:has(+ details.below-input) +{ + margin-bottom: 0; +} + +div.cell:has(> *:nth-child(2)) div.cell_input:first-child, +div.cell:has(> *:nth-child(2)) details:first-child +{ + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +div.cell:has(> *:nth-child(2)) div.cell_input:last-child, +div.cell:has(> *:nth-child(2)) details:last-child +{ + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +/* intra-label styles for collapsibles */ +div.cell.container details.hide.above-input>summary, +div.cell.container details.hide.below-input>summary, +div.cell.container details.hide.above-output>summary +{ + display: block; + border-left: none; +} + +div.cell details.hide>summary>p.admonition-title { + display: list-item; + margin-bottom: 0; +} + +div.cell details.hide:not([open]) { + padding-bottom: 0; +} + +div.cell details.hide[open]>summary>p.collapsed { + display: none; +} + +div.cell details.hide:not([open])>summary>p.expanded { + display: none; +} + +@keyframes collapsed-fade-in { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} +div.cell details.hide[open]>summary~* { + -moz-animation: collapsed-fade-in 0.3s ease-in-out; + -webkit-animation: collapsed-fade-in 0.3s ease-in-out; + animation: collapsed-fade-in 0.3s ease-in-out; +} + +/* Clear conflicting styles for details and admonitions set by some themes */ +div.cell details.admonition summary::before { + content: unset; +} + +/* Math align to the left */ +.cell_output .MathJax_Display { + text-align: left !important; +} + +/** source code line numbers **/ +span.linenos { + opacity: 0.5; +} + +/* Inline text from `paste` operation */ + +span.pasted-text { + font-weight: bold; +} + +span.pasted-inline img { + max-height: 2em; +} + +tbody span.pasted-inline img { + max-height: none; +} + + +/* Adding scroll bars if tags: output_scroll, scroll-output, and scroll-input + * On screens, we want to scroll, but on print show all + * + * It was before in https://github.com/executablebooks/sphinx-book-theme/blob/eb1b6baf098b27605e8f2b7b2979b17ebf1b9540/src/sphinx_book_theme/assets/styles/extensions/_myst-nb.scss +*/ +div.cell:is( + .tag_output_scroll, + .tag_scroll-output, + .config_scroll_outputs + ) + div.cell_output, +div.cell.tag_scroll-input div.cell_input { + max-height: 24em; + overflow-y: auto; + max-width: 100%; + overflow-x: auto; +} + +div.cell.config_scroll_outputs div.cell_output:has(img) { + /* If the output cell has image(s), allow it to take 90% of viewport height + but still bounded between 24em and 60em */ + max-height: clamp(24em, 90vh, 60em); +} + +/* Custom scrollbars */ +div.cell:is( + .tag_output_scroll, + .tag_scroll-output, + .config_scroll_outputs + ) + div.cell_output::-webkit-scrollbar, +div.cell.tag_scroll-input div.cell_input::-webkit-scrollbar { + width: var(--mystnb-scrollbar-width); + height: var(--mystnb-scrollbar-height); +} + +div.cell:is( + .tag_output_scroll, + .tag_scroll-output, + .config_scroll_outputs + ) + div.cell_output::-webkit-scrollbar-thumb, +div.cell.tag_scroll-input div.cell_input::-webkit-scrollbar-thumb { + background: var(--mystnb-scrollbar-thumb-color); + border-radius: var(--mystnb-scrollbar-thumb-border-radius); +} + +div.cell:is( + .tag_output_scroll, + .tag_scroll-output, + .config_scroll_outputs + ) + div.cell_output::-webkit-scrollbar-thumb:hover, +div.cell.tag_scroll-input div.cell_input::-webkit-scrollbar-thumb:hover { + background: var(--mystnb-scrollbar-thumb-hover-color); +} + +/* In print mode, unset scroll styles */ +@media print { + div.cell:is( + .tag_output_scroll, + .tag_scroll-output, + .config_scroll_outputs + ) + div.cell_output, + div.cell.tag_scroll-input div.cell_input { + max-height: unset; + overflow-y: visible; + max-width: unset; + overflow-x: visible; + } +} + +/* Font colors for translated ANSI escape sequences +Color values are copied from Jupyter Notebook +https://github.com/jupyter/notebook/blob/52581f8eda9b319eb0390ac77fe5903c38f81e3e/notebook/static/notebook/less/ansicolors.less#L14-L21 +Background colors from +https://nbsphinx.readthedocs.io/en/latest/code-cells.html#ANSI-Colors +*/ +div.highlight .-Color-Bold { + font-weight: bold; +} + +div.highlight .-Color[class*=-Black] { + color: #3E424D +} + +div.highlight .-Color[class*=-Red] { + color: #E75C58 +} + +div.highlight .-Color[class*=-Green] { + color: #00A250 +} + +div.highlight .-Color[class*=-Yellow] { + color: #DDB62B +} + +div.highlight .-Color[class*=-Blue] { + color: #208FFB +} + +div.highlight .-Color[class*=-Magenta] { + color: #D160C4 +} + +div.highlight .-Color[class*=-Cyan] { + color: #60C6C8 +} + +div.highlight .-Color[class*=-White] { + color: #C5C1B4 +} + +div.highlight .-Color[class*=-BGBlack] { + background-color: #3E424D +} + +div.highlight .-Color[class*=-BGRed] { + background-color: #E75C58 +} + +div.highlight .-Color[class*=-BGGreen] { + background-color: #00A250 +} + +div.highlight .-Color[class*=-BGYellow] { + background-color: #DDB62B +} + +div.highlight .-Color[class*=-BGBlue] { + background-color: #208FFB +} + +div.highlight .-Color[class*=-BGMagenta] { + background-color: #D160C4 +} + +div.highlight .-Color[class*=-BGCyan] { + background-color: #60C6C8 +} + +div.highlight .-Color[class*=-BGWhite] { + background-color: #C5C1B4 +} + +/* Font colors for 8-bit ANSI */ + +div.highlight .-Color[class*=-C0] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC0] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C1] { + color: #800000 +} + +div.highlight .-Color[class*=-BGC1] { + background-color: #800000 +} + +div.highlight .-Color[class*=-C2] { + color: #008000 +} + +div.highlight .-Color[class*=-BGC2] { + background-color: #008000 +} + +div.highlight .-Color[class*=-C3] { + color: #808000 +} + +div.highlight .-Color[class*=-BGC3] { + background-color: #808000 +} + +div.highlight .-Color[class*=-C4] { + color: #000080 +} + +div.highlight .-Color[class*=-BGC4] { + background-color: #000080 +} + +div.highlight .-Color[class*=-C5] { + color: #800080 +} + +div.highlight .-Color[class*=-BGC5] { + background-color: #800080 +} + +div.highlight .-Color[class*=-C6] { + color: #008080 +} + +div.highlight .-Color[class*=-BGC6] { + background-color: #008080 +} + +div.highlight .-Color[class*=-C7] { + color: #C0C0C0 +} + +div.highlight .-Color[class*=-BGC7] { + background-color: #C0C0C0 +} + +div.highlight .-Color[class*=-C8] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC8] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C9] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC9] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C10] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC10] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C11] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC11] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C12] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC12] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C13] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC13] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C14] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC14] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C15] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC15] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C16] { + color: #000000 +} + +div.highlight .-Color[class*=-BGC16] { + background-color: #000000 +} + +div.highlight .-Color[class*=-C17] { + color: #00005F +} + +div.highlight .-Color[class*=-BGC17] { + background-color: #00005F +} + +div.highlight .-Color[class*=-C18] { + color: #000087 +} + +div.highlight .-Color[class*=-BGC18] { + background-color: #000087 +} + +div.highlight .-Color[class*=-C19] { + color: #0000AF +} + +div.highlight .-Color[class*=-BGC19] { + background-color: #0000AF +} + +div.highlight .-Color[class*=-C20] { + color: #0000D7 +} + +div.highlight .-Color[class*=-BGC20] { + background-color: #0000D7 +} + +div.highlight .-Color[class*=-C21] { + color: #0000FF +} + +div.highlight .-Color[class*=-BGC21] { + background-color: #0000FF +} + +div.highlight .-Color[class*=-C22] { + color: #005F00 +} + +div.highlight .-Color[class*=-BGC22] { + background-color: #005F00 +} + +div.highlight .-Color[class*=-C23] { + color: #005F5F +} + +div.highlight .-Color[class*=-BGC23] { + background-color: #005F5F +} + +div.highlight .-Color[class*=-C24] { + color: #005F87 +} + +div.highlight .-Color[class*=-BGC24] { + background-color: #005F87 +} + +div.highlight .-Color[class*=-C25] { + color: #005FAF +} + +div.highlight .-Color[class*=-BGC25] { + background-color: #005FAF +} + +div.highlight .-Color[class*=-C26] { + color: #005FD7 +} + +div.highlight .-Color[class*=-BGC26] { + background-color: #005FD7 +} + +div.highlight .-Color[class*=-C27] { + color: #005FFF +} + +div.highlight .-Color[class*=-BGC27] { + background-color: #005FFF +} + +div.highlight .-Color[class*=-C28] { + color: #008700 +} + +div.highlight .-Color[class*=-BGC28] { + background-color: #008700 +} + +div.highlight .-Color[class*=-C29] { + color: #00875F +} + +div.highlight .-Color[class*=-BGC29] { + background-color: #00875F +} + +div.highlight .-Color[class*=-C30] { + color: #008787 +} + +div.highlight .-Color[class*=-BGC30] { + background-color: #008787 +} + +div.highlight .-Color[class*=-C31] { + color: #0087AF +} + +div.highlight .-Color[class*=-BGC31] { + background-color: #0087AF +} + +div.highlight .-Color[class*=-C32] { + color: #0087D7 +} + +div.highlight .-Color[class*=-BGC32] { + background-color: #0087D7 +} + +div.highlight .-Color[class*=-C33] { + color: #0087FF +} + +div.highlight .-Color[class*=-BGC33] { + background-color: #0087FF +} + +div.highlight .-Color[class*=-C34] { + color: #00AF00 +} + +div.highlight .-Color[class*=-BGC34] { + background-color: #00AF00 +} + +div.highlight .-Color[class*=-C35] { + color: #00AF5F +} + +div.highlight .-Color[class*=-BGC35] { + background-color: #00AF5F +} + +div.highlight .-Color[class*=-C36] { + color: #00AF87 +} + +div.highlight .-Color[class*=-BGC36] { + background-color: #00AF87 +} + +div.highlight .-Color[class*=-C37] { + color: #00AFAF +} + +div.highlight .-Color[class*=-BGC37] { + background-color: #00AFAF +} + +div.highlight .-Color[class*=-C38] { + color: #00AFD7 +} + +div.highlight .-Color[class*=-BGC38] { + background-color: #00AFD7 +} + +div.highlight .-Color[class*=-C39] { + color: #00AFFF +} + +div.highlight .-Color[class*=-BGC39] { + background-color: #00AFFF +} + +div.highlight .-Color[class*=-C40] { + color: #00D700 +} + +div.highlight .-Color[class*=-BGC40] { + background-color: #00D700 +} + +div.highlight .-Color[class*=-C41] { + color: #00D75F +} + +div.highlight .-Color[class*=-BGC41] { + background-color: #00D75F +} + +div.highlight .-Color[class*=-C42] { + color: #00D787 +} + +div.highlight .-Color[class*=-BGC42] { + background-color: #00D787 +} + +div.highlight .-Color[class*=-C43] { + color: #00D7AF +} + +div.highlight .-Color[class*=-BGC43] { + background-color: #00D7AF +} + +div.highlight .-Color[class*=-C44] { + color: #00D7D7 +} + +div.highlight .-Color[class*=-BGC44] { + background-color: #00D7D7 +} + +div.highlight .-Color[class*=-C45] { + color: #00D7FF +} + +div.highlight .-Color[class*=-BGC45] { + background-color: #00D7FF +} + +div.highlight .-Color[class*=-C46] { + color: #00FF00 +} + +div.highlight .-Color[class*=-BGC46] { + background-color: #00FF00 +} + +div.highlight .-Color[class*=-C47] { + color: #00FF5F +} + +div.highlight .-Color[class*=-BGC47] { + background-color: #00FF5F +} + +div.highlight .-Color[class*=-C48] { + color: #00FF87 +} + +div.highlight .-Color[class*=-BGC48] { + background-color: #00FF87 +} + +div.highlight .-Color[class*=-C49] { + color: #00FFAF +} + +div.highlight .-Color[class*=-BGC49] { + background-color: #00FFAF +} + +div.highlight .-Color[class*=-C50] { + color: #00FFD7 +} + +div.highlight .-Color[class*=-BGC50] { + background-color: #00FFD7 +} + +div.highlight .-Color[class*=-C51] { + color: #00FFFF +} + +div.highlight .-Color[class*=-BGC51] { + background-color: #00FFFF +} + +div.highlight .-Color[class*=-C52] { + color: #5F0000 +} + +div.highlight .-Color[class*=-BGC52] { + background-color: #5F0000 +} + +div.highlight .-Color[class*=-C53] { + color: #5F005F +} + +div.highlight .-Color[class*=-BGC53] { + background-color: #5F005F +} + +div.highlight .-Color[class*=-C54] { + color: #5F0087 +} + +div.highlight .-Color[class*=-BGC54] { + background-color: #5F0087 +} + +div.highlight .-Color[class*=-C55] { + color: #5F00AF +} + +div.highlight .-Color[class*=-BGC55] { + background-color: #5F00AF +} + +div.highlight .-Color[class*=-C56] { + color: #5F00D7 +} + +div.highlight .-Color[class*=-BGC56] { + background-color: #5F00D7 +} + +div.highlight .-Color[class*=-C57] { + color: #5F00FF +} + +div.highlight .-Color[class*=-BGC57] { + background-color: #5F00FF +} + +div.highlight .-Color[class*=-C58] { + color: #5F5F00 +} + +div.highlight .-Color[class*=-BGC58] { + background-color: #5F5F00 +} + +div.highlight .-Color[class*=-C59] { + color: #5F5F5F +} + +div.highlight .-Color[class*=-BGC59] { + background-color: #5F5F5F +} + +div.highlight .-Color[class*=-C60] { + color: #5F5F87 +} + +div.highlight .-Color[class*=-BGC60] { + background-color: #5F5F87 +} + +div.highlight .-Color[class*=-C61] { + color: #5F5FAF +} + +div.highlight .-Color[class*=-BGC61] { + background-color: #5F5FAF +} + +div.highlight .-Color[class*=-C62] { + color: #5F5FD7 +} + +div.highlight .-Color[class*=-BGC62] { + background-color: #5F5FD7 +} + +div.highlight .-Color[class*=-C63] { + color: #5F5FFF +} + +div.highlight .-Color[class*=-BGC63] { + background-color: #5F5FFF +} + +div.highlight .-Color[class*=-C64] { + color: #5F8700 +} + +div.highlight .-Color[class*=-BGC64] { + background-color: #5F8700 +} + +div.highlight .-Color[class*=-C65] { + color: #5F875F +} + +div.highlight .-Color[class*=-BGC65] { + background-color: #5F875F +} + +div.highlight .-Color[class*=-C66] { + color: #5F8787 +} + +div.highlight .-Color[class*=-BGC66] { + background-color: #5F8787 +} + +div.highlight .-Color[class*=-C67] { + color: #5F87AF +} + +div.highlight .-Color[class*=-BGC67] { + background-color: #5F87AF +} + +div.highlight .-Color[class*=-C68] { + color: #5F87D7 +} + +div.highlight .-Color[class*=-BGC68] { + background-color: #5F87D7 +} + +div.highlight .-Color[class*=-C69] { + color: #5F87FF +} + +div.highlight .-Color[class*=-BGC69] { + background-color: #5F87FF +} + +div.highlight .-Color[class*=-C70] { + color: #5FAF00 +} + +div.highlight .-Color[class*=-BGC70] { + background-color: #5FAF00 +} + +div.highlight .-Color[class*=-C71] { + color: #5FAF5F +} + +div.highlight .-Color[class*=-BGC71] { + background-color: #5FAF5F +} + +div.highlight .-Color[class*=-C72] { + color: #5FAF87 +} + +div.highlight .-Color[class*=-BGC72] { + background-color: #5FAF87 +} + +div.highlight .-Color[class*=-C73] { + color: #5FAFAF +} + +div.highlight .-Color[class*=-BGC73] { + background-color: #5FAFAF +} + +div.highlight .-Color[class*=-C74] { + color: #5FAFD7 +} + +div.highlight .-Color[class*=-BGC74] { + background-color: #5FAFD7 +} + +div.highlight .-Color[class*=-C75] { + color: #5FAFFF +} + +div.highlight .-Color[class*=-BGC75] { + background-color: #5FAFFF +} + +div.highlight .-Color[class*=-C76] { + color: #5FD700 +} + +div.highlight .-Color[class*=-BGC76] { + background-color: #5FD700 +} + +div.highlight .-Color[class*=-C77] { + color: #5FD75F +} + +div.highlight .-Color[class*=-BGC77] { + background-color: #5FD75F +} + +div.highlight .-Color[class*=-C78] { + color: #5FD787 +} + +div.highlight .-Color[class*=-BGC78] { + background-color: #5FD787 +} + +div.highlight .-Color[class*=-C79] { + color: #5FD7AF +} + +div.highlight .-Color[class*=-BGC79] { + background-color: #5FD7AF +} + +div.highlight .-Color[class*=-C80] { + color: #5FD7D7 +} + +div.highlight .-Color[class*=-BGC80] { + background-color: #5FD7D7 +} + +div.highlight .-Color[class*=-C81] { + color: #5FD7FF +} + +div.highlight .-Color[class*=-BGC81] { + background-color: #5FD7FF +} + +div.highlight .-Color[class*=-C82] { + color: #5FFF00 +} + +div.highlight .-Color[class*=-BGC82] { + background-color: #5FFF00 +} + +div.highlight .-Color[class*=-C83] { + color: #5FFF5F +} + +div.highlight .-Color[class*=-BGC83] { + background-color: #5FFF5F +} + +div.highlight .-Color[class*=-C84] { + color: #5FFF87 +} + +div.highlight .-Color[class*=-BGC84] { + background-color: #5FFF87 +} + +div.highlight .-Color[class*=-C85] { + color: #5FFFAF +} + +div.highlight .-Color[class*=-BGC85] { + background-color: #5FFFAF +} + +div.highlight .-Color[class*=-C86] { + color: #5FFFD7 +} + +div.highlight .-Color[class*=-BGC86] { + background-color: #5FFFD7 +} + +div.highlight .-Color[class*=-C87] { + color: #5FFFFF +} + +div.highlight .-Color[class*=-BGC87] { + background-color: #5FFFFF +} + +div.highlight .-Color[class*=-C88] { + color: #870000 +} + +div.highlight .-Color[class*=-BGC88] { + background-color: #870000 +} + +div.highlight .-Color[class*=-C89] { + color: #87005F +} + +div.highlight .-Color[class*=-BGC89] { + background-color: #87005F +} + +div.highlight .-Color[class*=-C90] { + color: #870087 +} + +div.highlight .-Color[class*=-BGC90] { + background-color: #870087 +} + +div.highlight .-Color[class*=-C91] { + color: #8700AF +} + +div.highlight .-Color[class*=-BGC91] { + background-color: #8700AF +} + +div.highlight .-Color[class*=-C92] { + color: #8700D7 +} + +div.highlight .-Color[class*=-BGC92] { + background-color: #8700D7 +} + +div.highlight .-Color[class*=-C93] { + color: #8700FF +} + +div.highlight .-Color[class*=-BGC93] { + background-color: #8700FF +} + +div.highlight .-Color[class*=-C94] { + color: #875F00 +} + +div.highlight .-Color[class*=-BGC94] { + background-color: #875F00 +} + +div.highlight .-Color[class*=-C95] { + color: #875F5F +} + +div.highlight .-Color[class*=-BGC95] { + background-color: #875F5F +} + +div.highlight .-Color[class*=-C96] { + color: #875F87 +} + +div.highlight .-Color[class*=-BGC96] { + background-color: #875F87 +} + +div.highlight .-Color[class*=-C97] { + color: #875FAF +} + +div.highlight .-Color[class*=-BGC97] { + background-color: #875FAF +} + +div.highlight .-Color[class*=-C98] { + color: #875FD7 +} + +div.highlight .-Color[class*=-BGC98] { + background-color: #875FD7 +} + +div.highlight .-Color[class*=-C99] { + color: #875FFF +} + +div.highlight .-Color[class*=-BGC99] { + background-color: #875FFF +} + +div.highlight .-Color[class*=-C100] { + color: #878700 +} + +div.highlight .-Color[class*=-BGC100] { + background-color: #878700 +} + +div.highlight .-Color[class*=-C101] { + color: #87875F +} + +div.highlight .-Color[class*=-BGC101] { + background-color: #87875F +} + +div.highlight .-Color[class*=-C102] { + color: #878787 +} + +div.highlight .-Color[class*=-BGC102] { + background-color: #878787 +} + +div.highlight .-Color[class*=-C103] { + color: #8787AF +} + +div.highlight .-Color[class*=-BGC103] { + background-color: #8787AF +} + +div.highlight .-Color[class*=-C104] { + color: #8787D7 +} + +div.highlight .-Color[class*=-BGC104] { + background-color: #8787D7 +} + +div.highlight .-Color[class*=-C105] { + color: #8787FF +} + +div.highlight .-Color[class*=-BGC105] { + background-color: #8787FF +} + +div.highlight .-Color[class*=-C106] { + color: #87AF00 +} + +div.highlight .-Color[class*=-BGC106] { + background-color: #87AF00 +} + +div.highlight .-Color[class*=-C107] { + color: #87AF5F +} + +div.highlight .-Color[class*=-BGC107] { + background-color: #87AF5F +} + +div.highlight .-Color[class*=-C108] { + color: #87AF87 +} + +div.highlight .-Color[class*=-BGC108] { + background-color: #87AF87 +} + +div.highlight .-Color[class*=-C109] { + color: #87AFAF +} + +div.highlight .-Color[class*=-BGC109] { + background-color: #87AFAF +} + +div.highlight .-Color[class*=-C110] { + color: #87AFD7 +} + +div.highlight .-Color[class*=-BGC110] { + background-color: #87AFD7 +} + +div.highlight .-Color[class*=-C111] { + color: #87AFFF +} + +div.highlight .-Color[class*=-BGC111] { + background-color: #87AFFF +} + +div.highlight .-Color[class*=-C112] { + color: #87D700 +} + +div.highlight .-Color[class*=-BGC112] { + background-color: #87D700 +} + +div.highlight .-Color[class*=-C113] { + color: #87D75F +} + +div.highlight .-Color[class*=-BGC113] { + background-color: #87D75F +} + +div.highlight .-Color[class*=-C114] { + color: #87D787 +} + +div.highlight .-Color[class*=-BGC114] { + background-color: #87D787 +} + +div.highlight .-Color[class*=-C115] { + color: #87D7AF +} + +div.highlight .-Color[class*=-BGC115] { + background-color: #87D7AF +} + +div.highlight .-Color[class*=-C116] { + color: #87D7D7 +} + +div.highlight .-Color[class*=-BGC116] { + background-color: #87D7D7 +} + +div.highlight .-Color[class*=-C117] { + color: #87D7FF +} + +div.highlight .-Color[class*=-BGC117] { + background-color: #87D7FF +} + +div.highlight .-Color[class*=-C118] { + color: #87FF00 +} + +div.highlight .-Color[class*=-BGC118] { + background-color: #87FF00 +} + +div.highlight .-Color[class*=-C119] { + color: #87FF5F +} + +div.highlight .-Color[class*=-BGC119] { + background-color: #87FF5F +} + +div.highlight .-Color[class*=-C120] { + color: #87FF87 +} + +div.highlight .-Color[class*=-BGC120] { + background-color: #87FF87 +} + +div.highlight .-Color[class*=-C121] { + color: #87FFAF +} + +div.highlight .-Color[class*=-BGC121] { + background-color: #87FFAF +} + +div.highlight .-Color[class*=-C122] { + color: #87FFD7 +} + +div.highlight .-Color[class*=-BGC122] { + background-color: #87FFD7 +} + +div.highlight .-Color[class*=-C123] { + color: #87FFFF +} + +div.highlight .-Color[class*=-BGC123] { + background-color: #87FFFF +} + +div.highlight .-Color[class*=-C124] { + color: #AF0000 +} + +div.highlight .-Color[class*=-BGC124] { + background-color: #AF0000 +} + +div.highlight .-Color[class*=-C125] { + color: #AF005F +} + +div.highlight .-Color[class*=-BGC125] { + background-color: #AF005F +} + +div.highlight .-Color[class*=-C126] { + color: #AF0087 +} + +div.highlight .-Color[class*=-BGC126] { + background-color: #AF0087 +} + +div.highlight .-Color[class*=-C127] { + color: #AF00AF +} + +div.highlight .-Color[class*=-BGC127] { + background-color: #AF00AF +} + +div.highlight .-Color[class*=-C128] { + color: #AF00D7 +} + +div.highlight .-Color[class*=-BGC128] { + background-color: #AF00D7 +} + +div.highlight .-Color[class*=-C129] { + color: #AF00FF +} + +div.highlight .-Color[class*=-BGC129] { + background-color: #AF00FF +} + +div.highlight .-Color[class*=-C130] { + color: #AF5F00 +} + +div.highlight .-Color[class*=-BGC130] { + background-color: #AF5F00 +} + +div.highlight .-Color[class*=-C131] { + color: #AF5F5F +} + +div.highlight .-Color[class*=-BGC131] { + background-color: #AF5F5F +} + +div.highlight .-Color[class*=-C132] { + color: #AF5F87 +} + +div.highlight .-Color[class*=-BGC132] { + background-color: #AF5F87 +} + +div.highlight .-Color[class*=-C133] { + color: #AF5FAF +} + +div.highlight .-Color[class*=-BGC133] { + background-color: #AF5FAF +} + +div.highlight .-Color[class*=-C134] { + color: #AF5FD7 +} + +div.highlight .-Color[class*=-BGC134] { + background-color: #AF5FD7 +} + +div.highlight .-Color[class*=-C135] { + color: #AF5FFF +} + +div.highlight .-Color[class*=-BGC135] { + background-color: #AF5FFF +} + +div.highlight .-Color[class*=-C136] { + color: #AF8700 +} + +div.highlight .-Color[class*=-BGC136] { + background-color: #AF8700 +} + +div.highlight .-Color[class*=-C137] { + color: #AF875F +} + +div.highlight .-Color[class*=-BGC137] { + background-color: #AF875F +} + +div.highlight .-Color[class*=-C138] { + color: #AF8787 +} + +div.highlight .-Color[class*=-BGC138] { + background-color: #AF8787 +} + +div.highlight .-Color[class*=-C139] { + color: #AF87AF +} + +div.highlight .-Color[class*=-BGC139] { + background-color: #AF87AF +} + +div.highlight .-Color[class*=-C140] { + color: #AF87D7 +} + +div.highlight .-Color[class*=-BGC140] { + background-color: #AF87D7 +} + +div.highlight .-Color[class*=-C141] { + color: #AF87FF +} + +div.highlight .-Color[class*=-BGC141] { + background-color: #AF87FF +} + +div.highlight .-Color[class*=-C142] { + color: #AFAF00 +} + +div.highlight .-Color[class*=-BGC142] { + background-color: #AFAF00 +} + +div.highlight .-Color[class*=-C143] { + color: #AFAF5F +} + +div.highlight .-Color[class*=-BGC143] { + background-color: #AFAF5F +} + +div.highlight .-Color[class*=-C144] { + color: #AFAF87 +} + +div.highlight .-Color[class*=-BGC144] { + background-color: #AFAF87 +} + +div.highlight .-Color[class*=-C145] { + color: #AFAFAF +} + +div.highlight .-Color[class*=-BGC145] { + background-color: #AFAFAF +} + +div.highlight .-Color[class*=-C146] { + color: #AFAFD7 +} + +div.highlight .-Color[class*=-BGC146] { + background-color: #AFAFD7 +} + +div.highlight .-Color[class*=-C147] { + color: #AFAFFF +} + +div.highlight .-Color[class*=-BGC147] { + background-color: #AFAFFF +} + +div.highlight .-Color[class*=-C148] { + color: #AFD700 +} + +div.highlight .-Color[class*=-BGC148] { + background-color: #AFD700 +} + +div.highlight .-Color[class*=-C149] { + color: #AFD75F +} + +div.highlight .-Color[class*=-BGC149] { + background-color: #AFD75F +} + +div.highlight .-Color[class*=-C150] { + color: #AFD787 +} + +div.highlight .-Color[class*=-BGC150] { + background-color: #AFD787 +} + +div.highlight .-Color[class*=-C151] { + color: #AFD7AF +} + +div.highlight .-Color[class*=-BGC151] { + background-color: #AFD7AF +} + +div.highlight .-Color[class*=-C152] { + color: #AFD7D7 +} + +div.highlight .-Color[class*=-BGC152] { + background-color: #AFD7D7 +} + +div.highlight .-Color[class*=-C153] { + color: #AFD7FF +} + +div.highlight .-Color[class*=-BGC153] { + background-color: #AFD7FF +} + +div.highlight .-Color[class*=-C154] { + color: #AFFF00 +} + +div.highlight .-Color[class*=-BGC154] { + background-color: #AFFF00 +} + +div.highlight .-Color[class*=-C155] { + color: #AFFF5F +} + +div.highlight .-Color[class*=-BGC155] { + background-color: #AFFF5F +} + +div.highlight .-Color[class*=-C156] { + color: #AFFF87 +} + +div.highlight .-Color[class*=-BGC156] { + background-color: #AFFF87 +} + +div.highlight .-Color[class*=-C157] { + color: #AFFFAF +} + +div.highlight .-Color[class*=-BGC157] { + background-color: #AFFFAF +} + +div.highlight .-Color[class*=-C158] { + color: #AFFFD7 +} + +div.highlight .-Color[class*=-BGC158] { + background-color: #AFFFD7 +} + +div.highlight .-Color[class*=-C159] { + color: #AFFFFF +} + +div.highlight .-Color[class*=-BGC159] { + background-color: #AFFFFF +} + +div.highlight .-Color[class*=-C160] { + color: #D70000 +} + +div.highlight .-Color[class*=-BGC160] { + background-color: #D70000 +} + +div.highlight .-Color[class*=-C161] { + color: #D7005F +} + +div.highlight .-Color[class*=-BGC161] { + background-color: #D7005F +} + +div.highlight .-Color[class*=-C162] { + color: #D70087 +} + +div.highlight .-Color[class*=-BGC162] { + background-color: #D70087 +} + +div.highlight .-Color[class*=-C163] { + color: #D700AF +} + +div.highlight .-Color[class*=-BGC163] { + background-color: #D700AF +} + +div.highlight .-Color[class*=-C164] { + color: #D700D7 +} + +div.highlight .-Color[class*=-BGC164] { + background-color: #D700D7 +} + +div.highlight .-Color[class*=-C165] { + color: #D700FF +} + +div.highlight .-Color[class*=-BGC165] { + background-color: #D700FF +} + +div.highlight .-Color[class*=-C166] { + color: #D75F00 +} + +div.highlight .-Color[class*=-BGC166] { + background-color: #D75F00 +} + +div.highlight .-Color[class*=-C167] { + color: #D75F5F +} + +div.highlight .-Color[class*=-BGC167] { + background-color: #D75F5F +} + +div.highlight .-Color[class*=-C168] { + color: #D75F87 +} + +div.highlight .-Color[class*=-BGC168] { + background-color: #D75F87 +} + +div.highlight .-Color[class*=-C169] { + color: #D75FAF +} + +div.highlight .-Color[class*=-BGC169] { + background-color: #D75FAF +} + +div.highlight .-Color[class*=-C170] { + color: #D75FD7 +} + +div.highlight .-Color[class*=-BGC170] { + background-color: #D75FD7 +} + +div.highlight .-Color[class*=-C171] { + color: #D75FFF +} + +div.highlight .-Color[class*=-BGC171] { + background-color: #D75FFF +} + +div.highlight .-Color[class*=-C172] { + color: #D78700 +} + +div.highlight .-Color[class*=-BGC172] { + background-color: #D78700 +} + +div.highlight .-Color[class*=-C173] { + color: #D7875F +} + +div.highlight .-Color[class*=-BGC173] { + background-color: #D7875F +} + +div.highlight .-Color[class*=-C174] { + color: #D78787 +} + +div.highlight .-Color[class*=-BGC174] { + background-color: #D78787 +} + +div.highlight .-Color[class*=-C175] { + color: #D787AF +} + +div.highlight .-Color[class*=-BGC175] { + background-color: #D787AF +} + +div.highlight .-Color[class*=-C176] { + color: #D787D7 +} + +div.highlight .-Color[class*=-BGC176] { + background-color: #D787D7 +} + +div.highlight .-Color[class*=-C177] { + color: #D787FF +} + +div.highlight .-Color[class*=-BGC177] { + background-color: #D787FF +} + +div.highlight .-Color[class*=-C178] { + color: #D7AF00 +} + +div.highlight .-Color[class*=-BGC178] { + background-color: #D7AF00 +} + +div.highlight .-Color[class*=-C179] { + color: #D7AF5F +} + +div.highlight .-Color[class*=-BGC179] { + background-color: #D7AF5F +} + +div.highlight .-Color[class*=-C180] { + color: #D7AF87 +} + +div.highlight .-Color[class*=-BGC180] { + background-color: #D7AF87 +} + +div.highlight .-Color[class*=-C181] { + color: #D7AFAF +} + +div.highlight .-Color[class*=-BGC181] { + background-color: #D7AFAF +} + +div.highlight .-Color[class*=-C182] { + color: #D7AFD7 +} + +div.highlight .-Color[class*=-BGC182] { + background-color: #D7AFD7 +} + +div.highlight .-Color[class*=-C183] { + color: #D7AFFF +} + +div.highlight .-Color[class*=-BGC183] { + background-color: #D7AFFF +} + +div.highlight .-Color[class*=-C184] { + color: #D7D700 +} + +div.highlight .-Color[class*=-BGC184] { + background-color: #D7D700 +} + +div.highlight .-Color[class*=-C185] { + color: #D7D75F +} + +div.highlight .-Color[class*=-BGC185] { + background-color: #D7D75F +} + +div.highlight .-Color[class*=-C186] { + color: #D7D787 +} + +div.highlight .-Color[class*=-BGC186] { + background-color: #D7D787 +} + +div.highlight .-Color[class*=-C187] { + color: #D7D7AF +} + +div.highlight .-Color[class*=-BGC187] { + background-color: #D7D7AF +} + +div.highlight .-Color[class*=-C188] { + color: #D7D7D7 +} + +div.highlight .-Color[class*=-BGC188] { + background-color: #D7D7D7 +} + +div.highlight .-Color[class*=-C189] { + color: #D7D7FF +} + +div.highlight .-Color[class*=-BGC189] { + background-color: #D7D7FF +} + +div.highlight .-Color[class*=-C190] { + color: #D7FF00 +} + +div.highlight .-Color[class*=-BGC190] { + background-color: #D7FF00 +} + +div.highlight .-Color[class*=-C191] { + color: #D7FF5F +} + +div.highlight .-Color[class*=-BGC191] { + background-color: #D7FF5F +} + +div.highlight .-Color[class*=-C192] { + color: #D7FF87 +} + +div.highlight .-Color[class*=-BGC192] { + background-color: #D7FF87 +} + +div.highlight .-Color[class*=-C193] { + color: #D7FFAF +} + +div.highlight .-Color[class*=-BGC193] { + background-color: #D7FFAF +} + +div.highlight .-Color[class*=-C194] { + color: #D7FFD7 +} + +div.highlight .-Color[class*=-BGC194] { + background-color: #D7FFD7 +} + +div.highlight .-Color[class*=-C195] { + color: #D7FFFF +} + +div.highlight .-Color[class*=-BGC195] { + background-color: #D7FFFF +} + +div.highlight .-Color[class*=-C196] { + color: #FF0000 +} + +div.highlight .-Color[class*=-BGC196] { + background-color: #FF0000 +} + +div.highlight .-Color[class*=-C197] { + color: #FF005F +} + +div.highlight .-Color[class*=-BGC197] { + background-color: #FF005F +} + +div.highlight .-Color[class*=-C198] { + color: #FF0087 +} + +div.highlight .-Color[class*=-BGC198] { + background-color: #FF0087 +} + +div.highlight .-Color[class*=-C199] { + color: #FF00AF +} + +div.highlight .-Color[class*=-BGC199] { + background-color: #FF00AF +} + +div.highlight .-Color[class*=-C200] { + color: #FF00D7 +} + +div.highlight .-Color[class*=-BGC200] { + background-color: #FF00D7 +} + +div.highlight .-Color[class*=-C201] { + color: #FF00FF +} + +div.highlight .-Color[class*=-BGC201] { + background-color: #FF00FF +} + +div.highlight .-Color[class*=-C202] { + color: #FF5F00 +} + +div.highlight .-Color[class*=-BGC202] { + background-color: #FF5F00 +} + +div.highlight .-Color[class*=-C203] { + color: #FF5F5F +} + +div.highlight .-Color[class*=-BGC203] { + background-color: #FF5F5F +} + +div.highlight .-Color[class*=-C204] { + color: #FF5F87 +} + +div.highlight .-Color[class*=-BGC204] { + background-color: #FF5F87 +} + +div.highlight .-Color[class*=-C205] { + color: #FF5FAF +} + +div.highlight .-Color[class*=-BGC205] { + background-color: #FF5FAF +} + +div.highlight .-Color[class*=-C206] { + color: #FF5FD7 +} + +div.highlight .-Color[class*=-BGC206] { + background-color: #FF5FD7 +} + +div.highlight .-Color[class*=-C207] { + color: #FF5FFF +} + +div.highlight .-Color[class*=-BGC207] { + background-color: #FF5FFF +} + +div.highlight .-Color[class*=-C208] { + color: #FF8700 +} + +div.highlight .-Color[class*=-BGC208] { + background-color: #FF8700 +} + +div.highlight .-Color[class*=-C209] { + color: #FF875F +} + +div.highlight .-Color[class*=-BGC209] { + background-color: #FF875F +} + +div.highlight .-Color[class*=-C210] { + color: #FF8787 +} + +div.highlight .-Color[class*=-BGC210] { + background-color: #FF8787 +} + +div.highlight .-Color[class*=-C211] { + color: #FF87AF +} + +div.highlight .-Color[class*=-BGC211] { + background-color: #FF87AF +} + +div.highlight .-Color[class*=-C212] { + color: #FF87D7 +} + +div.highlight .-Color[class*=-BGC212] { + background-color: #FF87D7 +} + +div.highlight .-Color[class*=-C213] { + color: #FF87FF +} + +div.highlight .-Color[class*=-BGC213] { + background-color: #FF87FF +} + +div.highlight .-Color[class*=-C214] { + color: #FFAF00 +} + +div.highlight .-Color[class*=-BGC214] { + background-color: #FFAF00 +} + +div.highlight .-Color[class*=-C215] { + color: #FFAF5F +} + +div.highlight .-Color[class*=-BGC215] { + background-color: #FFAF5F +} + +div.highlight .-Color[class*=-C216] { + color: #FFAF87 +} + +div.highlight .-Color[class*=-BGC216] { + background-color: #FFAF87 +} + +div.highlight .-Color[class*=-C217] { + color: #FFAFAF +} + +div.highlight .-Color[class*=-BGC217] { + background-color: #FFAFAF +} + +div.highlight .-Color[class*=-C218] { + color: #FFAFD7 +} + +div.highlight .-Color[class*=-BGC218] { + background-color: #FFAFD7 +} + +div.highlight .-Color[class*=-C219] { + color: #FFAFFF +} + +div.highlight .-Color[class*=-BGC219] { + background-color: #FFAFFF +} + +div.highlight .-Color[class*=-C220] { + color: #FFD700 +} + +div.highlight .-Color[class*=-BGC220] { + background-color: #FFD700 +} + +div.highlight .-Color[class*=-C221] { + color: #FFD75F +} + +div.highlight .-Color[class*=-BGC221] { + background-color: #FFD75F +} + +div.highlight .-Color[class*=-C222] { + color: #FFD787 +} + +div.highlight .-Color[class*=-BGC222] { + background-color: #FFD787 +} + +div.highlight .-Color[class*=-C223] { + color: #FFD7AF +} + +div.highlight .-Color[class*=-BGC223] { + background-color: #FFD7AF +} + +div.highlight .-Color[class*=-C224] { + color: #FFD7D7 +} + +div.highlight .-Color[class*=-BGC224] { + background-color: #FFD7D7 +} + +div.highlight .-Color[class*=-C225] { + color: #FFD7FF +} + +div.highlight .-Color[class*=-BGC225] { + background-color: #FFD7FF +} + +div.highlight .-Color[class*=-C226] { + color: #FFFF00 +} + +div.highlight .-Color[class*=-BGC226] { + background-color: #FFFF00 +} + +div.highlight .-Color[class*=-C227] { + color: #FFFF5F +} + +div.highlight .-Color[class*=-BGC227] { + background-color: #FFFF5F +} + +div.highlight .-Color[class*=-C228] { + color: #FFFF87 +} + +div.highlight .-Color[class*=-BGC228] { + background-color: #FFFF87 +} + +div.highlight .-Color[class*=-C229] { + color: #FFFFAF +} + +div.highlight .-Color[class*=-BGC229] { + background-color: #FFFFAF +} + +div.highlight .-Color[class*=-C230] { + color: #FFFFD7 +} + +div.highlight .-Color[class*=-BGC230] { + background-color: #FFFFD7 +} + +div.highlight .-Color[class*=-C231] { + color: #FFFFFF +} + +div.highlight .-Color[class*=-BGC231] { + background-color: #FFFFFF +} + +div.highlight .-Color[class*=-C232] { + color: #080808 +} + +div.highlight .-Color[class*=-BGC232] { + background-color: #080808 +} + +div.highlight .-Color[class*=-C233] { + color: #121212 +} + +div.highlight .-Color[class*=-BGC233] { + background-color: #121212 +} + +div.highlight .-Color[class*=-C234] { + color: #1C1C1C +} + +div.highlight .-Color[class*=-BGC234] { + background-color: #1C1C1C +} + +div.highlight .-Color[class*=-C235] { + color: #262626 +} + +div.highlight .-Color[class*=-BGC235] { + background-color: #262626 +} + +div.highlight .-Color[class*=-C236] { + color: #303030 +} + +div.highlight .-Color[class*=-BGC236] { + background-color: #303030 +} + +div.highlight .-Color[class*=-C237] { + color: #3A3A3A +} + +div.highlight .-Color[class*=-BGC237] { + background-color: #3A3A3A +} + +div.highlight .-Color[class*=-C238] { + color: #444444 +} + +div.highlight .-Color[class*=-BGC238] { + background-color: #444444 +} + +div.highlight .-Color[class*=-C239] { + color: #4E4E4E +} + +div.highlight .-Color[class*=-BGC239] { + background-color: #4E4E4E +} + +div.highlight .-Color[class*=-C240] { + color: #585858 +} + +div.highlight .-Color[class*=-BGC240] { + background-color: #585858 +} + +div.highlight .-Color[class*=-C241] { + color: #626262 +} + +div.highlight .-Color[class*=-BGC241] { + background-color: #626262 +} + +div.highlight .-Color[class*=-C242] { + color: #6C6C6C +} + +div.highlight .-Color[class*=-BGC242] { + background-color: #6C6C6C +} + +div.highlight .-Color[class*=-C243] { + color: #767676 +} + +div.highlight .-Color[class*=-BGC243] { + background-color: #767676 +} + +div.highlight .-Color[class*=-C244] { + color: #808080 +} + +div.highlight .-Color[class*=-BGC244] { + background-color: #808080 +} + +div.highlight .-Color[class*=-C245] { + color: #8A8A8A +} + +div.highlight .-Color[class*=-BGC245] { + background-color: #8A8A8A +} + +div.highlight .-Color[class*=-C246] { + color: #949494 +} + +div.highlight .-Color[class*=-BGC246] { + background-color: #949494 +} + +div.highlight .-Color[class*=-C247] { + color: #9E9E9E +} + +div.highlight .-Color[class*=-BGC247] { + background-color: #9E9E9E +} + +div.highlight .-Color[class*=-C248] { + color: #A8A8A8 +} + +div.highlight .-Color[class*=-BGC248] { + background-color: #A8A8A8 +} + +div.highlight .-Color[class*=-C249] { + color: #B2B2B2 +} + +div.highlight .-Color[class*=-BGC249] { + background-color: #B2B2B2 +} + +div.highlight .-Color[class*=-C250] { + color: #BCBCBC +} + +div.highlight .-Color[class*=-BGC250] { + background-color: #BCBCBC +} + +div.highlight .-Color[class*=-C251] { + color: #C6C6C6 +} + +div.highlight .-Color[class*=-BGC251] { + background-color: #C6C6C6 +} + +div.highlight .-Color[class*=-C252] { + color: #D0D0D0 +} + +div.highlight .-Color[class*=-BGC252] { + background-color: #D0D0D0 +} + +div.highlight .-Color[class*=-C253] { + color: #DADADA +} + +div.highlight .-Color[class*=-BGC253] { + background-color: #DADADA +} + +div.highlight .-Color[class*=-C254] { + color: #E4E4E4 +} + +div.highlight .-Color[class*=-BGC254] { + background-color: #E4E4E4 +} + +div.highlight .-Color[class*=-C255] { + color: #EEEEEE +} + +div.highlight .-Color[class*=-BGC255] { + background-color: #EEEEEE +} diff --git a/_static/original.svg b/_static/original.svg new file mode 100644 index 000000000..6ba0ece99 --- /dev/null +++ b/_static/original.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/_static/original_dark.svg b/_static/original_dark.svg new file mode 100644 index 000000000..fbdf20ea7 --- /dev/null +++ b/_static/original_dark.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 000000000..7107cec93 Binary files /dev/null and b/_static/plus.png differ diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 000000000..d7dd57783 --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,152 @@ +html[data-theme="light"] .highlight pre { line-height: 125%; } +html[data-theme="light"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="light"] .highlight .hll { background-color: #fae4c2 } +html[data-theme="light"] .highlight { background: #fefefe; color: #080808 } +html[data-theme="light"] .highlight .c { color: #515151 } /* Comment */ +html[data-theme="light"] .highlight .err { color: #A12236 } /* Error */ +html[data-theme="light"] .highlight .k { color: #6730C5 } /* Keyword */ +html[data-theme="light"] .highlight .l { color: #7F4707 } /* Literal */ +html[data-theme="light"] .highlight .n { color: #080808 } /* Name */ +html[data-theme="light"] .highlight .o { color: #00622F } /* Operator */ +html[data-theme="light"] .highlight .p { color: #080808 } /* Punctuation */ +html[data-theme="light"] .highlight .ch { color: #515151 } /* Comment.Hashbang */ +html[data-theme="light"] .highlight .cm { color: #515151 } /* Comment.Multiline */ +html[data-theme="light"] .highlight .cp { color: #515151 } /* Comment.Preproc */ +html[data-theme="light"] .highlight .cpf { color: #515151 } /* Comment.PreprocFile */ +html[data-theme="light"] .highlight .c1 { color: #515151 } /* Comment.Single */ +html[data-theme="light"] .highlight .cs { color: #515151 } /* Comment.Special */ +html[data-theme="light"] .highlight .gd { color: #005B82 } /* Generic.Deleted */ +html[data-theme="light"] .highlight .ge { font-style: italic } /* Generic.Emph */ +html[data-theme="light"] .highlight .gh { color: #005B82 } /* Generic.Heading */ +html[data-theme="light"] .highlight .gs { font-weight: bold } /* Generic.Strong */ +html[data-theme="light"] .highlight .gu { color: #005B82 } /* Generic.Subheading */ +html[data-theme="light"] .highlight .kc { color: #6730C5 } /* Keyword.Constant */ +html[data-theme="light"] .highlight .kd { color: #6730C5 } /* Keyword.Declaration */ +html[data-theme="light"] .highlight .kn { color: #6730C5 } /* Keyword.Namespace */ +html[data-theme="light"] .highlight .kp { color: #6730C5 } /* Keyword.Pseudo */ +html[data-theme="light"] .highlight .kr { color: #6730C5 } /* Keyword.Reserved */ +html[data-theme="light"] .highlight .kt { color: #7F4707 } /* Keyword.Type */ +html[data-theme="light"] .highlight .ld { color: #7F4707 } /* Literal.Date */ +html[data-theme="light"] .highlight .m { color: #7F4707 } /* Literal.Number */ +html[data-theme="light"] .highlight .s { color: #00622F } /* Literal.String */ +html[data-theme="light"] .highlight .na { color: #912583 } /* Name.Attribute */ +html[data-theme="light"] .highlight .nb { color: #7F4707 } /* Name.Builtin */ +html[data-theme="light"] .highlight .nc { color: #005B82 } /* Name.Class */ +html[data-theme="light"] .highlight .no { color: #005B82 } /* Name.Constant */ +html[data-theme="light"] .highlight .nd { color: #7F4707 } /* Name.Decorator */ +html[data-theme="light"] .highlight .ni { color: #00622F } /* Name.Entity */ +html[data-theme="light"] .highlight .ne { color: #6730C5 } /* Name.Exception */ +html[data-theme="light"] .highlight .nf { color: #005B82 } /* Name.Function */ +html[data-theme="light"] .highlight .nl { color: #7F4707 } /* Name.Label */ +html[data-theme="light"] .highlight .nn { color: #080808 } /* Name.Namespace */ +html[data-theme="light"] .highlight .nx { color: #080808 } /* Name.Other */ +html[data-theme="light"] .highlight .py { color: #005B82 } /* Name.Property */ +html[data-theme="light"] .highlight .nt { color: #005B82 } /* Name.Tag */ +html[data-theme="light"] .highlight .nv { color: #A12236 } /* Name.Variable */ +html[data-theme="light"] .highlight .ow { color: #6730C5 } /* Operator.Word */ +html[data-theme="light"] .highlight .pm { color: #080808 } /* Punctuation.Marker */ +html[data-theme="light"] .highlight .w { color: #080808 } /* Text.Whitespace */ +html[data-theme="light"] .highlight .mb { color: #7F4707 } /* Literal.Number.Bin */ +html[data-theme="light"] .highlight .mf { color: #7F4707 } /* Literal.Number.Float */ +html[data-theme="light"] .highlight .mh { color: #7F4707 } /* Literal.Number.Hex */ +html[data-theme="light"] .highlight .mi { color: #7F4707 } /* Literal.Number.Integer */ +html[data-theme="light"] .highlight .mo { color: #7F4707 } /* Literal.Number.Oct */ +html[data-theme="light"] .highlight .sa { color: #00622F } /* Literal.String.Affix */ +html[data-theme="light"] .highlight .sb { color: #00622F } /* Literal.String.Backtick */ +html[data-theme="light"] .highlight .sc { color: #00622F } /* Literal.String.Char */ +html[data-theme="light"] .highlight .dl { color: #00622F } /* Literal.String.Delimiter */ +html[data-theme="light"] .highlight .sd { color: #00622F } /* Literal.String.Doc */ +html[data-theme="light"] .highlight .s2 { color: #00622F } /* Literal.String.Double */ +html[data-theme="light"] .highlight .se { color: #00622F } /* Literal.String.Escape */ +html[data-theme="light"] .highlight .sh { color: #00622F } /* Literal.String.Heredoc */ +html[data-theme="light"] .highlight .si { color: #00622F } /* Literal.String.Interpol */ +html[data-theme="light"] .highlight .sx { color: #00622F } /* Literal.String.Other */ +html[data-theme="light"] .highlight .sr { color: #A12236 } /* Literal.String.Regex */ +html[data-theme="light"] .highlight .s1 { color: #00622F } /* Literal.String.Single */ +html[data-theme="light"] .highlight .ss { color: #005B82 } /* Literal.String.Symbol */ +html[data-theme="light"] .highlight .bp { color: #7F4707 } /* Name.Builtin.Pseudo */ +html[data-theme="light"] .highlight .fm { color: #005B82 } /* Name.Function.Magic */ +html[data-theme="light"] .highlight .vc { color: #A12236 } /* Name.Variable.Class */ +html[data-theme="light"] .highlight .vg { color: #A12236 } /* Name.Variable.Global */ +html[data-theme="light"] .highlight .vi { color: #A12236 } /* Name.Variable.Instance */ +html[data-theme="light"] .highlight .vm { color: #7F4707 } /* Name.Variable.Magic */ +html[data-theme="light"] .highlight .il { color: #7F4707 } /* Literal.Number.Integer.Long */ +html[data-theme="dark"] .highlight pre { line-height: 125%; } +html[data-theme="dark"] .highlight td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +html[data-theme="dark"] .highlight .hll { background-color: #ffd9002e } +html[data-theme="dark"] .highlight { background: #2b2b2b; color: #F8F8F2 } +html[data-theme="dark"] .highlight .c { color: #FFD900 } /* Comment */ +html[data-theme="dark"] .highlight .err { color: #FFA07A } /* Error */ +html[data-theme="dark"] .highlight .k { color: #DCC6E0 } /* Keyword */ +html[data-theme="dark"] .highlight .l { color: #FFD900 } /* Literal */ +html[data-theme="dark"] .highlight .n { color: #F8F8F2 } /* Name */ +html[data-theme="dark"] .highlight .o { color: #ABE338 } /* Operator */ +html[data-theme="dark"] .highlight .p { color: #F8F8F2 } /* Punctuation */ +html[data-theme="dark"] .highlight .ch { color: #FFD900 } /* Comment.Hashbang */ +html[data-theme="dark"] .highlight .cm { color: #FFD900 } /* Comment.Multiline */ +html[data-theme="dark"] .highlight .cp { color: #FFD900 } /* Comment.Preproc */ +html[data-theme="dark"] .highlight .cpf { color: #FFD900 } /* Comment.PreprocFile */ +html[data-theme="dark"] .highlight .c1 { color: #FFD900 } /* Comment.Single */ +html[data-theme="dark"] .highlight .cs { color: #FFD900 } /* Comment.Special */ +html[data-theme="dark"] .highlight .gd { color: #00E0E0 } /* Generic.Deleted */ +html[data-theme="dark"] .highlight .ge { font-style: italic } /* Generic.Emph */ +html[data-theme="dark"] .highlight .gh { color: #00E0E0 } /* Generic.Heading */ +html[data-theme="dark"] .highlight .gs { font-weight: bold } /* Generic.Strong */ +html[data-theme="dark"] .highlight .gu { color: #00E0E0 } /* Generic.Subheading */ +html[data-theme="dark"] .highlight .kc { color: #DCC6E0 } /* Keyword.Constant */ +html[data-theme="dark"] .highlight .kd { color: #DCC6E0 } /* Keyword.Declaration */ +html[data-theme="dark"] .highlight .kn { color: #DCC6E0 } /* Keyword.Namespace */ +html[data-theme="dark"] .highlight .kp { color: #DCC6E0 } /* Keyword.Pseudo */ +html[data-theme="dark"] .highlight .kr { color: #DCC6E0 } /* Keyword.Reserved */ +html[data-theme="dark"] .highlight .kt { color: #FFD900 } /* Keyword.Type */ +html[data-theme="dark"] .highlight .ld { color: #FFD900 } /* Literal.Date */ +html[data-theme="dark"] .highlight .m { color: #FFD900 } /* Literal.Number */ +html[data-theme="dark"] .highlight .s { color: #ABE338 } /* Literal.String */ +html[data-theme="dark"] .highlight .na { color: #FFD900 } /* Name.Attribute */ +html[data-theme="dark"] .highlight .nb { color: #FFD900 } /* Name.Builtin */ +html[data-theme="dark"] .highlight .nc { color: #00E0E0 } /* Name.Class */ +html[data-theme="dark"] .highlight .no { color: #00E0E0 } /* Name.Constant */ +html[data-theme="dark"] .highlight .nd { color: #FFD900 } /* Name.Decorator */ +html[data-theme="dark"] .highlight .ni { color: #ABE338 } /* Name.Entity */ +html[data-theme="dark"] .highlight .ne { color: #DCC6E0 } /* Name.Exception */ +html[data-theme="dark"] .highlight .nf { color: #00E0E0 } /* Name.Function */ +html[data-theme="dark"] .highlight .nl { color: #FFD900 } /* Name.Label */ +html[data-theme="dark"] .highlight .nn { color: #F8F8F2 } /* Name.Namespace */ +html[data-theme="dark"] .highlight .nx { color: #F8F8F2 } /* Name.Other */ +html[data-theme="dark"] .highlight .py { color: #00E0E0 } /* Name.Property */ +html[data-theme="dark"] .highlight .nt { color: #00E0E0 } /* Name.Tag */ +html[data-theme="dark"] .highlight .nv { color: #FFA07A } /* Name.Variable */ +html[data-theme="dark"] .highlight .ow { color: #DCC6E0 } /* Operator.Word */ +html[data-theme="dark"] .highlight .pm { color: #F8F8F2 } /* Punctuation.Marker */ +html[data-theme="dark"] .highlight .w { color: #F8F8F2 } /* Text.Whitespace */ +html[data-theme="dark"] .highlight .mb { color: #FFD900 } /* Literal.Number.Bin */ +html[data-theme="dark"] .highlight .mf { color: #FFD900 } /* Literal.Number.Float */ +html[data-theme="dark"] .highlight .mh { color: #FFD900 } /* Literal.Number.Hex */ +html[data-theme="dark"] .highlight .mi { color: #FFD900 } /* Literal.Number.Integer */ +html[data-theme="dark"] .highlight .mo { color: #FFD900 } /* Literal.Number.Oct */ +html[data-theme="dark"] .highlight .sa { color: #ABE338 } /* Literal.String.Affix */ +html[data-theme="dark"] .highlight .sb { color: #ABE338 } /* Literal.String.Backtick */ +html[data-theme="dark"] .highlight .sc { color: #ABE338 } /* Literal.String.Char */ +html[data-theme="dark"] .highlight .dl { color: #ABE338 } /* Literal.String.Delimiter */ +html[data-theme="dark"] .highlight .sd { color: #ABE338 } /* Literal.String.Doc */ +html[data-theme="dark"] .highlight .s2 { color: #ABE338 } /* Literal.String.Double */ +html[data-theme="dark"] .highlight .se { color: #ABE338 } /* Literal.String.Escape */ +html[data-theme="dark"] .highlight .sh { color: #ABE338 } /* Literal.String.Heredoc */ +html[data-theme="dark"] .highlight .si { color: #ABE338 } /* Literal.String.Interpol */ +html[data-theme="dark"] .highlight .sx { color: #ABE338 } /* Literal.String.Other */ +html[data-theme="dark"] .highlight .sr { color: #FFA07A } /* Literal.String.Regex */ +html[data-theme="dark"] .highlight .s1 { color: #ABE338 } /* Literal.String.Single */ +html[data-theme="dark"] .highlight .ss { color: #00E0E0 } /* Literal.String.Symbol */ +html[data-theme="dark"] .highlight .bp { color: #FFD900 } /* Name.Builtin.Pseudo */ +html[data-theme="dark"] .highlight .fm { color: #00E0E0 } /* Name.Function.Magic */ +html[data-theme="dark"] .highlight .vc { color: #FFA07A } /* Name.Variable.Class */ +html[data-theme="dark"] .highlight .vg { color: #FFA07A } /* Name.Variable.Global */ +html[data-theme="dark"] .highlight .vi { color: #FFA07A } /* Name.Variable.Instance */ +html[data-theme="dark"] .highlight .vm { color: #FFD900 } /* Name.Variable.Magic */ +html[data-theme="dark"] .highlight .il { color: #FFD900 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/_static/scripts/bootstrap.js b/_static/scripts/bootstrap.js new file mode 100644 index 000000000..c8178debb --- /dev/null +++ b/_static/scripts/bootstrap.js @@ -0,0 +1,3 @@ +/*! For license information please see bootstrap.js.LICENSE.txt */ +(()=>{"use strict";var t={d:(e,i)=>{for(var n in i)t.o(i,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:i[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{afterMain:()=>E,afterRead:()=>v,afterWrite:()=>C,applyStyles:()=>$,arrow:()=>J,auto:()=>a,basePlacements:()=>l,beforeMain:()=>y,beforeRead:()=>_,beforeWrite:()=>A,bottom:()=>s,clippingParents:()=>d,computeStyles:()=>it,createPopper:()=>Dt,createPopperBase:()=>St,createPopperLite:()=>$t,detectOverflow:()=>_t,end:()=>h,eventListeners:()=>st,flip:()=>bt,hide:()=>wt,left:()=>r,main:()=>w,modifierPhases:()=>O,offset:()=>Et,placements:()=>g,popper:()=>f,popperGenerator:()=>Lt,popperOffsets:()=>At,preventOverflow:()=>Tt,read:()=>b,reference:()=>p,right:()=>o,start:()=>c,top:()=>n,variationPlacements:()=>m,viewport:()=>u,write:()=>T});var i={};t.r(i),t.d(i,{Alert:()=>Oe,Button:()=>ke,Carousel:()=>li,Collapse:()=>Ei,Dropdown:()=>Ki,Modal:()=>Ln,Offcanvas:()=>Kn,Popover:()=>bs,ScrollSpy:()=>Ls,Tab:()=>Js,Toast:()=>po,Tooltip:()=>fs});var n="top",s="bottom",o="right",r="left",a="auto",l=[n,s,o,r],c="start",h="end",d="clippingParents",u="viewport",f="popper",p="reference",m=l.reduce((function(t,e){return t.concat([e+"-"+c,e+"-"+h])}),[]),g=[].concat(l,[a]).reduce((function(t,e){return t.concat([e,e+"-"+c,e+"-"+h])}),[]),_="beforeRead",b="read",v="afterRead",y="beforeMain",w="main",E="afterMain",A="beforeWrite",T="write",C="afterWrite",O=[_,b,v,y,w,E,A,T,C];function x(t){return t?(t.nodeName||"").toLowerCase():null}function k(t){if(null==t)return window;if("[object Window]"!==t.toString()){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function L(t){return t instanceof k(t).Element||t instanceof Element}function S(t){return t instanceof k(t).HTMLElement||t instanceof HTMLElement}function D(t){return"undefined"!=typeof ShadowRoot&&(t instanceof k(t).ShadowRoot||t instanceof ShadowRoot)}const $={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach((function(t){var i=e.styles[t]||{},n=e.attributes[t]||{},s=e.elements[t];S(s)&&x(s)&&(Object.assign(s.style,i),Object.keys(n).forEach((function(t){var e=n[t];!1===e?s.removeAttribute(t):s.setAttribute(t,!0===e?"":e)})))}))},effect:function(t){var e=t.state,i={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,i.popper),e.styles=i,e.elements.arrow&&Object.assign(e.elements.arrow.style,i.arrow),function(){Object.keys(e.elements).forEach((function(t){var n=e.elements[t],s=e.attributes[t]||{},o=Object.keys(e.styles.hasOwnProperty(t)?e.styles[t]:i[t]).reduce((function(t,e){return t[e]="",t}),{});S(n)&&x(n)&&(Object.assign(n.style,o),Object.keys(s).forEach((function(t){n.removeAttribute(t)})))}))}},requires:["computeStyles"]};function I(t){return t.split("-")[0]}var N=Math.max,P=Math.min,M=Math.round;function j(){var t=navigator.userAgentData;return null!=t&&t.brands&&Array.isArray(t.brands)?t.brands.map((function(t){return t.brand+"/"+t.version})).join(" "):navigator.userAgent}function F(){return!/^((?!chrome|android).)*safari/i.test(j())}function H(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1);var n=t.getBoundingClientRect(),s=1,o=1;e&&S(t)&&(s=t.offsetWidth>0&&M(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&M(n.height)/t.offsetHeight||1);var r=(L(t)?k(t):window).visualViewport,a=!F()&&i,l=(n.left+(a&&r?r.offsetLeft:0))/s,c=(n.top+(a&&r?r.offsetTop:0))/o,h=n.width/s,d=n.height/o;return{width:h,height:d,top:c,right:l+h,bottom:c+d,left:l,x:l,y:c}}function B(t){var e=H(t),i=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-i)<=1&&(i=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:i,height:n}}function W(t,e){var i=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(i&&D(i)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function z(t){return k(t).getComputedStyle(t)}function R(t){return["table","td","th"].indexOf(x(t))>=0}function q(t){return((L(t)?t.ownerDocument:t.document)||window.document).documentElement}function V(t){return"html"===x(t)?t:t.assignedSlot||t.parentNode||(D(t)?t.host:null)||q(t)}function Y(t){return S(t)&&"fixed"!==z(t).position?t.offsetParent:null}function K(t){for(var e=k(t),i=Y(t);i&&R(i)&&"static"===z(i).position;)i=Y(i);return i&&("html"===x(i)||"body"===x(i)&&"static"===z(i).position)?e:i||function(t){var e=/firefox/i.test(j());if(/Trident/i.test(j())&&S(t)&&"fixed"===z(t).position)return null;var i=V(t);for(D(i)&&(i=i.host);S(i)&&["html","body"].indexOf(x(i))<0;){var n=z(i);if("none"!==n.transform||"none"!==n.perspective||"paint"===n.contain||-1!==["transform","perspective"].indexOf(n.willChange)||e&&"filter"===n.willChange||e&&n.filter&&"none"!==n.filter)return i;i=i.parentNode}return null}(t)||e}function Q(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function X(t,e,i){return N(t,P(e,i))}function U(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function G(t,e){return e.reduce((function(e,i){return e[i]=t,e}),{})}const J={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,i=t.state,a=t.name,c=t.options,h=i.elements.arrow,d=i.modifiersData.popperOffsets,u=I(i.placement),f=Q(u),p=[r,o].indexOf(u)>=0?"height":"width";if(h&&d){var m=function(t,e){return U("number"!=typeof(t="function"==typeof t?t(Object.assign({},e.rects,{placement:e.placement})):t)?t:G(t,l))}(c.padding,i),g=B(h),_="y"===f?n:r,b="y"===f?s:o,v=i.rects.reference[p]+i.rects.reference[f]-d[f]-i.rects.popper[p],y=d[f]-i.rects.reference[f],w=K(h),E=w?"y"===f?w.clientHeight||0:w.clientWidth||0:0,A=v/2-y/2,T=m[_],C=E-g[p]-m[b],O=E/2-g[p]/2+A,x=X(T,O,C),k=f;i.modifiersData[a]=((e={})[k]=x,e.centerOffset=x-O,e)}},effect:function(t){var e=t.state,i=t.options.element,n=void 0===i?"[data-popper-arrow]":i;null!=n&&("string"!=typeof n||(n=e.elements.popper.querySelector(n)))&&W(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Z(t){return t.split("-")[1]}var tt={top:"auto",right:"auto",bottom:"auto",left:"auto"};function et(t){var e,i=t.popper,a=t.popperRect,l=t.placement,c=t.variation,d=t.offsets,u=t.position,f=t.gpuAcceleration,p=t.adaptive,m=t.roundOffsets,g=t.isFixed,_=d.x,b=void 0===_?0:_,v=d.y,y=void 0===v?0:v,w="function"==typeof m?m({x:b,y}):{x:b,y};b=w.x,y=w.y;var E=d.hasOwnProperty("x"),A=d.hasOwnProperty("y"),T=r,C=n,O=window;if(p){var x=K(i),L="clientHeight",S="clientWidth";x===k(i)&&"static"!==z(x=q(i)).position&&"absolute"===u&&(L="scrollHeight",S="scrollWidth"),(l===n||(l===r||l===o)&&c===h)&&(C=s,y-=(g&&x===O&&O.visualViewport?O.visualViewport.height:x[L])-a.height,y*=f?1:-1),l!==r&&(l!==n&&l!==s||c!==h)||(T=o,b-=(g&&x===O&&O.visualViewport?O.visualViewport.width:x[S])-a.width,b*=f?1:-1)}var D,$=Object.assign({position:u},p&&tt),I=!0===m?function(t,e){var i=t.x,n=t.y,s=e.devicePixelRatio||1;return{x:M(i*s)/s||0,y:M(n*s)/s||0}}({x:b,y},k(i)):{x:b,y};return b=I.x,y=I.y,f?Object.assign({},$,((D={})[C]=A?"0":"",D[T]=E?"0":"",D.transform=(O.devicePixelRatio||1)<=1?"translate("+b+"px, "+y+"px)":"translate3d("+b+"px, "+y+"px, 0)",D)):Object.assign({},$,((e={})[C]=A?y+"px":"",e[T]=E?b+"px":"",e.transform="",e))}const it={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(t){var e=t.state,i=t.options,n=i.gpuAcceleration,s=void 0===n||n,o=i.adaptive,r=void 0===o||o,a=i.roundOffsets,l=void 0===a||a,c={placement:I(e.placement),variation:Z(e.placement),popper:e.elements.popper,popperRect:e.rects.popper,gpuAcceleration:s,isFixed:"fixed"===e.options.strategy};null!=e.modifiersData.popperOffsets&&(e.styles.popper=Object.assign({},e.styles.popper,et(Object.assign({},c,{offsets:e.modifiersData.popperOffsets,position:e.options.strategy,adaptive:r,roundOffsets:l})))),null!=e.modifiersData.arrow&&(e.styles.arrow=Object.assign({},e.styles.arrow,et(Object.assign({},c,{offsets:e.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:l})))),e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-placement":e.placement})},data:{}};var nt={passive:!0};const st={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(t){var e=t.state,i=t.instance,n=t.options,s=n.scroll,o=void 0===s||s,r=n.resize,a=void 0===r||r,l=k(e.elements.popper),c=[].concat(e.scrollParents.reference,e.scrollParents.popper);return o&&c.forEach((function(t){t.addEventListener("scroll",i.update,nt)})),a&&l.addEventListener("resize",i.update,nt),function(){o&&c.forEach((function(t){t.removeEventListener("scroll",i.update,nt)})),a&&l.removeEventListener("resize",i.update,nt)}},data:{}};var ot={left:"right",right:"left",bottom:"top",top:"bottom"};function rt(t){return t.replace(/left|right|bottom|top/g,(function(t){return ot[t]}))}var at={start:"end",end:"start"};function lt(t){return t.replace(/start|end/g,(function(t){return at[t]}))}function ct(t){var e=k(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function ht(t){return H(q(t)).left+ct(t).scrollLeft}function dt(t){var e=z(t),i=e.overflow,n=e.overflowX,s=e.overflowY;return/auto|scroll|overlay|hidden/.test(i+s+n)}function ut(t){return["html","body","#document"].indexOf(x(t))>=0?t.ownerDocument.body:S(t)&&dt(t)?t:ut(V(t))}function ft(t,e){var i;void 0===e&&(e=[]);var n=ut(t),s=n===(null==(i=t.ownerDocument)?void 0:i.body),o=k(n),r=s?[o].concat(o.visualViewport||[],dt(n)?n:[]):n,a=e.concat(r);return s?a:a.concat(ft(V(r)))}function pt(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function mt(t,e,i){return e===u?pt(function(t,e){var i=k(t),n=q(t),s=i.visualViewport,o=n.clientWidth,r=n.clientHeight,a=0,l=0;if(s){o=s.width,r=s.height;var c=F();(c||!c&&"fixed"===e)&&(a=s.offsetLeft,l=s.offsetTop)}return{width:o,height:r,x:a+ht(t),y:l}}(t,i)):L(e)?function(t,e){var i=H(t,!1,"fixed"===e);return i.top=i.top+t.clientTop,i.left=i.left+t.clientLeft,i.bottom=i.top+t.clientHeight,i.right=i.left+t.clientWidth,i.width=t.clientWidth,i.height=t.clientHeight,i.x=i.left,i.y=i.top,i}(e,i):pt(function(t){var e,i=q(t),n=ct(t),s=null==(e=t.ownerDocument)?void 0:e.body,o=N(i.scrollWidth,i.clientWidth,s?s.scrollWidth:0,s?s.clientWidth:0),r=N(i.scrollHeight,i.clientHeight,s?s.scrollHeight:0,s?s.clientHeight:0),a=-n.scrollLeft+ht(t),l=-n.scrollTop;return"rtl"===z(s||i).direction&&(a+=N(i.clientWidth,s?s.clientWidth:0)-o),{width:o,height:r,x:a,y:l}}(q(t)))}function gt(t){var e,i=t.reference,a=t.element,l=t.placement,d=l?I(l):null,u=l?Z(l):null,f=i.x+i.width/2-a.width/2,p=i.y+i.height/2-a.height/2;switch(d){case n:e={x:f,y:i.y-a.height};break;case s:e={x:f,y:i.y+i.height};break;case o:e={x:i.x+i.width,y:p};break;case r:e={x:i.x-a.width,y:p};break;default:e={x:i.x,y:i.y}}var m=d?Q(d):null;if(null!=m){var g="y"===m?"height":"width";switch(u){case c:e[m]=e[m]-(i[g]/2-a[g]/2);break;case h:e[m]=e[m]+(i[g]/2-a[g]/2)}}return e}function _t(t,e){void 0===e&&(e={});var i=e,r=i.placement,a=void 0===r?t.placement:r,c=i.strategy,h=void 0===c?t.strategy:c,m=i.boundary,g=void 0===m?d:m,_=i.rootBoundary,b=void 0===_?u:_,v=i.elementContext,y=void 0===v?f:v,w=i.altBoundary,E=void 0!==w&&w,A=i.padding,T=void 0===A?0:A,C=U("number"!=typeof T?T:G(T,l)),O=y===f?p:f,k=t.rects.popper,D=t.elements[E?O:y],$=function(t,e,i,n){var s="clippingParents"===e?function(t){var e=ft(V(t)),i=["absolute","fixed"].indexOf(z(t).position)>=0&&S(t)?K(t):t;return L(i)?e.filter((function(t){return L(t)&&W(t,i)&&"body"!==x(t)})):[]}(t):[].concat(e),o=[].concat(s,[i]),r=o[0],a=o.reduce((function(e,i){var s=mt(t,i,n);return e.top=N(s.top,e.top),e.right=P(s.right,e.right),e.bottom=P(s.bottom,e.bottom),e.left=N(s.left,e.left),e}),mt(t,r,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}(L(D)?D:D.contextElement||q(t.elements.popper),g,b,h),I=H(t.elements.reference),M=gt({reference:I,element:k,strategy:"absolute",placement:a}),j=pt(Object.assign({},k,M)),F=y===f?j:I,B={top:$.top-F.top+C.top,bottom:F.bottom-$.bottom+C.bottom,left:$.left-F.left+C.left,right:F.right-$.right+C.right},R=t.modifiersData.offset;if(y===f&&R){var Y=R[a];Object.keys(B).forEach((function(t){var e=[o,s].indexOf(t)>=0?1:-1,i=[n,s].indexOf(t)>=0?"y":"x";B[t]+=Y[i]*e}))}return B}const bt={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,h=t.name;if(!e.modifiersData[h]._skip){for(var d=i.mainAxis,u=void 0===d||d,f=i.altAxis,p=void 0===f||f,_=i.fallbackPlacements,b=i.padding,v=i.boundary,y=i.rootBoundary,w=i.altBoundary,E=i.flipVariations,A=void 0===E||E,T=i.allowedAutoPlacements,C=e.options.placement,O=I(C),x=_||(O!==C&&A?function(t){if(I(t)===a)return[];var e=rt(t);return[lt(t),e,lt(e)]}(C):[rt(C)]),k=[C].concat(x).reduce((function(t,i){return t.concat(I(i)===a?function(t,e){void 0===e&&(e={});var i=e,n=i.placement,s=i.boundary,o=i.rootBoundary,r=i.padding,a=i.flipVariations,c=i.allowedAutoPlacements,h=void 0===c?g:c,d=Z(n),u=d?a?m:m.filter((function(t){return Z(t)===d})):l,f=u.filter((function(t){return h.indexOf(t)>=0}));0===f.length&&(f=u);var p=f.reduce((function(e,i){return e[i]=_t(t,{placement:i,boundary:s,rootBoundary:o,padding:r})[I(i)],e}),{});return Object.keys(p).sort((function(t,e){return p[t]-p[e]}))}(e,{placement:i,boundary:v,rootBoundary:y,padding:b,flipVariations:A,allowedAutoPlacements:T}):i)}),[]),L=e.rects.reference,S=e.rects.popper,D=new Map,$=!0,N=k[0],P=0;P=0,B=H?"width":"height",W=_t(e,{placement:M,boundary:v,rootBoundary:y,altBoundary:w,padding:b}),z=H?F?o:r:F?s:n;L[B]>S[B]&&(z=rt(z));var R=rt(z),q=[];if(u&&q.push(W[j]<=0),p&&q.push(W[z]<=0,W[R]<=0),q.every((function(t){return t}))){N=M,$=!1;break}D.set(M,q)}if($)for(var V=function(t){var e=k.find((function(e){var i=D.get(e);if(i)return i.slice(0,t).every((function(t){return t}))}));if(e)return N=e,"break"},Y=A?3:1;Y>0&&"break"!==V(Y);Y--);e.placement!==N&&(e.modifiersData[h]._skip=!0,e.placement=N,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function vt(t,e,i){return void 0===i&&(i={x:0,y:0}),{top:t.top-e.height-i.y,right:t.right-e.width+i.x,bottom:t.bottom-e.height+i.y,left:t.left-e.width-i.x}}function yt(t){return[n,o,s,r].some((function(e){return t[e]>=0}))}const wt={name:"hide",enabled:!0,phase:"main",requiresIfExists:["preventOverflow"],fn:function(t){var e=t.state,i=t.name,n=e.rects.reference,s=e.rects.popper,o=e.modifiersData.preventOverflow,r=_t(e,{elementContext:"reference"}),a=_t(e,{altBoundary:!0}),l=vt(r,n),c=vt(a,s,o),h=yt(l),d=yt(c);e.modifiersData[i]={referenceClippingOffsets:l,popperEscapeOffsets:c,isReferenceHidden:h,hasPopperEscaped:d},e.attributes.popper=Object.assign({},e.attributes.popper,{"data-popper-reference-hidden":h,"data-popper-escaped":d})}},Et={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,i=t.options,s=t.name,a=i.offset,l=void 0===a?[0,0]:a,c=g.reduce((function(t,i){return t[i]=function(t,e,i){var s=I(t),a=[r,n].indexOf(s)>=0?-1:1,l="function"==typeof i?i(Object.assign({},e,{placement:t})):i,c=l[0],h=l[1];return c=c||0,h=(h||0)*a,[r,o].indexOf(s)>=0?{x:h,y:c}:{x:c,y:h}}(i,e.rects,l),t}),{}),h=c[e.placement],d=h.x,u=h.y;null!=e.modifiersData.popperOffsets&&(e.modifiersData.popperOffsets.x+=d,e.modifiersData.popperOffsets.y+=u),e.modifiersData[s]=c}},At={name:"popperOffsets",enabled:!0,phase:"read",fn:function(t){var e=t.state,i=t.name;e.modifiersData[i]=gt({reference:e.rects.reference,element:e.rects.popper,strategy:"absolute",placement:e.placement})},data:{}},Tt={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,i=t.options,a=t.name,l=i.mainAxis,h=void 0===l||l,d=i.altAxis,u=void 0!==d&&d,f=i.boundary,p=i.rootBoundary,m=i.altBoundary,g=i.padding,_=i.tether,b=void 0===_||_,v=i.tetherOffset,y=void 0===v?0:v,w=_t(e,{boundary:f,rootBoundary:p,padding:g,altBoundary:m}),E=I(e.placement),A=Z(e.placement),T=!A,C=Q(E),O="x"===C?"y":"x",x=e.modifiersData.popperOffsets,k=e.rects.reference,L=e.rects.popper,S="function"==typeof y?y(Object.assign({},e.rects,{placement:e.placement})):y,D="number"==typeof S?{mainAxis:S,altAxis:S}:Object.assign({mainAxis:0,altAxis:0},S),$=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,M={x:0,y:0};if(x){if(h){var j,F="y"===C?n:r,H="y"===C?s:o,W="y"===C?"height":"width",z=x[C],R=z+w[F],q=z-w[H],V=b?-L[W]/2:0,Y=A===c?k[W]:L[W],U=A===c?-L[W]:-k[W],G=e.elements.arrow,J=b&&G?B(G):{width:0,height:0},tt=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},et=tt[F],it=tt[H],nt=X(0,k[W],J[W]),st=T?k[W]/2-V-nt-et-D.mainAxis:Y-nt-et-D.mainAxis,ot=T?-k[W]/2+V+nt+it+D.mainAxis:U+nt+it+D.mainAxis,rt=e.elements.arrow&&K(e.elements.arrow),at=rt?"y"===C?rt.clientTop||0:rt.clientLeft||0:0,lt=null!=(j=null==$?void 0:$[C])?j:0,ct=z+ot-lt,ht=X(b?P(R,z+st-lt-at):R,z,b?N(q,ct):q);x[C]=ht,M[C]=ht-z}if(u){var dt,ut="x"===C?n:r,ft="x"===C?s:o,pt=x[O],mt="y"===O?"height":"width",gt=pt+w[ut],bt=pt-w[ft],vt=-1!==[n,r].indexOf(E),yt=null!=(dt=null==$?void 0:$[O])?dt:0,wt=vt?gt:pt-k[mt]-L[mt]-yt+D.altAxis,Et=vt?pt+k[mt]+L[mt]-yt-D.altAxis:bt,At=b&&vt?function(t,e,i){var n=X(t,e,i);return n>i?i:n}(wt,pt,Et):X(b?wt:gt,pt,b?Et:bt);x[O]=At,M[O]=At-pt}e.modifiersData[a]=M}},requiresIfExists:["offset"]};function Ct(t,e,i){void 0===i&&(i=!1);var n,s,o=S(e),r=S(e)&&function(t){var e=t.getBoundingClientRect(),i=M(e.width)/t.offsetWidth||1,n=M(e.height)/t.offsetHeight||1;return 1!==i||1!==n}(e),a=q(e),l=H(t,r,i),c={scrollLeft:0,scrollTop:0},h={x:0,y:0};return(o||!o&&!i)&&(("body"!==x(e)||dt(a))&&(c=(n=e)!==k(n)&&S(n)?{scrollLeft:(s=n).scrollLeft,scrollTop:s.scrollTop}:ct(n)),S(e)?((h=H(e,!0)).x+=e.clientLeft,h.y+=e.clientTop):a&&(h.x=ht(a))),{x:l.left+c.scrollLeft-h.x,y:l.top+c.scrollTop-h.y,width:l.width,height:l.height}}function Ot(t){var e=new Map,i=new Set,n=[];function s(t){i.add(t.name),[].concat(t.requires||[],t.requiresIfExists||[]).forEach((function(t){if(!i.has(t)){var n=e.get(t);n&&s(n)}})),n.push(t)}return t.forEach((function(t){e.set(t.name,t)})),t.forEach((function(t){i.has(t.name)||s(t)})),n}var xt={placement:"bottom",modifiers:[],strategy:"absolute"};function kt(){for(var t=arguments.length,e=new Array(t),i=0;iIt.has(t)&&It.get(t).get(e)||null,remove(t,e){if(!It.has(t))return;const i=It.get(t);i.delete(e),0===i.size&&It.delete(t)}},Pt="transitionend",Mt=t=>(t&&window.CSS&&window.CSS.escape&&(t=t.replace(/#([^\s"#']+)/g,((t,e)=>`#${CSS.escape(e)}`))),t),jt=t=>{t.dispatchEvent(new Event(Pt))},Ft=t=>!(!t||"object"!=typeof t)&&(void 0!==t.jquery&&(t=t[0]),void 0!==t.nodeType),Ht=t=>Ft(t)?t.jquery?t[0]:t:"string"==typeof t&&t.length>0?document.querySelector(Mt(t)):null,Bt=t=>{if(!Ft(t)||0===t.getClientRects().length)return!1;const e="visible"===getComputedStyle(t).getPropertyValue("visibility"),i=t.closest("details:not([open])");if(!i)return e;if(i!==t){const e=t.closest("summary");if(e&&e.parentNode!==i)return!1;if(null===e)return!1}return e},Wt=t=>!t||t.nodeType!==Node.ELEMENT_NODE||!!t.classList.contains("disabled")||(void 0!==t.disabled?t.disabled:t.hasAttribute("disabled")&&"false"!==t.getAttribute("disabled")),zt=t=>{if(!document.documentElement.attachShadow)return null;if("function"==typeof t.getRootNode){const e=t.getRootNode();return e instanceof ShadowRoot?e:null}return t instanceof ShadowRoot?t:t.parentNode?zt(t.parentNode):null},Rt=()=>{},qt=t=>{t.offsetHeight},Vt=()=>window.jQuery&&!document.body.hasAttribute("data-bs-no-jquery")?window.jQuery:null,Yt=[],Kt=()=>"rtl"===document.documentElement.dir,Qt=t=>{var e;e=()=>{const e=Vt();if(e){const i=t.NAME,n=e.fn[i];e.fn[i]=t.jQueryInterface,e.fn[i].Constructor=t,e.fn[i].noConflict=()=>(e.fn[i]=n,t.jQueryInterface)}},"loading"===document.readyState?(Yt.length||document.addEventListener("DOMContentLoaded",(()=>{for(const t of Yt)t()})),Yt.push(e)):e()},Xt=(t,e=[],i=t)=>"function"==typeof t?t(...e):i,Ut=(t,e,i=!0)=>{if(!i)return void Xt(t);const n=(t=>{if(!t)return 0;let{transitionDuration:e,transitionDelay:i}=window.getComputedStyle(t);const n=Number.parseFloat(e),s=Number.parseFloat(i);return n||s?(e=e.split(",")[0],i=i.split(",")[0],1e3*(Number.parseFloat(e)+Number.parseFloat(i))):0})(e)+5;let s=!1;const o=({target:i})=>{i===e&&(s=!0,e.removeEventListener(Pt,o),Xt(t))};e.addEventListener(Pt,o),setTimeout((()=>{s||jt(e)}),n)},Gt=(t,e,i,n)=>{const s=t.length;let o=t.indexOf(e);return-1===o?!i&&n?t[s-1]:t[0]:(o+=i?1:-1,n&&(o=(o+s)%s),t[Math.max(0,Math.min(o,s-1))])},Jt=/[^.]*(?=\..*)\.|.*/,Zt=/\..*/,te=/::\d+$/,ee={};let ie=1;const ne={mouseenter:"mouseover",mouseleave:"mouseout"},se=new Set(["click","dblclick","mouseup","mousedown","contextmenu","mousewheel","DOMMouseScroll","mouseover","mouseout","mousemove","selectstart","selectend","keydown","keypress","keyup","orientationchange","touchstart","touchmove","touchend","touchcancel","pointerdown","pointermove","pointerup","pointerleave","pointercancel","gesturestart","gesturechange","gestureend","focus","blur","change","reset","select","submit","focusin","focusout","load","unload","beforeunload","resize","move","DOMContentLoaded","readystatechange","error","abort","scroll"]);function oe(t,e){return e&&`${e}::${ie++}`||t.uidEvent||ie++}function re(t){const e=oe(t);return t.uidEvent=e,ee[e]=ee[e]||{},ee[e]}function ae(t,e,i=null){return Object.values(t).find((t=>t.callable===e&&t.delegationSelector===i))}function le(t,e,i){const n="string"==typeof e,s=n?i:e||i;let o=ue(t);return se.has(o)||(o=t),[n,s,o]}function ce(t,e,i,n,s){if("string"!=typeof e||!t)return;let[o,r,a]=le(e,i,n);if(e in ne){const t=t=>function(e){if(!e.relatedTarget||e.relatedTarget!==e.delegateTarget&&!e.delegateTarget.contains(e.relatedTarget))return t.call(this,e)};r=t(r)}const l=re(t),c=l[a]||(l[a]={}),h=ae(c,r,o?i:null);if(h)return void(h.oneOff=h.oneOff&&s);const d=oe(r,e.replace(Jt,"")),u=o?function(t,e,i){return function n(s){const o=t.querySelectorAll(e);for(let{target:r}=s;r&&r!==this;r=r.parentNode)for(const a of o)if(a===r)return pe(s,{delegateTarget:r}),n.oneOff&&fe.off(t,s.type,e,i),i.apply(r,[s])}}(t,i,r):function(t,e){return function i(n){return pe(n,{delegateTarget:t}),i.oneOff&&fe.off(t,n.type,e),e.apply(t,[n])}}(t,r);u.delegationSelector=o?i:null,u.callable=r,u.oneOff=s,u.uidEvent=d,c[d]=u,t.addEventListener(a,u,o)}function he(t,e,i,n,s){const o=ae(e[i],n,s);o&&(t.removeEventListener(i,o,Boolean(s)),delete e[i][o.uidEvent])}function de(t,e,i,n){const s=e[i]||{};for(const[o,r]of Object.entries(s))o.includes(n)&&he(t,e,i,r.callable,r.delegationSelector)}function ue(t){return t=t.replace(Zt,""),ne[t]||t}const fe={on(t,e,i,n){ce(t,e,i,n,!1)},one(t,e,i,n){ce(t,e,i,n,!0)},off(t,e,i,n){if("string"!=typeof e||!t)return;const[s,o,r]=le(e,i,n),a=r!==e,l=re(t),c=l[r]||{},h=e.startsWith(".");if(void 0===o){if(h)for(const i of Object.keys(l))de(t,l,i,e.slice(1));for(const[i,n]of Object.entries(c)){const s=i.replace(te,"");a&&!e.includes(s)||he(t,l,r,n.callable,n.delegationSelector)}}else{if(!Object.keys(c).length)return;he(t,l,r,o,s?i:null)}},trigger(t,e,i){if("string"!=typeof e||!t)return null;const n=Vt();let s=null,o=!0,r=!0,a=!1;e!==ue(e)&&n&&(s=n.Event(e,i),n(t).trigger(s),o=!s.isPropagationStopped(),r=!s.isImmediatePropagationStopped(),a=s.isDefaultPrevented());const l=pe(new Event(e,{bubbles:o,cancelable:!0}),i);return a&&l.preventDefault(),r&&t.dispatchEvent(l),l.defaultPrevented&&s&&s.preventDefault(),l}};function pe(t,e={}){for(const[i,n]of Object.entries(e))try{t[i]=n}catch(e){Object.defineProperty(t,i,{configurable:!0,get:()=>n})}return t}function me(t){if("true"===t)return!0;if("false"===t)return!1;if(t===Number(t).toString())return Number(t);if(""===t||"null"===t)return null;if("string"!=typeof t)return t;try{return JSON.parse(decodeURIComponent(t))}catch(e){return t}}function ge(t){return t.replace(/[A-Z]/g,(t=>`-${t.toLowerCase()}`))}const _e={setDataAttribute(t,e,i){t.setAttribute(`data-bs-${ge(e)}`,i)},removeDataAttribute(t,e){t.removeAttribute(`data-bs-${ge(e)}`)},getDataAttributes(t){if(!t)return{};const e={},i=Object.keys(t.dataset).filter((t=>t.startsWith("bs")&&!t.startsWith("bsConfig")));for(const n of i){let i=n.replace(/^bs/,"");i=i.charAt(0).toLowerCase()+i.slice(1,i.length),e[i]=me(t.dataset[n])}return e},getDataAttribute:(t,e)=>me(t.getAttribute(`data-bs-${ge(e)}`))};class be{static get Default(){return{}}static get DefaultType(){return{}}static get NAME(){throw new Error('You have to implement the static method "NAME", for each component!')}_getConfig(t){return t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t}_mergeConfigObj(t,e){const i=Ft(e)?_e.getDataAttribute(e,"config"):{};return{...this.constructor.Default,..."object"==typeof i?i:{},...Ft(e)?_e.getDataAttributes(e):{},..."object"==typeof t?t:{}}}_typeCheckConfig(t,e=this.constructor.DefaultType){for(const[n,s]of Object.entries(e)){const e=t[n],o=Ft(e)?"element":null==(i=e)?`${i}`:Object.prototype.toString.call(i).match(/\s([a-z]+)/i)[1].toLowerCase();if(!new RegExp(s).test(o))throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${n}" provided type "${o}" but expected type "${s}".`)}var i}}class ve extends be{constructor(t,e){super(),(t=Ht(t))&&(this._element=t,this._config=this._getConfig(e),Nt.set(this._element,this.constructor.DATA_KEY,this))}dispose(){Nt.remove(this._element,this.constructor.DATA_KEY),fe.off(this._element,this.constructor.EVENT_KEY);for(const t of Object.getOwnPropertyNames(this))this[t]=null}_queueCallback(t,e,i=!0){Ut(t,e,i)}_getConfig(t){return t=this._mergeConfigObj(t,this._element),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}static getInstance(t){return Nt.get(Ht(t),this.DATA_KEY)}static getOrCreateInstance(t,e={}){return this.getInstance(t)||new this(t,"object"==typeof e?e:null)}static get VERSION(){return"5.3.3"}static get DATA_KEY(){return`bs.${this.NAME}`}static get EVENT_KEY(){return`.${this.DATA_KEY}`}static eventName(t){return`${t}${this.EVENT_KEY}`}}const ye=t=>{let e=t.getAttribute("data-bs-target");if(!e||"#"===e){let i=t.getAttribute("href");if(!i||!i.includes("#")&&!i.startsWith("."))return null;i.includes("#")&&!i.startsWith("#")&&(i=`#${i.split("#")[1]}`),e=i&&"#"!==i?i.trim():null}return e?e.split(",").map((t=>Mt(t))).join(","):null},we={find:(t,e=document.documentElement)=>[].concat(...Element.prototype.querySelectorAll.call(e,t)),findOne:(t,e=document.documentElement)=>Element.prototype.querySelector.call(e,t),children:(t,e)=>[].concat(...t.children).filter((t=>t.matches(e))),parents(t,e){const i=[];let n=t.parentNode.closest(e);for(;n;)i.push(n),n=n.parentNode.closest(e);return i},prev(t,e){let i=t.previousElementSibling;for(;i;){if(i.matches(e))return[i];i=i.previousElementSibling}return[]},next(t,e){let i=t.nextElementSibling;for(;i;){if(i.matches(e))return[i];i=i.nextElementSibling}return[]},focusableChildren(t){const e=["a","button","input","textarea","select","details","[tabindex]",'[contenteditable="true"]'].map((t=>`${t}:not([tabindex^="-"])`)).join(",");return this.find(e,t).filter((t=>!Wt(t)&&Bt(t)))},getSelectorFromElement(t){const e=ye(t);return e&&we.findOne(e)?e:null},getElementFromSelector(t){const e=ye(t);return e?we.findOne(e):null},getMultipleElementsFromSelector(t){const e=ye(t);return e?we.find(e):[]}},Ee=(t,e="hide")=>{const i=`click.dismiss${t.EVENT_KEY}`,n=t.NAME;fe.on(document,i,`[data-bs-dismiss="${n}"]`,(function(i){if(["A","AREA"].includes(this.tagName)&&i.preventDefault(),Wt(this))return;const s=we.getElementFromSelector(this)||this.closest(`.${n}`);t.getOrCreateInstance(s)[e]()}))},Ae=".bs.alert",Te=`close${Ae}`,Ce=`closed${Ae}`;class Oe extends ve{static get NAME(){return"alert"}close(){if(fe.trigger(this._element,Te).defaultPrevented)return;this._element.classList.remove("show");const t=this._element.classList.contains("fade");this._queueCallback((()=>this._destroyElement()),this._element,t)}_destroyElement(){this._element.remove(),fe.trigger(this._element,Ce),this.dispose()}static jQueryInterface(t){return this.each((function(){const e=Oe.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}Ee(Oe,"close"),Qt(Oe);const xe='[data-bs-toggle="button"]';class ke extends ve{static get NAME(){return"button"}toggle(){this._element.setAttribute("aria-pressed",this._element.classList.toggle("active"))}static jQueryInterface(t){return this.each((function(){const e=ke.getOrCreateInstance(this);"toggle"===t&&e[t]()}))}}fe.on(document,"click.bs.button.data-api",xe,(t=>{t.preventDefault();const e=t.target.closest(xe);ke.getOrCreateInstance(e).toggle()})),Qt(ke);const Le=".bs.swipe",Se=`touchstart${Le}`,De=`touchmove${Le}`,$e=`touchend${Le}`,Ie=`pointerdown${Le}`,Ne=`pointerup${Le}`,Pe={endCallback:null,leftCallback:null,rightCallback:null},Me={endCallback:"(function|null)",leftCallback:"(function|null)",rightCallback:"(function|null)"};class je extends be{constructor(t,e){super(),this._element=t,t&&je.isSupported()&&(this._config=this._getConfig(e),this._deltaX=0,this._supportPointerEvents=Boolean(window.PointerEvent),this._initEvents())}static get Default(){return Pe}static get DefaultType(){return Me}static get NAME(){return"swipe"}dispose(){fe.off(this._element,Le)}_start(t){this._supportPointerEvents?this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX):this._deltaX=t.touches[0].clientX}_end(t){this._eventIsPointerPenTouch(t)&&(this._deltaX=t.clientX-this._deltaX),this._handleSwipe(),Xt(this._config.endCallback)}_move(t){this._deltaX=t.touches&&t.touches.length>1?0:t.touches[0].clientX-this._deltaX}_handleSwipe(){const t=Math.abs(this._deltaX);if(t<=40)return;const e=t/this._deltaX;this._deltaX=0,e&&Xt(e>0?this._config.rightCallback:this._config.leftCallback)}_initEvents(){this._supportPointerEvents?(fe.on(this._element,Ie,(t=>this._start(t))),fe.on(this._element,Ne,(t=>this._end(t))),this._element.classList.add("pointer-event")):(fe.on(this._element,Se,(t=>this._start(t))),fe.on(this._element,De,(t=>this._move(t))),fe.on(this._element,$e,(t=>this._end(t))))}_eventIsPointerPenTouch(t){return this._supportPointerEvents&&("pen"===t.pointerType||"touch"===t.pointerType)}static isSupported(){return"ontouchstart"in document.documentElement||navigator.maxTouchPoints>0}}const Fe=".bs.carousel",He=".data-api",Be="ArrowLeft",We="ArrowRight",ze="next",Re="prev",qe="left",Ve="right",Ye=`slide${Fe}`,Ke=`slid${Fe}`,Qe=`keydown${Fe}`,Xe=`mouseenter${Fe}`,Ue=`mouseleave${Fe}`,Ge=`dragstart${Fe}`,Je=`load${Fe}${He}`,Ze=`click${Fe}${He}`,ti="carousel",ei="active",ii=".active",ni=".carousel-item",si=ii+ni,oi={[Be]:Ve,[We]:qe},ri={interval:5e3,keyboard:!0,pause:"hover",ride:!1,touch:!0,wrap:!0},ai={interval:"(number|boolean)",keyboard:"boolean",pause:"(string|boolean)",ride:"(boolean|string)",touch:"boolean",wrap:"boolean"};class li extends ve{constructor(t,e){super(t,e),this._interval=null,this._activeElement=null,this._isSliding=!1,this.touchTimeout=null,this._swipeHelper=null,this._indicatorsElement=we.findOne(".carousel-indicators",this._element),this._addEventListeners(),this._config.ride===ti&&this.cycle()}static get Default(){return ri}static get DefaultType(){return ai}static get NAME(){return"carousel"}next(){this._slide(ze)}nextWhenVisible(){!document.hidden&&Bt(this._element)&&this.next()}prev(){this._slide(Re)}pause(){this._isSliding&&jt(this._element),this._clearInterval()}cycle(){this._clearInterval(),this._updateInterval(),this._interval=setInterval((()=>this.nextWhenVisible()),this._config.interval)}_maybeEnableCycle(){this._config.ride&&(this._isSliding?fe.one(this._element,Ke,(()=>this.cycle())):this.cycle())}to(t){const e=this._getItems();if(t>e.length-1||t<0)return;if(this._isSliding)return void fe.one(this._element,Ke,(()=>this.to(t)));const i=this._getItemIndex(this._getActive());if(i===t)return;const n=t>i?ze:Re;this._slide(n,e[t])}dispose(){this._swipeHelper&&this._swipeHelper.dispose(),super.dispose()}_configAfterMerge(t){return t.defaultInterval=t.interval,t}_addEventListeners(){this._config.keyboard&&fe.on(this._element,Qe,(t=>this._keydown(t))),"hover"===this._config.pause&&(fe.on(this._element,Xe,(()=>this.pause())),fe.on(this._element,Ue,(()=>this._maybeEnableCycle()))),this._config.touch&&je.isSupported()&&this._addTouchEventListeners()}_addTouchEventListeners(){for(const t of we.find(".carousel-item img",this._element))fe.on(t,Ge,(t=>t.preventDefault()));const t={leftCallback:()=>this._slide(this._directionToOrder(qe)),rightCallback:()=>this._slide(this._directionToOrder(Ve)),endCallback:()=>{"hover"===this._config.pause&&(this.pause(),this.touchTimeout&&clearTimeout(this.touchTimeout),this.touchTimeout=setTimeout((()=>this._maybeEnableCycle()),500+this._config.interval))}};this._swipeHelper=new je(this._element,t)}_keydown(t){if(/input|textarea/i.test(t.target.tagName))return;const e=oi[t.key];e&&(t.preventDefault(),this._slide(this._directionToOrder(e)))}_getItemIndex(t){return this._getItems().indexOf(t)}_setActiveIndicatorElement(t){if(!this._indicatorsElement)return;const e=we.findOne(ii,this._indicatorsElement);e.classList.remove(ei),e.removeAttribute("aria-current");const i=we.findOne(`[data-bs-slide-to="${t}"]`,this._indicatorsElement);i&&(i.classList.add(ei),i.setAttribute("aria-current","true"))}_updateInterval(){const t=this._activeElement||this._getActive();if(!t)return;const e=Number.parseInt(t.getAttribute("data-bs-interval"),10);this._config.interval=e||this._config.defaultInterval}_slide(t,e=null){if(this._isSliding)return;const i=this._getActive(),n=t===ze,s=e||Gt(this._getItems(),i,n,this._config.wrap);if(s===i)return;const o=this._getItemIndex(s),r=e=>fe.trigger(this._element,e,{relatedTarget:s,direction:this._orderToDirection(t),from:this._getItemIndex(i),to:o});if(r(Ye).defaultPrevented)return;if(!i||!s)return;const a=Boolean(this._interval);this.pause(),this._isSliding=!0,this._setActiveIndicatorElement(o),this._activeElement=s;const l=n?"carousel-item-start":"carousel-item-end",c=n?"carousel-item-next":"carousel-item-prev";s.classList.add(c),qt(s),i.classList.add(l),s.classList.add(l),this._queueCallback((()=>{s.classList.remove(l,c),s.classList.add(ei),i.classList.remove(ei,c,l),this._isSliding=!1,r(Ke)}),i,this._isAnimated()),a&&this.cycle()}_isAnimated(){return this._element.classList.contains("slide")}_getActive(){return we.findOne(si,this._element)}_getItems(){return we.find(ni,this._element)}_clearInterval(){this._interval&&(clearInterval(this._interval),this._interval=null)}_directionToOrder(t){return Kt()?t===qe?Re:ze:t===qe?ze:Re}_orderToDirection(t){return Kt()?t===Re?qe:Ve:t===Re?Ve:qe}static jQueryInterface(t){return this.each((function(){const e=li.getOrCreateInstance(this,t);if("number"!=typeof t){if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}else e.to(t)}))}}fe.on(document,Ze,"[data-bs-slide], [data-bs-slide-to]",(function(t){const e=we.getElementFromSelector(this);if(!e||!e.classList.contains(ti))return;t.preventDefault();const i=li.getOrCreateInstance(e),n=this.getAttribute("data-bs-slide-to");return n?(i.to(n),void i._maybeEnableCycle()):"next"===_e.getDataAttribute(this,"slide")?(i.next(),void i._maybeEnableCycle()):(i.prev(),void i._maybeEnableCycle())})),fe.on(window,Je,(()=>{const t=we.find('[data-bs-ride="carousel"]');for(const e of t)li.getOrCreateInstance(e)})),Qt(li);const ci=".bs.collapse",hi=`show${ci}`,di=`shown${ci}`,ui=`hide${ci}`,fi=`hidden${ci}`,pi=`click${ci}.data-api`,mi="show",gi="collapse",_i="collapsing",bi=`:scope .${gi} .${gi}`,vi='[data-bs-toggle="collapse"]',yi={parent:null,toggle:!0},wi={parent:"(null|element)",toggle:"boolean"};class Ei extends ve{constructor(t,e){super(t,e),this._isTransitioning=!1,this._triggerArray=[];const i=we.find(vi);for(const t of i){const e=we.getSelectorFromElement(t),i=we.find(e).filter((t=>t===this._element));null!==e&&i.length&&this._triggerArray.push(t)}this._initializeChildren(),this._config.parent||this._addAriaAndCollapsedClass(this._triggerArray,this._isShown()),this._config.toggle&&this.toggle()}static get Default(){return yi}static get DefaultType(){return wi}static get NAME(){return"collapse"}toggle(){this._isShown()?this.hide():this.show()}show(){if(this._isTransitioning||this._isShown())return;let t=[];if(this._config.parent&&(t=this._getFirstLevelChildren(".collapse.show, .collapse.collapsing").filter((t=>t!==this._element)).map((t=>Ei.getOrCreateInstance(t,{toggle:!1})))),t.length&&t[0]._isTransitioning)return;if(fe.trigger(this._element,hi).defaultPrevented)return;for(const e of t)e.hide();const e=this._getDimension();this._element.classList.remove(gi),this._element.classList.add(_i),this._element.style[e]=0,this._addAriaAndCollapsedClass(this._triggerArray,!0),this._isTransitioning=!0;const i=`scroll${e[0].toUpperCase()+e.slice(1)}`;this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(_i),this._element.classList.add(gi,mi),this._element.style[e]="",fe.trigger(this._element,di)}),this._element,!0),this._element.style[e]=`${this._element[i]}px`}hide(){if(this._isTransitioning||!this._isShown())return;if(fe.trigger(this._element,ui).defaultPrevented)return;const t=this._getDimension();this._element.style[t]=`${this._element.getBoundingClientRect()[t]}px`,qt(this._element),this._element.classList.add(_i),this._element.classList.remove(gi,mi);for(const t of this._triggerArray){const e=we.getElementFromSelector(t);e&&!this._isShown(e)&&this._addAriaAndCollapsedClass([t],!1)}this._isTransitioning=!0,this._element.style[t]="",this._queueCallback((()=>{this._isTransitioning=!1,this._element.classList.remove(_i),this._element.classList.add(gi),fe.trigger(this._element,fi)}),this._element,!0)}_isShown(t=this._element){return t.classList.contains(mi)}_configAfterMerge(t){return t.toggle=Boolean(t.toggle),t.parent=Ht(t.parent),t}_getDimension(){return this._element.classList.contains("collapse-horizontal")?"width":"height"}_initializeChildren(){if(!this._config.parent)return;const t=this._getFirstLevelChildren(vi);for(const e of t){const t=we.getElementFromSelector(e);t&&this._addAriaAndCollapsedClass([e],this._isShown(t))}}_getFirstLevelChildren(t){const e=we.find(bi,this._config.parent);return we.find(t,this._config.parent).filter((t=>!e.includes(t)))}_addAriaAndCollapsedClass(t,e){if(t.length)for(const i of t)i.classList.toggle("collapsed",!e),i.setAttribute("aria-expanded",e)}static jQueryInterface(t){const e={};return"string"==typeof t&&/show|hide/.test(t)&&(e.toggle=!1),this.each((function(){const i=Ei.getOrCreateInstance(this,e);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t]()}}))}}fe.on(document,pi,vi,(function(t){("A"===t.target.tagName||t.delegateTarget&&"A"===t.delegateTarget.tagName)&&t.preventDefault();for(const t of we.getMultipleElementsFromSelector(this))Ei.getOrCreateInstance(t,{toggle:!1}).toggle()})),Qt(Ei);const Ai="dropdown",Ti=".bs.dropdown",Ci=".data-api",Oi="ArrowUp",xi="ArrowDown",ki=`hide${Ti}`,Li=`hidden${Ti}`,Si=`show${Ti}`,Di=`shown${Ti}`,$i=`click${Ti}${Ci}`,Ii=`keydown${Ti}${Ci}`,Ni=`keyup${Ti}${Ci}`,Pi="show",Mi='[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)',ji=`${Mi}.${Pi}`,Fi=".dropdown-menu",Hi=Kt()?"top-end":"top-start",Bi=Kt()?"top-start":"top-end",Wi=Kt()?"bottom-end":"bottom-start",zi=Kt()?"bottom-start":"bottom-end",Ri=Kt()?"left-start":"right-start",qi=Kt()?"right-start":"left-start",Vi={autoClose:!0,boundary:"clippingParents",display:"dynamic",offset:[0,2],popperConfig:null,reference:"toggle"},Yi={autoClose:"(boolean|string)",boundary:"(string|element)",display:"string",offset:"(array|string|function)",popperConfig:"(null|object|function)",reference:"(string|element|object)"};class Ki extends ve{constructor(t,e){super(t,e),this._popper=null,this._parent=this._element.parentNode,this._menu=we.next(this._element,Fi)[0]||we.prev(this._element,Fi)[0]||we.findOne(Fi,this._parent),this._inNavbar=this._detectNavbar()}static get Default(){return Vi}static get DefaultType(){return Yi}static get NAME(){return Ai}toggle(){return this._isShown()?this.hide():this.show()}show(){if(Wt(this._element)||this._isShown())return;const t={relatedTarget:this._element};if(!fe.trigger(this._element,Si,t).defaultPrevented){if(this._createPopper(),"ontouchstart"in document.documentElement&&!this._parent.closest(".navbar-nav"))for(const t of[].concat(...document.body.children))fe.on(t,"mouseover",Rt);this._element.focus(),this._element.setAttribute("aria-expanded",!0),this._menu.classList.add(Pi),this._element.classList.add(Pi),fe.trigger(this._element,Di,t)}}hide(){if(Wt(this._element)||!this._isShown())return;const t={relatedTarget:this._element};this._completeHide(t)}dispose(){this._popper&&this._popper.destroy(),super.dispose()}update(){this._inNavbar=this._detectNavbar(),this._popper&&this._popper.update()}_completeHide(t){if(!fe.trigger(this._element,ki,t).defaultPrevented){if("ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.off(t,"mouseover",Rt);this._popper&&this._popper.destroy(),this._menu.classList.remove(Pi),this._element.classList.remove(Pi),this._element.setAttribute("aria-expanded","false"),_e.removeDataAttribute(this._menu,"popper"),fe.trigger(this._element,Li,t)}}_getConfig(t){if("object"==typeof(t=super._getConfig(t)).reference&&!Ft(t.reference)&&"function"!=typeof t.reference.getBoundingClientRect)throw new TypeError(`${Ai.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);return t}_createPopper(){if(void 0===e)throw new TypeError("Bootstrap's dropdowns require Popper (https://popper.js.org)");let t=this._element;"parent"===this._config.reference?t=this._parent:Ft(this._config.reference)?t=Ht(this._config.reference):"object"==typeof this._config.reference&&(t=this._config.reference);const i=this._getPopperConfig();this._popper=Dt(t,this._menu,i)}_isShown(){return this._menu.classList.contains(Pi)}_getPlacement(){const t=this._parent;if(t.classList.contains("dropend"))return Ri;if(t.classList.contains("dropstart"))return qi;if(t.classList.contains("dropup-center"))return"top";if(t.classList.contains("dropdown-center"))return"bottom";const e="end"===getComputedStyle(this._menu).getPropertyValue("--bs-position").trim();return t.classList.contains("dropup")?e?Bi:Hi:e?zi:Wi}_detectNavbar(){return null!==this._element.closest(".navbar")}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_getPopperConfig(){const t={placement:this._getPlacement(),modifiers:[{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"offset",options:{offset:this._getOffset()}}]};return(this._inNavbar||"static"===this._config.display)&&(_e.setDataAttribute(this._menu,"popper","static"),t.modifiers=[{name:"applyStyles",enabled:!1}]),{...t,...Xt(this._config.popperConfig,[t])}}_selectMenuItem({key:t,target:e}){const i=we.find(".dropdown-menu .dropdown-item:not(.disabled):not(:disabled)",this._menu).filter((t=>Bt(t)));i.length&&Gt(i,e,t===xi,!i.includes(e)).focus()}static jQueryInterface(t){return this.each((function(){const e=Ki.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}static clearMenus(t){if(2===t.button||"keyup"===t.type&&"Tab"!==t.key)return;const e=we.find(ji);for(const i of e){const e=Ki.getInstance(i);if(!e||!1===e._config.autoClose)continue;const n=t.composedPath(),s=n.includes(e._menu);if(n.includes(e._element)||"inside"===e._config.autoClose&&!s||"outside"===e._config.autoClose&&s)continue;if(e._menu.contains(t.target)&&("keyup"===t.type&&"Tab"===t.key||/input|select|option|textarea|form/i.test(t.target.tagName)))continue;const o={relatedTarget:e._element};"click"===t.type&&(o.clickEvent=t),e._completeHide(o)}}static dataApiKeydownHandler(t){const e=/input|textarea/i.test(t.target.tagName),i="Escape"===t.key,n=[Oi,xi].includes(t.key);if(!n&&!i)return;if(e&&!i)return;t.preventDefault();const s=this.matches(Mi)?this:we.prev(this,Mi)[0]||we.next(this,Mi)[0]||we.findOne(Mi,t.delegateTarget.parentNode),o=Ki.getOrCreateInstance(s);if(n)return t.stopPropagation(),o.show(),void o._selectMenuItem(t);o._isShown()&&(t.stopPropagation(),o.hide(),s.focus())}}fe.on(document,Ii,Mi,Ki.dataApiKeydownHandler),fe.on(document,Ii,Fi,Ki.dataApiKeydownHandler),fe.on(document,$i,Ki.clearMenus),fe.on(document,Ni,Ki.clearMenus),fe.on(document,$i,Mi,(function(t){t.preventDefault(),Ki.getOrCreateInstance(this).toggle()})),Qt(Ki);const Qi="backdrop",Xi="show",Ui=`mousedown.bs.${Qi}`,Gi={className:"modal-backdrop",clickCallback:null,isAnimated:!1,isVisible:!0,rootElement:"body"},Ji={className:"string",clickCallback:"(function|null)",isAnimated:"boolean",isVisible:"boolean",rootElement:"(element|string)"};class Zi extends be{constructor(t){super(),this._config=this._getConfig(t),this._isAppended=!1,this._element=null}static get Default(){return Gi}static get DefaultType(){return Ji}static get NAME(){return Qi}show(t){if(!this._config.isVisible)return void Xt(t);this._append();const e=this._getElement();this._config.isAnimated&&qt(e),e.classList.add(Xi),this._emulateAnimation((()=>{Xt(t)}))}hide(t){this._config.isVisible?(this._getElement().classList.remove(Xi),this._emulateAnimation((()=>{this.dispose(),Xt(t)}))):Xt(t)}dispose(){this._isAppended&&(fe.off(this._element,Ui),this._element.remove(),this._isAppended=!1)}_getElement(){if(!this._element){const t=document.createElement("div");t.className=this._config.className,this._config.isAnimated&&t.classList.add("fade"),this._element=t}return this._element}_configAfterMerge(t){return t.rootElement=Ht(t.rootElement),t}_append(){if(this._isAppended)return;const t=this._getElement();this._config.rootElement.append(t),fe.on(t,Ui,(()=>{Xt(this._config.clickCallback)})),this._isAppended=!0}_emulateAnimation(t){Ut(t,this._getElement(),this._config.isAnimated)}}const tn=".bs.focustrap",en=`focusin${tn}`,nn=`keydown.tab${tn}`,sn="backward",on={autofocus:!0,trapElement:null},rn={autofocus:"boolean",trapElement:"element"};class an extends be{constructor(t){super(),this._config=this._getConfig(t),this._isActive=!1,this._lastTabNavDirection=null}static get Default(){return on}static get DefaultType(){return rn}static get NAME(){return"focustrap"}activate(){this._isActive||(this._config.autofocus&&this._config.trapElement.focus(),fe.off(document,tn),fe.on(document,en,(t=>this._handleFocusin(t))),fe.on(document,nn,(t=>this._handleKeydown(t))),this._isActive=!0)}deactivate(){this._isActive&&(this._isActive=!1,fe.off(document,tn))}_handleFocusin(t){const{trapElement:e}=this._config;if(t.target===document||t.target===e||e.contains(t.target))return;const i=we.focusableChildren(e);0===i.length?e.focus():this._lastTabNavDirection===sn?i[i.length-1].focus():i[0].focus()}_handleKeydown(t){"Tab"===t.key&&(this._lastTabNavDirection=t.shiftKey?sn:"forward")}}const ln=".fixed-top, .fixed-bottom, .is-fixed, .sticky-top",cn=".sticky-top",hn="padding-right",dn="margin-right";class un{constructor(){this._element=document.body}getWidth(){const t=document.documentElement.clientWidth;return Math.abs(window.innerWidth-t)}hide(){const t=this.getWidth();this._disableOverFlow(),this._setElementAttributes(this._element,hn,(e=>e+t)),this._setElementAttributes(ln,hn,(e=>e+t)),this._setElementAttributes(cn,dn,(e=>e-t))}reset(){this._resetElementAttributes(this._element,"overflow"),this._resetElementAttributes(this._element,hn),this._resetElementAttributes(ln,hn),this._resetElementAttributes(cn,dn)}isOverflowing(){return this.getWidth()>0}_disableOverFlow(){this._saveInitialAttribute(this._element,"overflow"),this._element.style.overflow="hidden"}_setElementAttributes(t,e,i){const n=this.getWidth();this._applyManipulationCallback(t,(t=>{if(t!==this._element&&window.innerWidth>t.clientWidth+n)return;this._saveInitialAttribute(t,e);const s=window.getComputedStyle(t).getPropertyValue(e);t.style.setProperty(e,`${i(Number.parseFloat(s))}px`)}))}_saveInitialAttribute(t,e){const i=t.style.getPropertyValue(e);i&&_e.setDataAttribute(t,e,i)}_resetElementAttributes(t,e){this._applyManipulationCallback(t,(t=>{const i=_e.getDataAttribute(t,e);null!==i?(_e.removeDataAttribute(t,e),t.style.setProperty(e,i)):t.style.removeProperty(e)}))}_applyManipulationCallback(t,e){if(Ft(t))e(t);else for(const i of we.find(t,this._element))e(i)}}const fn=".bs.modal",pn=`hide${fn}`,mn=`hidePrevented${fn}`,gn=`hidden${fn}`,_n=`show${fn}`,bn=`shown${fn}`,vn=`resize${fn}`,yn=`click.dismiss${fn}`,wn=`mousedown.dismiss${fn}`,En=`keydown.dismiss${fn}`,An=`click${fn}.data-api`,Tn="modal-open",Cn="show",On="modal-static",xn={backdrop:!0,focus:!0,keyboard:!0},kn={backdrop:"(boolean|string)",focus:"boolean",keyboard:"boolean"};class Ln extends ve{constructor(t,e){super(t,e),this._dialog=we.findOne(".modal-dialog",this._element),this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._isShown=!1,this._isTransitioning=!1,this._scrollBar=new un,this._addEventListeners()}static get Default(){return xn}static get DefaultType(){return kn}static get NAME(){return"modal"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||this._isTransitioning||fe.trigger(this._element,_n,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._isTransitioning=!0,this._scrollBar.hide(),document.body.classList.add(Tn),this._adjustDialog(),this._backdrop.show((()=>this._showElement(t))))}hide(){this._isShown&&!this._isTransitioning&&(fe.trigger(this._element,pn).defaultPrevented||(this._isShown=!1,this._isTransitioning=!0,this._focustrap.deactivate(),this._element.classList.remove(Cn),this._queueCallback((()=>this._hideModal()),this._element,this._isAnimated())))}dispose(){fe.off(window,fn),fe.off(this._dialog,fn),this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}handleUpdate(){this._adjustDialog()}_initializeBackDrop(){return new Zi({isVisible:Boolean(this._config.backdrop),isAnimated:this._isAnimated()})}_initializeFocusTrap(){return new an({trapElement:this._element})}_showElement(t){document.body.contains(this._element)||document.body.append(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.scrollTop=0;const e=we.findOne(".modal-body",this._dialog);e&&(e.scrollTop=0),qt(this._element),this._element.classList.add(Cn),this._queueCallback((()=>{this._config.focus&&this._focustrap.activate(),this._isTransitioning=!1,fe.trigger(this._element,bn,{relatedTarget:t})}),this._dialog,this._isAnimated())}_addEventListeners(){fe.on(this._element,En,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():this._triggerBackdropTransition())})),fe.on(window,vn,(()=>{this._isShown&&!this._isTransitioning&&this._adjustDialog()})),fe.on(this._element,wn,(t=>{fe.one(this._element,yn,(e=>{this._element===t.target&&this._element===e.target&&("static"!==this._config.backdrop?this._config.backdrop&&this.hide():this._triggerBackdropTransition())}))}))}_hideModal(){this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._backdrop.hide((()=>{document.body.classList.remove(Tn),this._resetAdjustments(),this._scrollBar.reset(),fe.trigger(this._element,gn)}))}_isAnimated(){return this._element.classList.contains("fade")}_triggerBackdropTransition(){if(fe.trigger(this._element,mn).defaultPrevented)return;const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._element.style.overflowY;"hidden"===e||this._element.classList.contains(On)||(t||(this._element.style.overflowY="hidden"),this._element.classList.add(On),this._queueCallback((()=>{this._element.classList.remove(On),this._queueCallback((()=>{this._element.style.overflowY=e}),this._dialog)}),this._dialog),this._element.focus())}_adjustDialog(){const t=this._element.scrollHeight>document.documentElement.clientHeight,e=this._scrollBar.getWidth(),i=e>0;if(i&&!t){const t=Kt()?"paddingLeft":"paddingRight";this._element.style[t]=`${e}px`}if(!i&&t){const t=Kt()?"paddingRight":"paddingLeft";this._element.style[t]=`${e}px`}}_resetAdjustments(){this._element.style.paddingLeft="",this._element.style.paddingRight=""}static jQueryInterface(t,e){return this.each((function(){const i=Ln.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===i[t])throw new TypeError(`No method named "${t}"`);i[t](e)}}))}}fe.on(document,An,'[data-bs-toggle="modal"]',(function(t){const e=we.getElementFromSelector(this);["A","AREA"].includes(this.tagName)&&t.preventDefault(),fe.one(e,_n,(t=>{t.defaultPrevented||fe.one(e,gn,(()=>{Bt(this)&&this.focus()}))}));const i=we.findOne(".modal.show");i&&Ln.getInstance(i).hide(),Ln.getOrCreateInstance(e).toggle(this)})),Ee(Ln),Qt(Ln);const Sn=".bs.offcanvas",Dn=".data-api",$n=`load${Sn}${Dn}`,In="show",Nn="showing",Pn="hiding",Mn=".offcanvas.show",jn=`show${Sn}`,Fn=`shown${Sn}`,Hn=`hide${Sn}`,Bn=`hidePrevented${Sn}`,Wn=`hidden${Sn}`,zn=`resize${Sn}`,Rn=`click${Sn}${Dn}`,qn=`keydown.dismiss${Sn}`,Vn={backdrop:!0,keyboard:!0,scroll:!1},Yn={backdrop:"(boolean|string)",keyboard:"boolean",scroll:"boolean"};class Kn extends ve{constructor(t,e){super(t,e),this._isShown=!1,this._backdrop=this._initializeBackDrop(),this._focustrap=this._initializeFocusTrap(),this._addEventListeners()}static get Default(){return Vn}static get DefaultType(){return Yn}static get NAME(){return"offcanvas"}toggle(t){return this._isShown?this.hide():this.show(t)}show(t){this._isShown||fe.trigger(this._element,jn,{relatedTarget:t}).defaultPrevented||(this._isShown=!0,this._backdrop.show(),this._config.scroll||(new un).hide(),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),this._element.classList.add(Nn),this._queueCallback((()=>{this._config.scroll&&!this._config.backdrop||this._focustrap.activate(),this._element.classList.add(In),this._element.classList.remove(Nn),fe.trigger(this._element,Fn,{relatedTarget:t})}),this._element,!0))}hide(){this._isShown&&(fe.trigger(this._element,Hn).defaultPrevented||(this._focustrap.deactivate(),this._element.blur(),this._isShown=!1,this._element.classList.add(Pn),this._backdrop.hide(),this._queueCallback((()=>{this._element.classList.remove(In,Pn),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._config.scroll||(new un).reset(),fe.trigger(this._element,Wn)}),this._element,!0)))}dispose(){this._backdrop.dispose(),this._focustrap.deactivate(),super.dispose()}_initializeBackDrop(){const t=Boolean(this._config.backdrop);return new Zi({className:"offcanvas-backdrop",isVisible:t,isAnimated:!0,rootElement:this._element.parentNode,clickCallback:t?()=>{"static"!==this._config.backdrop?this.hide():fe.trigger(this._element,Bn)}:null})}_initializeFocusTrap(){return new an({trapElement:this._element})}_addEventListeners(){fe.on(this._element,qn,(t=>{"Escape"===t.key&&(this._config.keyboard?this.hide():fe.trigger(this._element,Bn))}))}static jQueryInterface(t){return this.each((function(){const e=Kn.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}fe.on(document,Rn,'[data-bs-toggle="offcanvas"]',(function(t){const e=we.getElementFromSelector(this);if(["A","AREA"].includes(this.tagName)&&t.preventDefault(),Wt(this))return;fe.one(e,Wn,(()=>{Bt(this)&&this.focus()}));const i=we.findOne(Mn);i&&i!==e&&Kn.getInstance(i).hide(),Kn.getOrCreateInstance(e).toggle(this)})),fe.on(window,$n,(()=>{for(const t of we.find(Mn))Kn.getOrCreateInstance(t).show()})),fe.on(window,zn,(()=>{for(const t of we.find("[aria-modal][class*=show][class*=offcanvas-]"))"fixed"!==getComputedStyle(t).position&&Kn.getOrCreateInstance(t).hide()})),Ee(Kn),Qt(Kn);const Qn={"*":["class","dir","id","lang","role",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],dd:[],div:[],dl:[],dt:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","srcset","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},Xn=new Set(["background","cite","href","itemtype","longdesc","poster","src","xlink:href"]),Un=/^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i,Gn=(t,e)=>{const i=t.nodeName.toLowerCase();return e.includes(i)?!Xn.has(i)||Boolean(Un.test(t.nodeValue)):e.filter((t=>t instanceof RegExp)).some((t=>t.test(i)))},Jn={allowList:Qn,content:{},extraClass:"",html:!1,sanitize:!0,sanitizeFn:null,template:"
"},Zn={allowList:"object",content:"object",extraClass:"(string|function)",html:"boolean",sanitize:"boolean",sanitizeFn:"(null|function)",template:"string"},ts={entry:"(string|element|function|null)",selector:"(string|element)"};class es extends be{constructor(t){super(),this._config=this._getConfig(t)}static get Default(){return Jn}static get DefaultType(){return Zn}static get NAME(){return"TemplateFactory"}getContent(){return Object.values(this._config.content).map((t=>this._resolvePossibleFunction(t))).filter(Boolean)}hasContent(){return this.getContent().length>0}changeContent(t){return this._checkContent(t),this._config.content={...this._config.content,...t},this}toHtml(){const t=document.createElement("div");t.innerHTML=this._maybeSanitize(this._config.template);for(const[e,i]of Object.entries(this._config.content))this._setContent(t,i,e);const e=t.children[0],i=this._resolvePossibleFunction(this._config.extraClass);return i&&e.classList.add(...i.split(" ")),e}_typeCheckConfig(t){super._typeCheckConfig(t),this._checkContent(t.content)}_checkContent(t){for(const[e,i]of Object.entries(t))super._typeCheckConfig({selector:e,entry:i},ts)}_setContent(t,e,i){const n=we.findOne(i,t);n&&((e=this._resolvePossibleFunction(e))?Ft(e)?this._putElementInTemplate(Ht(e),n):this._config.html?n.innerHTML=this._maybeSanitize(e):n.textContent=e:n.remove())}_maybeSanitize(t){return this._config.sanitize?function(t,e,i){if(!t.length)return t;if(i&&"function"==typeof i)return i(t);const n=(new window.DOMParser).parseFromString(t,"text/html"),s=[].concat(...n.body.querySelectorAll("*"));for(const t of s){const i=t.nodeName.toLowerCase();if(!Object.keys(e).includes(i)){t.remove();continue}const n=[].concat(...t.attributes),s=[].concat(e["*"]||[],e[i]||[]);for(const e of n)Gn(e,s)||t.removeAttribute(e.nodeName)}return n.body.innerHTML}(t,this._config.allowList,this._config.sanitizeFn):t}_resolvePossibleFunction(t){return Xt(t,[this])}_putElementInTemplate(t,e){if(this._config.html)return e.innerHTML="",void e.append(t);e.textContent=t.textContent}}const is=new Set(["sanitize","allowList","sanitizeFn"]),ns="fade",ss="show",os=".tooltip-inner",rs=".modal",as="hide.bs.modal",ls="hover",cs="focus",hs={AUTO:"auto",TOP:"top",RIGHT:Kt()?"left":"right",BOTTOM:"bottom",LEFT:Kt()?"right":"left"},ds={allowList:Qn,animation:!0,boundary:"clippingParents",container:!1,customClass:"",delay:0,fallbackPlacements:["top","right","bottom","left"],html:!1,offset:[0,6],placement:"top",popperConfig:null,sanitize:!0,sanitizeFn:null,selector:!1,template:'',title:"",trigger:"hover focus"},us={allowList:"object",animation:"boolean",boundary:"(string|element)",container:"(string|element|boolean)",customClass:"(string|function)",delay:"(number|object)",fallbackPlacements:"array",html:"boolean",offset:"(array|string|function)",placement:"(string|function)",popperConfig:"(null|object|function)",sanitize:"boolean",sanitizeFn:"(null|function)",selector:"(string|boolean)",template:"string",title:"(string|element|function)",trigger:"string"};class fs extends ve{constructor(t,i){if(void 0===e)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");super(t,i),this._isEnabled=!0,this._timeout=0,this._isHovered=null,this._activeTrigger={},this._popper=null,this._templateFactory=null,this._newContent=null,this.tip=null,this._setListeners(),this._config.selector||this._fixTitle()}static get Default(){return ds}static get DefaultType(){return us}static get NAME(){return"tooltip"}enable(){this._isEnabled=!0}disable(){this._isEnabled=!1}toggleEnabled(){this._isEnabled=!this._isEnabled}toggle(){this._isEnabled&&(this._activeTrigger.click=!this._activeTrigger.click,this._isShown()?this._leave():this._enter())}dispose(){clearTimeout(this._timeout),fe.off(this._element.closest(rs),as,this._hideModalHandler),this._element.getAttribute("data-bs-original-title")&&this._element.setAttribute("title",this._element.getAttribute("data-bs-original-title")),this._disposePopper(),super.dispose()}show(){if("none"===this._element.style.display)throw new Error("Please use show on visible elements");if(!this._isWithContent()||!this._isEnabled)return;const t=fe.trigger(this._element,this.constructor.eventName("show")),e=(zt(this._element)||this._element.ownerDocument.documentElement).contains(this._element);if(t.defaultPrevented||!e)return;this._disposePopper();const i=this._getTipElement();this._element.setAttribute("aria-describedby",i.getAttribute("id"));const{container:n}=this._config;if(this._element.ownerDocument.documentElement.contains(this.tip)||(n.append(i),fe.trigger(this._element,this.constructor.eventName("inserted"))),this._popper=this._createPopper(i),i.classList.add(ss),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.on(t,"mouseover",Rt);this._queueCallback((()=>{fe.trigger(this._element,this.constructor.eventName("shown")),!1===this._isHovered&&this._leave(),this._isHovered=!1}),this.tip,this._isAnimated())}hide(){if(this._isShown()&&!fe.trigger(this._element,this.constructor.eventName("hide")).defaultPrevented){if(this._getTipElement().classList.remove(ss),"ontouchstart"in document.documentElement)for(const t of[].concat(...document.body.children))fe.off(t,"mouseover",Rt);this._activeTrigger.click=!1,this._activeTrigger[cs]=!1,this._activeTrigger[ls]=!1,this._isHovered=null,this._queueCallback((()=>{this._isWithActiveTrigger()||(this._isHovered||this._disposePopper(),this._element.removeAttribute("aria-describedby"),fe.trigger(this._element,this.constructor.eventName("hidden")))}),this.tip,this._isAnimated())}}update(){this._popper&&this._popper.update()}_isWithContent(){return Boolean(this._getTitle())}_getTipElement(){return this.tip||(this.tip=this._createTipElement(this._newContent||this._getContentForTemplate())),this.tip}_createTipElement(t){const e=this._getTemplateFactory(t).toHtml();if(!e)return null;e.classList.remove(ns,ss),e.classList.add(`bs-${this.constructor.NAME}-auto`);const i=(t=>{do{t+=Math.floor(1e6*Math.random())}while(document.getElementById(t));return t})(this.constructor.NAME).toString();return e.setAttribute("id",i),this._isAnimated()&&e.classList.add(ns),e}setContent(t){this._newContent=t,this._isShown()&&(this._disposePopper(),this.show())}_getTemplateFactory(t){return this._templateFactory?this._templateFactory.changeContent(t):this._templateFactory=new es({...this._config,content:t,extraClass:this._resolvePossibleFunction(this._config.customClass)}),this._templateFactory}_getContentForTemplate(){return{[os]:this._getTitle()}}_getTitle(){return this._resolvePossibleFunction(this._config.title)||this._element.getAttribute("data-bs-original-title")}_initializeOnDelegatedTarget(t){return this.constructor.getOrCreateInstance(t.delegateTarget,this._getDelegateConfig())}_isAnimated(){return this._config.animation||this.tip&&this.tip.classList.contains(ns)}_isShown(){return this.tip&&this.tip.classList.contains(ss)}_createPopper(t){const e=Xt(this._config.placement,[this,t,this._element]),i=hs[e.toUpperCase()];return Dt(this._element,t,this._getPopperConfig(i))}_getOffset(){const{offset:t}=this._config;return"string"==typeof t?t.split(",").map((t=>Number.parseInt(t,10))):"function"==typeof t?e=>t(e,this._element):t}_resolvePossibleFunction(t){return Xt(t,[this._element])}_getPopperConfig(t){const e={placement:t,modifiers:[{name:"flip",options:{fallbackPlacements:this._config.fallbackPlacements}},{name:"offset",options:{offset:this._getOffset()}},{name:"preventOverflow",options:{boundary:this._config.boundary}},{name:"arrow",options:{element:`.${this.constructor.NAME}-arrow`}},{name:"preSetPlacement",enabled:!0,phase:"beforeMain",fn:t=>{this._getTipElement().setAttribute("data-popper-placement",t.state.placement)}}]};return{...e,...Xt(this._config.popperConfig,[e])}}_setListeners(){const t=this._config.trigger.split(" ");for(const e of t)if("click"===e)fe.on(this._element,this.constructor.eventName("click"),this._config.selector,(t=>{this._initializeOnDelegatedTarget(t).toggle()}));else if("manual"!==e){const t=e===ls?this.constructor.eventName("mouseenter"):this.constructor.eventName("focusin"),i=e===ls?this.constructor.eventName("mouseleave"):this.constructor.eventName("focusout");fe.on(this._element,t,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusin"===t.type?cs:ls]=!0,e._enter()})),fe.on(this._element,i,this._config.selector,(t=>{const e=this._initializeOnDelegatedTarget(t);e._activeTrigger["focusout"===t.type?cs:ls]=e._element.contains(t.relatedTarget),e._leave()}))}this._hideModalHandler=()=>{this._element&&this.hide()},fe.on(this._element.closest(rs),as,this._hideModalHandler)}_fixTitle(){const t=this._element.getAttribute("title");t&&(this._element.getAttribute("aria-label")||this._element.textContent.trim()||this._element.setAttribute("aria-label",t),this._element.setAttribute("data-bs-original-title",t),this._element.removeAttribute("title"))}_enter(){this._isShown()||this._isHovered?this._isHovered=!0:(this._isHovered=!0,this._setTimeout((()=>{this._isHovered&&this.show()}),this._config.delay.show))}_leave(){this._isWithActiveTrigger()||(this._isHovered=!1,this._setTimeout((()=>{this._isHovered||this.hide()}),this._config.delay.hide))}_setTimeout(t,e){clearTimeout(this._timeout),this._timeout=setTimeout(t,e)}_isWithActiveTrigger(){return Object.values(this._activeTrigger).includes(!0)}_getConfig(t){const e=_e.getDataAttributes(this._element);for(const t of Object.keys(e))is.has(t)&&delete e[t];return t={...e,..."object"==typeof t&&t?t:{}},t=this._mergeConfigObj(t),t=this._configAfterMerge(t),this._typeCheckConfig(t),t}_configAfterMerge(t){return t.container=!1===t.container?document.body:Ht(t.container),"number"==typeof t.delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),t}_getDelegateConfig(){const t={};for(const[e,i]of Object.entries(this._config))this.constructor.Default[e]!==i&&(t[e]=i);return t.selector=!1,t.trigger="manual",t}_disposePopper(){this._popper&&(this._popper.destroy(),this._popper=null),this.tip&&(this.tip.remove(),this.tip=null)}static jQueryInterface(t){return this.each((function(){const e=fs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Qt(fs);const ps=".popover-header",ms=".popover-body",gs={...fs.Default,content:"",offset:[0,8],placement:"right",template:'',trigger:"click"},_s={...fs.DefaultType,content:"(null|string|element|function)"};class bs extends fs{static get Default(){return gs}static get DefaultType(){return _s}static get NAME(){return"popover"}_isWithContent(){return this._getTitle()||this._getContent()}_getContentForTemplate(){return{[ps]:this._getTitle(),[ms]:this._getContent()}}_getContent(){return this._resolvePossibleFunction(this._config.content)}static jQueryInterface(t){return this.each((function(){const e=bs.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t]()}}))}}Qt(bs);const vs=".bs.scrollspy",ys=`activate${vs}`,ws=`click${vs}`,Es=`load${vs}.data-api`,As="active",Ts="[href]",Cs=".nav-link",Os=`${Cs}, .nav-item > ${Cs}, .list-group-item`,xs={offset:null,rootMargin:"0px 0px -25%",smoothScroll:!1,target:null,threshold:[.1,.5,1]},ks={offset:"(number|null)",rootMargin:"string",smoothScroll:"boolean",target:"element",threshold:"array"};class Ls extends ve{constructor(t,e){super(t,e),this._targetLinks=new Map,this._observableSections=new Map,this._rootElement="visible"===getComputedStyle(this._element).overflowY?null:this._element,this._activeTarget=null,this._observer=null,this._previousScrollData={visibleEntryTop:0,parentScrollTop:0},this.refresh()}static get Default(){return xs}static get DefaultType(){return ks}static get NAME(){return"scrollspy"}refresh(){this._initializeTargetsAndObservables(),this._maybeEnableSmoothScroll(),this._observer?this._observer.disconnect():this._observer=this._getNewObserver();for(const t of this._observableSections.values())this._observer.observe(t)}dispose(){this._observer.disconnect(),super.dispose()}_configAfterMerge(t){return t.target=Ht(t.target)||document.body,t.rootMargin=t.offset?`${t.offset}px 0px -30%`:t.rootMargin,"string"==typeof t.threshold&&(t.threshold=t.threshold.split(",").map((t=>Number.parseFloat(t)))),t}_maybeEnableSmoothScroll(){this._config.smoothScroll&&(fe.off(this._config.target,ws),fe.on(this._config.target,ws,Ts,(t=>{const e=this._observableSections.get(t.target.hash);if(e){t.preventDefault();const i=this._rootElement||window,n=e.offsetTop-this._element.offsetTop;if(i.scrollTo)return void i.scrollTo({top:n,behavior:"smooth"});i.scrollTop=n}})))}_getNewObserver(){const t={root:this._rootElement,threshold:this._config.threshold,rootMargin:this._config.rootMargin};return new IntersectionObserver((t=>this._observerCallback(t)),t)}_observerCallback(t){const e=t=>this._targetLinks.get(`#${t.target.id}`),i=t=>{this._previousScrollData.visibleEntryTop=t.target.offsetTop,this._process(e(t))},n=(this._rootElement||document.documentElement).scrollTop,s=n>=this._previousScrollData.parentScrollTop;this._previousScrollData.parentScrollTop=n;for(const o of t){if(!o.isIntersecting){this._activeTarget=null,this._clearActiveClass(e(o));continue}const t=o.target.offsetTop>=this._previousScrollData.visibleEntryTop;if(s&&t){if(i(o),!n)return}else s||t||i(o)}}_initializeTargetsAndObservables(){this._targetLinks=new Map,this._observableSections=new Map;const t=we.find(Ts,this._config.target);for(const e of t){if(!e.hash||Wt(e))continue;const t=we.findOne(decodeURI(e.hash),this._element);Bt(t)&&(this._targetLinks.set(decodeURI(e.hash),e),this._observableSections.set(e.hash,t))}}_process(t){this._activeTarget!==t&&(this._clearActiveClass(this._config.target),this._activeTarget=t,t.classList.add(As),this._activateParents(t),fe.trigger(this._element,ys,{relatedTarget:t}))}_activateParents(t){if(t.classList.contains("dropdown-item"))we.findOne(".dropdown-toggle",t.closest(".dropdown")).classList.add(As);else for(const e of we.parents(t,".nav, .list-group"))for(const t of we.prev(e,Os))t.classList.add(As)}_clearActiveClass(t){t.classList.remove(As);const e=we.find(`${Ts}.${As}`,t);for(const t of e)t.classList.remove(As)}static jQueryInterface(t){return this.each((function(){const e=Ls.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}fe.on(window,Es,(()=>{for(const t of we.find('[data-bs-spy="scroll"]'))Ls.getOrCreateInstance(t)})),Qt(Ls);const Ss=".bs.tab",Ds=`hide${Ss}`,$s=`hidden${Ss}`,Is=`show${Ss}`,Ns=`shown${Ss}`,Ps=`click${Ss}`,Ms=`keydown${Ss}`,js=`load${Ss}`,Fs="ArrowLeft",Hs="ArrowRight",Bs="ArrowUp",Ws="ArrowDown",zs="Home",Rs="End",qs="active",Vs="fade",Ys="show",Ks=".dropdown-toggle",Qs=`:not(${Ks})`,Xs='[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]',Us=`.nav-link${Qs}, .list-group-item${Qs}, [role="tab"]${Qs}, ${Xs}`,Gs=`.${qs}[data-bs-toggle="tab"], .${qs}[data-bs-toggle="pill"], .${qs}[data-bs-toggle="list"]`;class Js extends ve{constructor(t){super(t),this._parent=this._element.closest('.list-group, .nav, [role="tablist"]'),this._parent&&(this._setInitialAttributes(this._parent,this._getChildren()),fe.on(this._element,Ms,(t=>this._keydown(t))))}static get NAME(){return"tab"}show(){const t=this._element;if(this._elemIsActive(t))return;const e=this._getActiveElem(),i=e?fe.trigger(e,Ds,{relatedTarget:t}):null;fe.trigger(t,Is,{relatedTarget:e}).defaultPrevented||i&&i.defaultPrevented||(this._deactivate(e,t),this._activate(t,e))}_activate(t,e){t&&(t.classList.add(qs),this._activate(we.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.removeAttribute("tabindex"),t.setAttribute("aria-selected",!0),this._toggleDropDown(t,!0),fe.trigger(t,Ns,{relatedTarget:e})):t.classList.add(Ys)}),t,t.classList.contains(Vs)))}_deactivate(t,e){t&&(t.classList.remove(qs),t.blur(),this._deactivate(we.getElementFromSelector(t)),this._queueCallback((()=>{"tab"===t.getAttribute("role")?(t.setAttribute("aria-selected",!1),t.setAttribute("tabindex","-1"),this._toggleDropDown(t,!1),fe.trigger(t,$s,{relatedTarget:e})):t.classList.remove(Ys)}),t,t.classList.contains(Vs)))}_keydown(t){if(![Fs,Hs,Bs,Ws,zs,Rs].includes(t.key))return;t.stopPropagation(),t.preventDefault();const e=this._getChildren().filter((t=>!Wt(t)));let i;if([zs,Rs].includes(t.key))i=e[t.key===zs?0:e.length-1];else{const n=[Hs,Ws].includes(t.key);i=Gt(e,t.target,n,!0)}i&&(i.focus({preventScroll:!0}),Js.getOrCreateInstance(i).show())}_getChildren(){return we.find(Us,this._parent)}_getActiveElem(){return this._getChildren().find((t=>this._elemIsActive(t)))||null}_setInitialAttributes(t,e){this._setAttributeIfNotExists(t,"role","tablist");for(const t of e)this._setInitialAttributesOnChild(t)}_setInitialAttributesOnChild(t){t=this._getInnerElement(t);const e=this._elemIsActive(t),i=this._getOuterElement(t);t.setAttribute("aria-selected",e),i!==t&&this._setAttributeIfNotExists(i,"role","presentation"),e||t.setAttribute("tabindex","-1"),this._setAttributeIfNotExists(t,"role","tab"),this._setInitialAttributesOnTargetPanel(t)}_setInitialAttributesOnTargetPanel(t){const e=we.getElementFromSelector(t);e&&(this._setAttributeIfNotExists(e,"role","tabpanel"),t.id&&this._setAttributeIfNotExists(e,"aria-labelledby",`${t.id}`))}_toggleDropDown(t,e){const i=this._getOuterElement(t);if(!i.classList.contains("dropdown"))return;const n=(t,n)=>{const s=we.findOne(t,i);s&&s.classList.toggle(n,e)};n(Ks,qs),n(".dropdown-menu",Ys),i.setAttribute("aria-expanded",e)}_setAttributeIfNotExists(t,e,i){t.hasAttribute(e)||t.setAttribute(e,i)}_elemIsActive(t){return t.classList.contains(qs)}_getInnerElement(t){return t.matches(Us)?t:we.findOne(Us,t)}_getOuterElement(t){return t.closest(".nav-item, .list-group-item")||t}static jQueryInterface(t){return this.each((function(){const e=Js.getOrCreateInstance(this);if("string"==typeof t){if(void 0===e[t]||t.startsWith("_")||"constructor"===t)throw new TypeError(`No method named "${t}"`);e[t]()}}))}}fe.on(document,Ps,Xs,(function(t){["A","AREA"].includes(this.tagName)&&t.preventDefault(),Wt(this)||Js.getOrCreateInstance(this).show()})),fe.on(window,js,(()=>{for(const t of we.find(Gs))Js.getOrCreateInstance(t)})),Qt(Js);const Zs=".bs.toast",to=`mouseover${Zs}`,eo=`mouseout${Zs}`,io=`focusin${Zs}`,no=`focusout${Zs}`,so=`hide${Zs}`,oo=`hidden${Zs}`,ro=`show${Zs}`,ao=`shown${Zs}`,lo="hide",co="show",ho="showing",uo={animation:"boolean",autohide:"boolean",delay:"number"},fo={animation:!0,autohide:!0,delay:5e3};class po extends ve{constructor(t,e){super(t,e),this._timeout=null,this._hasMouseInteraction=!1,this._hasKeyboardInteraction=!1,this._setListeners()}static get Default(){return fo}static get DefaultType(){return uo}static get NAME(){return"toast"}show(){fe.trigger(this._element,ro).defaultPrevented||(this._clearTimeout(),this._config.animation&&this._element.classList.add("fade"),this._element.classList.remove(lo),qt(this._element),this._element.classList.add(co,ho),this._queueCallback((()=>{this._element.classList.remove(ho),fe.trigger(this._element,ao),this._maybeScheduleHide()}),this._element,this._config.animation))}hide(){this.isShown()&&(fe.trigger(this._element,so).defaultPrevented||(this._element.classList.add(ho),this._queueCallback((()=>{this._element.classList.add(lo),this._element.classList.remove(ho,co),fe.trigger(this._element,oo)}),this._element,this._config.animation)))}dispose(){this._clearTimeout(),this.isShown()&&this._element.classList.remove(co),super.dispose()}isShown(){return this._element.classList.contains(co)}_maybeScheduleHide(){this._config.autohide&&(this._hasMouseInteraction||this._hasKeyboardInteraction||(this._timeout=setTimeout((()=>{this.hide()}),this._config.delay)))}_onInteraction(t,e){switch(t.type){case"mouseover":case"mouseout":this._hasMouseInteraction=e;break;case"focusin":case"focusout":this._hasKeyboardInteraction=e}if(e)return void this._clearTimeout();const i=t.relatedTarget;this._element===i||this._element.contains(i)||this._maybeScheduleHide()}_setListeners(){fe.on(this._element,to,(t=>this._onInteraction(t,!0))),fe.on(this._element,eo,(t=>this._onInteraction(t,!1))),fe.on(this._element,io,(t=>this._onInteraction(t,!0))),fe.on(this._element,no,(t=>this._onInteraction(t,!1)))}_clearTimeout(){clearTimeout(this._timeout),this._timeout=null}static jQueryInterface(t){return this.each((function(){const e=po.getOrCreateInstance(this,t);if("string"==typeof t){if(void 0===e[t])throw new TypeError(`No method named "${t}"`);e[t](this)}}))}}function mo(t){"loading"!=document.readyState?t():document.addEventListener("DOMContentLoaded",t)}Ee(po),Qt(po),mo((function(){[].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')).map((function(t){return new fs(t,{delay:{show:500,hide:100}})}))})),mo((function(){document.getElementById("pst-back-to-top").addEventListener("click",(function(){document.body.scrollTop=0,document.documentElement.scrollTop=0}))})),mo((function(){var t=document.getElementById("pst-back-to-top"),e=document.getElementsByClassName("bd-header")[0].getBoundingClientRect();window.addEventListener("scroll",(function(){this.oldScroll>this.scrollY&&this.scrollY>e.bottom?t.style.display="block":t.style.display="none",this.oldScroll=this.scrollY}))})),window.bootstrap=i})(); +//# sourceMappingURL=bootstrap.js.map \ No newline at end of file diff --git a/_static/scripts/bootstrap.js.LICENSE.txt b/_static/scripts/bootstrap.js.LICENSE.txt new file mode 100644 index 000000000..28755c2c5 --- /dev/null +++ b/_static/scripts/bootstrap.js.LICENSE.txt @@ -0,0 +1,5 @@ +/*! + * Bootstrap v5.3.3 (https://getbootstrap.com/) + * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ diff --git a/_static/scripts/bootstrap.js.map b/_static/scripts/bootstrap.js.map new file mode 100644 index 000000000..4a3502aeb --- /dev/null +++ b/_static/scripts/bootstrap.js.map @@ -0,0 +1 @@ +{"version":3,"file":"scripts/bootstrap.js","mappings":"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","sources":["webpack://pydata_sphinx_theme/webpack/bootstrap","webpack://pydata_sphinx_theme/webpack/runtime/define property getters","webpack://pydata_sphinx_theme/webpack/runtime/hasOwnProperty shorthand","webpack://pydata_sphinx_theme/webpack/runtime/make namespace object","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/enums.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/instanceOf.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/applyStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getBasePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/math.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/userAgent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isLayoutViewport.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getBoundingClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getLayoutRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/contains.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getComputedStyle.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isTableElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentElement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getParentNode.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getOffsetParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getMainAxisFromPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/within.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergePaddingObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getFreshSideObject.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/expandToHashMap.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/arrow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getVariation.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/computeStyles.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/eventListeners.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositePlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getOppositeVariationPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getWindowScrollBarX.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/isScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getScrollParent.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/listScrollParents.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/rectToClientRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getClippingRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getViewportRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getDocumentRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/detectOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/flip.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/computeAutoPlacement.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/hide.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/offset.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/popperOffsets.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/modifiers/preventOverflow.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/getAltAxis.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getCompositeRect.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getNodeScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/dom-utils/getHTMLElementScroll.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/orderModifiers.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/createPopper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/debounce.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/utils/mergeByName.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper.js","webpack://pydata_sphinx_theme/./node_modules/@popperjs/core/lib/popper-lite.js","webpack://pydata_sphinx_theme/./node_modules/bootstrap/dist/js/bootstrap.esm.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/mixin.js","webpack://pydata_sphinx_theme/./src/pydata_sphinx_theme/assets/scripts/bootstrap.js"],"sourcesContent":["// The require scope\nvar __webpack_require__ = {};\n\n","// define getter functions for harmony exports\n__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n\t\t}\n\t}\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","export var top = 'top';\nexport var bottom = 'bottom';\nexport var right = 'right';\nexport var left = 'left';\nexport var auto = 'auto';\nexport var basePlacements = [top, bottom, right, left];\nexport var start = 'start';\nexport var end = 'end';\nexport var clippingParents = 'clippingParents';\nexport var viewport = 'viewport';\nexport var popper = 'popper';\nexport var reference = 'reference';\nexport var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) {\n return acc.concat([placement + \"-\" + start, placement + \"-\" + end]);\n}, []);\nexport var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) {\n return acc.concat([placement, placement + \"-\" + start, placement + \"-\" + end]);\n}, []); // modifiers that need to read the DOM\n\nexport var beforeRead = 'beforeRead';\nexport var read = 'read';\nexport var afterRead = 'afterRead'; // pure-logic modifiers\n\nexport var beforeMain = 'beforeMain';\nexport var main = 'main';\nexport var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state)\n\nexport var beforeWrite = 'beforeWrite';\nexport var write = 'write';\nexport var afterWrite = 'afterWrite';\nexport var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite];","export default function getNodeName(element) {\n return element ? (element.nodeName || '').toLowerCase() : null;\n}","export default function getWindow(node) {\n if (node == null) {\n return window;\n }\n\n if (node.toString() !== '[object Window]') {\n var ownerDocument = node.ownerDocument;\n return ownerDocument ? ownerDocument.defaultView || window : window;\n }\n\n return node;\n}","import getWindow from \"./getWindow.js\";\n\nfunction isElement(node) {\n var OwnElement = getWindow(node).Element;\n return node instanceof OwnElement || node instanceof Element;\n}\n\nfunction isHTMLElement(node) {\n var OwnElement = getWindow(node).HTMLElement;\n return node instanceof OwnElement || node instanceof HTMLElement;\n}\n\nfunction isShadowRoot(node) {\n // IE 11 has no ShadowRoot\n if (typeof ShadowRoot === 'undefined') {\n return false;\n }\n\n var OwnElement = getWindow(node).ShadowRoot;\n return node instanceof OwnElement || node instanceof ShadowRoot;\n}\n\nexport { isElement, isHTMLElement, isShadowRoot };","import getNodeName from \"../dom-utils/getNodeName.js\";\nimport { isHTMLElement } from \"../dom-utils/instanceOf.js\"; // This modifier takes the styles prepared by the `computeStyles` modifier\n// and applies them to the HTMLElements such as popper and arrow\n\nfunction applyStyles(_ref) {\n var state = _ref.state;\n Object.keys(state.elements).forEach(function (name) {\n var style = state.styles[name] || {};\n var attributes = state.attributes[name] || {};\n var element = state.elements[name]; // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n } // Flow doesn't support to extend this property, but it's the most\n // effective way to apply styles to an HTMLElement\n // $FlowFixMe[cannot-write]\n\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (name) {\n var value = attributes[name];\n\n if (value === false) {\n element.removeAttribute(name);\n } else {\n element.setAttribute(name, value === true ? '' : value);\n }\n });\n });\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state;\n var initialStyles = {\n popper: {\n position: state.options.strategy,\n left: '0',\n top: '0',\n margin: '0'\n },\n arrow: {\n position: 'absolute'\n },\n reference: {}\n };\n Object.assign(state.elements.popper.style, initialStyles.popper);\n state.styles = initialStyles;\n\n if (state.elements.arrow) {\n Object.assign(state.elements.arrow.style, initialStyles.arrow);\n }\n\n return function () {\n Object.keys(state.elements).forEach(function (name) {\n var element = state.elements[name];\n var attributes = state.attributes[name] || {};\n var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them\n\n var style = styleProperties.reduce(function (style, property) {\n style[property] = '';\n return style;\n }, {}); // arrow is optional + virtual elements\n\n if (!isHTMLElement(element) || !getNodeName(element)) {\n return;\n }\n\n Object.assign(element.style, style);\n Object.keys(attributes).forEach(function (attribute) {\n element.removeAttribute(attribute);\n });\n });\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'applyStyles',\n enabled: true,\n phase: 'write',\n fn: applyStyles,\n effect: effect,\n requires: ['computeStyles']\n};","import { auto } from \"../enums.js\";\nexport default function getBasePlacement(placement) {\n return placement.split('-')[0];\n}","export var max = Math.max;\nexport var min = Math.min;\nexport var round = Math.round;","export default function getUAString() {\n var uaData = navigator.userAgentData;\n\n if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) {\n return uaData.brands.map(function (item) {\n return item.brand + \"/\" + item.version;\n }).join(' ');\n }\n\n return navigator.userAgent;\n}","import getUAString from \"../utils/userAgent.js\";\nexport default function isLayoutViewport() {\n return !/^((?!chrome|android).)*safari/i.test(getUAString());\n}","import { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport { round } from \"../utils/math.js\";\nimport getWindow from \"./getWindow.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getBoundingClientRect(element, includeScale, isFixedStrategy) {\n if (includeScale === void 0) {\n includeScale = false;\n }\n\n if (isFixedStrategy === void 0) {\n isFixedStrategy = false;\n }\n\n var clientRect = element.getBoundingClientRect();\n var scaleX = 1;\n var scaleY = 1;\n\n if (includeScale && isHTMLElement(element)) {\n scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1;\n scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1;\n }\n\n var _ref = isElement(element) ? getWindow(element) : window,\n visualViewport = _ref.visualViewport;\n\n var addVisualOffsets = !isLayoutViewport() && isFixedStrategy;\n var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX;\n var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY;\n var width = clientRect.width / scaleX;\n var height = clientRect.height / scaleY;\n return {\n width: width,\n height: height,\n top: y,\n right: x + width,\n bottom: y + height,\n left: x,\n x: x,\n y: y\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\"; // Returns the layout rect of an element relative to its offsetParent. Layout\n// means it doesn't take into account transforms.\n\nexport default function getLayoutRect(element) {\n var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed.\n // Fixes https://github.com/popperjs/popper-core/issues/1223\n\n var width = element.offsetWidth;\n var height = element.offsetHeight;\n\n if (Math.abs(clientRect.width - width) <= 1) {\n width = clientRect.width;\n }\n\n if (Math.abs(clientRect.height - height) <= 1) {\n height = clientRect.height;\n }\n\n return {\n x: element.offsetLeft,\n y: element.offsetTop,\n width: width,\n height: height\n };\n}","import { isShadowRoot } from \"./instanceOf.js\";\nexport default function contains(parent, child) {\n var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method\n\n if (parent.contains(child)) {\n return true;\n } // then fallback to custom implementation with Shadow DOM support\n else if (rootNode && isShadowRoot(rootNode)) {\n var next = child;\n\n do {\n if (next && parent.isSameNode(next)) {\n return true;\n } // $FlowFixMe[prop-missing]: need a better way to handle this...\n\n\n next = next.parentNode || next.host;\n } while (next);\n } // Give up, the result is false\n\n\n return false;\n}","import getWindow from \"./getWindow.js\";\nexport default function getComputedStyle(element) {\n return getWindow(element).getComputedStyle(element);\n}","import getNodeName from \"./getNodeName.js\";\nexport default function isTableElement(element) {\n return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0;\n}","import { isElement } from \"./instanceOf.js\";\nexport default function getDocumentElement(element) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing]\n element.document) || window.document).documentElement;\n}","import getNodeName from \"./getNodeName.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport { isShadowRoot } from \"./instanceOf.js\";\nexport default function getParentNode(element) {\n if (getNodeName(element) === 'html') {\n return element;\n }\n\n return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle\n // $FlowFixMe[incompatible-return]\n // $FlowFixMe[prop-missing]\n element.assignedSlot || // step into the shadow DOM of the parent of a slotted node\n element.parentNode || ( // DOM Element detected\n isShadowRoot(element) ? element.host : null) || // ShadowRoot detected\n // $FlowFixMe[incompatible-call]: HTMLElement is a Node\n getDocumentElement(element) // fallback\n\n );\n}","import getWindow from \"./getWindow.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isHTMLElement, isShadowRoot } from \"./instanceOf.js\";\nimport isTableElement from \"./isTableElement.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getUAString from \"../utils/userAgent.js\";\n\nfunction getTrueOffsetParent(element) {\n if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837\n getComputedStyle(element).position === 'fixed') {\n return null;\n }\n\n return element.offsetParent;\n} // `.offsetParent` reports `null` for fixed elements, while absolute elements\n// return the containing block\n\n\nfunction getContainingBlock(element) {\n var isFirefox = /firefox/i.test(getUAString());\n var isIE = /Trident/i.test(getUAString());\n\n if (isIE && isHTMLElement(element)) {\n // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport\n var elementCss = getComputedStyle(element);\n\n if (elementCss.position === 'fixed') {\n return null;\n }\n }\n\n var currentNode = getParentNode(element);\n\n if (isShadowRoot(currentNode)) {\n currentNode = currentNode.host;\n }\n\n while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) {\n var css = getComputedStyle(currentNode); // This is non-exhaustive but covers the most common CSS properties that\n // create a containing block.\n // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block\n\n if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') {\n return currentNode;\n } else {\n currentNode = currentNode.parentNode;\n }\n }\n\n return null;\n} // Gets the closest ancestor positioned element. Handles some edge cases,\n// such as table ancestors and cross browser bugs.\n\n\nexport default function getOffsetParent(element) {\n var window = getWindow(element);\n var offsetParent = getTrueOffsetParent(element);\n\n while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === 'static') {\n offsetParent = getTrueOffsetParent(offsetParent);\n }\n\n if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle(offsetParent).position === 'static')) {\n return window;\n }\n\n return offsetParent || getContainingBlock(element) || window;\n}","export default function getMainAxisFromPlacement(placement) {\n return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y';\n}","import { max as mathMax, min as mathMin } from \"./math.js\";\nexport function within(min, value, max) {\n return mathMax(min, mathMin(value, max));\n}\nexport function withinMaxClamp(min, value, max) {\n var v = within(min, value, max);\n return v > max ? max : v;\n}","import getFreshSideObject from \"./getFreshSideObject.js\";\nexport default function mergePaddingObject(paddingObject) {\n return Object.assign({}, getFreshSideObject(), paddingObject);\n}","export default function getFreshSideObject() {\n return {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0\n };\n}","export default function expandToHashMap(value, keys) {\n return keys.reduce(function (hashMap, key) {\n hashMap[key] = value;\n return hashMap;\n }, {});\n}","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport contains from \"../dom-utils/contains.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport { within } from \"../utils/within.js\";\nimport mergePaddingObject from \"../utils/mergePaddingObject.js\";\nimport expandToHashMap from \"../utils/expandToHashMap.js\";\nimport { left, right, basePlacements, top, bottom } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar toPaddingObject = function toPaddingObject(padding, state) {\n padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, {\n placement: state.placement\n })) : padding;\n return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n};\n\nfunction arrow(_ref) {\n var _state$modifiersData$;\n\n var state = _ref.state,\n name = _ref.name,\n options = _ref.options;\n var arrowElement = state.elements.arrow;\n var popperOffsets = state.modifiersData.popperOffsets;\n var basePlacement = getBasePlacement(state.placement);\n var axis = getMainAxisFromPlacement(basePlacement);\n var isVertical = [left, right].indexOf(basePlacement) >= 0;\n var len = isVertical ? 'height' : 'width';\n\n if (!arrowElement || !popperOffsets) {\n return;\n }\n\n var paddingObject = toPaddingObject(options.padding, state);\n var arrowRect = getLayoutRect(arrowElement);\n var minProp = axis === 'y' ? top : left;\n var maxProp = axis === 'y' ? bottom : right;\n var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len];\n var startDiff = popperOffsets[axis] - state.rects.reference[axis];\n var arrowOffsetParent = getOffsetParent(arrowElement);\n var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0;\n var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is\n // outside of the popper bounds\n\n var min = paddingObject[minProp];\n var max = clientSize - arrowRect[len] - paddingObject[maxProp];\n var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference;\n var offset = within(min, center, max); // Prevents breaking syntax highlighting...\n\n var axisProp = axis;\n state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$);\n}\n\nfunction effect(_ref2) {\n var state = _ref2.state,\n options = _ref2.options;\n var _options$element = options.element,\n arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element;\n\n if (arrowElement == null) {\n return;\n } // CSS selector\n\n\n if (typeof arrowElement === 'string') {\n arrowElement = state.elements.popper.querySelector(arrowElement);\n\n if (!arrowElement) {\n return;\n }\n }\n\n if (!contains(state.elements.popper, arrowElement)) {\n return;\n }\n\n state.elements.arrow = arrowElement;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'arrow',\n enabled: true,\n phase: 'main',\n fn: arrow,\n effect: effect,\n requires: ['popperOffsets'],\n requiresIfExists: ['preventOverflow']\n};","export default function getVariation(placement) {\n return placement.split('-')[1];\n}","import { top, left, right, bottom, end } from \"../enums.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport getWindow from \"../dom-utils/getWindow.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getComputedStyle from \"../dom-utils/getComputedStyle.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport { round } from \"../utils/math.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar unsetSides = {\n top: 'auto',\n right: 'auto',\n bottom: 'auto',\n left: 'auto'\n}; // Round the offsets to the nearest suitable subpixel based on the DPR.\n// Zooming can change the DPR, but it seems to report a value that will\n// cleanly divide the values into the appropriate subpixels.\n\nfunction roundOffsetsByDPR(_ref, win) {\n var x = _ref.x,\n y = _ref.y;\n var dpr = win.devicePixelRatio || 1;\n return {\n x: round(x * dpr) / dpr || 0,\n y: round(y * dpr) / dpr || 0\n };\n}\n\nexport function mapToStyles(_ref2) {\n var _Object$assign2;\n\n var popper = _ref2.popper,\n popperRect = _ref2.popperRect,\n placement = _ref2.placement,\n variation = _ref2.variation,\n offsets = _ref2.offsets,\n position = _ref2.position,\n gpuAcceleration = _ref2.gpuAcceleration,\n adaptive = _ref2.adaptive,\n roundOffsets = _ref2.roundOffsets,\n isFixed = _ref2.isFixed;\n var _offsets$x = offsets.x,\n x = _offsets$x === void 0 ? 0 : _offsets$x,\n _offsets$y = offsets.y,\n y = _offsets$y === void 0 ? 0 : _offsets$y;\n\n var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({\n x: x,\n y: y\n }) : {\n x: x,\n y: y\n };\n\n x = _ref3.x;\n y = _ref3.y;\n var hasX = offsets.hasOwnProperty('x');\n var hasY = offsets.hasOwnProperty('y');\n var sideX = left;\n var sideY = top;\n var win = window;\n\n if (adaptive) {\n var offsetParent = getOffsetParent(popper);\n var heightProp = 'clientHeight';\n var widthProp = 'clientWidth';\n\n if (offsetParent === getWindow(popper)) {\n offsetParent = getDocumentElement(popper);\n\n if (getComputedStyle(offsetParent).position !== 'static' && position === 'absolute') {\n heightProp = 'scrollHeight';\n widthProp = 'scrollWidth';\n }\n } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it\n\n\n offsetParent = offsetParent;\n\n if (placement === top || (placement === left || placement === right) && variation === end) {\n sideY = bottom;\n var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing]\n offsetParent[heightProp];\n y -= offsetY - popperRect.height;\n y *= gpuAcceleration ? 1 : -1;\n }\n\n if (placement === left || (placement === top || placement === bottom) && variation === end) {\n sideX = right;\n var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing]\n offsetParent[widthProp];\n x -= offsetX - popperRect.width;\n x *= gpuAcceleration ? 1 : -1;\n }\n }\n\n var commonStyles = Object.assign({\n position: position\n }, adaptive && unsetSides);\n\n var _ref4 = roundOffsets === true ? roundOffsetsByDPR({\n x: x,\n y: y\n }, getWindow(popper)) : {\n x: x,\n y: y\n };\n\n x = _ref4.x;\n y = _ref4.y;\n\n if (gpuAcceleration) {\n var _Object$assign;\n\n return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? \"translate(\" + x + \"px, \" + y + \"px)\" : \"translate3d(\" + x + \"px, \" + y + \"px, 0)\", _Object$assign));\n }\n\n return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + \"px\" : '', _Object$assign2[sideX] = hasX ? x + \"px\" : '', _Object$assign2.transform = '', _Object$assign2));\n}\n\nfunction computeStyles(_ref5) {\n var state = _ref5.state,\n options = _ref5.options;\n var _options$gpuAccelerat = options.gpuAcceleration,\n gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat,\n _options$adaptive = options.adaptive,\n adaptive = _options$adaptive === void 0 ? true : _options$adaptive,\n _options$roundOffsets = options.roundOffsets,\n roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets;\n var commonStyles = {\n placement: getBasePlacement(state.placement),\n variation: getVariation(state.placement),\n popper: state.elements.popper,\n popperRect: state.rects.popper,\n gpuAcceleration: gpuAcceleration,\n isFixed: state.options.strategy === 'fixed'\n };\n\n if (state.modifiersData.popperOffsets != null) {\n state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.popperOffsets,\n position: state.options.strategy,\n adaptive: adaptive,\n roundOffsets: roundOffsets\n })));\n }\n\n if (state.modifiersData.arrow != null) {\n state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, {\n offsets: state.modifiersData.arrow,\n position: 'absolute',\n adaptive: false,\n roundOffsets: roundOffsets\n })));\n }\n\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-placement': state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'computeStyles',\n enabled: true,\n phase: 'beforeWrite',\n fn: computeStyles,\n data: {}\n};","import getWindow from \"../dom-utils/getWindow.js\"; // eslint-disable-next-line import/no-unused-modules\n\nvar passive = {\n passive: true\n};\n\nfunction effect(_ref) {\n var state = _ref.state,\n instance = _ref.instance,\n options = _ref.options;\n var _options$scroll = options.scroll,\n scroll = _options$scroll === void 0 ? true : _options$scroll,\n _options$resize = options.resize,\n resize = _options$resize === void 0 ? true : _options$resize;\n var window = getWindow(state.elements.popper);\n var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper);\n\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.addEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.addEventListener('resize', instance.update, passive);\n }\n\n return function () {\n if (scroll) {\n scrollParents.forEach(function (scrollParent) {\n scrollParent.removeEventListener('scroll', instance.update, passive);\n });\n }\n\n if (resize) {\n window.removeEventListener('resize', instance.update, passive);\n }\n };\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'eventListeners',\n enabled: true,\n phase: 'write',\n fn: function fn() {},\n effect: effect,\n data: {}\n};","var hash = {\n left: 'right',\n right: 'left',\n bottom: 'top',\n top: 'bottom'\n};\nexport default function getOppositePlacement(placement) {\n return placement.replace(/left|right|bottom|top/g, function (matched) {\n return hash[matched];\n });\n}","var hash = {\n start: 'end',\n end: 'start'\n};\nexport default function getOppositeVariationPlacement(placement) {\n return placement.replace(/start|end/g, function (matched) {\n return hash[matched];\n });\n}","import getWindow from \"./getWindow.js\";\nexport default function getWindowScroll(node) {\n var win = getWindow(node);\n var scrollLeft = win.pageXOffset;\n var scrollTop = win.pageYOffset;\n return {\n scrollLeft: scrollLeft,\n scrollTop: scrollTop\n };\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nexport default function getWindowScrollBarX(element) {\n // If has a CSS width greater than the viewport, then this will be\n // incorrect for RTL.\n // Popper 1 is broken in this case and never had a bug report so let's assume\n // it's not an issue. I don't think anyone ever specifies width on \n // anyway.\n // Browsers where the left scrollbar doesn't cause an issue report `0` for\n // this (e.g. Edge 2019, IE11, Safari)\n return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft;\n}","import getComputedStyle from \"./getComputedStyle.js\";\nexport default function isScrollParent(element) {\n // Firefox wants us to check `-x` and `-y` variations as well\n var _getComputedStyle = getComputedStyle(element),\n overflow = _getComputedStyle.overflow,\n overflowX = _getComputedStyle.overflowX,\n overflowY = _getComputedStyle.overflowY;\n\n return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX);\n}","import getParentNode from \"./getParentNode.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nexport default function getScrollParent(node) {\n if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) {\n // $FlowFixMe[incompatible-return]: assume body is always available\n return node.ownerDocument.body;\n }\n\n if (isHTMLElement(node) && isScrollParent(node)) {\n return node;\n }\n\n return getScrollParent(getParentNode(node));\n}","import getScrollParent from \"./getScrollParent.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport getWindow from \"./getWindow.js\";\nimport isScrollParent from \"./isScrollParent.js\";\n/*\ngiven a DOM element, return the list of all scroll parents, up the list of ancesors\nuntil we get to the top window object. This list is what we attach scroll listeners\nto, because if any of these parent elements scroll, we'll need to re-calculate the\nreference element's position.\n*/\n\nexport default function listScrollParents(element, list) {\n var _element$ownerDocumen;\n\n if (list === void 0) {\n list = [];\n }\n\n var scrollParent = getScrollParent(element);\n var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body);\n var win = getWindow(scrollParent);\n var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent;\n var updatedList = list.concat(target);\n return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here\n updatedList.concat(listScrollParents(getParentNode(target)));\n}","export default function rectToClientRect(rect) {\n return Object.assign({}, rect, {\n left: rect.x,\n top: rect.y,\n right: rect.x + rect.width,\n bottom: rect.y + rect.height\n });\n}","import { viewport } from \"../enums.js\";\nimport getViewportRect from \"./getViewportRect.js\";\nimport getDocumentRect from \"./getDocumentRect.js\";\nimport listScrollParents from \"./listScrollParents.js\";\nimport getOffsetParent from \"./getOffsetParent.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport { isElement, isHTMLElement } from \"./instanceOf.js\";\nimport getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getParentNode from \"./getParentNode.js\";\nimport contains from \"./contains.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport rectToClientRect from \"../utils/rectToClientRect.js\";\nimport { max, min } from \"../utils/math.js\";\n\nfunction getInnerBoundingClientRect(element, strategy) {\n var rect = getBoundingClientRect(element, false, strategy === 'fixed');\n rect.top = rect.top + element.clientTop;\n rect.left = rect.left + element.clientLeft;\n rect.bottom = rect.top + element.clientHeight;\n rect.right = rect.left + element.clientWidth;\n rect.width = element.clientWidth;\n rect.height = element.clientHeight;\n rect.x = rect.left;\n rect.y = rect.top;\n return rect;\n}\n\nfunction getClientRectFromMixedType(element, clippingParent, strategy) {\n return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element)));\n} // A \"clipping parent\" is an overflowable container with the characteristic of\n// clipping (or hiding) overflowing elements with a position different from\n// `initial`\n\n\nfunction getClippingParents(element) {\n var clippingParents = listScrollParents(getParentNode(element));\n var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle(element).position) >= 0;\n var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element;\n\n if (!isElement(clipperElement)) {\n return [];\n } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414\n\n\n return clippingParents.filter(function (clippingParent) {\n return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body';\n });\n} // Gets the maximum area that the element is visible in due to any number of\n// clipping parents\n\n\nexport default function getClippingRect(element, boundary, rootBoundary, strategy) {\n var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary);\n var clippingParents = [].concat(mainClippingParents, [rootBoundary]);\n var firstClippingParent = clippingParents[0];\n var clippingRect = clippingParents.reduce(function (accRect, clippingParent) {\n var rect = getClientRectFromMixedType(element, clippingParent, strategy);\n accRect.top = max(rect.top, accRect.top);\n accRect.right = min(rect.right, accRect.right);\n accRect.bottom = min(rect.bottom, accRect.bottom);\n accRect.left = max(rect.left, accRect.left);\n return accRect;\n }, getClientRectFromMixedType(element, firstClippingParent, strategy));\n clippingRect.width = clippingRect.right - clippingRect.left;\n clippingRect.height = clippingRect.bottom - clippingRect.top;\n clippingRect.x = clippingRect.left;\n clippingRect.y = clippingRect.top;\n return clippingRect;\n}","import getWindow from \"./getWindow.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport isLayoutViewport from \"./isLayoutViewport.js\";\nexport default function getViewportRect(element, strategy) {\n var win = getWindow(element);\n var html = getDocumentElement(element);\n var visualViewport = win.visualViewport;\n var width = html.clientWidth;\n var height = html.clientHeight;\n var x = 0;\n var y = 0;\n\n if (visualViewport) {\n width = visualViewport.width;\n height = visualViewport.height;\n var layoutViewport = isLayoutViewport();\n\n if (layoutViewport || !layoutViewport && strategy === 'fixed') {\n x = visualViewport.offsetLeft;\n y = visualViewport.offsetTop;\n }\n }\n\n return {\n width: width,\n height: height,\n x: x + getWindowScrollBarX(element),\n y: y\n };\n}","import getDocumentElement from \"./getDocumentElement.js\";\nimport getComputedStyle from \"./getComputedStyle.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getWindowScroll from \"./getWindowScroll.js\";\nimport { max } from \"../utils/math.js\"; // Gets the entire size of the scrollable document area, even extending outside\n// of the `` and `` rect bounds if horizontally scrollable\n\nexport default function getDocumentRect(element) {\n var _element$ownerDocumen;\n\n var html = getDocumentElement(element);\n var winScroll = getWindowScroll(element);\n var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body;\n var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0);\n var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0);\n var x = -winScroll.scrollLeft + getWindowScrollBarX(element);\n var y = -winScroll.scrollTop;\n\n if (getComputedStyle(body || html).direction === 'rtl') {\n x += max(html.clientWidth, body ? body.clientWidth : 0) - width;\n }\n\n return {\n width: width,\n height: height,\n x: x,\n y: y\n };\n}","import getBasePlacement from \"./getBasePlacement.js\";\nimport getVariation from \"./getVariation.js\";\nimport getMainAxisFromPlacement from \"./getMainAxisFromPlacement.js\";\nimport { top, right, bottom, left, start, end } from \"../enums.js\";\nexport default function computeOffsets(_ref) {\n var reference = _ref.reference,\n element = _ref.element,\n placement = _ref.placement;\n var basePlacement = placement ? getBasePlacement(placement) : null;\n var variation = placement ? getVariation(placement) : null;\n var commonX = reference.x + reference.width / 2 - element.width / 2;\n var commonY = reference.y + reference.height / 2 - element.height / 2;\n var offsets;\n\n switch (basePlacement) {\n case top:\n offsets = {\n x: commonX,\n y: reference.y - element.height\n };\n break;\n\n case bottom:\n offsets = {\n x: commonX,\n y: reference.y + reference.height\n };\n break;\n\n case right:\n offsets = {\n x: reference.x + reference.width,\n y: commonY\n };\n break;\n\n case left:\n offsets = {\n x: reference.x - element.width,\n y: commonY\n };\n break;\n\n default:\n offsets = {\n x: reference.x,\n y: reference.y\n };\n }\n\n var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null;\n\n if (mainAxis != null) {\n var len = mainAxis === 'y' ? 'height' : 'width';\n\n switch (variation) {\n case start:\n offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2);\n break;\n\n case end:\n offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2);\n break;\n\n default:\n }\n }\n\n return offsets;\n}","import getClippingRect from \"../dom-utils/getClippingRect.js\";\nimport getDocumentElement from \"../dom-utils/getDocumentElement.js\";\nimport getBoundingClientRect from \"../dom-utils/getBoundingClientRect.js\";\nimport computeOffsets from \"./computeOffsets.js\";\nimport rectToClientRect from \"./rectToClientRect.js\";\nimport { clippingParents, reference, popper, bottom, top, right, basePlacements, viewport } from \"../enums.js\";\nimport { isElement } from \"../dom-utils/instanceOf.js\";\nimport mergePaddingObject from \"./mergePaddingObject.js\";\nimport expandToHashMap from \"./expandToHashMap.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport default function detectOverflow(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n _options$placement = _options.placement,\n placement = _options$placement === void 0 ? state.placement : _options$placement,\n _options$strategy = _options.strategy,\n strategy = _options$strategy === void 0 ? state.strategy : _options$strategy,\n _options$boundary = _options.boundary,\n boundary = _options$boundary === void 0 ? clippingParents : _options$boundary,\n _options$rootBoundary = _options.rootBoundary,\n rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary,\n _options$elementConte = _options.elementContext,\n elementContext = _options$elementConte === void 0 ? popper : _options$elementConte,\n _options$altBoundary = _options.altBoundary,\n altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary,\n _options$padding = _options.padding,\n padding = _options$padding === void 0 ? 0 : _options$padding;\n var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements));\n var altContext = elementContext === popper ? reference : popper;\n var popperRect = state.rects.popper;\n var element = state.elements[altBoundary ? altContext : elementContext];\n var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy);\n var referenceClientRect = getBoundingClientRect(state.elements.reference);\n var popperOffsets = computeOffsets({\n reference: referenceClientRect,\n element: popperRect,\n strategy: 'absolute',\n placement: placement\n });\n var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets));\n var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect\n // 0 or negative = within the clipping rect\n\n var overflowOffsets = {\n top: clippingClientRect.top - elementClientRect.top + paddingObject.top,\n bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom,\n left: clippingClientRect.left - elementClientRect.left + paddingObject.left,\n right: elementClientRect.right - clippingClientRect.right + paddingObject.right\n };\n var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element\n\n if (elementContext === popper && offsetData) {\n var offset = offsetData[placement];\n Object.keys(overflowOffsets).forEach(function (key) {\n var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1;\n var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x';\n overflowOffsets[key] += offset[axis] * multiply;\n });\n }\n\n return overflowOffsets;\n}","import getOppositePlacement from \"../utils/getOppositePlacement.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getOppositeVariationPlacement from \"../utils/getOppositeVariationPlacement.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport computeAutoPlacement from \"../utils/computeAutoPlacement.js\";\nimport { bottom, top, start, right, left, auto } from \"../enums.js\";\nimport getVariation from \"../utils/getVariation.js\"; // eslint-disable-next-line import/no-unused-modules\n\nfunction getExpandedFallbackPlacements(placement) {\n if (getBasePlacement(placement) === auto) {\n return [];\n }\n\n var oppositePlacement = getOppositePlacement(placement);\n return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)];\n}\n\nfunction flip(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n\n if (state.modifiersData[name]._skip) {\n return;\n }\n\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis,\n specifiedFallbackPlacements = options.fallbackPlacements,\n padding = options.padding,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n _options$flipVariatio = options.flipVariations,\n flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio,\n allowedAutoPlacements = options.allowedAutoPlacements;\n var preferredPlacement = state.options.placement;\n var basePlacement = getBasePlacement(preferredPlacement);\n var isBasePlacement = basePlacement === preferredPlacement;\n var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement));\n var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) {\n return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n flipVariations: flipVariations,\n allowedAutoPlacements: allowedAutoPlacements\n }) : placement);\n }, []);\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var checksMap = new Map();\n var makeFallbackChecks = true;\n var firstFittingPlacement = placements[0];\n\n for (var i = 0; i < placements.length; i++) {\n var placement = placements[i];\n\n var _basePlacement = getBasePlacement(placement);\n\n var isStartVariation = getVariation(placement) === start;\n var isVertical = [top, bottom].indexOf(_basePlacement) >= 0;\n var len = isVertical ? 'width' : 'height';\n var overflow = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n altBoundary: altBoundary,\n padding: padding\n });\n var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top;\n\n if (referenceRect[len] > popperRect[len]) {\n mainVariationSide = getOppositePlacement(mainVariationSide);\n }\n\n var altVariationSide = getOppositePlacement(mainVariationSide);\n var checks = [];\n\n if (checkMainAxis) {\n checks.push(overflow[_basePlacement] <= 0);\n }\n\n if (checkAltAxis) {\n checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0);\n }\n\n if (checks.every(function (check) {\n return check;\n })) {\n firstFittingPlacement = placement;\n makeFallbackChecks = false;\n break;\n }\n\n checksMap.set(placement, checks);\n }\n\n if (makeFallbackChecks) {\n // `2` may be desired in some cases – research later\n var numberOfChecks = flipVariations ? 3 : 1;\n\n var _loop = function _loop(_i) {\n var fittingPlacement = placements.find(function (placement) {\n var checks = checksMap.get(placement);\n\n if (checks) {\n return checks.slice(0, _i).every(function (check) {\n return check;\n });\n }\n });\n\n if (fittingPlacement) {\n firstFittingPlacement = fittingPlacement;\n return \"break\";\n }\n };\n\n for (var _i = numberOfChecks; _i > 0; _i--) {\n var _ret = _loop(_i);\n\n if (_ret === \"break\") break;\n }\n }\n\n if (state.placement !== firstFittingPlacement) {\n state.modifiersData[name]._skip = true;\n state.placement = firstFittingPlacement;\n state.reset = true;\n }\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'flip',\n enabled: true,\n phase: 'main',\n fn: flip,\n requiresIfExists: ['offset'],\n data: {\n _skip: false\n }\n};","import getVariation from \"./getVariation.js\";\nimport { variationPlacements, basePlacements, placements as allPlacements } from \"../enums.js\";\nimport detectOverflow from \"./detectOverflow.js\";\nimport getBasePlacement from \"./getBasePlacement.js\";\nexport default function computeAutoPlacement(state, options) {\n if (options === void 0) {\n options = {};\n }\n\n var _options = options,\n placement = _options.placement,\n boundary = _options.boundary,\n rootBoundary = _options.rootBoundary,\n padding = _options.padding,\n flipVariations = _options.flipVariations,\n _options$allowedAutoP = _options.allowedAutoPlacements,\n allowedAutoPlacements = _options$allowedAutoP === void 0 ? allPlacements : _options$allowedAutoP;\n var variation = getVariation(placement);\n var placements = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) {\n return getVariation(placement) === variation;\n }) : basePlacements;\n var allowedPlacements = placements.filter(function (placement) {\n return allowedAutoPlacements.indexOf(placement) >= 0;\n });\n\n if (allowedPlacements.length === 0) {\n allowedPlacements = placements;\n } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions...\n\n\n var overflows = allowedPlacements.reduce(function (acc, placement) {\n acc[placement] = detectOverflow(state, {\n placement: placement,\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding\n })[getBasePlacement(placement)];\n return acc;\n }, {});\n return Object.keys(overflows).sort(function (a, b) {\n return overflows[a] - overflows[b];\n });\n}","import { top, bottom, left, right } from \"../enums.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\n\nfunction getSideOffsets(overflow, rect, preventedOffsets) {\n if (preventedOffsets === void 0) {\n preventedOffsets = {\n x: 0,\n y: 0\n };\n }\n\n return {\n top: overflow.top - rect.height - preventedOffsets.y,\n right: overflow.right - rect.width + preventedOffsets.x,\n bottom: overflow.bottom - rect.height + preventedOffsets.y,\n left: overflow.left - rect.width - preventedOffsets.x\n };\n}\n\nfunction isAnySideFullyClipped(overflow) {\n return [top, right, bottom, left].some(function (side) {\n return overflow[side] >= 0;\n });\n}\n\nfunction hide(_ref) {\n var state = _ref.state,\n name = _ref.name;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var preventedOffsets = state.modifiersData.preventOverflow;\n var referenceOverflow = detectOverflow(state, {\n elementContext: 'reference'\n });\n var popperAltOverflow = detectOverflow(state, {\n altBoundary: true\n });\n var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect);\n var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets);\n var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets);\n var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets);\n state.modifiersData[name] = {\n referenceClippingOffsets: referenceClippingOffsets,\n popperEscapeOffsets: popperEscapeOffsets,\n isReferenceHidden: isReferenceHidden,\n hasPopperEscaped: hasPopperEscaped\n };\n state.attributes.popper = Object.assign({}, state.attributes.popper, {\n 'data-popper-reference-hidden': isReferenceHidden,\n 'data-popper-escaped': hasPopperEscaped\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'hide',\n enabled: true,\n phase: 'main',\n requiresIfExists: ['preventOverflow'],\n fn: hide\n};","import getBasePlacement from \"../utils/getBasePlacement.js\";\nimport { top, left, right, placements } from \"../enums.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport function distanceAndSkiddingToXY(placement, rects, offset) {\n var basePlacement = getBasePlacement(placement);\n var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1;\n\n var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, {\n placement: placement\n })) : offset,\n skidding = _ref[0],\n distance = _ref[1];\n\n skidding = skidding || 0;\n distance = (distance || 0) * invertDistance;\n return [left, right].indexOf(basePlacement) >= 0 ? {\n x: distance,\n y: skidding\n } : {\n x: skidding,\n y: distance\n };\n}\n\nfunction offset(_ref2) {\n var state = _ref2.state,\n options = _ref2.options,\n name = _ref2.name;\n var _options$offset = options.offset,\n offset = _options$offset === void 0 ? [0, 0] : _options$offset;\n var data = placements.reduce(function (acc, placement) {\n acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset);\n return acc;\n }, {});\n var _data$state$placement = data[state.placement],\n x = _data$state$placement.x,\n y = _data$state$placement.y;\n\n if (state.modifiersData.popperOffsets != null) {\n state.modifiersData.popperOffsets.x += x;\n state.modifiersData.popperOffsets.y += y;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'offset',\n enabled: true,\n phase: 'main',\n requires: ['popperOffsets'],\n fn: offset\n};","import computeOffsets from \"../utils/computeOffsets.js\";\n\nfunction popperOffsets(_ref) {\n var state = _ref.state,\n name = _ref.name;\n // Offsets are the actual position the popper needs to have to be\n // properly positioned near its reference element\n // This is the most basic placement, and will be adjusted by\n // the modifiers in the next step\n state.modifiersData[name] = computeOffsets({\n reference: state.rects.reference,\n element: state.rects.popper,\n strategy: 'absolute',\n placement: state.placement\n });\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'popperOffsets',\n enabled: true,\n phase: 'read',\n fn: popperOffsets,\n data: {}\n};","import { top, left, right, bottom, start } from \"../enums.js\";\nimport getBasePlacement from \"../utils/getBasePlacement.js\";\nimport getMainAxisFromPlacement from \"../utils/getMainAxisFromPlacement.js\";\nimport getAltAxis from \"../utils/getAltAxis.js\";\nimport { within, withinMaxClamp } from \"../utils/within.js\";\nimport getLayoutRect from \"../dom-utils/getLayoutRect.js\";\nimport getOffsetParent from \"../dom-utils/getOffsetParent.js\";\nimport detectOverflow from \"../utils/detectOverflow.js\";\nimport getVariation from \"../utils/getVariation.js\";\nimport getFreshSideObject from \"../utils/getFreshSideObject.js\";\nimport { min as mathMin, max as mathMax } from \"../utils/math.js\";\n\nfunction preventOverflow(_ref) {\n var state = _ref.state,\n options = _ref.options,\n name = _ref.name;\n var _options$mainAxis = options.mainAxis,\n checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis,\n _options$altAxis = options.altAxis,\n checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis,\n boundary = options.boundary,\n rootBoundary = options.rootBoundary,\n altBoundary = options.altBoundary,\n padding = options.padding,\n _options$tether = options.tether,\n tether = _options$tether === void 0 ? true : _options$tether,\n _options$tetherOffset = options.tetherOffset,\n tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset;\n var overflow = detectOverflow(state, {\n boundary: boundary,\n rootBoundary: rootBoundary,\n padding: padding,\n altBoundary: altBoundary\n });\n var basePlacement = getBasePlacement(state.placement);\n var variation = getVariation(state.placement);\n var isBasePlacement = !variation;\n var mainAxis = getMainAxisFromPlacement(basePlacement);\n var altAxis = getAltAxis(mainAxis);\n var popperOffsets = state.modifiersData.popperOffsets;\n var referenceRect = state.rects.reference;\n var popperRect = state.rects.popper;\n var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, {\n placement: state.placement\n })) : tetherOffset;\n var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? {\n mainAxis: tetherOffsetValue,\n altAxis: tetherOffsetValue\n } : Object.assign({\n mainAxis: 0,\n altAxis: 0\n }, tetherOffsetValue);\n var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null;\n var data = {\n x: 0,\n y: 0\n };\n\n if (!popperOffsets) {\n return;\n }\n\n if (checkMainAxis) {\n var _offsetModifierState$;\n\n var mainSide = mainAxis === 'y' ? top : left;\n var altSide = mainAxis === 'y' ? bottom : right;\n var len = mainAxis === 'y' ? 'height' : 'width';\n var offset = popperOffsets[mainAxis];\n var min = offset + overflow[mainSide];\n var max = offset - overflow[altSide];\n var additive = tether ? -popperRect[len] / 2 : 0;\n var minLen = variation === start ? referenceRect[len] : popperRect[len];\n var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go\n // outside the reference bounds\n\n var arrowElement = state.elements.arrow;\n var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : {\n width: 0,\n height: 0\n };\n var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject();\n var arrowPaddingMin = arrowPaddingObject[mainSide];\n var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want\n // to include its full size in the calculation. If the reference is small\n // and near the edge of a boundary, the popper can overflow even if the\n // reference is not overflowing as well (e.g. virtual elements with no\n // width or height)\n\n var arrowLen = within(0, referenceRect[len], arrowRect[len]);\n var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis;\n var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis;\n var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow);\n var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0;\n var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0;\n var tetherMin = offset + minOffset - offsetModifierValue - clientOffset;\n var tetherMax = offset + maxOffset - offsetModifierValue;\n var preventedOffset = within(tether ? mathMin(min, tetherMin) : min, offset, tether ? mathMax(max, tetherMax) : max);\n popperOffsets[mainAxis] = preventedOffset;\n data[mainAxis] = preventedOffset - offset;\n }\n\n if (checkAltAxis) {\n var _offsetModifierState$2;\n\n var _mainSide = mainAxis === 'x' ? top : left;\n\n var _altSide = mainAxis === 'x' ? bottom : right;\n\n var _offset = popperOffsets[altAxis];\n\n var _len = altAxis === 'y' ? 'height' : 'width';\n\n var _min = _offset + overflow[_mainSide];\n\n var _max = _offset - overflow[_altSide];\n\n var isOriginSide = [top, left].indexOf(basePlacement) !== -1;\n\n var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0;\n\n var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis;\n\n var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max;\n\n var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max);\n\n popperOffsets[altAxis] = _preventedOffset;\n data[altAxis] = _preventedOffset - _offset;\n }\n\n state.modifiersData[name] = data;\n} // eslint-disable-next-line import/no-unused-modules\n\n\nexport default {\n name: 'preventOverflow',\n enabled: true,\n phase: 'main',\n fn: preventOverflow,\n requiresIfExists: ['offset']\n};","export default function getAltAxis(axis) {\n return axis === 'x' ? 'y' : 'x';\n}","import getBoundingClientRect from \"./getBoundingClientRect.js\";\nimport getNodeScroll from \"./getNodeScroll.js\";\nimport getNodeName from \"./getNodeName.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getWindowScrollBarX from \"./getWindowScrollBarX.js\";\nimport getDocumentElement from \"./getDocumentElement.js\";\nimport isScrollParent from \"./isScrollParent.js\";\nimport { round } from \"../utils/math.js\";\n\nfunction isElementScaled(element) {\n var rect = element.getBoundingClientRect();\n var scaleX = round(rect.width) / element.offsetWidth || 1;\n var scaleY = round(rect.height) / element.offsetHeight || 1;\n return scaleX !== 1 || scaleY !== 1;\n} // Returns the composite rect of an element relative to its offsetParent.\n// Composite means it takes into account transforms as well as layout.\n\n\nexport default function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) {\n if (isFixed === void 0) {\n isFixed = false;\n }\n\n var isOffsetParentAnElement = isHTMLElement(offsetParent);\n var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent);\n var documentElement = getDocumentElement(offsetParent);\n var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed);\n var scroll = {\n scrollLeft: 0,\n scrollTop: 0\n };\n var offsets = {\n x: 0,\n y: 0\n };\n\n if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {\n if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078\n isScrollParent(documentElement)) {\n scroll = getNodeScroll(offsetParent);\n }\n\n if (isHTMLElement(offsetParent)) {\n offsets = getBoundingClientRect(offsetParent, true);\n offsets.x += offsetParent.clientLeft;\n offsets.y += offsetParent.clientTop;\n } else if (documentElement) {\n offsets.x = getWindowScrollBarX(documentElement);\n }\n }\n\n return {\n x: rect.left + scroll.scrollLeft - offsets.x,\n y: rect.top + scroll.scrollTop - offsets.y,\n width: rect.width,\n height: rect.height\n };\n}","import getWindowScroll from \"./getWindowScroll.js\";\nimport getWindow from \"./getWindow.js\";\nimport { isHTMLElement } from \"./instanceOf.js\";\nimport getHTMLElementScroll from \"./getHTMLElementScroll.js\";\nexport default function getNodeScroll(node) {\n if (node === getWindow(node) || !isHTMLElement(node)) {\n return getWindowScroll(node);\n } else {\n return getHTMLElementScroll(node);\n }\n}","export default function getHTMLElementScroll(element) {\n return {\n scrollLeft: element.scrollLeft,\n scrollTop: element.scrollTop\n };\n}","import { modifierPhases } from \"../enums.js\"; // source: https://stackoverflow.com/questions/49875255\n\nfunction order(modifiers) {\n var map = new Map();\n var visited = new Set();\n var result = [];\n modifiers.forEach(function (modifier) {\n map.set(modifier.name, modifier);\n }); // On visiting object, check for its dependencies and visit them recursively\n\n function sort(modifier) {\n visited.add(modifier.name);\n var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []);\n requires.forEach(function (dep) {\n if (!visited.has(dep)) {\n var depModifier = map.get(dep);\n\n if (depModifier) {\n sort(depModifier);\n }\n }\n });\n result.push(modifier);\n }\n\n modifiers.forEach(function (modifier) {\n if (!visited.has(modifier.name)) {\n // check for visited object\n sort(modifier);\n }\n });\n return result;\n}\n\nexport default function orderModifiers(modifiers) {\n // order based on dependencies\n var orderedModifiers = order(modifiers); // order based on phase\n\n return modifierPhases.reduce(function (acc, phase) {\n return acc.concat(orderedModifiers.filter(function (modifier) {\n return modifier.phase === phase;\n }));\n }, []);\n}","import getCompositeRect from \"./dom-utils/getCompositeRect.js\";\nimport getLayoutRect from \"./dom-utils/getLayoutRect.js\";\nimport listScrollParents from \"./dom-utils/listScrollParents.js\";\nimport getOffsetParent from \"./dom-utils/getOffsetParent.js\";\nimport orderModifiers from \"./utils/orderModifiers.js\";\nimport debounce from \"./utils/debounce.js\";\nimport mergeByName from \"./utils/mergeByName.js\";\nimport detectOverflow from \"./utils/detectOverflow.js\";\nimport { isElement } from \"./dom-utils/instanceOf.js\";\nvar DEFAULT_OPTIONS = {\n placement: 'bottom',\n modifiers: [],\n strategy: 'absolute'\n};\n\nfunction areValidElements() {\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n return !args.some(function (element) {\n return !(element && typeof element.getBoundingClientRect === 'function');\n });\n}\n\nexport function popperGenerator(generatorOptions) {\n if (generatorOptions === void 0) {\n generatorOptions = {};\n }\n\n var _generatorOptions = generatorOptions,\n _generatorOptions$def = _generatorOptions.defaultModifiers,\n defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def,\n _generatorOptions$def2 = _generatorOptions.defaultOptions,\n defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2;\n return function createPopper(reference, popper, options) {\n if (options === void 0) {\n options = defaultOptions;\n }\n\n var state = {\n placement: 'bottom',\n orderedModifiers: [],\n options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions),\n modifiersData: {},\n elements: {\n reference: reference,\n popper: popper\n },\n attributes: {},\n styles: {}\n };\n var effectCleanupFns = [];\n var isDestroyed = false;\n var instance = {\n state: state,\n setOptions: function setOptions(setOptionsAction) {\n var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction;\n cleanupModifierEffects();\n state.options = Object.assign({}, defaultOptions, state.options, options);\n state.scrollParents = {\n reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [],\n popper: listScrollParents(popper)\n }; // Orders the modifiers based on their dependencies and `phase`\n // properties\n\n var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers\n\n state.orderedModifiers = orderedModifiers.filter(function (m) {\n return m.enabled;\n });\n runModifierEffects();\n return instance.update();\n },\n // Sync update – it will always be executed, even if not necessary. This\n // is useful for low frequency updates where sync behavior simplifies the\n // logic.\n // For high frequency updates (e.g. `resize` and `scroll` events), always\n // prefer the async Popper#update method\n forceUpdate: function forceUpdate() {\n if (isDestroyed) {\n return;\n }\n\n var _state$elements = state.elements,\n reference = _state$elements.reference,\n popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements\n // anymore\n\n if (!areValidElements(reference, popper)) {\n return;\n } // Store the reference and popper rects to be read by modifiers\n\n\n state.rects = {\n reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'),\n popper: getLayoutRect(popper)\n }; // Modifiers have the ability to reset the current update cycle. The\n // most common use case for this is the `flip` modifier changing the\n // placement, which then needs to re-run all the modifiers, because the\n // logic was previously ran for the previous placement and is therefore\n // stale/incorrect\n\n state.reset = false;\n state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier\n // is filled with the initial data specified by the modifier. This means\n // it doesn't persist and is fresh on each update.\n // To ensure persistent data, use `${name}#persistent`\n\n state.orderedModifiers.forEach(function (modifier) {\n return state.modifiersData[modifier.name] = Object.assign({}, modifier.data);\n });\n\n for (var index = 0; index < state.orderedModifiers.length; index++) {\n if (state.reset === true) {\n state.reset = false;\n index = -1;\n continue;\n }\n\n var _state$orderedModifie = state.orderedModifiers[index],\n fn = _state$orderedModifie.fn,\n _state$orderedModifie2 = _state$orderedModifie.options,\n _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2,\n name = _state$orderedModifie.name;\n\n if (typeof fn === 'function') {\n state = fn({\n state: state,\n options: _options,\n name: name,\n instance: instance\n }) || state;\n }\n }\n },\n // Async and optimistically optimized update – it will not be executed if\n // not necessary (debounced to run at most once-per-tick)\n update: debounce(function () {\n return new Promise(function (resolve) {\n instance.forceUpdate();\n resolve(state);\n });\n }),\n destroy: function destroy() {\n cleanupModifierEffects();\n isDestroyed = true;\n }\n };\n\n if (!areValidElements(reference, popper)) {\n return instance;\n }\n\n instance.setOptions(options).then(function (state) {\n if (!isDestroyed && options.onFirstUpdate) {\n options.onFirstUpdate(state);\n }\n }); // Modifiers have the ability to execute arbitrary code before the first\n // update cycle runs. They will be executed in the same order as the update\n // cycle. This is useful when a modifier adds some persistent data that\n // other modifiers need to use, but the modifier is run after the dependent\n // one.\n\n function runModifierEffects() {\n state.orderedModifiers.forEach(function (_ref) {\n var name = _ref.name,\n _ref$options = _ref.options,\n options = _ref$options === void 0 ? {} : _ref$options,\n effect = _ref.effect;\n\n if (typeof effect === 'function') {\n var cleanupFn = effect({\n state: state,\n name: name,\n instance: instance,\n options: options\n });\n\n var noopFn = function noopFn() {};\n\n effectCleanupFns.push(cleanupFn || noopFn);\n }\n });\n }\n\n function cleanupModifierEffects() {\n effectCleanupFns.forEach(function (fn) {\n return fn();\n });\n effectCleanupFns = [];\n }\n\n return instance;\n };\n}\nexport var createPopper = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules\n\nexport { detectOverflow };","export default function debounce(fn) {\n var pending;\n return function () {\n if (!pending) {\n pending = new Promise(function (resolve) {\n Promise.resolve().then(function () {\n pending = undefined;\n resolve(fn());\n });\n });\n }\n\n return pending;\n };\n}","export default function mergeByName(modifiers) {\n var merged = modifiers.reduce(function (merged, current) {\n var existing = merged[current.name];\n merged[current.name] = existing ? Object.assign({}, existing, current, {\n options: Object.assign({}, existing.options, current.options),\n data: Object.assign({}, existing.data, current.data)\n }) : current;\n return merged;\n }, {}); // IE11 does not support Object.values\n\n return Object.keys(merged).map(function (key) {\n return merged[key];\n });\n}","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nimport offset from \"./modifiers/offset.js\";\nimport flip from \"./modifiers/flip.js\";\nimport preventOverflow from \"./modifiers/preventOverflow.js\";\nimport arrow from \"./modifiers/arrow.js\";\nimport hide from \"./modifiers/hide.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles, offset, flip, preventOverflow, arrow, hide];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow }; // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper as createPopperLite } from \"./popper-lite.js\"; // eslint-disable-next-line import/no-unused-modules\n\nexport * from \"./modifiers/index.js\";","import { popperGenerator, detectOverflow } from \"./createPopper.js\";\nimport eventListeners from \"./modifiers/eventListeners.js\";\nimport popperOffsets from \"./modifiers/popperOffsets.js\";\nimport computeStyles from \"./modifiers/computeStyles.js\";\nimport applyStyles from \"./modifiers/applyStyles.js\";\nvar defaultModifiers = [eventListeners, popperOffsets, computeStyles, applyStyles];\nvar createPopper = /*#__PURE__*/popperGenerator({\n defaultModifiers: defaultModifiers\n}); // eslint-disable-next-line import/no-unused-modules\n\nexport { createPopper, popperGenerator, defaultModifiers, detectOverflow };","/*!\n * Bootstrap v5.3.3 (https://getbootstrap.com/)\n * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\nimport * as Popper from '@popperjs/core';\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\n */\n\nconst elementMap = new Map();\nconst Data = {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map());\n }\n const instanceMap = elementMap.get(element);\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);\n return;\n }\n instanceMap.set(key, instance);\n },\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null;\n }\n return null;\n },\n remove(element, key) {\n if (!elementMap.has(element)) {\n return;\n }\n const instanceMap = elementMap.get(element);\n instanceMap.delete(key);\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element);\n }\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000;\nconst MILLISECONDS_MULTIPLIER = 1000;\nconst TRANSITION_END = 'transitionend';\n\n/**\n * Properly escape IDs selectors to handle weird IDs\n * @param {string} selector\n * @returns {string}\n */\nconst parseSelector = selector => {\n if (selector && window.CSS && window.CSS.escape) {\n // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`);\n }\n return selector;\n};\n\n// Shout-out Angus Croll (https://goo.gl/pxwQGp)\nconst toType = object => {\n if (object === null || object === undefined) {\n return `${object}`;\n }\n return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase();\n};\n\n/**\n * Public Util API\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID);\n } while (document.getElementById(prefix));\n return prefix;\n};\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0;\n }\n\n // Get transition-duration of the element\n let {\n transitionDuration,\n transitionDelay\n } = window.getComputedStyle(element);\n const floatTransitionDuration = Number.parseFloat(transitionDuration);\n const floatTransitionDelay = Number.parseFloat(transitionDelay);\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0;\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0];\n transitionDelay = transitionDelay.split(',')[0];\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;\n};\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END));\n};\nconst isElement = object => {\n if (!object || typeof object !== 'object') {\n return false;\n }\n if (typeof object.jquery !== 'undefined') {\n object = object[0];\n }\n return typeof object.nodeType !== 'undefined';\n};\nconst getElement = object => {\n // it's a jQuery object or a node element\n if (isElement(object)) {\n return object.jquery ? object[0] : object;\n }\n if (typeof object === 'string' && object.length > 0) {\n return document.querySelector(parseSelector(object));\n }\n return null;\n};\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false;\n }\n const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible';\n // Handle `details` element as its content may falsie appear visible when it is closed\n const closedDetails = element.closest('details:not([open])');\n if (!closedDetails) {\n return elementIsVisible;\n }\n if (closedDetails !== element) {\n const summary = element.closest('summary');\n if (summary && summary.parentNode !== closedDetails) {\n return false;\n }\n if (summary === null) {\n return false;\n }\n }\n return elementIsVisible;\n};\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true;\n }\n if (element.classList.contains('disabled')) {\n return true;\n }\n if (typeof element.disabled !== 'undefined') {\n return element.disabled;\n }\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';\n};\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null;\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode();\n return root instanceof ShadowRoot ? root : null;\n }\n if (element instanceof ShadowRoot) {\n return element;\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null;\n }\n return findShadowRoot(element.parentNode);\n};\nconst noop = () => {};\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n element.offsetHeight; // eslint-disable-line no-unused-expressions\n};\nconst getjQuery = () => {\n if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return window.jQuery;\n }\n return null;\n};\nconst DOMContentLoadedCallbacks = [];\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n for (const callback of DOMContentLoadedCallbacks) {\n callback();\n }\n });\n }\n DOMContentLoadedCallbacks.push(callback);\n } else {\n callback();\n }\n};\nconst isRTL = () => document.documentElement.dir === 'rtl';\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery();\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME;\n const JQUERY_NO_CONFLICT = $.fn[name];\n $.fn[name] = plugin.jQueryInterface;\n $.fn[name].Constructor = plugin;\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT;\n return plugin.jQueryInterface;\n };\n }\n });\n};\nconst execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue;\n};\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback);\n return;\n }\n const durationPadding = 5;\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;\n let called = false;\n const handler = ({\n target\n }) => {\n if (target !== transitionElement) {\n return;\n }\n called = true;\n transitionElement.removeEventListener(TRANSITION_END, handler);\n execute(callback);\n };\n transitionElement.addEventListener(TRANSITION_END, handler);\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement);\n }\n }, emulatedDuration);\n};\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n const listLength = list.length;\n let index = list.indexOf(activeElement);\n\n // if the element does not exist in the list return an element\n // depending on the direction and if cycle is allowed\n if (index === -1) {\n return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];\n }\n index += shouldGetNext ? 1 : -1;\n if (isCycleAllowed) {\n index = (index + listLength) % listLength;\n }\n return list[Math.max(0, Math.min(index, listLength - 1))];\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/;\nconst stripNameRegex = /\\..*/;\nconst stripUidRegex = /::\\d+$/;\nconst eventRegistry = {}; // Events storage\nlet uidEvent = 1;\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n};\nconst nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);\n\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;\n}\nfunction getElementEvents(element) {\n const uid = makeEventUid(element);\n element.uidEvent = uid;\n eventRegistry[uid] = eventRegistry[uid] || {};\n return eventRegistry[uid];\n}\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n hydrateObj(event, {\n delegateTarget: element\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn);\n }\n return fn.apply(element, [event]);\n };\n}\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector);\n for (let {\n target\n } = event; target && target !== this; target = target.parentNode) {\n for (const domElement of domElements) {\n if (domElement !== target) {\n continue;\n }\n hydrateObj(event, {\n delegateTarget: target\n });\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn);\n }\n return fn.apply(target, [event]);\n }\n }\n };\n}\nfunction findHandler(events, callable, delegationSelector = null) {\n return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);\n}\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n const isDelegated = typeof handler === 'string';\n // TODO: tooltip passes `false` instead of selector, so we need to check\n const callable = isDelegated ? delegationFunction : handler || delegationFunction;\n let typeEvent = getTypeEvent(originalTypeEvent);\n if (!nativeEvents.has(typeEvent)) {\n typeEvent = originalTypeEvent;\n }\n return [isDelegated, callable, typeEvent];\n}\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (originalTypeEvent in customEvents) {\n const wrapFunction = fn => {\n return function (event) {\n if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {\n return fn.call(this, event);\n }\n };\n };\n callable = wrapFunction(callable);\n }\n const events = getElementEvents(element);\n const handlers = events[typeEvent] || (events[typeEvent] = {});\n const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);\n if (previousFunction) {\n previousFunction.oneOff = previousFunction.oneOff && oneOff;\n return;\n }\n const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));\n const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);\n fn.delegationSelector = isDelegated ? handler : null;\n fn.callable = callable;\n fn.oneOff = oneOff;\n fn.uidEvent = uid;\n handlers[uid] = fn;\n element.addEventListener(typeEvent, fn, isDelegated);\n}\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector);\n if (!fn) {\n return;\n }\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));\n delete events[typeEvent][fn.uidEvent];\n}\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {};\n for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n if (handlerKey.includes(namespace)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n}\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '');\n return customEvents[event] || event;\n}\nconst EventHandler = {\n on(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, false);\n },\n one(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, true);\n },\n off(element, originalTypeEvent, handler, delegationFunction) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return;\n }\n const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);\n const inNamespace = typeEvent !== originalTypeEvent;\n const events = getElementEvents(element);\n const storeElementEvent = events[typeEvent] || {};\n const isNamespace = originalTypeEvent.startsWith('.');\n if (typeof callable !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!Object.keys(storeElementEvent).length) {\n return;\n }\n removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);\n return;\n }\n if (isNamespace) {\n for (const elementEvent of Object.keys(events)) {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));\n }\n }\n for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n const handlerKey = keyHandlers.replace(stripUidRegex, '');\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);\n }\n }\n },\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null;\n }\n const $ = getjQuery();\n const typeEvent = getTypeEvent(event);\n const inNamespace = event !== typeEvent;\n let jQueryEvent = null;\n let bubbles = true;\n let nativeDispatch = true;\n let defaultPrevented = false;\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args);\n $(element).trigger(jQueryEvent);\n bubbles = !jQueryEvent.isPropagationStopped();\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();\n defaultPrevented = jQueryEvent.isDefaultPrevented();\n }\n const evt = hydrateObj(new Event(event, {\n bubbles,\n cancelable: true\n }), args);\n if (defaultPrevented) {\n evt.preventDefault();\n }\n if (nativeDispatch) {\n element.dispatchEvent(evt);\n }\n if (evt.defaultPrevented && jQueryEvent) {\n jQueryEvent.preventDefault();\n }\n return evt;\n }\n};\nfunction hydrateObj(obj, meta = {}) {\n for (const [key, value] of Object.entries(meta)) {\n try {\n obj[key] = value;\n } catch (_unused) {\n Object.defineProperty(obj, key, {\n configurable: true,\n get() {\n return value;\n }\n });\n }\n }\n return obj;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true;\n }\n if (value === 'false') {\n return false;\n }\n if (value === Number(value).toString()) {\n return Number(value);\n }\n if (value === '' || value === 'null') {\n return null;\n }\n if (typeof value !== 'string') {\n return value;\n }\n try {\n return JSON.parse(decodeURIComponent(value));\n } catch (_unused) {\n return value;\n }\n}\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);\n}\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);\n },\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);\n },\n getDataAttributes(element) {\n if (!element) {\n return {};\n }\n const attributes = {};\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '');\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length);\n attributes[pureKey] = normalizeData(element.dataset[key]);\n }\n return attributes;\n },\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Class definition\n */\n\nclass Config {\n // Getters\n static get Default() {\n return {};\n }\n static get DefaultType() {\n return {};\n }\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!');\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n return config;\n }\n _mergeConfigObj(config, element) {\n const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse\n\n return {\n ...this.constructor.Default,\n ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n ...(typeof config === 'object' ? config : {})\n };\n }\n _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n for (const [property, expectedTypes] of Object.entries(configTypes)) {\n const value = config[property];\n const valueType = isElement(value) ? 'element' : toType(value);\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`);\n }\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst VERSION = '5.3.3';\n\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n constructor(element, config) {\n super();\n element = getElement(element);\n if (!element) {\n return;\n }\n this._element = element;\n this._config = this._getConfig(config);\n Data.set(this._element, this.constructor.DATA_KEY, this);\n }\n\n // Public\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY);\n EventHandler.off(this._element, this.constructor.EVENT_KEY);\n for (const propertyName of Object.getOwnPropertyNames(this)) {\n this[propertyName] = null;\n }\n }\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated);\n }\n _getConfig(config) {\n config = this._mergeConfigObj(config, this._element);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n\n // Static\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY);\n }\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);\n }\n static get VERSION() {\n return VERSION;\n }\n static get DATA_KEY() {\n return `bs.${this.NAME}`;\n }\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`;\n }\n static eventName(name) {\n return `${name}${this.EVENT_KEY}`;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target');\n if (!selector || selector === '#') {\n let hrefAttribute = element.getAttribute('href');\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {\n return null;\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n hrefAttribute = `#${hrefAttribute.split('#')[1]}`;\n }\n selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null;\n }\n return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null;\n};\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector));\n },\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector);\n },\n children(element, selector) {\n return [].concat(...element.children).filter(child => child.matches(selector));\n },\n parents(element, selector) {\n const parents = [];\n let ancestor = element.parentNode.closest(selector);\n while (ancestor) {\n parents.push(ancestor);\n ancestor = ancestor.parentNode.closest(selector);\n }\n return parents;\n },\n prev(element, selector) {\n let previous = element.previousElementSibling;\n while (previous) {\n if (previous.matches(selector)) {\n return [previous];\n }\n previous = previous.previousElementSibling;\n }\n return [];\n },\n // TODO: this is now unused; remove later along with prev()\n next(element, selector) {\n let next = element.nextElementSibling;\n while (next) {\n if (next.matches(selector)) {\n return [next];\n }\n next = next.nextElementSibling;\n }\n return [];\n },\n focusableChildren(element) {\n const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable=\"true\"]'].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',');\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el));\n },\n getSelectorFromElement(element) {\n const selector = getSelector(element);\n if (selector) {\n return SelectorEngine.findOne(selector) ? selector : null;\n }\n return null;\n },\n getElementFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.findOne(selector) : null;\n },\n getMultipleElementsFromSelector(element) {\n const selector = getSelector(element);\n return selector ? SelectorEngine.find(selector) : [];\n }\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`;\n const name = component.NAME;\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`);\n const instance = component.getOrCreateInstance(target);\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]();\n });\n};\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$f = 'alert';\nconst DATA_KEY$a = 'bs.alert';\nconst EVENT_KEY$b = `.${DATA_KEY$a}`;\nconst EVENT_CLOSE = `close${EVENT_KEY$b}`;\nconst EVENT_CLOSED = `closed${EVENT_KEY$b}`;\nconst CLASS_NAME_FADE$5 = 'fade';\nconst CLASS_NAME_SHOW$8 = 'show';\n\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$f;\n }\n\n // Public\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);\n if (closeEvent.defaultPrevented) {\n return;\n }\n this._element.classList.remove(CLASS_NAME_SHOW$8);\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5);\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated);\n }\n\n // Private\n _destroyElement() {\n this._element.remove();\n EventHandler.trigger(this._element, EVENT_CLOSED);\n this.dispose();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Alert, 'close');\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$e = 'button';\nconst DATA_KEY$9 = 'bs.button';\nconst EVENT_KEY$a = `.${DATA_KEY$9}`;\nconst DATA_API_KEY$6 = '.data-api';\nconst CLASS_NAME_ACTIVE$3 = 'active';\nconst SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle=\"button\"]';\nconst EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`;\n\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME$e;\n }\n\n // Public\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3));\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this);\n if (config === 'toggle') {\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => {\n event.preventDefault();\n const button = event.target.closest(SELECTOR_DATA_TOGGLE$5);\n const data = Button.getOrCreateInstance(button);\n data.toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$d = 'swipe';\nconst EVENT_KEY$9 = '.bs.swipe';\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`;\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`;\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`;\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`;\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`;\nconst POINTER_TYPE_TOUCH = 'touch';\nconst POINTER_TYPE_PEN = 'pen';\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event';\nconst SWIPE_THRESHOLD = 40;\nconst Default$c = {\n endCallback: null,\n leftCallback: null,\n rightCallback: null\n};\nconst DefaultType$c = {\n endCallback: '(function|null)',\n leftCallback: '(function|null)',\n rightCallback: '(function|null)'\n};\n\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n constructor(element, config) {\n super();\n this._element = element;\n if (!element || !Swipe.isSupported()) {\n return;\n }\n this._config = this._getConfig(config);\n this._deltaX = 0;\n this._supportPointerEvents = Boolean(window.PointerEvent);\n this._initEvents();\n }\n\n // Getters\n static get Default() {\n return Default$c;\n }\n static get DefaultType() {\n return DefaultType$c;\n }\n static get NAME() {\n return NAME$d;\n }\n\n // Public\n dispose() {\n EventHandler.off(this._element, EVENT_KEY$9);\n }\n\n // Private\n _start(event) {\n if (!this._supportPointerEvents) {\n this._deltaX = event.touches[0].clientX;\n return;\n }\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX;\n }\n }\n _end(event) {\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX - this._deltaX;\n }\n this._handleSwipe();\n execute(this._config.endCallback);\n }\n _move(event) {\n this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX;\n }\n _handleSwipe() {\n const absDeltaX = Math.abs(this._deltaX);\n if (absDeltaX <= SWIPE_THRESHOLD) {\n return;\n }\n const direction = absDeltaX / this._deltaX;\n this._deltaX = 0;\n if (!direction) {\n return;\n }\n execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback);\n }\n _initEvents() {\n if (this._supportPointerEvents) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event));\n EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event));\n this._element.classList.add(CLASS_NAME_POINTER_EVENT);\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event));\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event));\n EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event));\n }\n }\n _eventIsPointerPenTouch(event) {\n return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);\n }\n\n // Static\n static isSupported() {\n return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$c = 'carousel';\nconst DATA_KEY$8 = 'bs.carousel';\nconst EVENT_KEY$8 = `.${DATA_KEY$8}`;\nconst DATA_API_KEY$5 = '.data-api';\nconst ARROW_LEFT_KEY$1 = 'ArrowLeft';\nconst ARROW_RIGHT_KEY$1 = 'ArrowRight';\nconst TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next';\nconst ORDER_PREV = 'prev';\nconst DIRECTION_LEFT = 'left';\nconst DIRECTION_RIGHT = 'right';\nconst EVENT_SLIDE = `slide${EVENT_KEY$8}`;\nconst EVENT_SLID = `slid${EVENT_KEY$8}`;\nconst EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`;\nconst EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`;\nconst EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`;\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`;\nconst EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`;\nconst CLASS_NAME_CAROUSEL = 'carousel';\nconst CLASS_NAME_ACTIVE$2 = 'active';\nconst CLASS_NAME_SLIDE = 'slide';\nconst CLASS_NAME_END = 'carousel-item-end';\nconst CLASS_NAME_START = 'carousel-item-start';\nconst CLASS_NAME_NEXT = 'carousel-item-next';\nconst CLASS_NAME_PREV = 'carousel-item-prev';\nconst SELECTOR_ACTIVE = '.active';\nconst SELECTOR_ITEM = '.carousel-item';\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM;\nconst SELECTOR_ITEM_IMG = '.carousel-item img';\nconst SELECTOR_INDICATORS = '.carousel-indicators';\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]';\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]';\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT\n};\nconst Default$b = {\n interval: 5000,\n keyboard: true,\n pause: 'hover',\n ride: false,\n touch: true,\n wrap: true\n};\nconst DefaultType$b = {\n interval: '(number|boolean)',\n // TODO:v6 remove boolean support\n keyboard: 'boolean',\n pause: '(string|boolean)',\n ride: '(boolean|string)',\n touch: 'boolean',\n wrap: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._interval = null;\n this._activeElement = null;\n this._isSliding = false;\n this.touchTimeout = null;\n this._swipeHelper = null;\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element);\n this._addEventListeners();\n if (this._config.ride === CLASS_NAME_CAROUSEL) {\n this.cycle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$b;\n }\n static get DefaultType() {\n return DefaultType$b;\n }\n static get NAME() {\n return NAME$c;\n }\n\n // Public\n next() {\n this._slide(ORDER_NEXT);\n }\n nextWhenVisible() {\n // FIXME TODO use `document.visibilityState`\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next();\n }\n }\n prev() {\n this._slide(ORDER_PREV);\n }\n pause() {\n if (this._isSliding) {\n triggerTransitionEnd(this._element);\n }\n this._clearInterval();\n }\n cycle() {\n this._clearInterval();\n this._updateInterval();\n this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval);\n }\n _maybeEnableCycle() {\n if (!this._config.ride) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.cycle());\n return;\n }\n this.cycle();\n }\n to(index) {\n const items = this._getItems();\n if (index > items.length - 1 || index < 0) {\n return;\n }\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index));\n return;\n }\n const activeIndex = this._getItemIndex(this._getActive());\n if (activeIndex === index) {\n return;\n }\n const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV;\n this._slide(order, items[index]);\n }\n dispose() {\n if (this._swipeHelper) {\n this._swipeHelper.dispose();\n }\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n config.defaultInterval = config.interval;\n return config;\n }\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event));\n }\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause());\n EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle());\n }\n if (this._config.touch && Swipe.isSupported()) {\n this._addTouchEventListeners();\n }\n }\n _addTouchEventListeners() {\n for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault());\n }\n const endCallBack = () => {\n if (this._config.pause !== 'hover') {\n return;\n }\n\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause();\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout);\n }\n this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval);\n };\n const swipeConfig = {\n leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n endCallback: endCallBack\n };\n this._swipeHelper = new Swipe(this._element, swipeConfig);\n }\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return;\n }\n const direction = KEY_TO_DIRECTION[event.key];\n if (direction) {\n event.preventDefault();\n this._slide(this._directionToOrder(direction));\n }\n }\n _getItemIndex(element) {\n return this._getItems().indexOf(element);\n }\n _setActiveIndicatorElement(index) {\n if (!this._indicatorsElement) {\n return;\n }\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement);\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2);\n activeIndicator.removeAttribute('aria-current');\n const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement);\n if (newActiveIndicator) {\n newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2);\n newActiveIndicator.setAttribute('aria-current', 'true');\n }\n }\n _updateInterval() {\n const element = this._activeElement || this._getActive();\n if (!element) {\n return;\n }\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10);\n this._config.interval = elementInterval || this._config.defaultInterval;\n }\n _slide(order, element = null) {\n if (this._isSliding) {\n return;\n }\n const activeElement = this._getActive();\n const isNext = order === ORDER_NEXT;\n const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap);\n if (nextElement === activeElement) {\n return;\n }\n const nextElementIndex = this._getItemIndex(nextElement);\n const triggerEvent = eventName => {\n return EventHandler.trigger(this._element, eventName, {\n relatedTarget: nextElement,\n direction: this._orderToDirection(order),\n from: this._getItemIndex(activeElement),\n to: nextElementIndex\n });\n };\n const slideEvent = triggerEvent(EVENT_SLIDE);\n if (slideEvent.defaultPrevented) {\n return;\n }\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n // TODO: change tests that use empty divs to avoid this check\n return;\n }\n const isCycling = Boolean(this._interval);\n this.pause();\n this._isSliding = true;\n this._setActiveIndicatorElement(nextElementIndex);\n this._activeElement = nextElement;\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;\n nextElement.classList.add(orderClassName);\n reflow(nextElement);\n activeElement.classList.add(directionalClassName);\n nextElement.classList.add(directionalClassName);\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName);\n nextElement.classList.add(CLASS_NAME_ACTIVE$2);\n activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName);\n this._isSliding = false;\n triggerEvent(EVENT_SLID);\n };\n this._queueCallback(completeCallBack, activeElement, this._isAnimated());\n if (isCycling) {\n this.cycle();\n }\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_SLIDE);\n }\n _getActive() {\n return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element);\n }\n _getItems() {\n return SelectorEngine.find(SELECTOR_ITEM, this._element);\n }\n _clearInterval() {\n if (this._interval) {\n clearInterval(this._interval);\n this._interval = null;\n }\n }\n _directionToOrder(direction) {\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT;\n }\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV;\n }\n _orderToDirection(order) {\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT;\n }\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT;\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Carousel.getOrCreateInstance(this, config);\n if (typeof config === 'number') {\n data.to(config);\n return;\n }\n if (typeof config === 'string') {\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return;\n }\n event.preventDefault();\n const carousel = Carousel.getOrCreateInstance(target);\n const slideIndex = this.getAttribute('data-bs-slide-to');\n if (slideIndex) {\n carousel.to(slideIndex);\n carousel._maybeEnableCycle();\n return;\n }\n if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n carousel.next();\n carousel._maybeEnableCycle();\n return;\n }\n carousel.prev();\n carousel._maybeEnableCycle();\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$3, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE);\n for (const carousel of carousels) {\n Carousel.getOrCreateInstance(carousel);\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$b = 'collapse';\nconst DATA_KEY$7 = 'bs.collapse';\nconst EVENT_KEY$7 = `.${DATA_KEY$7}`;\nconst DATA_API_KEY$4 = '.data-api';\nconst EVENT_SHOW$6 = `show${EVENT_KEY$7}`;\nconst EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`;\nconst EVENT_HIDE$6 = `hide${EVENT_KEY$7}`;\nconst EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`;\nconst EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`;\nconst CLASS_NAME_SHOW$7 = 'show';\nconst CLASS_NAME_COLLAPSE = 'collapse';\nconst CLASS_NAME_COLLAPSING = 'collapsing';\nconst CLASS_NAME_COLLAPSED = 'collapsed';\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`;\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal';\nconst WIDTH = 'width';\nconst HEIGHT = 'height';\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';\nconst SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle=\"collapse\"]';\nconst Default$a = {\n parent: null,\n toggle: true\n};\nconst DefaultType$a = {\n parent: '(null|element)',\n toggle: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isTransitioning = false;\n this._triggerArray = [];\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4);\n for (const elem of toggleList) {\n const selector = SelectorEngine.getSelectorFromElement(elem);\n const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);\n if (selector !== null && filterElement.length) {\n this._triggerArray.push(elem);\n }\n }\n this._initializeChildren();\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());\n }\n if (this._config.toggle) {\n this.toggle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$a;\n }\n static get DefaultType() {\n return DefaultType$a;\n }\n static get NAME() {\n return NAME$b;\n }\n\n // Public\n toggle() {\n if (this._isShown()) {\n this.hide();\n } else {\n this.show();\n }\n }\n show() {\n if (this._isTransitioning || this._isShown()) {\n return;\n }\n let activeChildren = [];\n\n // find active children\n if (this._config.parent) {\n activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {\n toggle: false\n }));\n }\n if (activeChildren.length && activeChildren[0]._isTransitioning) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n for (const activeInstance of activeChildren) {\n activeInstance.hide();\n }\n const dimension = this._getDimension();\n this._element.classList.remove(CLASS_NAME_COLLAPSE);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.style[dimension] = 0;\n this._addAriaAndCollapsedClass(this._triggerArray, true);\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n this._element.style[dimension] = '';\n EventHandler.trigger(this._element, EVENT_SHOWN$6);\n };\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);\n const scrollSize = `scroll${capitalizedDimension}`;\n this._queueCallback(complete, this._element, true);\n this._element.style[dimension] = `${this._element[scrollSize]}px`;\n }\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return;\n }\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6);\n if (startEvent.defaultPrevented) {\n return;\n }\n const dimension = this._getDimension();\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_COLLAPSING);\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7);\n for (const trigger of this._triggerArray) {\n const element = SelectorEngine.getElementFromSelector(trigger);\n if (element && !this._isShown(element)) {\n this._addAriaAndCollapsedClass([trigger], false);\n }\n }\n this._isTransitioning = true;\n const complete = () => {\n this._isTransitioning = false;\n this._element.classList.remove(CLASS_NAME_COLLAPSING);\n this._element.classList.add(CLASS_NAME_COLLAPSE);\n EventHandler.trigger(this._element, EVENT_HIDDEN$6);\n };\n this._element.style[dimension] = '';\n this._queueCallback(complete, this._element, true);\n }\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW$7);\n }\n\n // Private\n _configAfterMerge(config) {\n config.toggle = Boolean(config.toggle); // Coerce string values\n config.parent = getElement(config.parent);\n return config;\n }\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;\n }\n _initializeChildren() {\n if (!this._config.parent) {\n return;\n }\n const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4);\n for (const element of children) {\n const selected = SelectorEngine.getElementFromSelector(element);\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected));\n }\n }\n }\n _getFirstLevelChildren(selector) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);\n // remove children if greater depth\n return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));\n }\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return;\n }\n for (const element of triggerArray) {\n element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);\n element.setAttribute('aria-expanded', isOpen);\n }\n }\n\n // Static\n static jQueryInterface(config) {\n const _config = {};\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false;\n }\n return this.each(function () {\n const data = Collapse.getOrCreateInstance(this, _config);\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n }\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) {\n // preventDefault only for elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {\n event.preventDefault();\n }\n for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n Collapse.getOrCreateInstance(element, {\n toggle: false\n }).toggle();\n }\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$a = 'dropdown';\nconst DATA_KEY$6 = 'bs.dropdown';\nconst EVENT_KEY$6 = `.${DATA_KEY$6}`;\nconst DATA_API_KEY$3 = '.data-api';\nconst ESCAPE_KEY$2 = 'Escape';\nconst TAB_KEY$1 = 'Tab';\nconst ARROW_UP_KEY$1 = 'ArrowUp';\nconst ARROW_DOWN_KEY$1 = 'ArrowDown';\nconst RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE$5 = `hide${EVENT_KEY$6}`;\nconst EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`;\nconst EVENT_SHOW$5 = `show${EVENT_KEY$6}`;\nconst EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`;\nconst EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`;\nconst CLASS_NAME_SHOW$6 = 'show';\nconst CLASS_NAME_DROPUP = 'dropup';\nconst CLASS_NAME_DROPEND = 'dropend';\nconst CLASS_NAME_DROPSTART = 'dropstart';\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center';\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';\nconst SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)';\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`;\nconst SELECTOR_MENU = '.dropdown-menu';\nconst SELECTOR_NAVBAR = '.navbar';\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav';\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start';\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end';\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start';\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end';\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start';\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start';\nconst PLACEMENT_TOPCENTER = 'top';\nconst PLACEMENT_BOTTOMCENTER = 'bottom';\nconst Default$9 = {\n autoClose: true,\n boundary: 'clippingParents',\n display: 'dynamic',\n offset: [0, 2],\n popperConfig: null,\n reference: 'toggle'\n};\nconst DefaultType$9 = {\n autoClose: '(boolean|string)',\n boundary: '(string|element)',\n display: 'string',\n offset: '(array|string|function)',\n popperConfig: '(null|object|function)',\n reference: '(string|element|object)'\n};\n\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._popper = null;\n this._parent = this._element.parentNode; // dropdown wrapper\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);\n this._inNavbar = this._detectNavbar();\n }\n\n // Getters\n static get Default() {\n return Default$9;\n }\n static get DefaultType() {\n return DefaultType$9;\n }\n static get NAME() {\n return NAME$a;\n }\n\n // Public\n toggle() {\n return this._isShown() ? this.hide() : this.show();\n }\n show() {\n if (isDisabled(this._element) || this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget);\n if (showEvent.defaultPrevented) {\n return;\n }\n this._createPopper();\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n this._element.focus();\n this._element.setAttribute('aria-expanded', true);\n this._menu.classList.add(CLASS_NAME_SHOW$6);\n this._element.classList.add(CLASS_NAME_SHOW$6);\n EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget);\n }\n hide() {\n if (isDisabled(this._element) || !this._isShown()) {\n return;\n }\n const relatedTarget = {\n relatedTarget: this._element\n };\n this._completeHide(relatedTarget);\n }\n dispose() {\n if (this._popper) {\n this._popper.destroy();\n }\n super.dispose();\n }\n update() {\n this._inNavbar = this._detectNavbar();\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Private\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, relatedTarget);\n if (hideEvent.defaultPrevented) {\n return;\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n if (this._popper) {\n this._popper.destroy();\n }\n this._menu.classList.remove(CLASS_NAME_SHOW$6);\n this._element.classList.remove(CLASS_NAME_SHOW$6);\n this._element.setAttribute('aria-expanded', 'false');\n Manipulator.removeDataAttribute(this._menu, 'popper');\n EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget);\n }\n _getConfig(config) {\n config = super._getConfig(config);\n if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME$a.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`);\n }\n return config;\n }\n _createPopper() {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)');\n }\n let referenceElement = this._element;\n if (this._config.reference === 'parent') {\n referenceElement = this._parent;\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference);\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference;\n }\n const popperConfig = this._getPopperConfig();\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig);\n }\n _isShown() {\n return this._menu.classList.contains(CLASS_NAME_SHOW$6);\n }\n _getPlacement() {\n const parentDropdown = this._parent;\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n return PLACEMENT_TOPCENTER;\n }\n if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n return PLACEMENT_BOTTOMCENTER;\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;\n }\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;\n }\n _detectNavbar() {\n return this._element.closest(SELECTOR_NAVBAR) !== null;\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n };\n\n // Disable Popper if we have a static display or Dropdown is in Navbar\n if (this._inNavbar || this._config.display === 'static') {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }];\n }\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _selectMenuItem({\n key,\n target\n }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element));\n if (!items.length) {\n return;\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus();\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n static clearMenus(event) {\n if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) {\n return;\n }\n const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN);\n for (const toggle of openToggles) {\n const context = Dropdown.getInstance(toggle);\n if (!context || context._config.autoClose === false) {\n continue;\n }\n const composedPath = event.composedPath();\n const isMenuTarget = composedPath.includes(context._menu);\n if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {\n continue;\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue;\n }\n const relatedTarget = {\n relatedTarget: context._element\n };\n if (event.type === 'click') {\n relatedTarget.clickEvent = event;\n }\n context._completeHide(relatedTarget);\n }\n }\n static dataApiKeydownHandler(event) {\n // If not an UP | DOWN | ESCAPE key => not a dropdown command\n // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n const isInput = /input|textarea/i.test(event.target.tagName);\n const isEscapeEvent = event.key === ESCAPE_KEY$2;\n const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key);\n if (!isUpOrDownEvent && !isEscapeEvent) {\n return;\n }\n if (isInput && !isEscapeEvent) {\n return;\n }\n event.preventDefault();\n\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode);\n const instance = Dropdown.getOrCreateInstance(getToggleButton);\n if (isUpOrDownEvent) {\n event.stopPropagation();\n instance.show();\n instance._selectMenuItem(event);\n return;\n }\n if (instance._isShown()) {\n // else is escape and we check if it is shown\n event.stopPropagation();\n instance.hide();\n getToggleButton.focus();\n }\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);\nEventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) {\n event.preventDefault();\n Dropdown.getOrCreateInstance(this).toggle();\n});\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$9 = 'backdrop';\nconst CLASS_NAME_FADE$4 = 'fade';\nconst CLASS_NAME_SHOW$5 = 'show';\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`;\nconst Default$8 = {\n className: 'modal-backdrop',\n clickCallback: null,\n isAnimated: false,\n isVisible: true,\n // if false, we use the backdrop helper without adding any element to the dom\n rootElement: 'body' // give the choice to place backdrop under different elements\n};\nconst DefaultType$8 = {\n className: 'string',\n clickCallback: '(function|null)',\n isAnimated: 'boolean',\n isVisible: 'boolean',\n rootElement: '(element|string)'\n};\n\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isAppended = false;\n this._element = null;\n }\n\n // Getters\n static get Default() {\n return Default$8;\n }\n static get DefaultType() {\n return DefaultType$8;\n }\n static get NAME() {\n return NAME$9;\n }\n\n // Public\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._append();\n const element = this._getElement();\n if (this._config.isAnimated) {\n reflow(element);\n }\n element.classList.add(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n execute(callback);\n });\n }\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback);\n return;\n }\n this._getElement().classList.remove(CLASS_NAME_SHOW$5);\n this._emulateAnimation(() => {\n this.dispose();\n execute(callback);\n });\n }\n dispose() {\n if (!this._isAppended) {\n return;\n }\n EventHandler.off(this._element, EVENT_MOUSEDOWN);\n this._element.remove();\n this._isAppended = false;\n }\n\n // Private\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div');\n backdrop.className = this._config.className;\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE$4);\n }\n this._element = backdrop;\n }\n return this._element;\n }\n _configAfterMerge(config) {\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement);\n return config;\n }\n _append() {\n if (this._isAppended) {\n return;\n }\n const element = this._getElement();\n this._config.rootElement.append(element);\n EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback);\n });\n this._isAppended = true;\n }\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated);\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$8 = 'focustrap';\nconst DATA_KEY$5 = 'bs.focustrap';\nconst EVENT_KEY$5 = `.${DATA_KEY$5}`;\nconst EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`;\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`;\nconst TAB_KEY = 'Tab';\nconst TAB_NAV_FORWARD = 'forward';\nconst TAB_NAV_BACKWARD = 'backward';\nconst Default$7 = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n};\nconst DefaultType$7 = {\n autofocus: 'boolean',\n trapElement: 'element'\n};\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n this._isActive = false;\n this._lastTabNavDirection = null;\n }\n\n // Getters\n static get Default() {\n return Default$7;\n }\n static get DefaultType() {\n return DefaultType$7;\n }\n static get NAME() {\n return NAME$8;\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return;\n }\n if (this._config.autofocus) {\n this._config.trapElement.focus();\n }\n EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event));\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));\n this._isActive = true;\n }\n deactivate() {\n if (!this._isActive) {\n return;\n }\n this._isActive = false;\n EventHandler.off(document, EVENT_KEY$5);\n }\n\n // Private\n _handleFocusin(event) {\n const {\n trapElement\n } = this._config;\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return;\n }\n const elements = SelectorEngine.focusableChildren(trapElement);\n if (elements.length === 0) {\n trapElement.focus();\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus();\n } else {\n elements[0].focus();\n }\n }\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return;\n }\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';\nconst SELECTOR_STICKY_CONTENT = '.sticky-top';\nconst PROPERTY_PADDING = 'padding-right';\nconst PROPERTY_MARGIN = 'margin-right';\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body;\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth;\n return Math.abs(window.innerWidth - documentWidth);\n }\n hide() {\n const width = this.getWidth();\n this._disableOverFlow();\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width);\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width);\n }\n reset() {\n this._resetElementAttributes(this._element, 'overflow');\n this._resetElementAttributes(this._element, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING);\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN);\n }\n isOverflowing() {\n return this.getWidth() > 0;\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow');\n this._element.style.overflow = 'hidden';\n }\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth();\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return;\n }\n this._saveInitialAttribute(element, styleProperty);\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty);\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty);\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue);\n }\n }\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty);\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty);\n return;\n }\n Manipulator.removeDataAttribute(element, styleProperty);\n element.style.setProperty(styleProperty, value);\n };\n this._applyManipulationCallback(selector, manipulationCallBack);\n }\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector);\n return;\n }\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel);\n }\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$7 = 'modal';\nconst DATA_KEY$4 = 'bs.modal';\nconst EVENT_KEY$4 = `.${DATA_KEY$4}`;\nconst DATA_API_KEY$2 = '.data-api';\nconst ESCAPE_KEY$1 = 'Escape';\nconst EVENT_HIDE$4 = `hide${EVENT_KEY$4}`;\nconst EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`;\nconst EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`;\nconst EVENT_SHOW$4 = `show${EVENT_KEY$4}`;\nconst EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`;\nconst EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`;\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`;\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`;\nconst EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`;\nconst EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`;\nconst CLASS_NAME_OPEN = 'modal-open';\nconst CLASS_NAME_FADE$3 = 'fade';\nconst CLASS_NAME_SHOW$4 = 'show';\nconst CLASS_NAME_STATIC = 'modal-static';\nconst OPEN_SELECTOR$1 = '.modal.show';\nconst SELECTOR_DIALOG = '.modal-dialog';\nconst SELECTOR_MODAL_BODY = '.modal-body';\nconst SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle=\"modal\"]';\nconst Default$6 = {\n backdrop: true,\n focus: true,\n keyboard: true\n};\nconst DefaultType$6 = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._isShown = false;\n this._isTransitioning = false;\n this._scrollBar = new ScrollBarHelper();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$6;\n }\n static get DefaultType() {\n return DefaultType$6;\n }\n static get NAME() {\n return NAME$7;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._isTransitioning = true;\n this._scrollBar.hide();\n document.body.classList.add(CLASS_NAME_OPEN);\n this._adjustDialog();\n this._backdrop.show(() => this._showElement(relatedTarget));\n }\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._isShown = false;\n this._isTransitioning = true;\n this._focustrap.deactivate();\n this._element.classList.remove(CLASS_NAME_SHOW$4);\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());\n }\n dispose() {\n EventHandler.off(window, EVENT_KEY$4);\n EventHandler.off(this._dialog, EVENT_KEY$4);\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n handleUpdate() {\n this._adjustDialog();\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop),\n // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element);\n }\n this._element.style.display = 'block';\n this._element.removeAttribute('aria-hidden');\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.scrollTop = 0;\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);\n if (modalBody) {\n modalBody.scrollTop = 0;\n }\n reflow(this._element);\n this._element.classList.add(CLASS_NAME_SHOW$4);\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate();\n }\n this._isTransitioning = false;\n EventHandler.trigger(this._element, EVENT_SHOWN$4, {\n relatedTarget\n });\n };\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated());\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => {\n if (event.key !== ESCAPE_KEY$1) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n this._triggerBackdropTransition();\n });\n EventHandler.on(window, EVENT_RESIZE$1, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog();\n }\n });\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return;\n }\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition();\n return;\n }\n if (this._config.backdrop) {\n this.hide();\n }\n });\n });\n }\n _hideModal() {\n this._element.style.display = 'none';\n this._element.setAttribute('aria-hidden', true);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n this._isTransitioning = false;\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN);\n this._resetAdjustments();\n this._scrollBar.reset();\n EventHandler.trigger(this._element, EVENT_HIDDEN$4);\n });\n }\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE$3);\n }\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1);\n if (hideEvent.defaultPrevented) {\n return;\n }\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const initialOverflowY = this._element.style.overflowY;\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return;\n }\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden';\n }\n this._element.classList.add(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC);\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY;\n }, this._dialog);\n }, this._dialog);\n this._element.focus();\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;\n const scrollbarWidth = this._scrollBar.getWidth();\n const isBodyOverflowing = scrollbarWidth > 0;\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft';\n this._element.style[property] = `${scrollbarWidth}px`;\n }\n }\n _resetAdjustments() {\n this._element.style.paddingLeft = '';\n this._element.style.paddingRight = '';\n }\n\n // Static\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](relatedTarget);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n EventHandler.one(target, EVENT_SHOW$4, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$4, () => {\n if (isVisible(this)) {\n this.focus();\n }\n });\n });\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1);\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide();\n }\n const data = Modal.getOrCreateInstance(target);\n data.toggle(this);\n});\nenableDismissTrigger(Modal);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$6 = 'offcanvas';\nconst DATA_KEY$3 = 'bs.offcanvas';\nconst EVENT_KEY$3 = `.${DATA_KEY$3}`;\nconst DATA_API_KEY$1 = '.data-api';\nconst EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst ESCAPE_KEY = 'Escape';\nconst CLASS_NAME_SHOW$3 = 'show';\nconst CLASS_NAME_SHOWING$1 = 'showing';\nconst CLASS_NAME_HIDING = 'hiding';\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop';\nconst OPEN_SELECTOR = '.offcanvas.show';\nconst EVENT_SHOW$3 = `show${EVENT_KEY$3}`;\nconst EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`;\nconst EVENT_HIDE$3 = `hide${EVENT_KEY$3}`;\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`;\nconst EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`;\nconst EVENT_RESIZE = `resize${EVENT_KEY$3}`;\nconst EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`;\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`;\nconst SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle=\"offcanvas\"]';\nconst Default$5 = {\n backdrop: true,\n keyboard: true,\n scroll: false\n};\nconst DefaultType$5 = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n scroll: 'boolean'\n};\n\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n this._isShown = false;\n this._backdrop = this._initializeBackDrop();\n this._focustrap = this._initializeFocusTrap();\n this._addEventListeners();\n }\n\n // Getters\n static get Default() {\n return Default$5;\n }\n static get DefaultType() {\n return DefaultType$5;\n }\n static get NAME() {\n return NAME$6;\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget);\n }\n show(relatedTarget) {\n if (this._isShown) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, {\n relatedTarget\n });\n if (showEvent.defaultPrevented) {\n return;\n }\n this._isShown = true;\n this._backdrop.show();\n if (!this._config.scroll) {\n new ScrollBarHelper().hide();\n }\n this._element.setAttribute('aria-modal', true);\n this._element.setAttribute('role', 'dialog');\n this._element.classList.add(CLASS_NAME_SHOWING$1);\n const completeCallBack = () => {\n if (!this._config.scroll || this._config.backdrop) {\n this._focustrap.activate();\n }\n this._element.classList.add(CLASS_NAME_SHOW$3);\n this._element.classList.remove(CLASS_NAME_SHOWING$1);\n EventHandler.trigger(this._element, EVENT_SHOWN$3, {\n relatedTarget\n });\n };\n this._queueCallback(completeCallBack, this._element, true);\n }\n hide() {\n if (!this._isShown) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3);\n if (hideEvent.defaultPrevented) {\n return;\n }\n this._focustrap.deactivate();\n this._element.blur();\n this._isShown = false;\n this._element.classList.add(CLASS_NAME_HIDING);\n this._backdrop.hide();\n const completeCallback = () => {\n this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING);\n this._element.removeAttribute('aria-modal');\n this._element.removeAttribute('role');\n if (!this._config.scroll) {\n new ScrollBarHelper().reset();\n }\n EventHandler.trigger(this._element, EVENT_HIDDEN$3);\n };\n this._queueCallback(completeCallback, this._element, true);\n }\n dispose() {\n this._backdrop.dispose();\n this._focustrap.deactivate();\n super.dispose();\n }\n\n // Private\n _initializeBackDrop() {\n const clickCallback = () => {\n if (this._config.backdrop === 'static') {\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n return;\n }\n this.hide();\n };\n\n // 'static' option will be translated to true, and booleans will keep their value\n const isVisible = Boolean(this._config.backdrop);\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: isVisible ? clickCallback : null\n });\n }\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n });\n }\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return;\n }\n if (this._config.keyboard) {\n this.hide();\n return;\n }\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);\n });\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config](this);\n });\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) {\n const target = SelectorEngine.getElementFromSelector(this);\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault();\n }\n if (isDisabled(this)) {\n return;\n }\n EventHandler.one(target, EVENT_HIDDEN$3, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus();\n }\n });\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);\n if (alreadyOpen && alreadyOpen !== target) {\n Offcanvas.getInstance(alreadyOpen).hide();\n }\n const data = Offcanvas.getOrCreateInstance(target);\n data.toggle(this);\n});\nEventHandler.on(window, EVENT_LOAD_DATA_API$2, () => {\n for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n Offcanvas.getOrCreateInstance(selector).show();\n }\n});\nEventHandler.on(window, EVENT_RESIZE, () => {\n for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n if (getComputedStyle(element).position !== 'fixed') {\n Offcanvas.getOrCreateInstance(element).hide();\n }\n }\n});\nenableDismissTrigger(Offcanvas);\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n// js-docs-start allow-list\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i;\nconst DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n dd: [],\n div: [],\n dl: [],\n dt: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n};\n// js-docs-end allow-list\n\nconst uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);\n\n/**\n * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n * contexts.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n */\n// eslint-disable-next-line unicorn/better-regex\nconst SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i;\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase();\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue));\n }\n return true;\n }\n\n // Check if a regular expression validates the attribute.\n return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));\n};\nfunction sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n if (!unsafeHtml.length) {\n return unsafeHtml;\n }\n if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n return sanitizeFunction(unsafeHtml);\n }\n const domParser = new window.DOMParser();\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'));\n for (const element of elements) {\n const elementName = element.nodeName.toLowerCase();\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove();\n continue;\n }\n const attributeList = [].concat(...element.attributes);\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);\n for (const attribute of attributeList) {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName);\n }\n }\n }\n return createdDocument.body.innerHTML;\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$5 = 'TemplateFactory';\nconst Default$4 = {\n allowList: DefaultAllowlist,\n content: {},\n // { selector : text , selector2 : text2 , }\n extraClass: '',\n html: false,\n sanitize: true,\n sanitizeFn: null,\n template: '
'\n};\nconst DefaultType$4 = {\n allowList: 'object',\n content: 'object',\n extraClass: '(string|function)',\n html: 'boolean',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n template: 'string'\n};\nconst DefaultContentType = {\n entry: '(string|element|function|null)',\n selector: '(string|element)'\n};\n\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n constructor(config) {\n super();\n this._config = this._getConfig(config);\n }\n\n // Getters\n static get Default() {\n return Default$4;\n }\n static get DefaultType() {\n return DefaultType$4;\n }\n static get NAME() {\n return NAME$5;\n }\n\n // Public\n getContent() {\n return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean);\n }\n hasContent() {\n return this.getContent().length > 0;\n }\n changeContent(content) {\n this._checkContent(content);\n this._config.content = {\n ...this._config.content,\n ...content\n };\n return this;\n }\n toHtml() {\n const templateWrapper = document.createElement('div');\n templateWrapper.innerHTML = this._maybeSanitize(this._config.template);\n for (const [selector, text] of Object.entries(this._config.content)) {\n this._setContent(templateWrapper, text, selector);\n }\n const template = templateWrapper.children[0];\n const extraClass = this._resolvePossibleFunction(this._config.extraClass);\n if (extraClass) {\n template.classList.add(...extraClass.split(' '));\n }\n return template;\n }\n\n // Private\n _typeCheckConfig(config) {\n super._typeCheckConfig(config);\n this._checkContent(config.content);\n }\n _checkContent(arg) {\n for (const [selector, content] of Object.entries(arg)) {\n super._typeCheckConfig({\n selector,\n entry: content\n }, DefaultContentType);\n }\n }\n _setContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template);\n if (!templateElement) {\n return;\n }\n content = this._resolvePossibleFunction(content);\n if (!content) {\n templateElement.remove();\n return;\n }\n if (isElement(content)) {\n this._putElementInTemplate(getElement(content), templateElement);\n return;\n }\n if (this._config.html) {\n templateElement.innerHTML = this._maybeSanitize(content);\n return;\n }\n templateElement.textContent = content;\n }\n _maybeSanitize(arg) {\n return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this]);\n }\n _putElementInTemplate(element, templateElement) {\n if (this._config.html) {\n templateElement.innerHTML = '';\n templateElement.append(element);\n return;\n }\n templateElement.textContent = element.textContent;\n }\n}\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$4 = 'tooltip';\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);\nconst CLASS_NAME_FADE$2 = 'fade';\nconst CLASS_NAME_MODAL = 'modal';\nconst CLASS_NAME_SHOW$2 = 'show';\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner';\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;\nconst EVENT_MODAL_HIDE = 'hide.bs.modal';\nconst TRIGGER_HOVER = 'hover';\nconst TRIGGER_FOCUS = 'focus';\nconst TRIGGER_CLICK = 'click';\nconst TRIGGER_MANUAL = 'manual';\nconst EVENT_HIDE$2 = 'hide';\nconst EVENT_HIDDEN$2 = 'hidden';\nconst EVENT_SHOW$2 = 'show';\nconst EVENT_SHOWN$2 = 'shown';\nconst EVENT_INSERTED = 'inserted';\nconst EVENT_CLICK$1 = 'click';\nconst EVENT_FOCUSIN$1 = 'focusin';\nconst EVENT_FOCUSOUT$1 = 'focusout';\nconst EVENT_MOUSEENTER = 'mouseenter';\nconst EVENT_MOUSELEAVE = 'mouseleave';\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n};\nconst Default$3 = {\n allowList: DefaultAllowlist,\n animation: true,\n boundary: 'clippingParents',\n container: false,\n customClass: '',\n delay: 0,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n html: false,\n offset: [0, 6],\n placement: 'top',\n popperConfig: null,\n sanitize: true,\n sanitizeFn: null,\n selector: false,\n template: '
' + '
' + '
' + '
',\n title: '',\n trigger: 'hover focus'\n};\nconst DefaultType$3 = {\n allowList: 'object',\n animation: 'boolean',\n boundary: '(string|element)',\n container: '(string|element|boolean)',\n customClass: '(string|function)',\n delay: '(number|object)',\n fallbackPlacements: 'array',\n html: 'boolean',\n offset: '(array|string|function)',\n placement: '(string|function)',\n popperConfig: '(null|object|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n selector: '(string|boolean)',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string'\n};\n\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)');\n }\n super(element, config);\n\n // Private\n this._isEnabled = true;\n this._timeout = 0;\n this._isHovered = null;\n this._activeTrigger = {};\n this._popper = null;\n this._templateFactory = null;\n this._newContent = null;\n\n // Protected\n this.tip = null;\n this._setListeners();\n if (!this._config.selector) {\n this._fixTitle();\n }\n }\n\n // Getters\n static get Default() {\n return Default$3;\n }\n static get DefaultType() {\n return DefaultType$3;\n }\n static get NAME() {\n return NAME$4;\n }\n\n // Public\n enable() {\n this._isEnabled = true;\n }\n disable() {\n this._isEnabled = false;\n }\n toggleEnabled() {\n this._isEnabled = !this._isEnabled;\n }\n toggle() {\n if (!this._isEnabled) {\n return;\n }\n this._activeTrigger.click = !this._activeTrigger.click;\n if (this._isShown()) {\n this._leave();\n return;\n }\n this._enter();\n }\n dispose() {\n clearTimeout(this._timeout);\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n if (this._element.getAttribute('data-bs-original-title')) {\n this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));\n }\n this._disposePopper();\n super.dispose();\n }\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements');\n }\n if (!(this._isWithContent() && this._isEnabled)) {\n return;\n }\n const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2));\n const shadowRoot = findShadowRoot(this._element);\n const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);\n if (showEvent.defaultPrevented || !isInTheDom) {\n return;\n }\n\n // TODO: v6 remove this or make it optional\n this._disposePopper();\n const tip = this._getTipElement();\n this._element.setAttribute('aria-describedby', tip.getAttribute('id'));\n const {\n container\n } = this._config;\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip);\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));\n }\n this._popper = this._createPopper(tip);\n tip.classList.add(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop);\n }\n }\n const complete = () => {\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2));\n if (this._isHovered === false) {\n this._leave();\n }\n this._isHovered = false;\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n hide() {\n if (!this._isShown()) {\n return;\n }\n const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2));\n if (hideEvent.defaultPrevented) {\n return;\n }\n const tip = this._getTipElement();\n tip.classList.remove(CLASS_NAME_SHOW$2);\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop);\n }\n }\n this._activeTrigger[TRIGGER_CLICK] = false;\n this._activeTrigger[TRIGGER_FOCUS] = false;\n this._activeTrigger[TRIGGER_HOVER] = false;\n this._isHovered = null; // it is a trick to support manual triggering\n\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return;\n }\n if (!this._isHovered) {\n this._disposePopper();\n }\n this._element.removeAttribute('aria-describedby');\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2));\n };\n this._queueCallback(complete, this.tip, this._isAnimated());\n }\n update() {\n if (this._popper) {\n this._popper.update();\n }\n }\n\n // Protected\n _isWithContent() {\n return Boolean(this._getTitle());\n }\n _getTipElement() {\n if (!this.tip) {\n this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());\n }\n return this.tip;\n }\n _createTipElement(content) {\n const tip = this._getTemplateFactory(content).toHtml();\n\n // TODO: remove this check in v6\n if (!tip) {\n return null;\n }\n tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2);\n // TODO: v6 the following can be achieved with CSS only\n tip.classList.add(`bs-${this.constructor.NAME}-auto`);\n const tipId = getUID(this.constructor.NAME).toString();\n tip.setAttribute('id', tipId);\n if (this._isAnimated()) {\n tip.classList.add(CLASS_NAME_FADE$2);\n }\n return tip;\n }\n setContent(content) {\n this._newContent = content;\n if (this._isShown()) {\n this._disposePopper();\n this.show();\n }\n }\n _getTemplateFactory(content) {\n if (this._templateFactory) {\n this._templateFactory.changeContent(content);\n } else {\n this._templateFactory = new TemplateFactory({\n ...this._config,\n // the `content` var has to be after `this._config`\n // to override config.content in case of popover\n content,\n extraClass: this._resolvePossibleFunction(this._config.customClass)\n });\n }\n return this._templateFactory;\n }\n _getContentForTemplate() {\n return {\n [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n };\n }\n _getTitle() {\n return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');\n }\n\n // Private\n _initializeOnDelegatedTarget(event) {\n return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());\n }\n _isAnimated() {\n return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2);\n }\n _isShown() {\n return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2);\n }\n _createPopper(tip) {\n const placement = execute(this._config.placement, [this, tip, this._element]);\n const attachment = AttachmentMap[placement.toUpperCase()];\n return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment));\n }\n _getOffset() {\n const {\n offset\n } = this._config;\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10));\n }\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element);\n }\n return offset;\n }\n _resolvePossibleFunction(arg) {\n return execute(arg, [this._element]);\n }\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [{\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n }, {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }, {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n }, {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n }, {\n name: 'preSetPlacement',\n enabled: true,\n phase: 'beforeMain',\n fn: data => {\n // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n this._getTipElement().setAttribute('data-popper-placement', data.state.placement);\n }\n }]\n };\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n };\n }\n _setListeners() {\n const triggers = this._config.trigger.split(' ');\n for (const trigger of triggers) {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context.toggle();\n });\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1);\n const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1);\n EventHandler.on(this._element, eventIn, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;\n context._enter();\n });\n EventHandler.on(this._element, eventOut, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event);\n context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);\n context._leave();\n });\n }\n }\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide();\n }\n };\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);\n }\n _fixTitle() {\n const title = this._element.getAttribute('title');\n if (!title) {\n return;\n }\n if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n this._element.setAttribute('aria-label', title);\n }\n this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility\n this._element.removeAttribute('title');\n }\n _enter() {\n if (this._isShown() || this._isHovered) {\n this._isHovered = true;\n return;\n }\n this._isHovered = true;\n this._setTimeout(() => {\n if (this._isHovered) {\n this.show();\n }\n }, this._config.delay.show);\n }\n _leave() {\n if (this._isWithActiveTrigger()) {\n return;\n }\n this._isHovered = false;\n this._setTimeout(() => {\n if (!this._isHovered) {\n this.hide();\n }\n }, this._config.delay.hide);\n }\n _setTimeout(handler, timeout) {\n clearTimeout(this._timeout);\n this._timeout = setTimeout(handler, timeout);\n }\n _isWithActiveTrigger() {\n return Object.values(this._activeTrigger).includes(true);\n }\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element);\n for (const dataAttribute of Object.keys(dataAttributes)) {\n if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n delete dataAttributes[dataAttribute];\n }\n }\n config = {\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n };\n config = this._mergeConfigObj(config);\n config = this._configAfterMerge(config);\n this._typeCheckConfig(config);\n return config;\n }\n _configAfterMerge(config) {\n config.container = config.container === false ? document.body : getElement(config.container);\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n };\n }\n if (typeof config.title === 'number') {\n config.title = config.title.toString();\n }\n if (typeof config.content === 'number') {\n config.content = config.content.toString();\n }\n return config;\n }\n _getDelegateConfig() {\n const config = {};\n for (const [key, value] of Object.entries(this._config)) {\n if (this.constructor.Default[key] !== value) {\n config[key] = value;\n }\n }\n config.selector = false;\n config.trigger = 'manual';\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config;\n }\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy();\n this._popper = null;\n }\n if (this.tip) {\n this.tip.remove();\n this.tip = null;\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tooltip);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$3 = 'popover';\nconst SELECTOR_TITLE = '.popover-header';\nconst SELECTOR_CONTENT = '.popover-body';\nconst Default$2 = {\n ...Tooltip.Default,\n content: '',\n offset: [0, 8],\n placement: 'right',\n template: '
' + '
' + '

' + '
' + '
',\n trigger: 'click'\n};\nconst DefaultType$2 = {\n ...Tooltip.DefaultType,\n content: '(null|string|element|function)'\n};\n\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n // Getters\n static get Default() {\n return Default$2;\n }\n static get DefaultType() {\n return DefaultType$2;\n }\n static get NAME() {\n return NAME$3;\n }\n\n // Overrides\n _isWithContent() {\n return this._getTitle() || this._getContent();\n }\n\n // Private\n _getContentForTemplate() {\n return {\n [SELECTOR_TITLE]: this._getTitle(),\n [SELECTOR_CONTENT]: this._getContent()\n };\n }\n _getContent() {\n return this._resolvePossibleFunction(this._config.content);\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config);\n if (typeof config !== 'string') {\n return;\n }\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`);\n }\n data[config]();\n });\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Popover);\n\n/**\n * --------------------------------------------------------------------------\n * Bootstrap scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n\n/**\n * Constants\n */\n\nconst NAME$2 = 'scrollspy';\nconst DATA_KEY$2 = 'bs.scrollspy';\nconst EVENT_KEY$2 = `.${DATA_KEY$2}`;\nconst DATA_API_KEY = '.data-api';\nconst EVENT_ACTIVATE = `activate${EVENT_KEY$2}`;\nconst EVENT_CLICK = `click${EVENT_KEY$2}`;\nconst EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`;\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';\nconst CLASS_NAME_ACTIVE$1 = 'active';\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]';\nconst SELECTOR_TARGET_LINKS = '[href]';\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';\nconst SELECTOR_NAV_LINKS = '.nav-link';\nconst SELECTOR_NAV_ITEMS = '.nav-item';\nconst SELECTOR_LIST_ITEMS = '.list-group-item';\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;\nconst SELECTOR_DROPDOWN = '.dropdown';\nconst SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle';\nconst Default$1 = {\n offset: null,\n // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: '0px 0px -25%',\n smoothScroll: false,\n target: null,\n threshold: [0.1, 0.5, 1]\n};\nconst DefaultType$1 = {\n offset: '(number|null)',\n // TODO v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: 'string',\n smoothScroll: 'boolean',\n target: 'element',\n threshold: 'array'\n};\n\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element, config);\n\n // this._element is the observablesContainer and config.target the menu links wrapper\n this._targetLinks = new Map();\n this._observableSections = new Map();\n this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;\n this._activeTarget = null;\n this._observer = null;\n this._previousScrollData = {\n visibleEntryTop: 0,\n parentScrollTop: 0\n };\n this.refresh(); // initialize\n }\n\n // Getters\n static get Default() {\n return Default$1;\n }\n static get DefaultType() {\n return DefaultType$1;\n }\n static get NAME() {\n return NAME$2;\n }\n\n // Public\n refresh() {\n this._initializeTargetsAndObservables();\n this._maybeEnableSmoothScroll();\n if (this._observer) {\n this._observer.disconnect();\n } else {\n this._observer = this._getNewObserver();\n }\n for (const section of this._observableSections.values()) {\n this._observer.observe(section);\n }\n }\n dispose() {\n this._observer.disconnect();\n super.dispose();\n }\n\n // Private\n _configAfterMerge(config) {\n // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n config.target = getElement(config.target) || document.body;\n\n // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin;\n if (typeof config.threshold === 'string') {\n config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value));\n }\n return config;\n }\n _maybeEnableSmoothScroll() {\n if (!this._config.smoothScroll) {\n return;\n }\n\n // unregister any previous listeners\n EventHandler.off(this._config.target, EVENT_CLICK);\n EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n const observableSection = this._observableSections.get(event.target.hash);\n if (observableSection) {\n event.preventDefault();\n const root = this._rootElement || window;\n const height = observableSection.offsetTop - this._element.offsetTop;\n if (root.scrollTo) {\n root.scrollTo({\n top: height,\n behavior: 'smooth'\n });\n return;\n }\n\n // Chrome 60 doesn't support `scrollTo`\n root.scrollTop = height;\n }\n });\n }\n _getNewObserver() {\n const options = {\n root: this._rootElement,\n threshold: this._config.threshold,\n rootMargin: this._config.rootMargin\n };\n return new IntersectionObserver(entries => this._observerCallback(entries), options);\n }\n\n // The logic of selection\n _observerCallback(entries) {\n const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);\n const activate = entry => {\n this._previousScrollData.visibleEntryTop = entry.target.offsetTop;\n this._process(targetElement(entry));\n };\n const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;\n const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;\n this._previousScrollData.parentScrollTop = parentScrollTop;\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n this._activeTarget = null;\n this._clearActiveClass(targetElement(entry));\n continue;\n }\n const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop;\n // if we are scrolling down, pick the bigger offsetTop\n if (userScrollsDown && entryIsLowerThanPrevious) {\n activate(entry);\n // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n if (!parentScrollTop) {\n return;\n }\n continue;\n }\n\n // if we are scrolling up, pick the smallest offsetTop\n if (!userScrollsDown && !entryIsLowerThanPrevious) {\n activate(entry);\n }\n }\n }\n _initializeTargetsAndObservables() {\n this._targetLinks = new Map();\n this._observableSections = new Map();\n const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target);\n for (const anchor of targetLinks) {\n // ensure that the anchor has an id and is not disabled\n if (!anchor.hash || isDisabled(anchor)) {\n continue;\n }\n const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element);\n\n // ensure that the observableSection exists & is visible\n if (isVisible(observableSection)) {\n this._targetLinks.set(decodeURI(anchor.hash), anchor);\n this._observableSections.set(anchor.hash, observableSection);\n }\n }\n }\n _process(target) {\n if (this._activeTarget === target) {\n return;\n }\n this._clearActiveClass(this._config.target);\n this._activeTarget = target;\n target.classList.add(CLASS_NAME_ACTIVE$1);\n this._activateParents(target);\n EventHandler.trigger(this._element, EVENT_ACTIVATE, {\n relatedTarget: target\n });\n }\n _activateParents(target) {\n // Activate dropdown parents\n if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1);\n return;\n }\n for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n // Set triggered links parents as active\n // With both
    and