# Grafana Foundation SDK – Python
A set of tools, types and *builder libraries* for building and manipulating Grafana objects in Python.
> [!NOTE]
> This branch contains **types and builders generated for Grafana v10.1.x.**
> Other supported versions of Grafana can be found at [this repository's root](https://github.com/grafana/grafana-foundation-sdk/).
## Installing
```shell
python3 -m pip install 'grafana_foundation_sdk==1733927908!10.1.0'
```
## Example usage
### Building a dashboard
```python
from grafana_foundation_sdk.builders.dashboard import Dashboard, Row
from grafana_foundation_sdk.builders.prometheus import Dataquery as PrometheusQuery
from grafana_foundation_sdk.builders.timeseries import Panel as Timeseries
from grafana_foundation_sdk.cog.encoder import JSONEncoder
from grafana_foundation_sdk.models.common import TimeZoneBrowser
def build_dashboard() -> Dashboard:
builder = (
Dashboard("[TEST] Node Exporter / Raspberry")
.uid("test-dashboard-raspberry")
.tags(["generated", "raspberrypi-node-integration"])
.refresh("1m")
.time("now-30m", "now")
.timezone(TimeZoneBrowser)
.with_row(Row("Overview"))
.with_panel(
Timeseries()
.title("Network Received")
.unit("bps")
.min_val(0)
.with_target(
PrometheusQuery()
.expr('rate(node_network_receive_bytes_total{job="integrations/raspberrypi-node", device!="lo"}[$__rate_interval]) * 8')
.legend_format("{{ device }}")
)
)
)
return builder
if __name__ == '__main__':
dashboard = build_dashboard().build()
encoder = JSONEncoder(sort_keys=True, indent=2)
print(encoder.encode(dashboard))
```
### Unmarshaling a dashboard
```python
import json
from grafana_foundation_sdk.cog.plugins import register_default_plugins
from grafana_foundation_sdk.models.dashboard import Dashboard as DashboardModel
if __name__ == '__main__':
# Required to correctly unmarshal panels and dataqueries
register_default_plugins()
with open("dashboard.json", "r") as f:
decoded_dashboard = DashboardModel.from_json(json.load(f))
print(decoded_dashboard)
```
### Defining a custom query type
While the SDK ships with support for all core datasources and their query types,
it can be extended for private/third-party plugins.
To do so, define a type and a builder for the custom query:
```python
# src/customquery.py
from typing import Any, Optional, Self
from grafana_foundation_sdk.cog import variants as cogvariants
from grafana_foundation_sdk.cog import runtime as cogruntime
from grafana_foundation_sdk.cog import builder
class CustomQuery(cogvariants.Dataquery):
# ref_id and hide are expected on all queries
ref_id: Optional[str]
hide: Optional[bool]
# query is specific to the CustomQuery type
query: str
def __init__(self, query: str, ref_id: Optional[str] = None, hide: Optional[bool] = None):
self.query = query
self.ref_id = ref_id
self.hide = hide
def to_json(self) -> dict[str, object]:
payload: dict[str, object] = {
"query": self.query,
}
if self.ref_id is not None:
payload["refId"] = self.ref_id
if self.hide is not None:
payload["hide"] = self.hide
return payload
@classmethod
def from_json(cls, data: dict[str, Any]) -> Self:
args: dict[str, Any] = {}
if "query" in data:
args["query"] = data["query"]
if "refId" in data:
args["ref_id"] = data["refId"]
if "hide" in data:
args["hide"] = data["hide"]
return cls(**args)
def custom_query_variant_config() -> cogruntime.DataqueryConfig:
return cogruntime.DataqueryConfig(
# datasource plugin ID
identifier="custom-query",
from_json_hook=CustomQuery.from_json,
)
class CustomQueryBuilder(builder.Builder[CustomQuery]):
_internal: CustomQuery
def __init__(self, query: str):
self._internal = CustomQuery(query=query)
def build(self) -> CustomQuery:
return self._internal
def ref_id(self, ref_id: str) -> Self:
self._internal.ref_id = ref_id
return self
def hide(self, hide: bool) -> Self:
self._internal.hide = hide
return self
```
Register the type with cog, and use it as usual to build a dashboard:
```python
from grafana_foundation_sdk.builders.dashboard import Dashboard, Row
from grafana_foundation_sdk.builders.timeseries import Panel as Timeseries
from grafana_foundation_sdk.cog.encoder import JSONEncoder
from grafana_foundation_sdk.cog.plugins import register_default_plugins
from grafana_foundation_sdk.cog.runtime import register_dataquery_variant
from src.customquery import custom_query_variant_config, CustomQueryBuilder
if __name__ == '__main__':
# Required to correctly unmarshal panels and dataqueries
register_default_plugins()
# This lets cog know about the newly created query type and how to unmarshal it.
register_dataquery_variant(custom_query_variant_config())
dashboard = (
Dashboard("Custom query type")
.uid("test-custom-query")
.refresh("1m")
.time("now-30m", "now")
.with_row(Row("Overview"))
.with_panel(
Timeseries()
.title("Sample panel")
.with_target(
CustomQueryBuilder("query here")
)
)
).build()
print(JSONEncoder(sort_keys=True, indent=2).encode(dashboard))
```
### Defining a custom panel type
While the SDK ships with support for all core panels, it can be extended for
private/third-party plugins.
To do so, define a type and a builder for the custom panel's options:
```python
# src/custompanel.py
from typing import Any, Self
from grafana_foundation_sdk.cog import builder
from grafana_foundation_sdk.cog import runtime as cogruntime
from grafana_foundation_sdk.builders.dashboard import Panel as PanelBuilder
from grafana_foundation_sdk.models import dashboard
class CustomPanelOptions:
make_beautiful: bool
def __init__(self, make_beautiful: bool = False):
self.make_beautiful = make_beautiful
def to_json(self) -> dict[str, object]:
return {
"makeBeautiful": self.make_beautiful,
}
@classmethod
def from_json(cls, data: dict[str, Any]) -> Self:
args: dict[str, Any] = {}
if "makeBeautiful" in data:
args["make_beautiful"] = data["makeBeautiful"]
return cls(**args)
def custom_panel_variant_config() -> cogruntime.PanelCfgConfig:
return cogruntime.PanelCfgConfig(
# plugin ID
identifier="custom-panel",
options_from_json_hook=CustomPanelOptions.from_json,
)
class CustomPanelBuilder(PanelBuilder, builder.Builder[dashboard.Panel]):
def __init__(self):
super().__init__()
# plugin ID
self._internal.type_val = "custom-panel"
def make_beautiful(self) -> Self:
if self._internal.options is None:
self._internal.options = CustomPanelOptions()
assert isinstance(self._internal.options, CustomPanelOptions)
self._internal.options.make_beautiful = True
return self
```
Register the type with cog, and use it as usual to build a dashboard:
```python
from grafana_foundation_sdk.builders.dashboard import Dashboard, Row
from grafana_foundation_sdk.cog.encoder import JSONEncoder
from grafana_foundation_sdk.cog.plugins import register_default_plugins
from grafana_foundation_sdk.cog.runtime import register_panelcfg_variant
from src.custompanel import custom_panel_variant_config, CustomPanelBuilder
if __name__ == '__main__':
# Required to correctly unmarshal panels and dataqueries
register_default_plugins()
# This lets cog know about the newly created panel type and how to unmarshal it.
register_panelcfg_variant(custom_panel_variant_config())
dashboard = (
Dashboard("Custom panel type")
.uid("test-custom-panel")
.refresh("1m")
.time("now-30m", "now")
.with_row(Row("Overview"))
.with_panel(
CustomPanelBuilder()
.title("Sample panel")
.make_beautiful()
)
).build()
print(JSONEncoder(sort_keys=True, indent=2).encode(dashboard))
```
## Maturity
The code in this repository should be considered as "public preview". While it is used by Grafana Labs in production, it still is under active development.
> [!NOTE]
> Bugs and issues are handled solely by Engineering teams. On-call support or SLAs are not available.
## License
[Apache 2.0 License](./LICENSE)
Raw data
{
"_id": null,
"home_page": null,
"name": "grafana-foundation-sdk",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.11",
"maintainer_email": null,
"keywords": "grafana, logs, metrics, observability, sdk, traces",
"author": "Grafana Labs",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/d8/f3/8b3ec4d0dd78e1d7c05c5467243bf2aeef2fec11143fb25869ae646fd9e7/grafana_foundation_sdk-1733927908!10.1.0.tar.gz",
"platform": null,
"description": "# Grafana Foundation SDK \u2013 Python\n\nA set of tools, types and *builder libraries* for building and manipulating Grafana objects in Python.\n\n> [!NOTE]\n> This branch contains **types and builders generated for Grafana v10.1.x.**\n> Other supported versions of Grafana can be found at [this repository's root](https://github.com/grafana/grafana-foundation-sdk/).\n\n## Installing\n\n```shell\npython3 -m pip install 'grafana_foundation_sdk==1733927908!10.1.0'\n```\n\n## Example usage\n\n### Building a dashboard\n\n```python\nfrom grafana_foundation_sdk.builders.dashboard import Dashboard, Row\nfrom grafana_foundation_sdk.builders.prometheus import Dataquery as PrometheusQuery\nfrom grafana_foundation_sdk.builders.timeseries import Panel as Timeseries\nfrom grafana_foundation_sdk.cog.encoder import JSONEncoder\nfrom grafana_foundation_sdk.models.common import TimeZoneBrowser\n\ndef build_dashboard() -> Dashboard:\n builder = (\n Dashboard(\"[TEST] Node Exporter / Raspberry\")\n .uid(\"test-dashboard-raspberry\")\n .tags([\"generated\", \"raspberrypi-node-integration\"])\n\n .refresh(\"1m\")\n .time(\"now-30m\", \"now\")\n .timezone(TimeZoneBrowser)\n\n .with_row(Row(\"Overview\"))\n .with_panel(\n Timeseries()\n .title(\"Network Received\")\n .unit(\"bps\")\n .min_val(0)\n .with_target(\n PrometheusQuery()\n .expr('rate(node_network_receive_bytes_total{job=\"integrations/raspberrypi-node\", device!=\"lo\"}[$__rate_interval]) * 8')\n .legend_format(\"{{ device }}\")\n )\n )\n )\n\n return builder\n\n\nif __name__ == '__main__':\n dashboard = build_dashboard().build()\n encoder = JSONEncoder(sort_keys=True, indent=2)\n\n print(encoder.encode(dashboard))\n```\n\n### Unmarshaling a dashboard\n\n```python\nimport json\n\nfrom grafana_foundation_sdk.cog.plugins import register_default_plugins\nfrom grafana_foundation_sdk.models.dashboard import Dashboard as DashboardModel\n\n\nif __name__ == '__main__':\n # Required to correctly unmarshal panels and dataqueries\n register_default_plugins()\n\n with open(\"dashboard.json\", \"r\") as f:\n decoded_dashboard = DashboardModel.from_json(json.load(f))\n print(decoded_dashboard)\n```\n\n### Defining a custom query type\n\nWhile the SDK ships with support for all core datasources and their query types,\nit can be extended for private/third-party plugins.\n\nTo do so, define a type and a builder for the custom query:\n\n```python\n# src/customquery.py\nfrom typing import Any, Optional, Self\n\nfrom grafana_foundation_sdk.cog import variants as cogvariants\nfrom grafana_foundation_sdk.cog import runtime as cogruntime\nfrom grafana_foundation_sdk.cog import builder\n\n\nclass CustomQuery(cogvariants.Dataquery):\n # ref_id and hide are expected on all queries\n ref_id: Optional[str]\n hide: Optional[bool]\n\n # query is specific to the CustomQuery type\n query: str\n\n def __init__(self, query: str, ref_id: Optional[str] = None, hide: Optional[bool] = None):\n self.query = query\n self.ref_id = ref_id\n self.hide = hide\n\n def to_json(self) -> dict[str, object]:\n payload: dict[str, object] = {\n \"query\": self.query,\n }\n if self.ref_id is not None:\n payload[\"refId\"] = self.ref_id\n if self.hide is not None:\n payload[\"hide\"] = self.hide\n return payload\n\n @classmethod\n def from_json(cls, data: dict[str, Any]) -> Self:\n args: dict[str, Any] = {}\n\n if \"query\" in data:\n args[\"query\"] = data[\"query\"]\n if \"refId\" in data:\n args[\"ref_id\"] = data[\"refId\"]\n if \"hide\" in data:\n args[\"hide\"] = data[\"hide\"]\n\n return cls(**args)\n\n\ndef custom_query_variant_config() -> cogruntime.DataqueryConfig:\n return cogruntime.DataqueryConfig(\n # datasource plugin ID\n identifier=\"custom-query\",\n from_json_hook=CustomQuery.from_json,\n )\n\n\nclass CustomQueryBuilder(builder.Builder[CustomQuery]):\n _internal: CustomQuery\n\n def __init__(self, query: str):\n self._internal = CustomQuery(query=query)\n\n def build(self) -> CustomQuery:\n return self._internal\n\n def ref_id(self, ref_id: str) -> Self:\n self._internal.ref_id = ref_id\n\n return self\n\n def hide(self, hide: bool) -> Self:\n self._internal.hide = hide\n\n return self\n```\n\nRegister the type with cog, and use it as usual to build a dashboard:\n\n```python\nfrom grafana_foundation_sdk.builders.dashboard import Dashboard, Row\nfrom grafana_foundation_sdk.builders.timeseries import Panel as Timeseries\nfrom grafana_foundation_sdk.cog.encoder import JSONEncoder\nfrom grafana_foundation_sdk.cog.plugins import register_default_plugins\nfrom grafana_foundation_sdk.cog.runtime import register_dataquery_variant\n\nfrom src.customquery import custom_query_variant_config, CustomQueryBuilder\n\n\nif __name__ == '__main__':\n # Required to correctly unmarshal panels and dataqueries\n register_default_plugins()\n\n # This lets cog know about the newly created query type and how to unmarshal it.\n register_dataquery_variant(custom_query_variant_config())\n\n dashboard = (\n Dashboard(\"Custom query type\")\n .uid(\"test-custom-query\")\n .refresh(\"1m\")\n .time(\"now-30m\", \"now\")\n\n .with_row(Row(\"Overview\"))\n .with_panel(\n Timeseries()\n .title(\"Sample panel\")\n .with_target(\n CustomQueryBuilder(\"query here\")\n )\n )\n ).build()\n\n print(JSONEncoder(sort_keys=True, indent=2).encode(dashboard))\n```\n\n### Defining a custom panel type\n\nWhile the SDK ships with support for all core panels, it can be extended for\nprivate/third-party plugins.\n\nTo do so, define a type and a builder for the custom panel's options:\n\n```python\n# src/custompanel.py\nfrom typing import Any, Self\n\nfrom grafana_foundation_sdk.cog import builder\nfrom grafana_foundation_sdk.cog import runtime as cogruntime\nfrom grafana_foundation_sdk.builders.dashboard import Panel as PanelBuilder\nfrom grafana_foundation_sdk.models import dashboard\n\n\nclass CustomPanelOptions:\n make_beautiful: bool\n\n def __init__(self, make_beautiful: bool = False):\n self.make_beautiful = make_beautiful\n\n def to_json(self) -> dict[str, object]:\n return {\n \"makeBeautiful\": self.make_beautiful,\n }\n\n @classmethod\n def from_json(cls, data: dict[str, Any]) -> Self:\n args: dict[str, Any] = {}\n\n if \"makeBeautiful\" in data:\n args[\"make_beautiful\"] = data[\"makeBeautiful\"]\n\n return cls(**args)\n\n\ndef custom_panel_variant_config() -> cogruntime.PanelCfgConfig:\n return cogruntime.PanelCfgConfig(\n # plugin ID\n identifier=\"custom-panel\",\n options_from_json_hook=CustomPanelOptions.from_json,\n )\n\n\nclass CustomPanelBuilder(PanelBuilder, builder.Builder[dashboard.Panel]):\n def __init__(self):\n super().__init__()\n # plugin ID\n self._internal.type_val = \"custom-panel\"\n\n def make_beautiful(self) -> Self:\n if self._internal.options is None:\n self._internal.options = CustomPanelOptions()\n\n assert isinstance(self._internal.options, CustomPanelOptions)\n\n self._internal.options.make_beautiful = True\n\n return self\n```\n\nRegister the type with cog, and use it as usual to build a dashboard:\n\n```python\nfrom grafana_foundation_sdk.builders.dashboard import Dashboard, Row\nfrom grafana_foundation_sdk.cog.encoder import JSONEncoder\nfrom grafana_foundation_sdk.cog.plugins import register_default_plugins\nfrom grafana_foundation_sdk.cog.runtime import register_panelcfg_variant\n\nfrom src.custompanel import custom_panel_variant_config, CustomPanelBuilder\n\n\nif __name__ == '__main__':\n # Required to correctly unmarshal panels and dataqueries\n register_default_plugins()\n\n # This lets cog know about the newly created panel type and how to unmarshal it.\n register_panelcfg_variant(custom_panel_variant_config())\n\n dashboard = (\n Dashboard(\"Custom panel type\")\n .uid(\"test-custom-panel\")\n .refresh(\"1m\")\n .time(\"now-30m\", \"now\")\n\n .with_row(Row(\"Overview\"))\n .with_panel(\n CustomPanelBuilder()\n .title(\"Sample panel\")\n .make_beautiful()\n )\n ).build()\n\n print(JSONEncoder(sort_keys=True, indent=2).encode(dashboard))\n```\n\n## Maturity\n\nThe code in this repository should be considered as \"public preview\". While it is used by Grafana Labs in production, it still is under active development.\n\n> [!NOTE]\n> Bugs and issues are handled solely by Engineering teams. On-call support or SLAs are not available.\n\n## License\n\n[Apache 2.0 License](./LICENSE)\n",
"bugtrack_url": null,
"license": null,
"summary": "A set of tools, types and libraries for building and manipulating Grafana objects.",
"version": "1733927908!10.1.0",
"project_urls": {
"Homepage": "https://github.com/grafana/grafana-foundation-sdk",
"Issues": "https://github.com/grafana/grafana-foundation-sdk/issues",
"Repository": "https://github.com/grafana/grafana-foundation-sdk.git"
},
"split_keywords": [
"grafana",
" logs",
" metrics",
" observability",
" sdk",
" traces"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "81790c210e324dcea0145adca32a840f1b98a8273f22e8e0a23a8c5814f607fb",
"md5": "e1608c8e0733b5db117303cd196f9b37",
"sha256": "b95798ad227aeaba0189a787bb257e52bce10a4bfa95b2f56d45bbcba82867c3"
},
"downloads": -1,
"filename": "grafana_foundation_sdk-1733927908!10.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "e1608c8e0733b5db117303cd196f9b37",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.11",
"size": 302773,
"upload_time": "2024-12-11T14:53:53",
"upload_time_iso_8601": "2024-12-11T14:53:53.775680Z",
"url": "https://files.pythonhosted.org/packages/81/79/0c210e324dcea0145adca32a840f1b98a8273f22e8e0a23a8c5814f607fb/grafana_foundation_sdk-1733927908!10.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "d8f38b3ec4d0dd78e1d7c05c5467243bf2aeef2fec11143fb25869ae646fd9e7",
"md5": "d9c1804e4175abb87cd1894cf9c3ad2b",
"sha256": "0d59354be3783319b48e5499b8e5e540dafa6e7c8c85b85c9f62ce5f65e5f4ab"
},
"downloads": -1,
"filename": "grafana_foundation_sdk-1733927908!10.1.0.tar.gz",
"has_sig": false,
"md5_digest": "d9c1804e4175abb87cd1894cf9c3ad2b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.11",
"size": 370461,
"upload_time": "2024-12-11T14:55:17",
"upload_time_iso_8601": "2024-12-11T14:55:17.976194Z",
"url": "https://files.pythonhosted.org/packages/d8/f3/8b3ec4d0dd78e1d7c05c5467243bf2aeef2fec11143fb25869ae646fd9e7/grafana_foundation_sdk-1733927908!10.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-11 14:55:17",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "grafana",
"github_project": "grafana-foundation-sdk",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "Babel",
"specs": [
[
"==",
"2.15.0"
]
]
},
{
"name": "brotlicffi",
"specs": [
[
"==",
"1.1.0.0"
]
]
},
{
"name": "certifi",
"specs": [
[
"==",
"2024.7.4"
]
]
},
{
"name": "cffi",
"specs": [
[
"==",
"1.17.1"
]
]
},
{
"name": "charset-normalizer",
"specs": [
[
"==",
"3.3.2"
]
]
},
{
"name": "click",
"specs": [
[
"==",
"8.1.7"
]
]
},
{
"name": "colorama",
"specs": [
[
"==",
"0.4.6"
]
]
},
{
"name": "ghp-import",
"specs": [
[
"==",
"2.1.0"
]
]
},
{
"name": "idna",
"specs": [
[
"==",
"3.7"
]
]
},
{
"name": "importlib_metadata",
"specs": [
[
"==",
"7.1.0"
]
]
},
{
"name": "Jinja2",
"specs": [
[
"==",
"3.1.4"
]
]
},
{
"name": "Markdown",
"specs": [
[
"==",
"3.7"
]
]
},
{
"name": "MarkupSafe",
"specs": [
[
"==",
"2.1.5"
]
]
},
{
"name": "mergedeep",
"specs": [
[
"==",
"1.3.4"
]
]
},
{
"name": "mkdocs",
"specs": [
[
"==",
"1.6.1"
]
]
},
{
"name": "mkdocs-get-deps",
"specs": [
[
"==",
"0.2.0"
]
]
},
{
"name": "mkdocs-material",
"specs": [
[
"==",
"9.5.39"
]
]
},
{
"name": "mkdocs-material-extensions",
"specs": [
[
"==",
"1.3.1"
]
]
},
{
"name": "mkdocs-nav-weight",
"specs": [
[
"==",
"0.2.0"
]
]
},
{
"name": "mypy",
"specs": [
[
"==",
"1.10.0"
]
]
},
{
"name": "mypy-extensions",
"specs": [
[
"==",
"1.0.0"
]
]
},
{
"name": "packaging",
"specs": [
[
"==",
"24.1"
]
]
},
{
"name": "paginate",
"specs": [
[
"==",
"0.5.7"
]
]
},
{
"name": "pathspec",
"specs": [
[
"==",
"0.12.1"
]
]
},
{
"name": "platformdirs",
"specs": [
[
"==",
"4.2.2"
]
]
},
{
"name": "pycparser",
"specs": [
[
"==",
"2.22"
]
]
},
{
"name": "Pygments",
"specs": [
[
"==",
"2.18.0"
]
]
},
{
"name": "pymdown-extensions",
"specs": [
[
"==",
"10.11.2"
]
]
},
{
"name": "python-dateutil",
"specs": [
[
"==",
"2.9.0.post0"
]
]
},
{
"name": "PyYAML",
"specs": [
[
"==",
"6.0.2"
]
]
},
{
"name": "pyyaml_env_tag",
"specs": [
[
"==",
"0.1"
]
]
},
{
"name": "regex",
"specs": [
[
"==",
"2024.5.15"
]
]
},
{
"name": "requests",
"specs": [
[
"==",
"2.32.3"
]
]
},
{
"name": "six",
"specs": [
[
"==",
"1.16.0"
]
]
},
{
"name": "toml",
"specs": [
[
"==",
"0.10.2"
]
]
},
{
"name": "typing_extensions",
"specs": [
[
"==",
"4.12.2"
]
]
},
{
"name": "urllib3",
"specs": [
[
"==",
"2.2.2"
]
]
},
{
"name": "watchdog",
"specs": [
[
"==",
"4.0.1"
]
]
},
{
"name": "zipp",
"specs": [
[
"==",
"3.19.2"
]
]
}
],
"lcname": "grafana-foundation-sdk"
}