holoviz-mcp 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. holoviz_mcp/__init__.py +18 -0
  2. holoviz_mcp/apps/__init__.py +1 -0
  3. holoviz_mcp/apps/configuration_viewer.py +116 -0
  4. holoviz_mcp/apps/holoviz_get_best_practices.py +173 -0
  5. holoviz_mcp/apps/holoviz_search.py +319 -0
  6. holoviz_mcp/apps/hvplot_get_docstring.py +255 -0
  7. holoviz_mcp/apps/hvplot_get_signature.py +252 -0
  8. holoviz_mcp/apps/hvplot_list_plot_types.py +83 -0
  9. holoviz_mcp/apps/panel_get_component.py +496 -0
  10. holoviz_mcp/apps/panel_get_component_parameters.py +467 -0
  11. holoviz_mcp/apps/panel_list_components.py +311 -0
  12. holoviz_mcp/apps/panel_list_packages.py +71 -0
  13. holoviz_mcp/apps/panel_search_components.py +312 -0
  14. holoviz_mcp/cli.py +75 -0
  15. holoviz_mcp/client.py +94 -0
  16. holoviz_mcp/config/__init__.py +29 -0
  17. holoviz_mcp/config/config.yaml +178 -0
  18. holoviz_mcp/config/loader.py +316 -0
  19. holoviz_mcp/config/models.py +208 -0
  20. holoviz_mcp/config/resources/best-practices/holoviews.md +423 -0
  21. holoviz_mcp/config/resources/best-practices/hvplot.md +465 -0
  22. holoviz_mcp/config/resources/best-practices/panel-material-ui.md +318 -0
  23. holoviz_mcp/config/resources/best-practices/panel.md +562 -0
  24. holoviz_mcp/config/schema.json +228 -0
  25. holoviz_mcp/holoviz_mcp/__init__.py +1 -0
  26. holoviz_mcp/holoviz_mcp/data.py +970 -0
  27. holoviz_mcp/holoviz_mcp/models.py +21 -0
  28. holoviz_mcp/holoviz_mcp/pages_design.md +407 -0
  29. holoviz_mcp/holoviz_mcp/server.py +220 -0
  30. holoviz_mcp/hvplot_mcp/__init__.py +1 -0
  31. holoviz_mcp/hvplot_mcp/server.py +146 -0
  32. holoviz_mcp/panel_mcp/__init__.py +17 -0
  33. holoviz_mcp/panel_mcp/data.py +319 -0
  34. holoviz_mcp/panel_mcp/models.py +124 -0
  35. holoviz_mcp/panel_mcp/server.py +443 -0
  36. holoviz_mcp/py.typed +0 -0
  37. holoviz_mcp/serve.py +36 -0
  38. holoviz_mcp/server.py +86 -0
  39. holoviz_mcp/shared/__init__.py +1 -0
  40. holoviz_mcp/shared/extract_tools.py +74 -0
  41. holoviz_mcp/thumbnails/configuration_viewer.png +0 -0
  42. holoviz_mcp/thumbnails/holoviz_get_best_practices.png +0 -0
  43. holoviz_mcp/thumbnails/holoviz_search.png +0 -0
  44. holoviz_mcp/thumbnails/hvplot_get_docstring.png +0 -0
  45. holoviz_mcp/thumbnails/hvplot_get_signature.png +0 -0
  46. holoviz_mcp/thumbnails/hvplot_list_plot_types.png +0 -0
  47. holoviz_mcp/thumbnails/panel_get_component.png +0 -0
  48. holoviz_mcp/thumbnails/panel_get_component_parameters.png +0 -0
  49. holoviz_mcp/thumbnails/panel_list_components.png +0 -0
  50. holoviz_mcp/thumbnails/panel_list_packages.png +0 -0
  51. holoviz_mcp/thumbnails/panel_search_components.png +0 -0
  52. holoviz_mcp-0.4.0.dist-info/METADATA +216 -0
  53. holoviz_mcp-0.4.0.dist-info/RECORD +56 -0
  54. holoviz_mcp-0.4.0.dist-info/WHEEL +4 -0
  55. holoviz_mcp-0.4.0.dist-info/entry_points.txt +2 -0
  56. holoviz_mcp-0.4.0.dist-info/licenses/LICENSE.txt +30 -0
@@ -0,0 +1,562 @@
1
+ ---
2
+ name: panel-development
3
+ description: Build interactive web applications and dashboards with HoloViz Panel. Create reactive, component-based UIs with widgets, layouts, templates, and real-time updates. Use when developing data apps, monitoring dashboards, data exploration tools, or any interactive Python web application. Supports file uploads, streaming data, multi-page apps, and integration with HoloViews, hvPlot, and the rest of the HoloViz and PyData ecosystem.
4
+ metadata:
5
+ version: "1.0.0"
6
+ author: holoviz
7
+ category: web-development
8
+ difficulty: intermediate
9
+ ---
10
+
11
+ # Panel Development Skill
12
+
13
+ This document provides best practices for developing dashboards and data apps with HoloViz Panel in Python .py files.
14
+
15
+ Please develop as an **Expert Python Developer** developing advanced data-driven, analytics and testable dashboards and applications would do. Keep the code short, concise, documented, testable and professional.
16
+
17
+ ## Dependencies
18
+
19
+ Core dependencies provided with the `panel` Python package:
20
+
21
+ - **panel**: Core application framework
22
+ - **param**: A declarative approach to creating classes with typed, validated, and documented parameters. Fundamental to Panel's reactive programming model.
23
+
24
+ Optional panel-extensions:
25
+
26
+ - **panel-material-ui**: Modern Material UI components. To replace the panel native widgets within the next two years.
27
+ - **panel-graphic-walker**: Modern Tableau like interface. Can offload computations to the server and thus scale to large datasets.
28
+
29
+ Optional dependencies from the HoloViz Ecosystem:
30
+
31
+ - **colorcet**: Perceptually uniform colormaps collection. Best for: scientific visualization requiring accurate color representation, avoiding rainbow colormaps, accessible color schemes. Integrates with hvPlot, HoloViews, Matplotlib, Bokeh.
32
+ - **datashader**: Renders large datasets (millions+ points) into images for visualization. Best for: big data visualization, geospatial datasets, scatter plots with millions of points, heatmaps of dense data. Requires hvPlot or HoloViews as frontend.
33
+ - **geoviews**: Geographic data visualization with map projections and tile sources. Best for: geographic/geospatial plots, map-based dashboards, when you need coordinate systems and projections. Built on HoloViews, works seamlessly with hvPlot.
34
+ - **holoviews**: Declarative data visualization library with composable elements. Best for: complex multi-layered plots, advanced interactivity (linked brushing, selection), when you need fine control over plot composition, scientific visualizations. More powerful but steeper learning curve than hvPlot.
35
+ - **holoviz-mcp**: Model Context Protocol server for HoloViz ecosystem. Provides access to detailed documentation, component search and best practices.
36
+ - **hvplot**: High-level plotting API with Pandas `.plot()`-like syntax. Best for: quick exploratory visualizations, interactive plots from DataFrames/Xarray, when you want interactivity without verbose code. Built on HoloViews.
37
+ - **hvsampledata**: Shared datasets for the HoloViz projects.
38
+
39
+ Optional dependencies from the wider PyData Ecosystem:
40
+
41
+ - **altair**: Declarative, grammar-of-graphics visualization library. Best for: statistical visualizations, interactive exploratory charts, when you need Vega-Lite's extensive chart gallery. Works well with Pandas/Polars DataFrames.
42
+ - **dask**: Parallel computing library for scaling Pandas DataFrames beyond memory. Best for: processing datasets larger than RAM, parallel computation across multiple cores/machines, lazy evaluation workflows.
43
+ - **duckdb**: High-performance analytical SQL database. Best for: fast SQL queries on DataFrames, aggregations on large datasets, when you need SQL interface, OLAP-style analytics. Much faster than Pandas for analytical queries.
44
+ - **matplotlib**: Low-level, highly customizable plotting library. Best for: publication-quality static plots, fine-grained control over every aspect of visualization, scientific plots, when you need pixel-perfect control.
45
+ - **pandas**: Industry-standard DataFrame library for tabular data. Best for: data cleaning, transformation, time series analysis, datasets that fit in memory. The default choice for most data work.
46
+ - **Plotly**: Interactive, publication-quality visualization library. Best for: 3D plots, complex interactive charts, animations, when you need hover tooltips and interactivity. Works well with Dash and Panel.
47
+ - **polars**: Modern, fast DataFrame library written in Rust. Best for: high-performance data processing, datasets that fit in memory but need speed, when you need lazy evaluation, better memory efficiency than Pandas.
48
+ - **xarray**: N-dimensional labeled arrays and datasets. Best for: multidimensional scientific data (climate, satellite imagery), data with multiple dimensions and coordinates, NetCDF/HDF5 files, geospatial raster data.
49
+ - **watchfiles**: Enables high performance file watching and autoreload for the panel server.
50
+
51
+ ## Common Use Cases
52
+
53
+ 1. **Real-time Monitoring Dashboards**: Live metrics and KPI displays
54
+ 2. **Data Exploration Tools**: Interactive data analysis applications
55
+ 3. **Configuration Interfaces**: Complex multi-step configuration UIs
56
+ 4. **Data Input Applications**: Validated form-based data collection
57
+ 5. **Report Viewers**: Interactive report generation and browsing
58
+ 6. **Administrative Interfaces**: Internal tools for data management
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ pip install panel watchfiles hvplot hvsampledata
64
+ ```
65
+
66
+ For development in .py files DO always include watchfiles for hotreload.
67
+
68
+ ## Best Practice Hello World App
69
+
70
+ Let's describe our best practices via a basic Hello World App:
71
+
72
+ ```python
73
+ # DO import panel as pn
74
+ import panel as pn
75
+ import param
76
+
77
+ # DO always run pn.extension
78
+ # DO remember to add any imports needed by panes, e.g. pn.extension("tabulator", "plotly", ...)
79
+ # DON'T add "bokeh" as an extension. It is not needed.
80
+ # Do use throttled=True when using slider unless you have a specific reason not to
81
+ pn.extension(throttled=True)
82
+
83
+ # DO organize functions to extract data separately as your app grows. Eventually in a separate data.py file.
84
+ # DO use caching to speed up the app, e.g. for expensive data loading or processing that would return the same result given same input arguments.
85
+ # DO add a ttl (time to live argument) for expensive data loading that changes over time
86
+ @pn.cache(max_items=3)
87
+ def extract(n=5):
88
+ return "Hello World" + "⭐" * n
89
+
90
+ text = extract()
91
+ text_len = len(text)
92
+
93
+ # DO organize functions to transform data separately as your app grows. Eventually in a separate transformations.py file
94
+ # DO add caching to speed up expensive data transformations
95
+ @pn.cache(max_items=3)
96
+ def transform(data: str, count: int=5)->str:
97
+ count = min(count, len(data))
98
+ return data[:count]
99
+
100
+ # DO organize functions to create plots separately as your app grows. Eventually in a separate plots.py file.
101
+ # DO organize custom components and views separately as your app grows. Eventually in separate components.py or views.py file(s).
102
+ # DO use param.Parameterized, pn.viewable.Viewer or similar approach to create new components and apps with state and reactivity
103
+ class HelloWorld(pn.viewable.Viewer):
104
+ # DO define parameters to hold state and drive the reactivity
105
+ characters = param.Integer(default=text_len, bounds=(0, text_len), doc="Number of characters to display")
106
+
107
+ def __init__(self, **params):
108
+ super().__init__(**params)
109
+
110
+ # DO use sizing_mode="stretch_width" for components unless "fixed" or other sizing_mode is specifically needed
111
+ with pn.config.set(sizing_mode="stretch_width"):
112
+ # DO create widgets using `.from_param` method
113
+ self._characters_input = pn.widgets.IntSlider.from_param(self.param.characters, margin=(10,20))
114
+
115
+ # DO Collect input widgets into horizontal, columnar layout unless other layout is specifically needed
116
+ self._inputs = pn.Column(self._characters_input, max_width=300)
117
+
118
+ # CRITICAL: Create panes ONCE with reactive content
119
+ # DON'T recreate panes and layouts in @param.depends methods - causes flickering!
120
+ # DO bind reactive methods/functions to panes for smooth updates
121
+ self._output_pane = pn.pane.Markdown(
122
+ self.model, # Reactive method reference
123
+ sizing_mode="stretch_width"
124
+ )
125
+
126
+ # DO collect output components into some layout like Column, Row, FlexBox or Grid depending on use case
127
+ self._outputs = pn.Column(self._output_pane)
128
+
129
+ # DO collect all of your components into a combined layout useful for displaying in notebooks etc.
130
+ self._panel = pn.Row(self._inputs, self._outputs)
131
+
132
+ # DO use caching to speed up bound methods that are expensive to compute or load data and return the same result for a given state of the class.
133
+ # DO add a ttl (time to live argument) for expensive data loading that changes over time.
134
+ @pn.cache(max_items=3)
135
+ # DO prefer .depends over .bind over .rx for reactivity methods on Parameterized classes as it can be typed and documented
136
+ # DON'T use `watch=True` or `.watch(...)` methods to update UI components directly.
137
+ # DO use `watch=True` or `.watch(...)` for updating the state parameters or triggering side effects like saving files or sending email.
138
+ @param.depends("characters")
139
+ def model(self):
140
+ # CRITICAL: Return ONLY the content, NOT the layout/pane
141
+ # The pane was created once in __init__, this just updates its content
142
+ return transform(text, self.characters)
143
+
144
+ # DO use `watch=True` or `.watch(...)` for updating the state parameters or triggering side effects like saving files or sending email.
145
+ @param.depends("characters", watch=True)
146
+ def _inform_user(self):
147
+ print(f"User selected to show {self.characters} characters.")
148
+
149
+ # DO provide a method for displaying the component in a notebook setting, i.e. without using a Template or other element that cannot be displayed in a notebook setting.
150
+ def __panel__(self):
151
+ return self._panel
152
+
153
+ # DO provide a method to create a .servable app
154
+ @classmethod
155
+ def create_app(cls, **params):
156
+ instance = cls(**params)
157
+ # DO use a Template or similar page layout for served apps
158
+ template = pn.template.FastListTemplate(
159
+ # DO provide a title for the app
160
+ title="Hello World App",
161
+ # DO provide optional image, optional app description, optional navigation menu, input widgets, optional documentation and optional links in the sidebar
162
+ # DO provide as list of components or a list of single horizontal layout like Column as the sidebar by default is 300 px wide
163
+ sidebar=[instance._inputs],
164
+ # DO provide a list of layouts and output components in the main area of the app.
165
+ # DO use Grid or FlexBox layouts for complex dashboard layouts instead of combination Rows and Columns.
166
+ main=[instance._outputs],
167
+ )
168
+ return template
169
+
170
+ # DON'T provide a `if __name__ == "__main__":` method to serve the app with `python`
171
+ # DO provide a method to serve the app with `panel serve`
172
+ if pn.state.served:
173
+ # Mark components to be displayed in the app with .servable()
174
+ HelloWorld.create_app().servable()
175
+ ```
176
+
177
+ DO serve the app with
178
+
179
+ ```bash
180
+ panel serve path_to_this_file.py --show --dev
181
+ ```
182
+
183
+ DON'T serve with `python path_to_this_file.py`.
184
+
185
+ ## Best Practice Hello World Tests
186
+
187
+ With panel you can easily create tests to test user behaviour without having to write client side tests.
188
+
189
+ DO always create separate tests in the `tests` folder:
190
+
191
+ ```python
192
+ # DO put tests in a separate test file.
193
+ # DO always test that the reactivity works as expected
194
+ def test_characters_reactivity():
195
+ """
196
+ Test characters reactivity.
197
+ """
198
+ # DO test the default values of bound
199
+ hello_world = HelloWorld()
200
+ # DO test the reactivity of bound methods when parameters change
201
+ assert hello_world.model() == text[:hello_world.characters]
202
+ hello_world.characters = 5
203
+ assert hello_world.model() == text[:5]
204
+ hello_world.characters = 3
205
+ assert hello_world.model() == text[:3]
206
+ ```
207
+
208
+ DO run the tests with:
209
+
210
+ ```bash
211
+ pytest tests/path/to/test_file.py
212
+ ```
213
+
214
+ DO fix any errors identified.
215
+
216
+ ## Key Patterns
217
+
218
+ ### Parameter-Driven Architecture
219
+
220
+ - DO use `param.Parameterized` or `pn.viewable.Viewer` classes to organize and manage state
221
+ - DO create widgets with `.from_param()` method
222
+ - DO use `@param.depends()` for reactive methods
223
+ - DO use `@param.depends(..., watch=True)` to update parameter/ state values and for side-effects like sending an email.
224
+ - DO group related parameters in separate `Parameterized` or `Viewable` classes
225
+
226
+
227
+ ```python
228
+ # ❌ AVOID: Updating panes and other components directly. This makes it hard to reason about application flow and state
229
+ @param.depends('value', watch=True)
230
+ def update_plot(self):
231
+ self.output_pane.object = transform(text, self.characters)
232
+ ```
233
+
234
+ ### Create Static Layout with Reactive Content (CRITICAL)
235
+
236
+ **The Golden Rule: Create layout structure ONCE, update content REACTIVELY**
237
+
238
+ This pattern eliminates flickering and creates professional Panel applications:
239
+
240
+ ```python
241
+ # ✅ CORRECT: Create panes ONCE in __init__, bind reactive content
242
+ class Dashboard(pn.viewable.Viewer):
243
+ filter_value = param.String(default="all")
244
+
245
+ chart = param.Parameter()
246
+
247
+ def __init__(self, **params):
248
+ super().__init__(**params)
249
+
250
+ # 1. Create static panes with reactive content
251
+ self._summary_pane = pn.pane.Markdown(self._summary_text)
252
+ self._chart_pane = pn.pane.HoloViews(self.param.chart)
253
+
254
+ # 2. Create static layout structure
255
+ self._layout = pn.Column(
256
+ "# Dashboard", # Static title
257
+ self._summary_pane, # Reactive content
258
+ self._chart_pane, # Reactive content
259
+ )
260
+
261
+ # ✅ Good: Reactive content method
262
+ # Will be run multiple times when filter_value updates if multiple panes or reactive functions depend on the _summary_text method
263
+ @param.depends("filter_value")
264
+ def _summary_text(self):
265
+ # Returns string content only, NOT a pane
266
+ return f"**Count**: {len(self._get_data())}"
267
+
268
+ # ✅ Good: Reactive update of chart parameter
269
+ # Will be run only one time when filter_value updates - even if multiple panes or reactive functions depend on the chart value
270
+ @param.depends("filter_value", watch=True, on_init=True)
271
+ def _update_chart(self):
272
+ # updates the chart object only, NOT a pane
273
+ self.chart = self._get_data().hvplot.bar()
274
+
275
+ def __panel__(self):
276
+ return self._layout
277
+
278
+ # ❌ WRONG: Recreating layout in @param.depends - causes flickering!
279
+ class BadDashboard(pn.viewable.Viewer):
280
+ filter_value = param.String(default="all")
281
+
282
+ @param.depends("filter_value")
283
+ def view(self):
284
+ # DON'T recreate panes/layouts on every parameter change!
285
+ return pn.Column(
286
+ "# Dashboard",
287
+ pn.pane.Markdown(f"**Count**: {len(self._get_data())}"),
288
+ pn.pane.HoloViews(self._get_data().hvplot.bar()),
289
+ )
290
+ ```
291
+
292
+ **Why This Matters:**
293
+
294
+ - ✅ Smooth updates without layout reconstruction
295
+ - ✅ No flickering - seamless transitions
296
+ - ✅ Better performance - avoids unnecessary DOM updates
297
+ - ✅ Professional UX
298
+
299
+ **Key Rules:**
300
+
301
+ 1. DO create main layout structure and panes ONCE in `__init__`
302
+ 2. DO bind panes to reactive methods or parameters (DON'T recreate them)
303
+ 3. Reactive methods should return CONTENT only (strings, plots, dataframes), NOT panes/layouts
304
+ 4. Use `@param.depends` for reactive methods that update pane content
305
+
306
+ ### Widgets
307
+
308
+ Use
309
+
310
+ - `pn.widgets.IntSlider`, `pn.widgets.Select`, `pn.widgets.DateRangeSlider` and other widgets for input
311
+ - `pn.widgets.Tabulator` to display tabular data like DataFrames
312
+
313
+ ### Panes
314
+
315
+ Use panes to display data:
316
+
317
+ - `pn.pane.Markdown` and `pn.pane.HTML` to display strings
318
+ - `pn.pane.HoloViews`, `pn.pane.Plotly`, `pn.pane.Matplotlib` or `pn.pane.ECharts` to display plots
319
+
320
+ ### Layouts
321
+
322
+ Use layouts to layout components:
323
+
324
+ - DO use `pn.Column`, `pn.Row`, `pn.Tabs`, `pn.Accordion` for layouts
325
+
326
+ ### Templates
327
+
328
+ - DO use `pn.template.FastListTemplate` or other templates for served apps:
329
+
330
+ ```python
331
+ template = pn.template.FastListTemplate(
332
+ title="Hello World App",
333
+ sidebar=[instance._inputs],
334
+ main=[instance._outputs],
335
+ main_layout=None,
336
+ )
337
+ ```
338
+
339
+ - In the `sidebar`, DO use the order: 1) optional logo, 2) description, 3) input widgets, 4) documentation
340
+ - Do set `main_layout=None` for a modern styling.
341
+
342
+ ### Responsive Design
343
+
344
+ - DO use `sizing_mode="stretch_width"` by default:
345
+
346
+ ```python
347
+ with pn.config.set(sizing_mode="stretch_width"):
348
+ character_input = pn.widgets...
349
+ output_pane = pn.pane....
350
+ ```
351
+
352
+ - DO use `FlexBox`, `GridSpec` or `GridBox` for complex, responsive grid layouts
353
+ - DO set appropriate `min_width`, `min_height`, `max_width` and `max_height` to prevent layout collapse
354
+
355
+ ### Extensions
356
+
357
+ DO remember to add extensions like "tabulator", "plotly" etc. to `pn.extension` to make sure their Javascript is loaded:
358
+
359
+ ```python
360
+ # ✅ Good
361
+ pn.extension("tabulator", "plotly")
362
+ ```
363
+
364
+ DON'T add "bokeh". It's already loaded:
365
+
366
+ ```python
367
+ # ❌ Bad
368
+ pn.extension("bokeh")
369
+ ```
370
+
371
+ ### Servable()
372
+
373
+ DO make the main component `.servable()` to include it in the served app and use `pn.state.served` to run the main method when the app is panel serve'd.
374
+
375
+ ```python
376
+ # ✅ Correct:
377
+ if pn.state.served:
378
+ main().servable()
379
+
380
+ # ❌ Incorrect:
381
+ if __name__ == "__main__":
382
+ main().servable()
383
+
384
+ # ❌ Don't: Works, but not how we want to serve the app:
385
+ if __name__ == "__main__":
386
+ main().show()
387
+ ```
388
+
389
+ ### Performance Optimization
390
+
391
+ - **Defer load**: Defer load to after the app is shown to the user: `pn.extension(defer_load=True, loading_indicator=True, ...)`
392
+ - **Lazy-load components** using Tabs or Accordion for heavy content
393
+ - **Use caching** with `@pn.cache` decorator for expensive computations
394
+ - **Use async/await**: Implement asynchronous patterns for I/O operations
395
+ - **Use faster frameworks**: Replace slower Pandas with faster Polars or DuckDB
396
+ - **Offload to threads**: Consider using threading for CPU-intensive tasks
397
+ - **Offload to external processes**: Consider offloading heavy computations to external processes like databases, scheduled (airflow) jobs, REST apis etc.
398
+ - **Profile callbacks** with `@pn.io.profiler` to identify bottlenecks
399
+
400
+ If you experience memory issues, make sure to:
401
+
402
+ - **Limit history**: Cap data history sizes in streaming applications
403
+ - **Clear caches**: Periodically call `pn.state.clear_caches()`
404
+ - **Restart periodically**: Schedule application restarts for long-running production apps
405
+ - **Profile memory**: Use memory profilers (memory_profiler, tracemalloc) to identify leaks
406
+
407
+ ### Code Organization
408
+
409
+ - **Separate concerns**: Keep UI code separate from business logic using Param classes
410
+ - **Create reusable components**: Extract common patterns into functions or classes
411
+ - **Use templates** for consistent application structure across pages
412
+ - **Organize modules**: Group related components and utilities
413
+ - **Document parameters**: Add clear docstrings to Parameterized classes
414
+
415
+ ## Workflows
416
+
417
+ ### Lookup additional information
418
+
419
+ - If the HoloViz MCP server is available DO use the HoloViz MCP server to access relevant documentation including how-to guides, component reference guides, and detailed component docstrings and parameter information.
420
+ - If the HoloViz MCP server is not available, DO search the web. For example searching the Panel website for `Tabulator` related information via [https://panel.holoviz.org/search.html?q=Tabulator](https://panel.holoviz.org/search.html?q=Tabulator) url.
421
+
422
+ ### Test the app with pytest
423
+
424
+ DO add tests to the `tests` folder and run them with `pytest tests/path/to/test_file.py`.
425
+
426
+ - DO structure your code with Parameterized components, so that reactivity and user interactions can be tested easily.
427
+ - DO separate UI logic from business logic to enable unit testing
428
+ - DO separate data extraction, data transformation, plots generation, custom components and views, styles etc. to enable unit testing as your app grows
429
+ - DO fix any test errors and rerun the tests
430
+ - DO run the tests and fix errors before serving the app and asking the user to run manual tests
431
+
432
+ ### Serve the App with panel serve
433
+
434
+ DO always start and keep running a development server `panel serve path_to_file.py --dev --show` with hot reload while developing!
435
+
436
+ - Due to `--show` flag, a browser tab will automatically open showing your app.
437
+ - Due to `--dev` flag, the panel server and app will automatically reload if you change the code.
438
+ - The app will be served at http://localhost:5006/.
439
+ - DO make sure the correct virtual environment is activated before serving the app.
440
+ - DO fix any errors that show up in the terminal. Consider adding new tests to ensure they don't happen again.
441
+ - DON'T stop or restart the server after changing the code. The app will automatically reload.
442
+ - If you see 'Cannot start Bokeh server, port 5006 is already in use' in the terminal, DO serve the app on another port with `--port {port-number}` flag.
443
+ - DO remind the user to test the application on multiple screen sizes (desktop, tablet, mobile)
444
+ - DON'T use legacy `--autoreload` flag
445
+ - DON't run `python path_to_file.py` to test or serve the app.
446
+
447
+ ## Quick Reference
448
+
449
+ ### Widget Creation
450
+ ```python
451
+ # ✅ Good: Parameter-driven
452
+ widget = pn.widgets.Select.from_param(self.param.model_type, name="Model Type")
453
+
454
+ # ❌ Avoid: Manual management with links
455
+ widget = pn.widgets.Select(options=['A', 'B'], value='A')
456
+ widget.link(self, value='model_type') # Hard to reason about
457
+ ```
458
+
459
+ ### Reactive Updates Pattern
460
+
461
+ ```python
462
+ # ✅ BEST: Static pane with reactive content (for classes)
463
+ class MyComponent(pn.viewable.Viewer):
464
+ value = param.Number(default=10)
465
+
466
+ def __init__(self, **params):
467
+ super().__init__(**params)
468
+ self._plot_pane = pn.pane.Matplotlib(self._create_plot)
469
+
470
+ @param.depends('value')
471
+ def _create_plot(self):
472
+ return create_plot(self.value) # Returns plot only, not pane
473
+
474
+ # ✅ GOOD: pn.bind for functions
475
+ slider = pn.widgets.IntSlider(value=10)
476
+ plot_pane = pn.pane.Matplotlib(pn.bind(create_plot, slider))
477
+
478
+ # ❌ AVOID: Recreating panes and other components directly. This causes flickering.
479
+ @param.depends('value')
480
+ def view(self):
481
+ return pn.pane.Matplotlib(create_plot(self.value)) # DON'T!
482
+
483
+ # ❌ AVOID: Updating panes and other components directly. This makes it hard to reason about application flow and state
484
+ @param.depends('value', watch=True)
485
+ def update_plot(self):
486
+ self.plot_pane.object = create_plot(self.value)
487
+ ```
488
+
489
+ ### Static Components Pattern
490
+
491
+ ```python
492
+ # DO: Create static layout with reactive content
493
+ def _get_kpi_card(self):
494
+ return pn.pane.HTML(
495
+ pn.Column(
496
+ "📊 Key Performance Metrics",
497
+ self.kpi_value # Reactive reference
498
+ ),
499
+ styles={"padding": "20px", "border": "1px solid #ddd"},
500
+ sizing_mode="stretch_width"
501
+ )
502
+
503
+ @param.depends("characters")
504
+ def kpi_value(self):
505
+ return f"The kpi is {self.characters}"
506
+ ```
507
+
508
+ ## Other Guidance
509
+
510
+ ### CheckButtonGroup
511
+
512
+ - DO arrange vertically when displaying `CheckButtonGroup` in a sidebar `CheckButtonGroup(..., vertical=True)`.
513
+
514
+ ### Tabulator
515
+
516
+ - DO set `Tabulator.disabled=True` unless you would like the user to be able to edit the table.
517
+
518
+ ### Bind
519
+
520
+ - DON't bind a function to nothing: `pn.bind(some_func)`. Just run the function instead `some_func()`.
521
+
522
+ ## Plotting
523
+
524
+ - DO use bar charts over pie Charts.
525
+
526
+ ### HoloViews/hvPlot
527
+
528
+ - DO let Panel control the renderer theme
529
+ - DON'T set `hv.renderer('bokeh').theme = 'dark_minimal'`
530
+
531
+ DO follow the hvplot and holoviews best practice guides!
532
+
533
+ ### Plotly
534
+
535
+ Do set the template (theme) depending on the `theme` of the app.
536
+
537
+ ```python
538
+ def create_plot(self) -> go.Figure:
539
+ fig = ...
540
+ template = "plotly_dark" if pn.state.theme=="dark" else "plotly_white"
541
+ fig.update_layout(
542
+ template=template, # Change template to align with the theme
543
+ paper_bgcolor='rgba(0,0,0,0)', # Change to transparent background to align with the app background
544
+ plot_bgcolor='rgba(0,0,0,0)' # Change to transparent background to align with the app background
545
+ )
546
+ return fig
547
+ ```
548
+
549
+ ### Date time widgets
550
+
551
+ When comparing to date or time values to Pandas series convert to `Timestamp`:
552
+
553
+ ```python
554
+ start_date, end_date = self.date_range
555
+ # DO convert date objects to pandas Timestamp for proper comparison
556
+ start_date = pd.Timestamp(start_date)
557
+ end_date = pd.Timestamp(end_date)
558
+ filtered = filtered[
559
+ (filtered['date'] >= start_date) &
560
+ (filtered['date'] <= end_date)
561
+ ]
562
+ ```