diff --git a/.build-constraints.txt b/.build-constraints.txt index 68b1167..ca77717 100644 --- a/.build-constraints.txt +++ b/.build-constraints.txt @@ -13,7 +13,7 @@ pluggy==1.6.0 \ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 # via hatchling -trove-classifiers==2026.4.28.13 \ - --hash=sha256:8f4b1eb4e16296b57d612965444f87a83861cc989a0451ac97fe4265ddef03b8 \ - --hash=sha256:c85bb8a53c3de7330d1699b844ed9fb809a602a09ac15dc79ad6d1a509be0676 +trove-classifiers==2026.5.7.17 \ + --hash=sha256:5ec0800de5e2ddbd7c663cb4c0c15328f132dc168813897c18866c5c7b93db33 \ + --hash=sha256:a04a48f8f0a787cb996514d3969ac7608aa3c60cb15d073c1e02801e60533e80 # via hatchling diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml deleted file mode 100644 index 824c3ec..0000000 --- a/.github/workflows/publish.yaml +++ /dev/null @@ -1,438 +0,0 @@ -name: publish - -on: - push: - branches: [ main, preview ] - workflow_dispatch: - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - publish: - runs-on: html_publisher - environment: - name: ${{ github.ref_name == 'main' && 'github-pages' || 'preview' }} - url: ${{ steps.deployment.outputs.page_url }} - steps: - - - name: Checkout project - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 - with: - python-version: "3.12" - - - name: Install dependencies - run: | - pip install --upgrade pip - pip install nbconvert jupyter-book sphinx markdown beautifulsoup4 - - - name: Convert notebooks and create single-page app - run: | - mkdir -p site - - # Convert .ipynb notebooks to HTML fragments (body content only) - for notebook in */*.ipynb; do - if [ -f "$notebook" ]; then - jupyter nbconvert "$notebook" --to html \ - --template classic --HTMLExporter.theme=light \ - --no-prompt --stdout > "temp_$(basename "$notebook" .ipynb).html" - fi - done - - # Convert .py Databricks notebooks to HTML fragments - chmod +x .github/scripts/convert_notebooks.py - python3 .github/scripts/convert_notebooks.py - - # Create single-page application with embedded content - python3 << 'EOF' - import os - import json - import markdown - import re - import glob - from bs4 import BeautifulSoup - - # Read README.md - readme_content = "" - if os.path.exists('README.md'): - with open('README.md', 'r') as f: - readme_content = markdown.markdown(f.read()) - - # Get repository name and format title - repo_name = os.environ.get('GITHUB_REPOSITORY', '').split('/')[-1] - title = ' '.join(word.capitalize() for word in repo_name.split('-')) + ' Accelerator' - - # Collect all notebook content - notebooks = {} - - # Read .py notebook fragments - if os.path.exists('notebook_fragments.json'): - with open('notebook_fragments.json', 'r') as f: - py_notebooks = json.load(f) - notebooks.update(py_notebooks) - - # Read .ipynb notebook content (extract body from temp files) - for temp_file in glob.glob('temp_*.html'): - if 'fragment' not in temp_file: # These are the nbconvert outputs - name = temp_file.replace('temp_', '').replace('.html', '') - with open(temp_file, 'r') as f: - content = f.read() - # Extract body content - soup = BeautifulSoup(content, 'html.parser') - body = soup.find('body') - if body: - # Find the notebook container - container = body.find('div', class_='container') - if container: - notebooks[name] = str(container) - else: - notebooks[name] = str(body) - - # Create single-page application - html = f''' - - - {title} - - - - - - - -
- -
{title}
- View on GitHub -
- -
- - -
- -
- {readme_content} -
''' - - # Add notebook sections - for name, content in notebooks.items(): - html += f''' - -
- {content} -
''' - - html += f''' -
-
- - - - - - ''' - - # Write the single-page application - with open('site/index.html', 'w') as f: - f.write(html) - - print(f"Created single-page application with {len(notebooks)} notebooks") - - # Clean up temporary files - for temp_file in glob.glob('temp_*.html'): - os.remove(temp_file) - if os.path.exists('notebook_fragments.json'): - os.remove('notebook_fragments.json') - EOF - - - name: Upload artifact - uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b - with: - path: 'site' - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..3494226 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,68 @@ +name: publish + +on: + push: + branches: [ main, preview ] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + name: Build Docusaurus + runs-on: html_publisher + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Setup uv + uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1 + with: + version: "0.11.2" + enable-cache: true + cache-dependency-glob: "**/uv.lock" + + - name: Setup Node.js + uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + with: + node-version: 22 + cache: yarn + cache-dependency-path: docs/python-data-sources/yarn.lock + + - name: Install docs dependencies + run: make docs-install + + - name: Build website + run: make docs-build + + - name: Upload Build Artifact + uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0 + with: + path: docs/python-data-sources/build + + deploy: + name: Deploy to GitHub Pages + needs: build + + permissions: + pages: write + id-token: write + + environment: + name: ${{ github.ref_name == 'main' && 'github-pages' || 'preview' }} + url: ${{ steps.deployment.outputs.page_url }} + + runs-on: html_publisher + + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 diff --git a/Makefile b/Makefile index cac5de3..58f4a7d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all clean dev fmt lint test coverage lock-dependencies verify-lock build +.PHONY: all clean dev fmt lint test coverage lock-dependencies verify-lock build docs-install docs-build docs-serve docs-serve-dev docs-clean all: clean lint fmt test @@ -6,7 +6,7 @@ export UV_FROZEN := 1 UV_RUN := uv run --exact --all-extras -clean: +clean: docs-clean rm -fr .venv clean htmlcov .pytest_cache .ruff_cache .coverage coverage.xml dist find . -name '__pycache__' -print0 | xargs -0 rm -fr @@ -29,7 +29,7 @@ verify-lock: if [ -n "$$bad" ]; then \ echo "uv.lock contains non-public registry URLs:"; \ echo "$$bad"; \ - echo "Run 'make lock' to regenerate and sanitize."; \ + echo "Run 'make lock-dependencies' to regenerate and sanitize."; \ exit 1; \ fi @@ -55,3 +55,21 @@ coverage: e2e: $(UV_RUN) pytest -rs --timeout 30 tests/e2e + +docs-install: + yarn --cwd docs/python-data-sources install --frozen-lockfile + +docs-build: + uv run --group docs pydoc-markdown + yarn --cwd docs/python-data-sources build + +docs-serve-dev: + uv run --group docs pydoc-markdown + yarn --cwd docs/python-data-sources start + +docs-serve: docs-build + yarn --cwd docs/python-data-sources serve + +docs-clean: + rm -rf docs/python-data-sources/build docs/python-data-sources/.docusaurus docs/python-data-sources/.cache + find docs/python-data-sources/docs/reference/api -mindepth 1 -not -name 'index.mdx' -exec rm -rf {} + diff --git a/docs/python-data-sources/.gitignore b/docs/python-data-sources/.gitignore new file mode 100644 index 0000000..2f584ae --- /dev/null +++ b/docs/python-data-sources/.gitignore @@ -0,0 +1,23 @@ +# Dependencies +/node_modules + +# Production +/build + +# Generated files +.docusaurus +.cache-loader + +# Generated API reference (pydoc-markdown output) +/docs/reference/api/python_data_sources/ + +# Misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/docs/python-data-sources/docs/demos.mdx b/docs/python-data-sources/docs/demos.mdx new file mode 100644 index 0000000..c87d316 --- /dev/null +++ b/docs/python-data-sources/docs/demos.mdx @@ -0,0 +1,14 @@ +--- +sidebar_position: 3 +--- + +# Demos + +This project contains example notebooks in [`examples/`](https://github.com/databricks-industry-solutions/python-data-sources/tree/main/examples) +for you to work with custom data sources. + +* [`examples/zipdcm/zip-dicom-demo.ipynb`](https://github.com/databricks-industry-solutions/python-data-sources/blob/main/examples/zipdcm/zip-dicom-demo.ipynb) shows how to register a `zipdcm` data source, read a zipped archive of DICOM files, and access the pixel arrays. + +## Contributing a new demo + +New example notebooks are welcome. Place the notebook under `examples//` and reference it from this page. diff --git a/docs/python-data-sources/docs/installation.mdx b/docs/python-data-sources/docs/installation.mdx new file mode 100644 index 0000000..75c2c36 --- /dev/null +++ b/docs/python-data-sources/docs/installation.mdx @@ -0,0 +1,55 @@ +--- +sidebar_position: 2 +--- + +import Admonition from '@theme/Admonition'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Installation + +Data sources can be installed from PyPI using `pip` commands. Each data source is packaged as an optional extra module. You can +limit installation of data sources and their dependencies to only the sources you need. + + +Installing Python Data Sources from this library requires Spark 4.0+. Each data source includes its required Python dependencies. + + +## Installing data sources + +List required data sources when installing using `pip` commands. + +```bash +pip install "python-data-sources[mcap]" # MCAP robotics logs +pip install "python-data-sources[mqtt]" # MQTT streaming +pip install "python-data-sources[zipdcm]" # DICOM in ZIP +pip install "python-data-sources[mcap,mqtt]" # Both MCAP and MQTT +pip install "python-data-sources[all]" # Installs all data sources +``` + + +Each data source requires its own dependencies. Because new data sources may be added to the project over time, it is best to +limit installation to sources required for your workloads. + + +## Registering data sources + +Data sources must be registered to be used in an active `SparkSession`. Once data sources are registered, you can reference them +using their data source name (e.g. "mcap" for the MCAP data source). See the [API reference](/docs/reference) for the full list of +registered data sources and their options. + +```python +from pyspark.sql import SparkSession +from python_data_sources.mcap import McapDataSource + +# Register the data source +spark = SparkSession.builder.getOrCreate() +spark.dataSource.register(McapDataSource) + +# Get the data source name +mcap_source_name = McapDataSource.name() # 'mcap' + +# Read MCAP data using the registered data source +df = spark.read.format("mcap").load("/path/to/logs/") +df.show() +``` diff --git a/docs/python-data-sources/docs/motivation.mdx b/docs/python-data-sources/docs/motivation.mdx new file mode 100644 index 0000000..f2d4514 --- /dev/null +++ b/docs/python-data-sources/docs/motivation.mdx @@ -0,0 +1,25 @@ +--- +sidebar_position: 1 +--- + +# Motivation + +Apache Spark ships native data sources commonly used in analytics workloads. The long tail of data sources is not covered by +standard Spark capabilities and is served by a patchwork of Java libraries or user-defined functions that are difficult to maintain. + +The [PySpark DataSource API](https://spark.apache.org/docs/latest/api/python/reference/pyspark.sql/datasource.html) allows users to implement and register Spark data source formats using `spark.read.format(...)`. +Sources that implement `DataSourceReader` and `DataSourceWriter` contracts can be used to read or write data with Spark. + +**python-data-sources** is a collection of Python-native connectors for formats and protocols that don't have a built-in Spark reader. + +## Connectors + +| Connector | Read | Write | Notes | +|-----------|------|--------|----------------------------------------------------------------| +| `mcap` | ✓ | — | MCAP container format used for robotics, AV, and ROS bag logs. | +| `mqtt` | ✓ | — | MQTT broker as a Structured Streaming source. | +| `zipdcm` | ✓ | — | DICOM medical images packaged inside ZIP archives. | + +## Contributing + +If you need a connector that isn't provided as a Python Data Source, [open a GitHub issue](https://github.com/databricks-industry-solutions/python-data-sources/issues). diff --git a/docs/python-data-sources/docs/reference/api/common/range_partition.md b/docs/python-data-sources/docs/reference/api/common/range_partition.md new file mode 100644 index 0000000..9b9f224 --- /dev/null +++ b/docs/python-data-sources/docs/reference/api/common/range_partition.md @@ -0,0 +1,13 @@ +--- +sidebar_label: range_partition +title: python_data_sources.common.range_partition +--- + +## RangePartition Objects + +```python +class RangePartition(InputPartition) +``` + +This DataSource InputPartition class provides tracking of ranges within a list + diff --git a/docs/python-data-sources/docs/reference/api/index.mdx b/docs/python-data-sources/docs/reference/api/index.mdx new file mode 100644 index 0000000..c2888a2 --- /dev/null +++ b/docs/python-data-sources/docs/reference/api/index.mdx @@ -0,0 +1,9 @@ +--- +sidebar_position: 1 +--- + +# API Reference + +The module-by-module API reference is generated by `pydoc-markdown` from the docstrings under `src/python_data_sources/`. Run `make docs-build` to regenerate the surrounding pages. + +If you're reading this in a freshly cloned tree, the per-module pages aren't here yet — they're written into this directory the first time `make docs-build` runs. diff --git a/docs/python-data-sources/docs/reference/api/mcap/mcap_datasource.md b/docs/python-data-sources/docs/reference/api/mcap/mcap_datasource.md new file mode 100644 index 0000000..e8c675c --- /dev/null +++ b/docs/python-data-sources/docs/reference/api/mcap/mcap_datasource.md @@ -0,0 +1,117 @@ +--- +sidebar_label: mcap_datasource +title: python_data_sources.mcap.mcap_datasource +--- + +### path\_handler + +```python +def path_handler(path: str, + glob_pattern: str, + recursive: bool = False) -> list +``` + +Discover files matching the glob pattern in the given path. + +**Arguments**: + +- `path` - Path to search for files +- `glob_pattern` - Glob pattern to match files (e.g., "*.mcap") +- `recursive` - If True, recursively search subdirectories using rglob + + +**Returns**: + + List of file paths matching the pattern + +### decode\_protobuf\_message + +```python +def decode_protobuf_message(message, schema, _reader) +``` + +Decode protobuf messages. + +### decode\_json\_message + +```python +def decode_json_message(message, _schema, _reader) +``` + +Decode JSON messages. + +### decode\_fallback + +```python +def decode_fallback(message, _schema, _reader) +``` + +Fallback decoder for unknown formats. + +## MCAPDataSourceReader Objects + +```python +class MCAPDataSourceReader(DataSourceReader) +``` + +Facilitate reading MCAP (ROS 2 bag) files. + +### partitions + +```python +def partitions() -> Sequence[RangePartition] +``` + +Compute 'splits' of the data to read. + +**Returns**: + + List of RangePartition objects + +### read + +```python +def read(partition: InputPartition) -> Iterator[tuple] +``` + +Executor level method, performs read by Range Partition. + +**Arguments**: + +- `partition` - The partition to read + + +**Returns**: + + Iterator of tuples (sequence, topic, schema, encoding, log_time, data_json) + +## MCAPDataSource Objects + +```python +class MCAPDataSource(DataSource) +``` + +A data source for batch query over MCAP (ROS 2 bag) files. + +Usage: + # Read all topics + df = spark.read.format("mcap").option("path", "/path/to/mcap/files").load() + + # Filter by specific topic at read time (more efficient than DataFrame filter) + df = spark.read.format("mcap") .option("path", "/path/to/mcap/files") .option("topicFilter", "pose") .load() + +Options: + - path: Path to MCAP file(s) or directory (required) + - pathGlobFilter: Glob pattern for file matching (default: "*.mcap") + - numPartitions: Number of partitions to split files across (default: 4) + - recursiveFileLookup: Recursively search subdirectories (default: false) + - topicFilter: Filter messages by topic name (optional). Use "*" or omit to read all topics. + +Schema: + - sequence: BIGINT - The message sequence number from MCAP + - topic: STRING - The message topic + - schema: STRING - The schema name + - encoding: STRING - The encoding type (protobuf, json, etc.) + - log_time: BIGINT - The message timestamp in nanoseconds + - data: STRING - JSON string containing all message fields + diff --git a/docs/python-data-sources/docs/reference/api/mqtt/mqtt_streaming.md b/docs/python-data-sources/docs/reference/api/mqtt/mqtt_streaming.md new file mode 100644 index 0000000..a6682d1 --- /dev/null +++ b/docs/python-data-sources/docs/reference/api/mqtt/mqtt_streaming.md @@ -0,0 +1,157 @@ +--- +sidebar_label: mqtt_streaming +title: python_data_sources.mqtt.mqtt_streaming +--- + +## MqttDataSource Objects + +```python +class MqttDataSource(DataSource) +``` + +A PySpark DataSource for reading MQTT messages from a broker. + +This data source allows you to stream MQTT messages into Spark DataFrames, +supporting various MQTT broker configurations including authentication, +SSL/TLS encryption, and different quality of service levels. + +Input validation is performed on critical parameters to ensure connection reliability. + +Supported options: +- broker_address: MQTT broker hostname or IP address (required, validated) +* Must be a valid hostname or IP address format +* Cannot be None, empty, or whitespace-only +- port: Broker port number (default: 8883, validated) +* Must be an integer in range 1-65535 +- username: Authentication username (optional) +- password: Authentication password (optional) +- topic: MQTT topic to subscribe to (default: "#" for all topics) +- qos: Quality of Service level 0-2 (default: 0, validated) +* Must be 0, 1, or 2 (standard MQTT QoS levels) +- require_tls: Enable SSL/TLS encryption (default: true) +- keepalive: Keep alive interval in seconds (default: 60) + +Example usage: +spark.readStream.format("mqtt_pub_sub") +.option("broker_address", "mqtt.example.com") +.option("topic", "sensors/+/temperature") +.option("username", "user") +.option("password", "pass") +.load() + +**Raises**: + +- `ValueError` - If broker_address, port, clean_session, or qos parameters are invalid. + +### name + +```python +@classmethod +def name(cls) +``` + +Returns the name of the data source. + +### \_\_init\_\_ + +```python +def __init__(options) +``` + +Initialize the MQTT data source with configuration options. + +**Arguments**: + +- `options` _dict_ - Configuration options for the MQTT connection. + See class docstring for supported options. + +### schema + +```python +def schema() +``` + +Define the schema of the data source. + +**Returns**: + +- `StructType` - The schema of the data source. + +### streamReader + +```python +def streamReader(schema: StructType) +``` + +Create and return a stream reader for MQTT data. + +**Arguments**: + +- `schema` _StructType_ - The schema for the streaming data. + + +**Returns**: + +- `MqttSimpleStreamReader` - A stream reader instance configured for MQTT. + +## MqttSimpleStreamReader Objects + +```python +class MqttSimpleStreamReader(SimpleDataSourceStreamReader) +``` + +### \_\_init\_\_ + +```python +def __init__(_schema, options) +``` + +Initialize the MQTT simple stream reader with configuration options. + +**Arguments**: + +- `_schema` _StructType_ - The schema for the streaming data. +- `options` _dict_ - Configuration options for the MQTT connection. + See class docstring for supported options. + +### latestOffset + +```python +def latestOffset() -> dict +``` + +Returns the current latest offset that the next microbatch will read to. + +### partitions + +```python +def partitions(start: dict, end: dict) +``` + +Plans the partitioning of the current microbatch defined by start and end offset. It +needs to return a sequence of :class:`InputPartition` objects. + +### read + +```python +def read(_) +``` + +Read MQTT messages from the broker. + +**Returns**: + +- `Iterator[list]` - An iterator of lists containing the MQTT message data. + The list contains the following elements: + - received_time: The time the message was received. + - topic: The topic of the message. + - message: The payload of the message. + - is_duplicate: Whether the message is a duplicate. + - qos: The quality of service level of the message. + - is_retained: Whether the message is retained. + + +**Raises**: + +- `Exception` - If the connection to the broker fails. + diff --git a/docs/python-data-sources/docs/reference/api/sidebar.json b/docs/python-data-sources/docs/reference/api/sidebar.json new file mode 100644 index 0000000..97ab080 --- /dev/null +++ b/docs/python-data-sources/docs/reference/api/sidebar.json @@ -0,0 +1,41 @@ +{ + "items": [ + { + "items": [ + { + "items": [ + "reference/api/python_data_sources/common/range_partition" + ], + "label": "python_data_sources.common", + "type": "category" + }, + { + "items": [ + "reference/api/python_data_sources/mcap/mcap_datasource" + ], + "label": "python_data_sources.mcap", + "type": "category" + }, + { + "items": [ + "reference/api/python_data_sources/mqtt/mqtt_streaming" + ], + "label": "python_data_sources.mqtt", + "type": "category" + }, + { + "items": [ + "reference/api/python_data_sources/zipdcm/zip_dcm_ds", + "reference/api/python_data_sources/zipdcm/zip_dcm_utils" + ], + "label": "python_data_sources.zipdcm", + "type": "category" + } + ], + "label": "python_data_sources", + "type": "category" + } + ], + "label": "Reference", + "type": "category" +} \ No newline at end of file diff --git a/docs/python-data-sources/docs/reference/api/zipdcm/zip_dcm_ds.md b/docs/python-data-sources/docs/reference/api/zipdcm/zip_dcm_ds.md new file mode 100644 index 0000000..6bdba5c --- /dev/null +++ b/docs/python-data-sources/docs/reference/api/zipdcm/zip_dcm_ds.md @@ -0,0 +1,40 @@ +--- +sidebar_label: zip_dcm_ds +title: python_data_sources.zipdcm.zip_dcm_ds +--- + +## ZipDCMDataSourceReader Objects + +```python +class ZipDCMDataSourceReader(DataSourceReader) +``` + +Facilitate reading Zipfiles full of DCM (DICOM) files. + +### partitions + +```python +def partitions() -> Sequence[RangePartition] +``` + +Compute 'splits' of the data to read + self.paths is the list of files discovered and now need to be partitioned. + +### read + +```python +def read( + partition: InputPartition +) -> Iterator[tuple] | Iterator["RecordBatch"] +``` + +Executor level method, performs read by Range Partition + +## ZipDCMDataSource Objects + +```python +class ZipDCMDataSource(DataSource) +``` + +A data source for batch query over zipped DICOM files the `ZipFile` and `PyDicom` libraries. + diff --git a/docs/python-data-sources/docs/reference/api/zipdcm/zip_dcm_utils.md b/docs/python-data-sources/docs/reference/api/zipdcm/zip_dcm_utils.md new file mode 100644 index 0000000..a80db0f --- /dev/null +++ b/docs/python-data-sources/docs/reference/api/zipdcm/zip_dcm_utils.md @@ -0,0 +1,45 @@ +--- +sidebar_label: zip_dcm_utils +title: python_data_sources.zipdcm.zip_dcm_utils +--- + +### readzipdcm + +```python +def readzipdcm(partition: RangePartition, paths: list, + dicom_keys_filter: list[str]) -> Iterator[list[Any]] +``` + +Generator function to extract DICOM metadata from .dcm files within ZIP archives. + +Iterates over a partitioned list of ZIP file paths, opens each ZIP file, and processes files +with a '.dcm' extension. For each DICOM file, reads the header metadata, removes specified +large keys from the metadata dictionary, computes a SHA-1 hash of the pixel data, and yields +the results as a list. + +**Arguments**: + +- `partition` _RangePartition_ - An object with 'start' and 'end' attributes specifying the range of paths to process. +- `paths` _list_ - List of ZIP file paths to process. +- `dicom_keys_filter` _list_ - List of metadata keys to remove from the extracted DICOM metadata. + + +**Yields**: + +- `list` - A list containing: + - rowid (int): Unique row identifier. + - Either concatenation of: + - zip_file_path (str): Path to the ZIP file. + - '/' + - name_in_zip (str): Name of the DICOM file within the ZIP archive. + Or: + - dcm_file_path (str): Path to the dcm file. + - meta (dict): Filtered DICOM metadata dictionary with an added 'pixel_hash' key. + + +**Notes**: + + - Assumes that the DICOM files can be read directly from the ZIP archive without extraction. + - The 'pixel_hash' is computed using SHA-1 on the pixel array of the DICOM file. + - Logging is performed at various steps for debugging purposes. + diff --git a/docs/python-data-sources/docs/reference/index.mdx b/docs/python-data-sources/docs/reference/index.mdx new file mode 100644 index 0000000..00d130e --- /dev/null +++ b/docs/python-data-sources/docs/reference/index.mdx @@ -0,0 +1,11 @@ +--- +sidebar_position: 4 +--- + +# Reference + +This section is the technical reference for `python-data-sources`. + +- [**API reference**](/docs/reference/api) — auto-generated module-by-module documentation for every public class and function in `src/python_data_sources`. Generated from docstrings by [`pydoc-markdown`](https://github.com/NiklasRosenstein/pydoc-markdown). + +If something is missing or wrong, the source of truth is the docstring in the code — fix it there and the next docs build will pick it up. See [`CONTRIBUTING.md`](https://github.com/databricks-industry-solutions/python-data-sources/blob/main/CONTRIBUTING.md) for how to rebuild the docs locally. diff --git a/docs/python-data-sources/docusaurus.config.ts b/docs/python-data-sources/docusaurus.config.ts new file mode 100644 index 0000000..2229bdf --- /dev/null +++ b/docs/python-data-sources/docusaurus.config.ts @@ -0,0 +1,203 @@ +import path from 'path'; +import { themes as prismThemes } from 'prism-react-renderer'; +import type { Config } from '@docusaurus/types'; +import type * as Preset from '@docusaurus/preset-classic'; + +// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) + +const config: Config = { + title: 'Python Data Sources', + tagline: 'A collection of custom PySpark data source connectors for various formats.', + favicon: 'img/logo.svg', + + url: 'https://databricks-industry-solutions.github.io', + baseUrl: '/python-data-sources/', + trailingSlash: true, + + organizationName: 'databricks-industry-solutions', + projectName: 'python-data-sources', + + onBrokenLinks: 'throw', + onDuplicateRoutes: 'throw', + onBrokenAnchors: 'throw', + + markdown: { + mermaid: true, + hooks: { + onBrokenMarkdownLinks: 'throw', + }, + }, + themes: ['@docusaurus/theme-mermaid'], + + i18n: { + defaultLocale: 'en', + locales: ['en'], + }, + + plugins: [ + async () => ({ + name: 'fix-vscode-languageserver-types-resolution', + configureWebpack() { + // theme-mermaid → mermaid → langium → vscode-languageserver-types. + // vscode-languageserver-types defaults to UMD (main.js) which triggers + // "Critical dependency: require function is used in a way in which + // dependencies cannot be statically extracted". Force ESM build instead. + const vscodeTypesPath = path.join( + __dirname, + 'node_modules/vscode-languageserver-types/lib/esm/main.js', + ); + return { + resolve: { + alias: { + 'vscode-languageserver-types': vscodeTypesPath, + }, + }, + }; + }, + }), + async (context, options) => { + return { + name: 'docusaurus-plugin-tailwindcss', + configurePostCss(postcssOptions) { + postcssOptions.plugins = [ + require('tailwindcss'), + require('autoprefixer'), + ]; + return postcssOptions; + }, + }; + }, + 'docusaurus-plugin-image-zoom', + 'docusaurus-lunr-search', + [ + // TODO: pinned to pre-release alpha; revisit when a stable 2.0.0 is published + // https://www.npmjs.com/package/@signalwire/docusaurus-plugin-llms-txt + '@signalwire/docusaurus-plugin-llms-txt', + { + markdown: { + enableFiles: true, + relativePaths: true, + includeBlog: false, + includePages: true, + includeDocs: true, + includeVersionedDocs: false, + excludeRoutes: [], + }, + llmsTxt: { + enableLlmsFullTxt: true, + includeBlog: false, + includePages: true, + includeDocs: true, + excludeRoutes: [], + + siteTitle: 'Python Data Sources', + siteDescription: 'A collection of custom PySpark data source connectors for various formats.', + + autoSectionDepth: 1, + autoSectionPosition: 100, + + sections: [ + { + id: 'getting-started', + name: 'Getting Started', + description: 'Installation and motivation for Python Data Sources', + position: 1, + routes: [ + { route: '/python-data-sources/docs/installation' }, + { route: '/python-data-sources/docs/motivation' }, + ], + }, + { + id: 'reference', + name: 'Reference', + description: 'API reference and technical documentation', + position: 2, + routes: [ + { route: '/python-data-sources/docs/reference/**' }, + ], + }, + { + id: 'demos', + name: 'Demos', + description: 'Example notebooks and demos', + position: 3, + routes: [ + { route: '/python-data-sources/docs/demos' }, + ], + }, + { + id: 'home', + name: 'Home', + description: 'Python Data Sources homepage', + position: 0, + routes: [ + { route: '/python-data-sources/' }, + { route: '/python-data-sources/index' }, + ], + }, + ], + }, + }, + ], + ], + + presets: [ + [ + 'classic', + { + docs: { + sidebarPath: './sidebars.ts', + editUrl: + 'https://github.com/databricks-industry-solutions/python-data-sources/tree/main/docs/python-data-sources/', + }, + blog: false, + theme: { + customCss: './src/css/custom.css', + }, + } satisfies Preset.Options, + ], + ], + + themeConfig: { + colorMode: { + defaultMode: 'dark', + respectPrefersColorScheme: false, + }, + navbar: { + title: 'Python Data Sources', + logo: { + alt: 'Python Data Sources Logo', + src: 'img/logo.svg', + }, + items: [ + { + type: 'search', + position: 'right', + }, + { + href: 'https://github.com/databricks-industry-solutions/python-data-sources', + position: 'right', + className: 'header-github-link', + 'aria-label': 'GitHub repository', + }, + ], + }, + footer: { + links: [], + copyright: `Copyright © ${new Date().getFullYear()} Databricks Industry Solutions. Docs built with Docusaurus.`, + }, + prism: { + theme: prismThemes.oneLight, + darkTheme: prismThemes.oneDark, + }, + zoom: { + selector: 'article img', + background: { + light: '#F8FAFC', + dark: '#F8FAFC', + }, + }, + } satisfies Preset.ThemeConfig, +}; + +export default config; diff --git a/docs/python-data-sources/package.json b/docs/python-data-sources/package.json new file mode 100644 index 0000000..2613bea --- /dev/null +++ b/docs/python-data-sources/package.json @@ -0,0 +1,60 @@ +{ + "name": "python-data-sources", + "version": "0.0.0", + "private": true, + "scripts": { + "docusaurus": "docusaurus", + "start": "docusaurus start", + "build": "docusaurus build", + "swizzle": "docusaurus swizzle", + "deploy": "docusaurus deploy", + "clear": "docusaurus clear", + "serve": "docusaurus serve", + "write-translations": "docusaurus write-translations", + "write-heading-ids": "docusaurus write-heading-ids", + "typecheck": "tsc" + }, + "dependencies": { + "@docusaurus/core": "^3.8.1", + "@docusaurus/preset-classic": "^3.8.1", + "@docusaurus/theme-mermaid": "^3.8.1", + "@mdx-js/react": "^3.0.0", + "@signalwire/docusaurus-plugin-llms-txt": "2.0.0-alpha.7", + "clsx": "^2.1.1", + "docusaurus-lunr-search": "3.6.1", + "docusaurus-plugin-image-zoom": "^2.0.0", + "lucide-react": "^0.471.1", + "prism-react-renderer": "^2.3.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "tsx": "^4.20.3" + }, + "devDependencies": { + "@docusaurus/module-type-aliases": "^3.8.1", + "@docusaurus/tsconfig": "^3.8.1", + "@docusaurus/types": "^3.8.1", + "@tailwindcss/typography": "^0.5.16", + "autoprefixer": "^10.4.20", + "postcss": "^8.5.1", + "tailwindcss": "^3.4.17", + "typescript": "~5.6.2" + }, + "browserslist": { + "production": [ + ">0.5%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 3 chrome version", + "last 3 firefox version", + "last 5 safari version" + ] + }, + "engines": { + "node": ">=18.0" + }, + "resolutions": { + "webpack": "~5.97.0" + } +} diff --git a/docs/python-data-sources/sidebars.ts b/docs/python-data-sources/sidebars.ts new file mode 100644 index 0000000..eb6d05c --- /dev/null +++ b/docs/python-data-sources/sidebars.ts @@ -0,0 +1,12 @@ +import type { SidebarsConfig } from '@docusaurus/plugin-content-docs'; + +// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) + +const sidebars: SidebarsConfig = { + // Generated from the docs/ folder structure + tutorialSidebar: [ + { type: 'autogenerated', dirName: '.' }, + ], +}; + +export default sidebars; diff --git a/docs/python-data-sources/src/components/Button.tsx b/docs/python-data-sources/src/components/Button.tsx new file mode 100644 index 0000000..e4c2ba7 --- /dev/null +++ b/docs/python-data-sources/src/components/Button.tsx @@ -0,0 +1,65 @@ +import React, { CSSProperties } from 'react'; +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; + +type Button = { + size?: 'sm' | 'lg' | 'small' | 'medium' | 'large' | null; + outline?: boolean; + variant: 'primary' | 'secondary' | 'danger' | 'warning' | 'success' | 'info' | 'link' | string; + block?: boolean; + disabled?: boolean; + className?: string; + style?: CSSProperties; + link: string; + label: string; + linkClassName?: string; +}; + +export default function Button({ + size = null, + outline = false, + variant = 'primary', + block = false, + disabled = false, + className, + style, + link, + label, + linkClassName, +}: Button) { + const sizeMap = { + sm: 'sm', + small: 'sm', + lg: 'lg', + large: 'lg', + medium: null, + }; + const buttonSize = size ? sizeMap[size] : ''; + const sizeClass = buttonSize ? `button--${buttonSize}` : ''; + const outlineClass = outline ? 'button--outline' : ''; + const variantClass = variant ? `button--${variant}` : ''; + const blockClass = block ? 'button--block' : ''; + const disabledClass = disabled ? 'disabled' : ''; + const destination = disabled ? null : link; + return ( + + + + ); +} diff --git a/docs/python-data-sources/src/css/custom.css b/docs/python-data-sources/src/css/custom.css new file mode 100644 index 0000000..3333a17 --- /dev/null +++ b/docs/python-data-sources/src/css/custom.css @@ -0,0 +1,79 @@ +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* import fonts: DM Mono and DM Sans */ +@import url('https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap'); + + +/* configure tailwindcss */ +@tailwind base; +@tailwind components; +@tailwind utilities; + + + +:root { + --ifm-font-family-base: 'DM Sans'; + --ifm-heading-font-family: 'DM Sans'; + --ifm-font-family-monospace: 'DM Mono', monospace; + + --ifm-code-font-size: 95%; + --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); +} + + + +.font-mono { + font-family: "DM Mono", monospace; + font-style: normal; +} + +.font-sans { + font-family: "DM Sans", sans-serif; + font-style: normal; +} + +button { + @apply font-sans; +} + +#search_input_react { + @apply font-sans; +} + + +.header-github-link::before { + content: ''; + width: 24px; + height: 24px; + display: flex; + background-color: var(--ifm-navbar-link-color); + mask-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E"); + transition: background-color var(--ifm-transition-fast) + var(--ifm-transition-timing-default); +} + +.header-github-link:hover::before { + background-color: var(--ifm-navbar-link-hover-color); +} + +/* TOC sidebar header (desktop and mobile) */ +.theme-doc-toc-desktop .table-of-contents::before, +.theme-doc-toc-desktop .tableOfContents::before { + content: "📄 On this page:"; + display: block; + font-weight: 600; + margin-bottom: 0.5rem; + color: var(--ifm-color-content); +} + +.theme-doc-toc-mobile .menu__list::before { + content: "📄 On this page:"; + display: block; + font-weight: 600; + margin: 0.5rem 0 0.25rem 0.5rem; + color: var(--ifm-color-content); +} diff --git a/docs/python-data-sources/src/pages/index.tsx b/docs/python-data-sources/src/pages/index.tsx new file mode 100644 index 0000000..a747b85 --- /dev/null +++ b/docs/python-data-sources/src/pages/index.tsx @@ -0,0 +1,189 @@ +import Layout from '@theme/Layout'; +import { JSX } from 'react'; +import Button from '../components/Button'; +import { Database, Radio, Image, FileText, Cog, Zap } from 'lucide-react'; + +const Hero = () => { + return ( +
+
+ Python Data Sources Logo +
+ +

+ Python Data Sources +

+

+ Provided by{' '} + + Databricks Industry Solutions + +

+

+ A collection of custom PySpark data source connectors for formats and protocols + that don't have a built-in Spark reader. +

+ +
+
+
+ ); +}; + +const Sources = () => { + const sources = [ + { + title: 'MCAP', + description: 'Read robotics and autonomy logs (MCAP format) into Spark DataFrames.', + icon: FileText, + }, + { + title: 'MQTT', + description: 'Stream IoT telemetry from MQTT brokers as a Spark Structured Streaming source.', + icon: Radio, + }, + { + title: 'ZipDICOM', + description: 'Decode medical imaging archives (DICOM in ZIP) into queryable pixel data.', + icon: Image, + }, + ]; + + return ( +
+

+ Available Sources +

+
+ {sources.map((source, index) => { + const Icon = source.icon; + return ( +
+ +

{source.title}

+

{source.description}

+
+ ); + })} +
+
+ ); +}; + +const Capabilities = () => { + const capabilities = [ + { + title: 'Spark DataSource V2 API', + description: 'Implemented against the Python DataSource API — usable from PySpark and Databricks runtime.', + icon: Database, + }, + { + title: 'Batch & Streaming', + description: 'Both batch reads and Spark Structured Streaming sources are supported where appropriate.', + icon: Zap, + }, + { + title: 'Modular Install', + description: 'Each connector is an optional extra (mcap, mqtt, zipdcm) — install only what you need.', + icon: Cog, + }, + ]; + + return ( +
+

+ Capabilities +

+
+ {capabilities.map((capability, index) => { + const Icon = capability.icon; + return ( +
+ +

{capability.title}

+

{capability.description}

+
+ ); + })} +
+
+ ); +}; + +const CallToAction = () => { + return ( +
+

+ Plug a new source into Spark 🚀 +

+

+ Follow the installation guide to add Python Data Sources to your PySpark project. +

+
+ ); +}; + +export default function Home(): JSX.Element { + return ( + +
+
+
+ + + + +
+
+
+
+ ); +} diff --git a/docs/python-data-sources/static/.nojekyll b/docs/python-data-sources/static/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/python-data-sources/static/img/logo.svg b/docs/python-data-sources/static/img/logo.svg new file mode 100644 index 0000000..615e5e3 --- /dev/null +++ b/docs/python-data-sources/static/img/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/python-data-sources/tailwind.config.ts b/docs/python-data-sources/tailwind.config.ts new file mode 100644 index 0000000..0953ba0 --- /dev/null +++ b/docs/python-data-sources/tailwind.config.ts @@ -0,0 +1,17 @@ +import type { Config } from 'tailwindcss'; + +export default { + content: [ + './docs/**/*.md', + './docs/**/*.mdx', + './src/**/*.{js,jsx,ts,tsx}', + ], + darkMode: ['class', '[data-theme="dark"]'], + theme: {}, + plugins: [ + require('@tailwindcss/typography'), + ], + corePlugins: { + preflight: false, // to make headings H1-H6 work correctly + }, +} satisfies Config; diff --git a/docs/python-data-sources/tsconfig.json b/docs/python-data-sources/tsconfig.json new file mode 100644 index 0000000..920d7a6 --- /dev/null +++ b/docs/python-data-sources/tsconfig.json @@ -0,0 +1,8 @@ +{ + // This file is not used in compilation. It is here just for a nice editor experience. + "extends": "@docusaurus/tsconfig", + "compilerOptions": { + "baseUrl": "." + }, + "exclude": [".docusaurus", "build"] +} diff --git a/pyproject.toml b/pyproject.toml index 3c7ac3f..896f548 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -86,10 +86,41 @@ e2e = [ "databricks-labs-blueprint~=0.12.0", "databricks-sdk~=0.88", ] +docs = [ + "pydoc-markdown>=4.8.2,<5.0", +] [tool.uv] required-version = "~=0.11.0" +[tool.pydoc-markdown] +loaders = [ + { type = "python", search_path = [ + "./src", + ], packages = [ + "python_data_sources", + ] }, +] +processors = [ + { type = "smart" }, + { type = "crossref" }, + { type = "filter", skip_empty_modules = true }, +] +hooks = { post-render = [ + "cp -r docs/python-data-sources/docs/reference/api/python_data_sources/* docs/python-data-sources/docs/reference/api/", + "rm -rf docs/python-data-sources/docs/reference/api/python_data_sources", +] } + +[tool.pydoc-markdown.renderer] +type = "docusaurus" +docs_base_path = "docs/python-data-sources/docs" +relative_output_path = "reference/api" +relative_sidebar_path = "sidebar.json" + +# by default method and function are on level 4 which is too deep for docusaurus +# we want to move them to level 3 +markdown = { header_level_by_type = { Method = 3, Function = 3, Class = 2, Module = 1 } } + [tool.pytest.ini_options] addopts = [ "--import-mode=importlib", diff --git a/uv.lock b/uv.lock index 7e63f35..440c836 100644 --- a/uv.lock +++ b/uv.lock @@ -16,6 +16,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/af/0f/3b8fdc946b4d9cc8cc1e8af42c4e409468c84441b933d037e101b3d72d86/astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec", size = 275612, upload-time = "2025-07-13T18:04:21.07Z" }, ] +[[package]] +name = "black" +version = "23.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "mypy-extensions" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/f4/a57cde4b60da0e249073009f4a9087e9e0a955deae78d3c2a493208d0c5c/black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5", size = 620809, upload-time = "2023-12-22T23:06:17.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/16/8726cedc83be841dfa854bbeef1288ee82272282a71048d7935292182b0b/black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e", size = 1569989, upload-time = "2023-12-22T23:20:22.158Z" }, + { url = "https://files.pythonhosted.org/packages/d2/1e/30f5eafcc41b8378890ba39b693fa111f7dca8a2620ba5162075d95ffe46/black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec", size = 1398647, upload-time = "2023-12-22T23:19:57.225Z" }, + { url = "https://files.pythonhosted.org/packages/99/de/ddb45cc044256431d96d846ce03164d149d81ca606b5172224d1872e0b58/black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e", size = 1720450, upload-time = "2023-12-22T23:08:52.675Z" }, + { url = "https://files.pythonhosted.org/packages/98/2b/54e5dbe9be5a10cbea2259517206ff7b6a452bb34e07508c7e1395950833/black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9", size = 1351070, upload-time = "2023-12-22T23:09:32.762Z" }, + { url = "https://files.pythonhosted.org/packages/7b/14/4da7b12a9abc43a601c215cb5a3d176734578da109f0dbf0a832ed78be09/black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e", size = 194363, upload-time = "2023-12-22T23:06:14.278Z" }, +] + [[package]] name = "certifi" version = "2026.4.22" @@ -101,6 +121,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, ] +[[package]] +name = "click" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, +] + [[package]] name = "colorama" version = "0.4.6" @@ -203,6 +235,46 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9d/9a/0fea98a70cf1749d41d738836f6349d97945f7c89433a259a6c2642eefeb/cryptography-48.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:16cd65b9330583e4619939b3a3843eec1e6e789744bb01e7c7e2e62e33c239c8", size = 3792100, upload-time = "2026-05-04T22:59:14.884Z" }, ] +[[package]] +name = "databind" +version = "4.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "nr-date" }, + { name = "nr-stream" }, + { name = "typeapi" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/9e/835a5211eeb7228a0e3870d54def48dd7951dbd951f51b30900020a5f9fc/databind-4.5.4.tar.gz", hash = "sha256:342e170a219b1661e5c1b20778b532aecfa67e46560ba75beb7e2c6faa2150b5", size = 43193, upload-time = "2026-04-02T22:21:47.318Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/db/3b8eb860b5baef89b72c7aadcc5072e1648ca0c98d6ba4b9e4eabbdc2cf5/databind-4.5.4-py3-none-any.whl", hash = "sha256:78467f874a3e80bcd1d3de349167587a0d369831bc64c03798520be86074f96e", size = 52381, upload-time = "2026-04-02T22:21:45.389Z" }, +] + +[[package]] +name = "databind-core" +version = "4.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "databind" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/dc/b63a6f6a404146e8e3f1226c9243a5cb30784a1f75218d014cafce9a411f/databind_core-4.5.4.tar.gz", hash = "sha256:a7a47af183d4a8046c893fc19fa9c085f287a15e57a05e58345016086ce0f807", size = 974, upload-time = "2026-04-02T22:21:56.588Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/cf/1d1f4d37b4112f26ea5086d54200837d1dcbddaa536f3a70bb1d8b48ed9a/databind_core-4.5.4-py3-none-any.whl", hash = "sha256:25482c352f4f6fcade7c106c665553a18febeccda2972c00cf5af19f473960ab", size = 1666, upload-time = "2026-04-02T22:21:55.504Z" }, +] + +[[package]] +name = "databind-json" +version = "4.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "databind" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ab/72/9af59950a23ff6a03062acd85879de289595168ec43d6cec57253d00497c/databind_json-4.5.4.tar.gz", hash = "sha256:2c714f9c3039a81e42fc3826e47d7826ef020d93131c34daf4c9ae0483108e4d", size = 966, upload-time = "2026-04-02T22:22:05.538Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/d4/e00d531202314e29d90c9496f6b4730e3647128b9866180b8ce8ebb79394/databind_json-4.5.4-py3-none-any.whl", hash = "sha256:22e6faaeb6f2ec5cf815fd597a539dfe1f4846b80b618760112f4fe59a0898cc", size = 1664, upload-time = "2026-04-02T22:22:04.204Z" }, +] + [[package]] name = "databricks-labs-blueprint" version = "0.12.0" @@ -277,6 +349,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/07/a9/63b4f6ef3078e4d060faaad85c4e43667189c3429edd68d5cbddc45187ca/databricks_sdk-0.106.0-py3-none-any.whl", hash = "sha256:db9de5566279ae80dfea0ecd37231e8968523ef984e83cbe4bd77e50c59aa6a5", size = 879452, upload-time = "2026-04-30T12:33:18.116Z" }, ] +[[package]] +name = "deprecated" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/85/12f0a49a7c4ffb70572b6c2ef13c90c88fd190debda93b23f026b25f9634/deprecated-1.3.1.tar.gz", hash = "sha256:b1b50e0ff0c1fddaa5708a2c6b0a6588bb09b892825ab2b214ac9ea9d92a5223", size = 2932523, upload-time = "2025-10-30T08:19:02.757Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/d0/205d54408c08b13550c733c4b85429e7ead111c7f0014309637425520a9a/deprecated-1.3.1-py2.py3-none-any.whl", hash = "sha256:597bfef186b6f60181535a29fbe44865ce137a5079f295b479886c82729d5f3f", size = 11298, upload-time = "2025-10-30T08:19:00.758Z" }, +] + [[package]] name = "dill" version = "0.4.1" @@ -286,6 +370,40 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/1e/77/dc8c558f7593132cf8fefec57c4f60c83b16941c574ac5f619abb3ae7933/dill-0.4.1-py3-none-any.whl", hash = "sha256:1e1ce33e978ae97fcfcff5638477032b801c46c7c65cf717f95fbc2248f79a9d", size = 120019, upload-time = "2026-01-19T02:36:55.663Z" }, ] +[[package]] +name = "docspec" +version = "2.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "databind-core" }, + { name = "databind-json" }, + { name = "deprecated" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/fe/1ad244d0ca186b5386050ec30dfd59bd3dbeea5baec33ca861dd43b922e6/docspec-2.2.2.tar.gz", hash = "sha256:c772c6facfce839176b647701082c7a22b3d22d872d392552cf5d65e0348c919", size = 14086, upload-time = "2025-05-06T12:39:59.466Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/57/1011f2e88743a818cced9a95d54200ba6a05decaf43fd91d8c6ed9f6470d/docspec-2.2.2-py3-none-any.whl", hash = "sha256:854d25401e7ec2d155b0c1e001e25819d16b6df3a7575212a7f340ae8b00122e", size = 9726, upload-time = "2025-05-06T12:39:58.047Z" }, +] + +[[package]] +name = "docspec-python" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "black" }, + { name = "docspec" }, + { name = "nr-util" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/88/99c5e27a894f01290364563c84838cf68f1a8629474b5bbfc3bf35a8d923/docspec_python-2.2.1.tar.gz", hash = "sha256:c41b850b4d6f4de30999ea6f82c9cdb9183d9bcba45559ee9173d3dab7281559", size = 13838, upload-time = "2023-05-28T11:24:19.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/49/b8d1a2fa01b6f7a1a9daa1d485efc7684489028d6a356fc2bc5b40131061/docspec_python-2.2.1-py3-none-any.whl", hash = "sha256:76ac41d35a8face35b2d766c2e8a416fb8832359785d396f0d53bcb00f178e54", size = 16093, upload-time = "2023-05-28T11:24:17.261Z" }, +] + +[[package]] +name = "docstring-parser" +version = "0.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/ce/5d6a3782b9f88097ce3e579265015db3372ae78d12f67629b863a9208c96/docstring_parser-0.11.tar.gz", hash = "sha256:93b3f8f481c7d24e37c5d9f30293c89e2933fa209421c8abd731dd3ef0715ecb", size = 22775, upload-time = "2021-09-30T07:44:10.288Z" } + [[package]] name = "eradicate" version = "2.3.0" @@ -335,6 +453,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7f/cc/9b681a170efab4868a032631dea1e8446d8ec718a7f657b94d49d1a12643/isort-6.1.0-py3-none-any.whl", hash = "sha256:58d8927ecce74e5087aef019f778d4081a3b6c98f15a80ba35782ca8a2097784", size = 94329, upload-time = "2025-10-01T16:26:43.291Z" }, ] +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, +] + [[package]] name = "lz4" version = "4.4.5" @@ -367,6 +497,47 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/4d/a175459fb29f909e13e57c8f475181ad8085d8d7869bd8ad99033e3ee5fa/lz4-4.4.5-cp313-cp313t-win_arm64.whl", hash = "sha256:28ccaeb7c5222454cd5f60fcd152564205bcb801bd80e125949d2dfbadc76bbd", size = 91504, upload-time = "2025-11-03T13:02:17.313Z" }, ] +[[package]] +name = "markupsafe" +version = "3.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, +] + [[package]] name = "mcap" version = "1.3.1" @@ -402,6 +573,46 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, ] +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "nr-date" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a0/92/08110dd3d7ff5e2b852a220752eb6c40183839f5b7cc91f9f38dd2298e7d/nr_date-2.1.0.tar.gz", hash = "sha256:0643aea13bcdc2a8bc56af9d5e6a89ef244c9744a1ef00cdc735902ba7f7d2e6", size = 8789, upload-time = "2023-08-16T13:46:04.114Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/10/1d2b00172537c1522fe64bbc6fb16b015632a02f7b3864e788ccbcb4dd85/nr_date-2.1.0-py3-none-any.whl", hash = "sha256:bd672a9dfbdcf7c4b9289fea6750c42490eaee08036a72059dcc78cb236ed568", size = 10496, upload-time = "2023-08-16T13:46:02.627Z" }, +] + +[[package]] +name = "nr-stream" +version = "1.1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/37/e4d36d852c441233c306c5fbd98147685dce3ac9b0a8bbf4a587d0ea29ea/nr_stream-1.1.5.tar.gz", hash = "sha256:eb0216c6bfc61a46d4568dba3b588502c610ec8ddef4ac98f3932a2bd7264f65", size = 10053, upload-time = "2023-02-14T22:44:09.074Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/e1/f93485fe09aa36c0e1a3b76363efa1791241f7f863a010f725c95e8a74fe/nr_stream-1.1.5-py3-none-any.whl", hash = "sha256:47e12150b331ad2cb729cfd9d2abd281c9949809729ba461c6aa87dd9927b2d4", size = 10448, upload-time = "2023-02-14T22:44:07.72Z" }, +] + +[[package]] +name = "nr-util" +version = "0.8.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "deprecated" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/0c/078c567d95e25564bc1ede3c2cf6ce1c91f50648c83786354b47224326da/nr.util-0.8.12.tar.gz", hash = "sha256:a4549c2033d99d2f0379b3f3d233fd2a8ade286bbf0b3ad0cc7cea16022214f4", size = 63707, upload-time = "2022-06-20T13:29:29.192Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/58/eab08df9dbd69d9e21fc5e7be6f67454f386336ec71e6b64e378a2dddea4/nr.util-0.8.12-py3-none-any.whl", hash = "sha256:91da02ac9795eb8e015372275c1efe54bac9051231ee9b0e7e6f96b0b4e7d2bb", size = 90319, upload-time = "2022-06-20T13:29:27.312Z" }, +] + [[package]] name = "numpy" version = "2.4.4" @@ -496,6 +707,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e8/74/09298ca9740beed1d3504e073d67e128aa07e5ca5ca2824b0c674c0b8676/pandas-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:7cadd7e9a44ec13b621aec60f9150e744cfc7a3dd32924a7e2f45edff31823b0", size = 10488652, upload-time = "2026-03-31T06:47:40.612Z" }, ] +[[package]] +name = "pathspec" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" }, +] + [[package]] name = "platformdirs" version = "4.9.6" @@ -606,6 +826,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/27/a6/98651e752a49f341aa99aa3f6c8ba361728dfc064242884355419df63669/pydicom-3.0.1-py3-none-any.whl", hash = "sha256:db32f78b2641bd7972096b8289111ddab01fb221610de8d7afa835eb938adb41", size = 2376126, upload-time = "2024-09-22T02:02:41.616Z" }, ] +[[package]] +name = "pydoc-markdown" +version = "4.8.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "click" }, + { name = "databind-core" }, + { name = "databind-json" }, + { name = "docspec" }, + { name = "docspec-python" }, + { name = "docstring-parser" }, + { name = "jinja2" }, + { name = "nr-util" }, + { name = "pyyaml" }, + { name = "requests" }, + { name = "tomli" }, + { name = "tomli-w" }, + { name = "watchdog" }, + { name = "yapf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/8a/2c7f7ad656d22371a596d232fc140327b958d7f1d491b889632ea0cb7e87/pydoc_markdown-4.8.2.tar.gz", hash = "sha256:fb6c927e31386de17472d42f9bd3d3be2905977d026f6216881c65145aa67f0b", size = 44506, upload-time = "2023-06-26T12:37:01.152Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/5a/ce0b056d9a95fd0c06a6cfa5972477d79353392d19230c748a7ba5a9df04/pydoc_markdown-4.8.2-py3-none-any.whl", hash = "sha256:203f74119e6bb2f9deba43d452422de7c8ec31955b61e0620fa4dd8c2611715f", size = 67830, upload-time = "2023-06-26T12:36:59.502Z" }, +] + [[package]] name = "pygments" version = "2.20.0" @@ -821,6 +1066,9 @@ dev = [ { name = "python-data-sources", extra = ["all"] }, { name = "ruff" }, ] +docs = [ + { name = "pydoc-markdown" }, +] e2e = [ { name = "coverage" }, { name = "databricks-labs-blueprint" }, @@ -891,6 +1139,7 @@ dev = [ { name = "python-data-sources", extras = ["all"] }, { name = "ruff", specifier = "~=0.3.4" }, ] +docs = [{ name = "pydoc-markdown", specifier = ">=4.8.2,<5.0" }] e2e = [ { name = "coverage", extras = ["toml"], specifier = "~=7.13.4" }, { name = "databricks-labs-blueprint", specifier = "~=0.12.0" }, @@ -1022,6 +1271,42 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ef/9e/14f111ddbc0b7f15e3ce886a69548c26fa1eb3571a63c1bf5f060cbe9f43/sqlglot-26.29.0-py3-none-any.whl", hash = "sha256:373376bab433d55b291ccbbf9fd59e08a59cde059bdb64e32d1f15accc07ac65", size = 470167, upload-time = "2025-06-17T21:02:45.067Z" }, ] +[[package]] +name = "tomli" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" }, + { url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" }, + { url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" }, + { url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" }, + { url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" }, + { url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" }, + { url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" }, + { url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" }, + { url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" }, + { url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" }, + { url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" }, + { url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" }, + { url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" }, +] + +[[package]] +name = "tomli-w" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/19/75/241269d1da26b624c0d5e110e8149093c759b7a286138f4efd61a60e75fe/tomli_w-1.2.0.tar.gz", hash = "sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021", size = 7184, upload-time = "2025-01-15T12:07:24.262Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl", hash = "sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90", size = 6675, upload-time = "2025-01-15T12:07:22.074Z" }, +] + [[package]] name = "tomlkit" version = "0.14.0" @@ -1031,6 +1316,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, ] +[[package]] +name = "typeapi" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d5/92/5a23ad34aa877edf00906166e339bfdc571543ea183ea7ab727bb01516c7/typeapi-2.3.0.tar.gz", hash = "sha256:a60d11f72c5ec27338cfd1c807f035b0b16ed2e3b798fb1c1d34fc5589f544be", size = 122687, upload-time = "2025-10-23T13:44:11.26Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/84/021bbeb7edb990dd6875cb6ab08d32faaa49fec63453d863730260a01f9e/typeapi-2.3.0-py3-none-any.whl", hash = "sha256:576b7dcb94412e91c5cae107a393674f8f99c10a24beb8be2302e3fed21d5cc2", size = 26858, upload-time = "2025-10-23T13:44:09.833Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + [[package]] name = "tzdata" version = "2026.2" @@ -1049,6 +1355,84 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, ] +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +] + +[[package]] +name = "wrapt" +version = "2.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/64/925f213fdcbb9baeb1530449ac71a4d57fc361c053d06bf78d0c5c7cd80c/wrapt-2.1.2.tar.gz", hash = "sha256:3996a67eecc2c68fd47b4e3c564405a5777367adfd9b8abb58387b63ee83b21e", size = 81678, upload-time = "2026-03-06T02:53:25.134Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/b6/1db817582c49c7fcbb7df6809d0f515af29d7c2fbf57eb44c36e98fb1492/wrapt-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ff2aad9c4cda28a8f0653fc2d487596458c2a3f475e56ba02909e950a9efa6a9", size = 61255, upload-time = "2026-03-06T02:52:45.663Z" }, + { url = "https://files.pythonhosted.org/packages/a2/16/9b02a6b99c09227c93cd4b73acc3678114154ec38da53043c0ddc1fba0dc/wrapt-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6433ea84e1cfacf32021d2a4ee909554ade7fd392caa6f7c13f1f4bf7b8e8748", size = 61848, upload-time = "2026-03-06T02:53:48.728Z" }, + { url = "https://files.pythonhosted.org/packages/af/aa/ead46a88f9ec3a432a4832dfedb84092fc35af2d0ba40cd04aea3889f247/wrapt-2.1.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c20b757c268d30d6215916a5fa8461048d023865d888e437fab451139cad6c8e", size = 121433, upload-time = "2026-03-06T02:54:40.328Z" }, + { url = "https://files.pythonhosted.org/packages/3a/9f/742c7c7cdf58b59085a1ee4b6c37b013f66ac33673a7ef4aaed5e992bc33/wrapt-2.1.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:79847b83eb38e70d93dc392c7c5b587efe65b3e7afcc167aa8abd5d60e8761c8", size = 123013, upload-time = "2026-03-06T02:53:26.58Z" }, + { url = "https://files.pythonhosted.org/packages/e8/44/2c3dd45d53236b7ed7c646fcf212251dc19e48e599debd3926b52310fafb/wrapt-2.1.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f8fba1bae256186a83d1875b2b1f4e2d1242e8fac0f58ec0d7e41b26967b965c", size = 117326, upload-time = "2026-03-06T02:53:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/74/e2/b17d66abc26bd96f89dec0ecd0ef03da4a1286e6ff793839ec431b9fae57/wrapt-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e3d3b35eedcf5f7d022291ecd7533321c4775f7b9cd0050a31a68499ba45757c", size = 121444, upload-time = "2026-03-06T02:54:09.5Z" }, + { url = "https://files.pythonhosted.org/packages/3c/62/e2977843fdf9f03daf1586a0ff49060b1b2fc7ff85a7ea82b6217c1ae36e/wrapt-2.1.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:6f2c5390460de57fa9582bc8a1b7a6c86e1a41dfad74c5225fc07044c15cc8d1", size = 116237, upload-time = "2026-03-06T02:54:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/88/dd/27fc67914e68d740bce512f11734aec08696e6b17641fef8867c00c949fc/wrapt-2.1.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7dfa9f2cf65d027b951d05c662cc99ee3bd01f6e4691ed39848a7a5fffc902b2", size = 120563, upload-time = "2026-03-06T02:53:20.412Z" }, + { url = "https://files.pythonhosted.org/packages/ec/9f/b750b3692ed2ef4705cb305bd68858e73010492b80e43d2a4faa5573cbe7/wrapt-2.1.2-cp312-cp312-win32.whl", hash = "sha256:eba8155747eb2cae4a0b913d9ebd12a1db4d860fc4c829d7578c7b989bd3f2f0", size = 58198, upload-time = "2026-03-06T02:53:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/8e/b2/feecfe29f28483d888d76a48f03c4c4d8afea944dbee2b0cd3380f9df032/wrapt-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:1c51c738d7d9faa0b3601708e7e2eda9bf779e1b601dce6c77411f2a1b324a63", size = 60441, upload-time = "2026-03-06T02:52:47.138Z" }, + { url = "https://files.pythonhosted.org/packages/44/e1/e328f605d6e208547ea9fd120804fcdec68536ac748987a68c47c606eea8/wrapt-2.1.2-cp312-cp312-win_arm64.whl", hash = "sha256:c8e46ae8e4032792eb2f677dbd0d557170a8e5524d22acc55199f43efedd39bf", size = 58836, upload-time = "2026-03-06T02:53:22.053Z" }, + { url = "https://files.pythonhosted.org/packages/4c/7a/d936840735c828b38d26a854e85d5338894cda544cb7a85a9d5b8b9c4df7/wrapt-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787fd6f4d67befa6fe2abdffcbd3de2d82dfc6fb8a6d850407c53332709d030b", size = 61259, upload-time = "2026-03-06T02:53:41.922Z" }, + { url = "https://files.pythonhosted.org/packages/5e/88/9a9b9a90ac8ca11c2fdb6a286cb3a1fc7dd774c00ed70929a6434f6bc634/wrapt-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4bdf26e03e6d0da3f0e9422fd36bcebf7bc0eeb55fdf9c727a09abc6b9fe472e", size = 61851, upload-time = "2026-03-06T02:52:48.672Z" }, + { url = "https://files.pythonhosted.org/packages/03/a9/5b7d6a16fd6533fed2756900fc8fc923f678179aea62ada6d65c92718c00/wrapt-2.1.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bbac24d879aa22998e87f6b3f481a5216311e7d53c7db87f189a7a0266dafffb", size = 121446, upload-time = "2026-03-06T02:54:14.013Z" }, + { url = "https://files.pythonhosted.org/packages/45/bb/34c443690c847835cfe9f892be78c533d4f32366ad2888972c094a897e39/wrapt-2.1.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:16997dfb9d67addc2e3f41b62a104341e80cac52f91110dece393923c0ebd5ca", size = 123056, upload-time = "2026-03-06T02:54:10.829Z" }, + { url = "https://files.pythonhosted.org/packages/93/b9/ff205f391cb708f67f41ea148545f2b53ff543a7ac293b30d178af4d2271/wrapt-2.1.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:162e4e2ba7542da9027821cb6e7c5e068d64f9a10b5f15512ea28e954893a267", size = 117359, upload-time = "2026-03-06T02:53:03.623Z" }, + { url = "https://files.pythonhosted.org/packages/1f/3d/1ea04d7747825119c3c9a5e0874a40b33594ada92e5649347c457d982805/wrapt-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f29c827a8d9936ac320746747a016c4bc66ef639f5cd0d32df24f5eacbf9c69f", size = 121479, upload-time = "2026-03-06T02:53:45.844Z" }, + { url = "https://files.pythonhosted.org/packages/78/cc/ee3a011920c7a023b25e8df26f306b2484a531ab84ca5c96260a73de76c0/wrapt-2.1.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:a9dd9813825f7ecb018c17fd147a01845eb330254dff86d3b5816f20f4d6aaf8", size = 116271, upload-time = "2026-03-06T02:54:46.356Z" }, + { url = "https://files.pythonhosted.org/packages/98/fd/e5ff7ded41b76d802cf1191288473e850d24ba2e39a6ec540f21ae3b57cb/wrapt-2.1.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6f8dbdd3719e534860d6a78526aafc220e0241f981367018c2875178cf83a413", size = 120573, upload-time = "2026-03-06T02:52:50.163Z" }, + { url = "https://files.pythonhosted.org/packages/47/c5/242cae3b5b080cd09bacef0591691ba1879739050cc7c801ff35c8886b66/wrapt-2.1.2-cp313-cp313-win32.whl", hash = "sha256:5c35b5d82b16a3bc6e0a04349b606a0582bc29f573786aebe98e0c159bc48db6", size = 58205, upload-time = "2026-03-06T02:53:47.494Z" }, + { url = "https://files.pythonhosted.org/packages/12/69/c358c61e7a50f290958809b3c61ebe8b3838ea3e070d7aac9814f95a0528/wrapt-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f8bc1c264d8d1cf5b3560a87bbdd31131573eb25f9f9447bb6252b8d4c44a3a1", size = 60452, upload-time = "2026-03-06T02:53:30.038Z" }, + { url = "https://files.pythonhosted.org/packages/8e/66/c8a6fcfe321295fd8c0ab1bd685b5a01462a9b3aa2f597254462fc2bc975/wrapt-2.1.2-cp313-cp313-win_arm64.whl", hash = "sha256:3beb22f674550d5634642c645aba4c72a2c66fb185ae1aebe1e955fae5a13baf", size = 58842, upload-time = "2026-03-06T02:52:52.114Z" }, + { url = "https://files.pythonhosted.org/packages/da/55/9c7052c349106e0b3f17ae8db4b23a691a963c334de7f9dbd60f8f74a831/wrapt-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0fc04bc8664a8bc4c8e00b37b5355cffca2535209fba1abb09ae2b7c76ddf82b", size = 63075, upload-time = "2026-03-06T02:53:19.108Z" }, + { url = "https://files.pythonhosted.org/packages/09/a8/ce7b4006f7218248dd71b7b2b732d0710845a0e49213b18faef64811ffef/wrapt-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a9b9d50c9af998875a1482a038eb05755dfd6fe303a313f6a940bb53a83c3f18", size = 63719, upload-time = "2026-03-06T02:54:33.452Z" }, + { url = "https://files.pythonhosted.org/packages/e4/e5/2ca472e80b9e2b7a17f106bb8f9df1db11e62101652ce210f66935c6af67/wrapt-2.1.2-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2d3ff4f0024dd224290c0eabf0240f1bfc1f26363431505fb1b0283d3b08f11d", size = 152643, upload-time = "2026-03-06T02:52:42.721Z" }, + { url = "https://files.pythonhosted.org/packages/36/42/30f0f2cefca9d9cbf6835f544d825064570203c3e70aa873d8ae12e23791/wrapt-2.1.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3278c471f4468ad544a691b31bb856374fbdefb7fee1a152153e64019379f015", size = 158805, upload-time = "2026-03-06T02:54:25.441Z" }, + { url = "https://files.pythonhosted.org/packages/bb/67/d08672f801f604889dcf58f1a0b424fe3808860ede9e03affc1876b295af/wrapt-2.1.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8914c754d3134a3032601c6984db1c576e6abaf3fc68094bb8ab1379d75ff92", size = 145990, upload-time = "2026-03-06T02:53:57.456Z" }, + { url = "https://files.pythonhosted.org/packages/68/a7/fd371b02e73babec1de6ade596e8cd9691051058cfdadbfd62a5898f3295/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ff95d4264e55839be37bafe1536db2ab2de19da6b65f9244f01f332b5286cfbf", size = 155670, upload-time = "2026-03-06T02:54:55.309Z" }, + { url = "https://files.pythonhosted.org/packages/86/2d/9fe0095dfdb621009f40117dcebf41d7396c2c22dca6eac779f4c007b86c/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:76405518ca4e1b76fbb1b9f686cff93aebae03920cc55ceeec48ff9f719c5f67", size = 144357, upload-time = "2026-03-06T02:54:24.092Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b6/ec7b4a254abbe4cde9fa15c5d2cca4518f6b07d0f1b77d4ee9655e30280e/wrapt-2.1.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c0be8b5a74c5824e9359b53e7e58bef71a729bacc82e16587db1c4ebc91f7c5a", size = 150269, upload-time = "2026-03-06T02:53:31.268Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6b/2fabe8ebf148f4ee3c782aae86a795cc68ffe7d432ef550f234025ce0cfa/wrapt-2.1.2-cp313-cp313t-win32.whl", hash = "sha256:f01277d9a5fc1862f26f7626da9cf443bebc0abd2f303f41c5e995b15887dabd", size = 59894, upload-time = "2026-03-06T02:54:15.391Z" }, + { url = "https://files.pythonhosted.org/packages/ca/fb/9ba66fc2dedc936de5f8073c0217b5d4484e966d87723415cc8262c5d9c2/wrapt-2.1.2-cp313-cp313t-win_amd64.whl", hash = "sha256:84ce8f1c2104d2f6daa912b1b5b039f331febfeee74f8042ad4e04992bd95c8f", size = 63197, upload-time = "2026-03-06T02:54:41.943Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1c/012d7423c95d0e337117723eb8ecf73c622ce15a97847e84cf3f8f26cd7e/wrapt-2.1.2-cp313-cp313t-win_arm64.whl", hash = "sha256:a93cd767e37faeddbe07d8fc4212d5cba660af59bdb0f6372c93faaa13e6e679", size = 60363, upload-time = "2026-03-06T02:54:48.093Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c7/8528ac2dfa2c1e6708f647df7ae144ead13f0a31146f43c7264b4942bf12/wrapt-2.1.2-py3-none-any.whl", hash = "sha256:b8fd6fa2b2c4e7621808f8c62e8317f4aae56e59721ad933bac5239d913cf0e8", size = 43993, upload-time = "2026-03-06T02:53:12.905Z" }, +] + +[[package]] +name = "yapf" +version = "0.43.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/97/b6f296d1e9cc1ec25c7604178b48532fa5901f721bcf1b8d8148b13e5588/yapf-0.43.0.tar.gz", hash = "sha256:00d3aa24bfedff9420b2e0d5d9f5ab6d9d4268e72afbf59bb3fa542781d5218e", size = 254907, upload-time = "2024-11-14T00:11:41.584Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/37/81/6acd6601f61e31cfb8729d3da6d5df966f80f374b78eff83760714487338/yapf-0.43.0-py3-none-any.whl", hash = "sha256:224faffbc39c428cb095818cf6ef5511fdab6f7430a10783fdfb292ccf2852ca", size = 256158, upload-time = "2024-11-14T00:11:39.37Z" }, +] + [[package]] name = "zstandard" version = "0.25.0"