Skip to content

Commit ba2305a

Browse files
committed
large set of changes to add Jupyter kernel
1 parent 6fd394f commit ba2305a

File tree

21 files changed

+3332
-391
lines changed

21 files changed

+3332
-391
lines changed

README.md

Lines changed: 74 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,30 +26,33 @@ of week, days of month and months). An event trigger specifies the event type, a
2626
Python trigger test based on the event data that runs the Python function if true.
2727

2828
Pyscript implements a Python interpreter using the ast parser output, in a fully async manner. That
29-
allows several of the "magic" features to be implemented in a seamless Pythonesque manner, such as
29+
allows several of the "magic" features to be implemented in a seamless Pythonic manner, such as
3030
binding of variables to states and functions to services. Pyscript supports imports, although the
3131
valid import list is restricted for security reasons. Pyscript does not (yet) support some language
3232
features like declaring new objects, `try/except`, generators and some syntax like `with` and
3333
`yield`. Pyscript provides a handful of additional built-in functions that connect to HASS
3434
features, like logging, accessing state variables as strings (if you need to compute their names
3535
dynamically), sleeping and waiting for triggers.
3636

37+
Pyscript also provides a kernel that interfaces with the Jupyter front-ends (eg, notebook, console
38+
and lab). That allows you to develop and test pyscript code interactively. Plus you can interact
39+
with much of HASS by looking at state variables, calling services etc, in a similar way to [HASS
40+
CLI](https://github.com/home-assistant-ecosystem/home-assistant-cli), althought the CLI provides
41+
status on many other parts of HASS.
42+
43+
For more information about the Jupyter kernel, see this [README](https://github.com/custom-components/pyscript/jupyter).
44+
There is also a [Jupyter notebook tutorial](https://github.com/custom-components/pyscript/jupyter/blob/master/pyscript_tutorial.ipynb),
45+
which can be downlaoded and run interactively in Jupyter notebook connected to your live HASS with pyscript.
46+
3747
Pyscript provides functionality that complements the existing automations, templates and triggers.
3848
Pyscript is most similar to [AppDaemon](https://appdaemon.readthedocs.io/en/latest/), and
3949
some similarities and differences are discussed in this
4050
[Wiki page](https://github.com/custom-components/pyscript/wiki/Comparing-Pyscript-to-AppDaemon).
41-
Pyscripts presents a simplified and more integrated binding for Python scripting than
51+
Pyscript with Jupyter makes it extremely easy to learn, use and debug. Pyscripts presents
52+
a simplified and more integrated binding for Python scripting than
4253
[Python Scripts](https://www.home-assistant.io/integrations/python_script), which requires
4354
a lot more expertise and scaffolding using direct access to Home Assistant internals.
4455

45-
## Roadmap
46-
47-
Currently I'm working on adding a kernel that will allow you to use Jupyter (notebook, console etc)
48-
as an interactive front-end for pyscript. That will allow you to develop and test pyscript code
49-
interactively, which will be much easier than the edit / reload cycle currently needed. Plus you
50-
can interact with much of HASS by looking at state variables, calling services etc, in a similar
51-
way to [HASS CLI](https://github.com/home-assistant-ecosystem/home-assistant-cli).
52-
5356
## Installation
5457

5558
### Option 1: HACS
@@ -58,7 +61,7 @@ Under HACS -> Integrations, select "+", search for `pyscript` and install it.
5861

5962
### Option 2: Manual
6063

61-
Fetch the [latest release](https://github.com/custom-components/pyscript/releases) zip file `hass-custom-pyscript.zip`
64+
From the [latest release](https://github.com/custom-components/pyscript/releases) download the zip file `hass-custom-pyscript.zip`
6265
```bash
6366
cd YOUR_HASS_CONFIG_DIRECTORY # same place as configuration.yaml
6467
mkdir -p custom_components/pyscript
@@ -75,6 +78,11 @@ mkdir -p YOUR_HASS_CONFIG_DIRECTORY/custom_components
7578
cp -pr pyscript/custom_components/pyscript YOUR_HASS_CONFIG_DIRECTORY/custom_components
7679
```
7780

81+
### Install Jupyter Kerne
82+
83+
Installing the pyscript Jupyter kernel is optional. The steps to install and use it are in
84+
this [README](https://github.com/custom-components/pyscript/jupyter).
85+
7886
## Configuration
7987

8088
* Add `pyscript:` to `<config>/configuration.yaml`; pyscript doesn't have any configuration settings
@@ -218,17 +226,24 @@ as many `.py` script files as you like. Each `.py` file can contain as many func
218226
The file names themselves can be anything, so long as they have a `.py` extension. You might like
219227
to group related functions in one file.
220228

221-
Like regular Python, functions within a file can call each other, and can share global variables (if
222-
necessary), but just within that one file. There is currently no way to directly call functions or
223-
access regular variables in another source file: they are isolated from each other, although they
224-
can use triggers or use service calls to invoke other functions, or state variables to share state.
229+
Like regular Python, functions within each script file can call each other, and can share global
230+
variables (if necessary), but just within that one file. Each file has its own separate global
231+
context. Each Jupyter session also has its own separate global context, so functions, triggers,
232+
variables and services defined in each interactive session are isolated from the script files and
233+
other Jupyter sessions. Pyscript provides some utility functions to switch global contexts, which
234+
allows an interactive Jupyter session to interact directly with functions and global variables
235+
created by a script file, or even another Jupyter session.
236+
237+
Even if you can't directly call one function from another script file, HASS state variables are
238+
global and services can be called from any script file.
225239

226240
Reloading the `.py` files is accomplished by calling the `pyscript.reload` service, which is the one
227241
built-in service (so you can't create your own service with that name). All function definitions,
228242
services and triggers are re-created on `reload`. Any currently running functions (ie, functions
229243
that have been triggered and are actively executing Python code or waiting inside `task.sleep()`
230244
or `task.wait_until()`) are not stopped by `reload` - they continue to run until they finish
231-
(return).
245+
(return). You can terminate these running functions too on `reload` if you prefer by using
246+
`task.unique()` in both the script file preamble and inside those functions.
232247

233248
## Accessing state variables
234249

@@ -569,14 +584,14 @@ has a numeric value, you might want to convert it to a numeric type (eg, using `
569584

570585
#### Service Calls
571586

572-
`service.call(domain, name, **kwargs={})` calls the service `domain.name` with the given keyword arguments as
587+
`service.call(domain, name, **kwargs)` calls the service `domain.name` with the given keyword arguments as
573588
parameters.
574589

575590
`service.has_service(domain, name)` returns whether the service `domain.name` exists.
576591

577592
#### Event Firing
578593

579-
`event.fire(event_type, **kwarg={})` sends an event with the given `event_type` string and the keyword
594+
`event.fire(event_type, **kwargs)` sends an event with the given `event_type` string and the keyword
580595
parameters as the event data.
581596

582597
#### Logging functions
@@ -591,21 +606,22 @@ The [Logger](/integrations/logger/) component can be used to specify the logging
591606
below the configured level will not appear in the log. Each log message function uses a log name of
592607
the form:
593608
```python
594-
homeassistant.components.pyscript.func.FUNCNAME
609+
homeassistant.components.pyscript.file.FILENAME.FUNCNAME
595610
```
596611
where `FUNCNAME` is the name of the top-level Python function (e.g., the one called by a trigger or
597-
service). That allows you to set the log level for each Python top-level function separately if
598-
necessary. That setting also applies to any other Python functions that the top-level Python
599-
function calls. For example, these settings:
612+
service), defined in the script file `FILENAME.py`. That allows you to set the log level for each
613+
Python top-level function separately if necessary. That setting also applies to any other Python
614+
functions that the top-level Python function calls. For example, these settings:
600615
```yaml
601616
logger:
602617
default: info
603618
logs:
604-
homeassistant.components.pyscript.func: info
605-
homeassistant.components.pyscript.func.my_function: debug
619+
homeassistant.components.pyscript.file: info
620+
homeassistant.components.pyscript.file.my_scripts.my_function: debug
606621
```
607622
will log all messages at `info` or higher (ie: `log.info()`, `log.warning()` and `log.error()`), and
608-
inside `my_function` (and any other functions it calls) will log all messages at `debug` or higher.
623+
inside `my_function` defined in the script file `my_scripts.py` (and any other functions it calls)
624+
will log all messages at `debug` or higher.
609625

610626
#### Sleep
611627

@@ -619,6 +635,11 @@ with the same `task_name`. The name can be any string. If `kill_me` is `True` th
619635
task is killed if another task that is running previously called `task.unique` with the same
620636
`task_name`.
621637

638+
Note that `task.unique` applies across all global contexts. It's up to you to use a convention
639+
for `task_name` that avoids accidential collisions. For example, you could use a prefix of the
640+
script file name, so that all `task_unique` calls in `FILENAME.py` use a `task_name` that
641+
starts with `"FILENAME."`.
642+
622643
#### Waiting for events
623644

624645
`task.wait_until()` allows functions to wait for events, using identical syntax to the decorators.
@@ -716,17 +737,43 @@ Summary: use trigger decorators whenever you can. Be especially cautious using `
716737
to wait for events; you must make sure your logic is robust to missing events that happen before or
717738
after `task.wait_until()` runs.
718739

740+
#### Global Context functions
741+
742+
Each pyscript script file runs inside its own global context, which means their global variables and
743+
functions are isolated from other script files. Each Jupyter session also runs in its own global
744+
context. For a script file called `FILENAME.py`, its global context is `file.FILENAME`. Each
745+
Jupyter global context name is `jupyter_NNN` where `NNN` is a unique integer starting at 0.
746+
747+
In normal use you don't need to worry about global contexts. But for interactive debugging and
748+
development, you might want your Jupyter session to access variables and functions defined in
749+
a script file. Three functions are provided for getting, setting and listing the global
750+
contexts. That allows you to interactively change the global context during a Jupyter
751+
session. You could also use these functions in your script files, but that is strongly
752+
discouraged. Here are the functions:
753+
- `pyscript.get_global_ctx()` returns the current global context name.
754+
- `pyscript.list_global_ctx()` lists all the global contexts, with the current global context listed first.
755+
- `pyscript.set_global_ctx(new_ctx_name)` sets the current global context to the given name.
756+
757+
When you exit a Jupyter session, its global context is deleted, which means any triggers, functions,
758+
services and variables you created are deleted (HASS state variables survive). If you switch to
759+
a script file's context, then any triggers, functions, services or variables you interactively
760+
create there will persist after you exit the Jupyter session. However, if you don't update the
761+
corresponding script file, then upon the next pyscript reload or HASS restart, those interactive
762+
changes will be lost.
763+
719764
## Contributing
720765

721-
You are encouraged to submit PRs, bug reports, feature requests or add to the Wiki with examples
722-
and tutorials.
766+
Contributions are welcome! You are encouraged to submit PRs, bug reports, feature requests or
767+
add to the Wiki with examples and tutorials. It would be fun to hear about unique and clever
768+
applications you develop.
723769

724770
## Useful Links
725771

726772
* [Documentation](https://github.com/custom-components/pyscript/blob/master/README.md)
727773
* [Issues](https://github.com/custom-components/pyscript/issues)
728774
* [Wiki](https://github.com/custom-components/pyscript/wiki)
729775
* [GitHub repository](https://github.com/custom-components/pyscript) (please add a star if you like `pyscript`!)
776+
* [Jupyter notebook tutorial](https://github.com/custom-components/pyscript/jupyter/blob/master/pyscript_tutorial.ipynb)
730777

731778
## Copyright
732779

0 commit comments

Comments
 (0)