Add JupyterLite Site (#57)

pull/58/head
Nicholas Bollweg 2021-07-06 09:40:08 -04:00 zatwierdzone przez GitHub
rodzic 2f6dd87c41
commit 4c40027cf0
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
35 zmienionych plików z 799 dodań i 183 usunięć

Wyświetl plik

@ -27,7 +27,6 @@ dependencies:
- jupyter-lsp-python-lsp-server
- jupyterlab-classic
- jupyterlab-lsp
- jupyterlab-tour
- matplotlib-base
- nbgitpuller
- networkx
@ -51,8 +50,13 @@ dependencies:
- pytest-check-links
- sphinx
- sphinx-jsonschema
- sphinxext-rediraffe
# for lite
- jupyterlab-tour
# TODO: resolve upstream
- sqlalchemy <1.4
- pip:
- jupyterlite ==0.1.0a3
### ipydrawio-docs-deps ###

Wyświetl plik

@ -1,3 +1,4 @@
# basicall ipydrawio[all,test] and ipydrawio-export... plus robot
black
doit
flake8

Wyświetl plik

@ -27,6 +27,7 @@ env:
PYTHONIOENCODING: 'utf-8'
PIP_DISABLE_PIP_VERSION_CHECK: '1'
CONDARC: .github/.condarc
CI: '1'
# our stuff
ATEST_RETRIES: '3'
@ -307,7 +308,7 @@ jobs:
# taskkill /F /IM python.exe || echo "no python"
# taskkill /F /IM node.exe || echo "no node"
- name: report (atest)
- name: upload (atest)
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
@ -325,6 +326,8 @@ jobs:
matrix:
os: ['ubuntu']
python-version: ['3.9']
env:
ATEST_ARGS: '["--exclude","NOTapp:lite"]'
steps:
- name: checkout
uses: actions/checkout@v2
@ -367,3 +370,26 @@ jobs:
shell: bash -l {0}
run: |
doit $DOIT_N_TEST check
- name: lite tests
shell: bash -l {0}
run: |
doit $DOIT_N_TEST test
- name: upload (atest lite)
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name:
ipydrawio ${{ github.run_number }} atest ${{ matrix.os }}${{
matrix.python-version }}
path: ./build/atest
- name: upload (docs)
if: ${{ always() }}
uses: actions/upload-artifact@v2
with:
name:
ipydrawio ${{ github.run_number }} docs ${{ matrix.os }}${{
matrix.python-version }}
path: ./build/docs

3
.gitignore vendored
Wyświetl plik

@ -1,6 +1,7 @@
__pycache__
_output
.coverage*
.doit*
*doit.db
.ipynb_checkpoints
.pabotsuitenames
.virtual_documents/

Wyświetl plik

@ -19,7 +19,7 @@
```bash
mamba install -c conda-forge ipydrawio # recommended, or...
conda install -c conda-forge ipydrawio # or...
pip install ipydrawio
pip install ipydrawio[all]
```
## Features

Wyświetl plik

@ -22,23 +22,35 @@ Library ./ports.py
*** Keywords ***
Setup Server and Browser
${home} = Setup Home
${port} = Get Unused Port
${root} = Get Server Root
Set Global Variable ${PORT} ${port}
Set Global Variable ${URL} http://localhost:${PORT}${BASE}
Setup Real Server port=${port} home=${HOME} root=${root}
Get Server Root
${root} = Normalize Path ${OUTPUT DIR}${/}..${/}..${/}..
[Return] ${root}
Setup Home
${accel} = Evaluate "COMMAND" if "${OS}" == "Darwin" else "CTRL"
Set Global Variable ${ACCEL} ${accel}
${token} = Generate Random String
Set Global Variable ${TOKEN} ${token}
${home} = Set Variable ${OUTPUT DIR}${/}home
Set Global Variable ${HOME} ${home}
${root} = Normalize Path ${OUTPUT DIR}${/}..${/}..${/}..
Create Directory ${home}
Set Screenshot Directory ${OUTPUT DIR}${/}screenshots
[Return] ${home}
Setup Real Server
[Arguments] ${port} ${home} ${root}
Set Global Variable ${LAB LOG} ${OUTPUT DIR}${/}lab.log
Set Global Variable ${PREVIOUS LAB LOG LENGTH} 0
Create Notebok Server Config ${home}
Initialize User Settings
${cmd} = Create Lab Launch Command ${root}
Set Screenshot Directory ${OUTPUT DIR}${/}screenshots
Set Global Variable ${LAB LOG} ${OUTPUT DIR}${/}lab.log
Set Global Variable ${PREVIOUS LAB LOG LENGTH} 0
${server} = Start Process ${cmd} shell=yes env:HOME=${home} cwd=${home} stdout=${LAB LOG}
... stderr=STDOUT
Set Global Variable ${SERVER} ${server}
@ -86,13 +98,19 @@ Reset Plugin Settings
Tear Down Everything
Close All Browsers
Tear Down Real Server
Tear Down Real Server
Evaluate __import__("urllib.request").request.urlopen("${URL}api/shutdown?token=${TOKEN}", data=[])
Wait For Process ${SERVER} timeout=30s
Terminate All Processes
Terminate All Processes kill=${True}
Wait For Splash
Go To ${URL}lab?reset&token=${TOKEN}
[Arguments] ${lab url}=${EMPTY}
Run Keyword If """${lab url}"""
... Go To ${lab url}
... ELSE Go To ${URL}lab?reset&token=${TOKEN}
Set Window Size 1920 1080
Wait Until Page Contains Element ${SPLASH} timeout=30s
Wait Until Page Does Not Contain Element ${SPLASH} timeout=10s

Wyświetl plik

@ -14,10 +14,3 @@
*** Settings ***
Documentation IPyDrawio
Resource _Keywords.robot
Resource _Variables.robot
Suite Setup Setup Server and Browser
Suite Teardown Tear Down Everything
Test Setup Maybe Reset Application State
Test Teardown Maybe Reset Application State
Force Tags os:${OS.lower()} py:${PY}

Wyświetl plik

@ -14,7 +14,7 @@
*** Settings ***
Documentation Does custom create work?
Resource _Keywords.robot
Resource ../_Keywords.robot
Library OperatingSystem
Force Tags component:document component:custom
Suite Setup Set Screenshot Directory ${OUTPUT DIR}${/}screenshots${/}custom

Wyświetl plik

@ -14,9 +14,9 @@
*** Settings ***
Documentation Are export formats sane?
Resource _Keywords.robot
Resource ../_Keywords.robot
Library OperatingSystem
Library ./pdf.py
Library ../pdf.py
Force Tags component:document
*** Variables ***

Wyświetl plik

@ -14,8 +14,8 @@
*** Settings ***
Documentation Does the media type (mimerenderer) work?
Resource _Keywords.robot
Resource _Notebook.robot
Resource ../_Keywords.robot
Resource ../_Notebook.robot
Force Tags component:media
Library OperatingSystem

Wyświetl plik

@ -14,7 +14,7 @@
*** Settings ***
Documentation Are Diagram settings usable?
Resource _Keywords.robot
Resource ../_Keywords.robot
Library OperatingSystem
Force Tags component:settings

Wyświetl plik

@ -14,9 +14,9 @@
*** Settings ***
Documentation smoke tests
Resource _Keywords.robot
Resource ../_Keywords.robot
*** Test Cases ***
Smoke
[Documentation] Does the app even load?
Capture Page Screenshot smoke.png
Capture Page Screenshot 00-smoke.png

Wyświetl plik

@ -14,8 +14,8 @@
*** Settings ***
Documentation Does the Jupyter Widget work?
Resource _Keywords.robot
Resource _Notebook.robot
Resource ../_Keywords.robot
Resource ../_Notebook.robot
Force Tags component:widget
Library OperatingSystem

Wyświetl plik

@ -0,0 +1,23 @@
# Copyright 2021 ipydrawio contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*** Settings ***
Documentation Lab
Resource ../_Keywords.robot
Resource ../_Variables.robot
Suite Setup Setup Server and Browser
Suite Teardown Tear Down Everything
Test Setup Maybe Reset Application State
Test Teardown Maybe Reset Application State
Force Tags os:${OS.lower()} py:${PY} app:lab

Wyświetl plik

@ -0,0 +1,40 @@
# Copyright 2021 ipydrawio contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*** Settings ***
Documentation Test some diagrams in lite
Resource ../_Keywords.robot
Resource ./_Keywords.robot
Library OperatingSystem
Force Tags component:document
Suite Setup Set Screenshot Directory ${OUTPUT DIR}${/}lite${/}documents
*** Test Cases ***
Test Examples
[Documentation] Do all of the examples work?
${examples} = List Files In Directory ${DEMO} *.dio*
FOR ${file} IN @{EXAMPLES}
Try to Close All Tabs
Run Keyword If ${file.__contains__('.dio')} Example Should Load ${file}
END
*** Keywords ***
Example Should Load
[Arguments] ${file}
[Documentation] Does one example work?
Ensure File Browser is Open
Double Click Element css:[title*\="${file}"]
Unselect Frame
Wait Until Element is Visible ${CSS DIO IFRAME} timeout=20s
Capture Page Screenshot 00-${file}-loaded.png

Wyświetl plik

@ -0,0 +1,24 @@
# Copyright 2021 ipydrawio contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*** Settings ***
Documentation Check the vitals of a Lite site
Resource ../_Keywords.robot
Resource ./_Keywords.robot
Suite Setup Set Screenshot Directory ${OUTPUT DIR}${/}lite${/}smoke
*** Test Cases ***
Does Lite load?
[Documentation] Can we load the JupyterLite site?
Capture Page Screenshot 00-lite-smoke.png

Wyświetl plik

@ -0,0 +1,76 @@
# Copyright 2021 ipydrawio contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*** Settings ***
Documentation A work-in-progress set of keywords for JupyterLite
Library OperatingSystem
Library Process
Library ../ports.py
*** Variable ***
${NEXT LITE LOG} ${0}
*** Keywords ***
Start JupyterLite Process
[Arguments] ${task} ${cwd} @{args}
[Documentation] Start a `jupyter lite` process
${p} = Start Process jupyter lite ${task} @{args}
... cwd=${cwd} stdout=${OUTPUT DIR}${/}lite-${NEXT LITE LOG}.log stderr=STDOUT
Set Global Variable ${NEXT LITE LOG} ${NEXT LITE LOG + 1}
[Return] ${p}
Start JupyterLite Server
[Documentation] Start _the_ `jupyter lite` server
[Arguments] ${cwd} @{args}
Set Environment Variable MOZ_HEADLESS 1
${prefix} = Set Variable /@rf/
${port} = Get Unused Port
${url} = Set Variable http://localhost:${port}${prefix}lab/index.html
Set Global Variable ${LITE URL} ${url}
${p} = Start JupyterLite Process serve ${cwd}
... @{args} --port ${port} --base-url ${prefix}
Set Global Variable ${LITE SERVER} ${p}
Close All Browsers
${service args} = Create List --log warn
Create WebDriver Firefox
... service_log_path=${OUTPUT DIR}${/}geckodriver-lite.log
... service_args=${service args}
Open JupyterLite
Set Environment Variable MOZ_HEADLESS 1
${service args} = Create List --log warn
Wait For Splash ${LITE URL}
Stop JupyterLite Server
[Documentation] Stop _the_ `jupyter lite` server
Close All Browsers
Terminate Process ${LITE SERVER}
Start Lite Test
[Documentation] Start with a blank browser
Open JupyterLite
Clean Up Lite Test
[Documentation] Clean up
... TODO: how might we clear the application cache?
Close All Browsers
Start Lite Suite
[Documentation] Ensure lite assets are available
Set Screenshot Directory ${OUTPUT DIR}${/}lite
Start JupyterLite Server ${DEMO}
Clean Up Lite Suite
[Documentation] Clean up after lite
Stop JupyterLite Server

Wyświetl plik

@ -0,0 +1,24 @@
# Copyright 2021 ipydrawio contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*** Settings ***
Documentation Lite
Library OperatingSystem
Force Tags app:lite
Resource ./_Keywords.robot
Resource ../_Keywords.robot
Suite Setup Start Lite Suite
Suite Teardown Clean Up Lite Suite
Test Setup Start Lite Test
Test Teardown Clean Up Lite Test

6
demo/.gitignore vendored 100644
Wyświetl plik

@ -0,0 +1,6 @@
_output
*.dio
*.dio.svg
*.ipynb
*doit.db
files/

Wyświetl plik

@ -0,0 +1,15 @@
{
"jupyter-config-data": {
"appName": "IPyDrawioLite",
"disabledExtensions": [
"@deathbeds/ipydrawio-pdf",
"@jupyterlab-classic/lab-extension",
"@jupyterlab/server-proxy",
"@krassowski/jupyterlab-lsp",
"jupyterlab-server-proxy",
"nbdime-jupyterlab"
],
"faviconUrl": "./favicon.ico"
},
"jupyter-lite-schema-version": 0
}

Wyświetl plik

@ -0,0 +1,12 @@
{
"LiteBuildConfig": {
"apps": ["lab"],
"files": ["."],
"ignore_files": [
"(lab/|_output|.gitignore|doit.db|.json|test|Test|jupyter_config|log$)"
],
"lite_dir": ".",
"output_archive": "../build/demo/ipydrawio-lite-1.1.1.tgz",
"output_dir": "../build/demo"
}
}

Wyświetl plik

@ -0,0 +1,51 @@
{
"@jupyterlab/apputils-extension:palette": {
"modal": false
},
"@jupyterlab/extensionmanager-extension:plugin": {
"enabled": false
},
"jupyterlab-tour:user-tours": {
"tours": [
{
"id": "ipydrawiolite",
"label": "IPyDrawioLite",
"options": {
"styles": {
"options": {
"arrowColor": "#7b8aff",
"beaconSize": 48,
"primaryColor": "#7b8aff"
}
}
},
"steps": [
{
"content": "This is running in JupyterLite",
"target": "#jp-MainLogo"
},
{
"content": "It can open multi-page drawio XML files, as well as editable SVG and PNG files, or even Jupyter Notebooks",
"target": ".jp-DirListing-item svg[data-icon*='drawio']"
},
{
"content": "You can create new diagrams from the launcher...",
"target": ".jp-Launcher-sectionHeader svg[data-icon='drawio:drawio']"
},
{
"content": "... start with a blank canvas XML file with your default diagram theme...",
"target": ".jp-LauncherCard[title='Create a blank .dio file']"
},
{
"content": "...or one of many templates, formats, and themes",
"target": ".jp-LauncherCard[title='Create a diagram with customized formats, templates, and UI']"
},
{
"content": "The diagram theme is configurable from the Settings menu",
"target": ".lm-MenuBar-item:nth-last-child(2)"
}
]
}
]
}
}

Wyświetl plik

@ -26,6 +26,7 @@
--pst-color-inline-code: 0, 22, 192;
--pst-color-preformatted-background: 240, 248, 255;
--pst-color-headerlink: var(--pst-color-active-navigation);
--ipd-brand-color0: rgb(var(--pst-color-active-navigation));
}
code {
@ -163,3 +164,44 @@ div.cell div.cell_input {
text-align: left;
padding: calc(0.25 * var(--ipd-card-padding)) 0;
}
.demo-sidebar {
text-align: center;
}
.demo-sidebar .btn-primary {
background-color: var(--ipd-brand-color0);
font-size: 150%;
opacity: 0.5;
transition: all 0.2s;
}
.demo-sidebar .btn-primary:hover,
.demo-sidebar .btn-primary:focus,
.demo-sidebar .btn-primary:active {
opacity: 1;
transition: all 0.2s;
}
#try-it-now .warning {
float: right;
margin-left: 1em;
margin-top: 0 !important;
width: 50%;
}
#try-it-now .warning em:empty {
display: none;
}
hr {
clear: both;
}
.tooltip-inner {
background-color: var(--ipd-brand-color0);
}
.tooltip .arrow:before {
border-color: var(--ipd-brand-color0);
}

26
docs/_templates/demo.html vendored 100644
Wyświetl plik

@ -0,0 +1,26 @@
<div class="demo-sidebar">
<a
href="{{ pathto('demo/index') }}"
class="btn btn-primary"
target="_blank"
data-toggle="tooltip"
data-placement="right"
title="Try IPyDrawio in your browser. Right now."
>
<i class="fas fa-lightbulb"></i>
Try <strong>IPyDrawio</strong> Now
</a>
<br/>
or <a
href="{{ pathto(demo_tarball, True) }}"
data-toggle="tooltip"
data-placement="bottom"
title="Download the app archive"
>
<i class="fas fa-download"></i>
download the demo
</a>
<hr/>
<em>Powered by <a href="https://jupyterlite.rtfd.io" target="_blank">JupyterLite</a></em>
<script>$(function() {$('[data-toggle="tooltip"]').tooltip();});</script>
</div>

Wyświetl plik

@ -50,6 +50,8 @@ extensions = [
"sphinx.ext.intersphinx",
"sphinx.ext.autosectionlabel",
"sphinx.ext.autodoc",
# for routing
"sphinxext.rediraffe",
"sphinx-jsonschema",
"autodoc_traits",
]
@ -58,16 +60,12 @@ autosectionlabel_prefix_document = True
myst_heading_anchors = 3
suppress_warnings = ["autosectionlabel.*"]
# rediraffe_redirects = {
# "try/index": "_static/index",
# "try/lab/index": "_static/lab/index",
# "try/classic/index": "_static/classic/tree/index",
# }
rediraffe_redirects = {"demo/index": "_static/lab/index"}
# files
# templates_path = ["_templates"]
templates_path = ["_templates"]
html_favicon = "_static/favicon.ico"
html_static_path = ["_static"]
html_static_path = ["_static", "../build/demo"]
exclude_patterns = [
".ipynb_checkpoints",
"**/.ipynb_checkpoints",
@ -90,8 +88,15 @@ html_logo = "_static/logo.svg"
html_theme_options = {
"github_url": APP_DATA["repository"]["url"],
"use_edit_page_button": True,
# "navbar_start": ["launch.html"],
# "navbar_center": ["navbar-logo.html", "navbar-nav.html"],
# "navbar_start": ["navbar-logo.html", "launch.html"],
}
html_sidebars = {
"**": [
"demo.html",
"search-field.html",
"sidebar-nav-bs.html",
"sidebar-ethical-ads.html",
]
}
html_context = {
@ -99,6 +104,7 @@ html_context = {
"github_repo": "ipydrawio",
"github_version": "master",
"doc_path": "docs",
"demo_tarball": f"_static/ipydrawio-lite-{release}.tgz",
}
@ -121,10 +127,19 @@ def clean_schema(app: Sphinx, error):
def before_rtd_build(app: Sphinx, error):
"""performs the full frontend build, and ensures the typedoc"""
subprocess.check_call(
["doit", "-n4", "build", "setup:pip:check", "docs:typedoc:mystify"],
cwd=str(ROOT),
)
for task in [
"build",
"setup:pip:check",
"docs:typedoc:mystify",
"demo",
]:
subprocess.check_call(
[
"doit",
task,
],
cwd=str(ROOT),
)
def setup(app):

Wyświetl plik

@ -27,8 +27,13 @@ dependencies:
- pytest-check-links
- sphinx
- sphinx-jsonschema
- sphinxext-rediraffe
# for lite
- jupyterlab-tour
# TODO: resolve upstream
- sqlalchemy <1.4
- pip:
- jupyterlite ==0.1.0a3
### ipydrawio-docs-deps ###

Wyświetl plik

@ -1,8 +1,38 @@
# IPyDrawio
[![What is IPyDrawio?][poster]][poster]
> Interactive diagram documents and [widgets] in [JupyterLab], powered by
> [drawio].
[poster]: ./Poster.dio.svg
[drawio]: https://github.com/jgraph/drawio
[jupyterlab]: https://github.com/jupyterlab/jupyterlab
[widgets]: https://github.com/jupyter-widgets/ipywidgets
## Try it Now
```{warning}
<details>
<summary>The demo has some <strong>pointy edges</strong>...</summary>
<ul>
<li>
<em>Diagram Documents</em> and <em>Rich Display</em> work properly, <em>Widgets</em> are <em>close</em>, but can't be properly installed, yet
</li>
<li>
<code>jupyterlite</code> is <em>alpha</em> software, and evolving quickly
</li>
<li>
<strong>Don't</strong> use the demo site for important work
</li>
</ul>
</details>
```
Click the _Try IPyDrawio Now_ button to launch a new browser tab running
IPyDrawio. It contains all of the example _Diagrams_ from this documentation
site.
[demo]: ./demo/index
---
## Get Started
@ -10,7 +40,7 @@ Install `ipydrawio` (and optionally `ipydrawio-export`) with `pip`, `mamba`, or
`conda`:
```bash
pip install ipydrawio
pip install ipydrawio[all]
# or
mamba install -c conda-forge ipydrawio
# or
@ -19,10 +49,27 @@ conda install -c conda-forge ipydrawio
Start JupyterLab and [start making diagrams](./diagrams/index.md)!
---
## Features
[![What is IPyDrawio?][poster]][poster]
[poster]: ./Poster.dio.svg
## More Screenshots and Examples
> these are editable SVG files made with IPyDrawio, and can be found in the
> [demo].
### What can you draw with IPyDrawio
[![What can you draw with IPyDrawio?][what-can-you-draw]][what-can-you-draw]
[what-can-you-draw]: ./_static/what-can-you-draw.dio.svg
---
## Learn More
```{toctree}

204
dodo.py
Wyświetl plik

@ -29,7 +29,6 @@ maybe before you push
# See the License for the specific language governing permissions and
# limitations under the License.
import pprint
import shutil
import subprocess
import time
@ -41,20 +40,6 @@ from doit.tools import PythonInteractiveAction, config_changed
import scripts.project as P
print_ = pprint.pprint
console = None
try:
import rich.console
import rich.markdown
console = rich.console.Console()
print_ = console.print
except ImportError:
pass
DOIT_CONFIG = dict(
backend="sqlite3",
verbosity=2,
@ -78,7 +63,9 @@ def task_all():
P.OK_PROVISION,
P.SHA256SUMS,
],
actions=[(_show, ["nothing left to do"], {"shasums": P.SHA256SUMS.read_text})],
actions=[
(P._show, ["nothing left to do"], {"shasums": P.SHA256SUMS.read_text})
],
)
@ -118,7 +105,7 @@ def task_submodules():
if any([x.startswith("-") for x in subs]) and P.DRAWIO.exists():
shutil.rmtree(P.DRAWIO)
return _ok(
return P._ok(
dict(
uptodate=[config_changed({"subs": subs})],
actions=[_clean, ["git", "submodule", "update", "--init", "--recursive"]],
@ -156,7 +143,7 @@ def task_setup():
ci_af = {"wheel": P.PY_WHEEL[pkg], "sdist": P.PY_SDIST[pkg]}[P.CI_ARTIFACT]
dist_af = P.DIST / ci_af.name
yield _ok(
yield P._ok(
dict(
name=f"py:{pkg}",
file_dep=[dist_af],
@ -177,7 +164,7 @@ def task_setup():
extra_deps = []
if pkg != "ipydrawio":
extra_deps += [P.OK_PYSETUP["ipydrawio"]]
yield _ok(
yield P._ok(
dict(
name=f"py:{pkg}",
file_dep=[pkg_setup, P.PY_SETUP_CFG[pkg], *ext_deps, *extra_deps],
@ -210,7 +197,7 @@ def task_setup():
P.OK_PYSETUP[pkg],
)
yield _ok(
yield P._ok(
dict(
name="pip:check",
file_dep=[*P.OK_PYSETUP.values()],
@ -232,7 +219,7 @@ def task_setup():
if P.TESTING_IN_CI:
enable_args = ["echo", "'(installed by pip)'"]
yield _ok(
yield P._ok(
dict(
name=f"ext:{ext}",
doc=f"ensure {ext} is a serverextension",
@ -252,7 +239,7 @@ def task_lint():
if P.TESTING_IN_CI:
return
yield _ok(
yield P._ok(
dict(
name="isort",
file_dep=[*P.ALL_PY, P.SETUP_CFG],
@ -260,7 +247,7 @@ def task_lint():
),
P.OK_ISORT,
)
yield _ok(
yield P._ok(
dict(
name="black",
file_dep=[*P.ALL_PY, P.OK_ISORT],
@ -268,7 +255,7 @@ def task_lint():
),
P.OK_BLACK,
)
yield _ok(
yield P._ok(
dict(
name="flake8",
file_dep=[*P.ALL_PY, P.OK_BLACK, P.SETUP_CFG],
@ -276,7 +263,7 @@ def task_lint():
),
P.OK_FLAKE8,
)
yield _ok(
yield P._ok(
dict(
name="pyflakes",
file_dep=[*P.ALL_PY, P.OK_BLACK],
@ -284,23 +271,46 @@ def task_lint():
),
P.OK_PYFLAKES,
)
yield _ok(
dict(
name="prettier",
file_dep=[P.YARN_INTEGRITY, *P.ALL_PRETTIER],
actions=[
[
"jlpm",
"prettier",
"--list-different",
"--write",
*sorted(p.relative_to(P.ROOT) for p in P.ALL_PRETTIER),
]
],
),
P.OK_PRETTIER,
)
yield _ok(
prettier_args = [
"jlpm",
"--silent",
"prettier",
"--list-different",
"--write",
]
if P.CI:
yield P._ok(
dict(
name="prettier",
file_dep=[P.YARN_INTEGRITY, *P.ALL_PRETTIER],
actions=[[*prettier_args, *P.ALL_PRETTIER]],
),
P.OK_PRETTIER,
)
else:
pretty_tasks = []
for path in P.ALL_PRETTIER:
name = f"prettier:{path.relative_to(P.ROOT)}"
pretty_tasks += [f"lint:{name}"]
yield dict(
name=name,
file_dep=[P.YARN_INTEGRITY, path],
actions=[[*prettier_args, path]],
)
yield P._ok(
dict(
name="prettier",
file_dep=[P.YARN_INTEGRITY, *P.ALL_PRETTIER],
task_dep=pretty_tasks,
actions=[["echo", "OK"]],
),
P.OK_PRETTIER,
)
yield P._ok(
dict(
name="eslint",
file_dep=[
@ -326,7 +336,7 @@ def task_lint():
actions=[["jupyter", "ipydrawio", "clean", dio_file]],
)
yield _ok(
yield P._ok(
dict(
name="dio:clean",
file_dep=[*P.ALL_DIO],
@ -336,10 +346,10 @@ def task_lint():
P.OK_DIOLINT,
)
yield _ok(
yield P._ok(
dict(
name="all",
actions=[_echo_ok("all ok")],
actions=[P._echo_ok("all ok")],
file_dep=[
P.OK_BLACK,
P.OK_FLAKE8,
@ -351,7 +361,7 @@ def task_lint():
P.OK_LINT,
)
yield _ok(
yield P._ok(
dict(
name="robot:tidy",
file_dep=P.ALL_ROBOT,
@ -360,7 +370,7 @@ def task_lint():
P.OK_ROBOTIDY,
)
yield _ok(
yield P._ok(
dict(
name="robot:lint",
file_dep=[*P.ALL_ROBOT, P.OK_ROBOTIDY],
@ -369,7 +379,7 @@ def task_lint():
P.OK_RFLINT,
)
yield _ok(
yield P._ok(
dict(
name="robot:dryrun",
file_dep=[*P.ALL_ROBOT, P.OK_RFLINT],
@ -384,7 +394,7 @@ def task_build():
if P.TESTING_IN_CI:
return
yield _ok(
yield P._ok(
dict(
name="js:pre",
file_dep=[
@ -400,7 +410,7 @@ def task_build():
P.OK_JS_BUILD_PRE,
)
yield _ok(
yield P._ok(
dict(
name="js",
file_dep=[P.YARN_INTEGRITY, P.OK_JS_BUILD_PRE, *P.ALL_TS, *P.ALL_CSS],
@ -437,7 +447,7 @@ def task_build():
P.JS_PKG_JSON[pkg].parent / pkg_data["jupyterlab"]["outputDir"]
).resolve()
yield _ok(
yield P._ok(
dict(
name=f"ext:build:{pkg}",
actions=[
@ -553,7 +563,7 @@ def task_conda_build():
def task_conda_test():
for name, pkg in P.CONDA_PKGS.items():
yield _ok(
yield P._ok(
dict(
name=f"test:{name}",
file_dep=[pkg],
@ -660,6 +670,38 @@ def task_watch():
)
def task_demo():
if not P.LITE_PREFIX:
return
demo_dest = []
for path in P.ALL_DEMO_CONTENTS:
dest = P.DEMO / path.name.replace(" ", "_")
demo_dest += [dest]
yield dict(
name=f"stage:{path.name}",
file_dep=[path],
targets=[dest],
actions=[(P._copy_one, [path, dest])],
)
lite_src_files = [
p
for p in P.DEMO.rglob("*")
if not p.is_dir()
and "/_output/" not in str(p)
and not p.name.endswith(".tgz")
and ".doit" not in p.name
]
yield dict(
name="archive",
file_dep=[*demo_dest, *lite_src_files],
targets=[P.DEMO_ARCHIVE, P.DEMO_HASHES],
actions=[P._build_lite],
)
def task_docs():
"""build the docs"""
if P.TESTING_IN_CI:
@ -720,10 +762,25 @@ def task_docs():
],
)
sphinx_deps = [
P.DOCS_CONF,
P.DOCS_FAVICON_ICO,
P.OK_PIP_CHECK,
*P.DOCS_SRC,
]
sphinx_task_deps = []
if P.LITE_PREFIX:
sphinx_deps += [
P.DEMO_HASHES,
P.DEMO_ARCHIVE,
]
yield dict(
name="sphinx",
doc="build the documentation site with sphinx",
file_dep=[P.DOCS_CONF, P.DOCS_FAVICON_ICO, P.OK_PIP_CHECK, *P.DOCS_SRC],
file_dep=sphinx_deps,
task_dep=sphinx_task_deps,
actions=[
["sphinx-build", *P.SPHINX_ARGS, "-j8", "-b", "html", P.DOCS, P.DOCS_BUILD]
],
@ -735,7 +792,7 @@ def task_docs():
def task_check():
"""check built artifacts"""
file_dep = [*P.DOCS_BUILD.rglob("*.html")]
yield _ok(
yield P._ok(
dict(
name="links",
file_dep=[*file_dep, P.DOCS_BUILDINFO],
@ -745,7 +802,7 @@ def task_check():
"--check-anchors",
"--check-links-ignore",
"^https?://",
*[p for p in file_dep if p.name != "schema.html"],
*[p for p in file_dep if p.name not in ["schema.html"]],
]
],
),
@ -755,7 +812,7 @@ def task_check():
def task_provision():
"""ensure the ipydrawio-export server has been provisioned with npm (ick)"""
return _ok(
return P._ok(
dict(
file_dep=[*P.OK_SERVEREXT.values()],
actions=[
@ -779,7 +836,7 @@ def _pytest(setup_py):
def task_test():
"""run tests"""
if not P.TESTING_IN_CI:
yield _ok(
yield P._ok(
dict(
name="integrity",
file_dep=[
@ -797,7 +854,7 @@ def task_test():
)
for pkg, setup in P.PY_SETUP.items():
yield _ok(
yield P._ok(
dict(
name=f"pytest:{pkg}",
uptodate=[config_changed(dict(PYTEST_ARGS=P.PYTEST_ARGS))],
@ -822,9 +879,9 @@ def task_test():
]
if not P.TESTING_IN_CI:
file_dep += [P.OK_ROBOT_DRYRUN, *P.OK_SERVEREXT.values()]
file_dep += [P.OK_ROBOT_DRYRUN, P.DEMO_HASHES, *P.OK_SERVEREXT.values()]
yield _ok(
yield P._ok(
dict(
name="robot",
uptodate=[config_changed(dict(ATEST_ARGS=P.ATEST_ARGS))],
@ -833,30 +890,3 @@ def task_test():
),
P.OK_ATEST,
)
# utilities
def _echo_ok(msg):
def _echo():
print(msg, flush=True)
return True
return _echo
def _ok(task, ok):
task.setdefault("targets", []).append(ok)
task["actions"] = [
lambda: [ok.exists() and ok.unlink(), True][-1],
*task["actions"],
lambda: [ok.parent.mkdir(exist_ok=True), ok.write_text("ok"), True][-1],
]
return task
def _show(*args, **kwargs):
for arg in args:
print_(arg()) if callable(arg) else print_(arg)
for kw, kwarg in kwargs.items():
print_(rich.markdown.Markdown(f"# {kw}") if console else kw)
print_(kwarg()) if callable(kwarg) else print_(kwarg)

Wyświetl plik

@ -54,6 +54,7 @@ zip_safe = False
install_requires =
ipydrawio >=1.1.0
jupyterlab ==3.*
lxml
pillow
pypdf2

Wyświetl plik

@ -19,7 +19,7 @@
```bash
mamba install -c conda-forge ipydrawio # recommended, or...
conda install -c conda-forge ipydrawio # or...
pip install ipydrawio
pip install ipydrawio[all]
```
## Features

Wyświetl plik

@ -53,10 +53,22 @@ include_package_data = True
zip_safe = False
install_requires =
ipywidgets >=7.6
jupyterlab >=3,<4
jupyterlab_widgets >=1
ipywidgets >=7.6,<8
[options.extras_require]
clean =
lxml
lab =
jupyterlab ==3.*
jupyterlab_widgets >=1
all =
%(clean)s
%(lab)s
test =
%(all)s
pytest
pytest-console-scripts
pytest-cov
[options.packages.find]
where =

Wyświetl plik

@ -20,7 +20,12 @@ import traitlets as T
from jupyter_core.application import JupyterApp, base_aliases, base_flags
from ._version import __version__
from .utils import MX_CLEAN_ATTRS, clean_drawio_file
try:
# the clean command isn't _critical_
from .clean import MX_CLEAN_ATTRS, clean_drawio_file
except ImportError: # pragma: no cover
clean_drawio_file = None
class BaseApp(JupyterApp):
@ -31,54 +36,58 @@ class BaseApp(JupyterApp):
return self.__doc__.splitlines()[0].strip()
class CleanApp(BaseApp):
"""clean drawio files"""
if clean_drawio_file:
dio_files = T.Tuple()
pretty = T.Bool(True, help="pretty-print the XML").tag(config=True)
mx_attrs = T.Tuple(MX_CLEAN_ATTRS, help="attributes to clean").tag(config=True)
indent = T.Int(2, help="if pretty-printing, the indent level").tag(config=True)
tabs = T.Bool(False, help="indent with tabs instead of spaces").tag(config=True)
class CleanApp(BaseApp):
"""clean drawio files"""
flags = dict(
**base_flags,
**{
"no-pretty": (
{"CleanApp": {"pretty": False}},
"Do not pretty-print the XML",
),
"tabs": (
{"CleanApp": {"tabs": True}},
"Indent with tabs instead of spaces",
),
}
)
aliases = dict(
**base_aliases, **{"mx-attrs": "CleanApp.mx_attrs", "indent": "CleanApp.indent"}
)
dio_files = T.Tuple()
pretty = T.Bool(True, help="pretty-print the XML").tag(config=True)
mx_attrs = T.Tuple(MX_CLEAN_ATTRS, help="attributes to clean").tag(config=True)
indent = T.Int(2, help="if pretty-printing, the indent level").tag(config=True)
tabs = T.Bool(False, help="indent with tabs instead of spaces").tag(config=True)
def parse_command_line(self, argv=None):
super().parse_command_line(argv)
self.dio_files = [Path(p).resolve() for p in self.extra_args]
flags = dict(
**base_flags,
**{
"no-pretty": (
{"CleanApp": {"pretty": False}},
"Do not pretty-print the XML",
),
"tabs": (
{"CleanApp": {"tabs": True}},
"Indent with tabs instead of spaces",
),
}
)
aliases = dict(
**base_aliases,
**{"mx-attrs": "CleanApp.mx_attrs", "indent": "CleanApp.indent"}
)
def start(self):
for path in self.dio_files:
clean_drawio_file(
path,
pretty=self.pretty,
mx_attrs=self.mx_attrs,
indent=self.indent,
tabs=self.tabs,
)
def parse_command_line(self, argv=None):
super().parse_command_line(argv)
self.dio_files = [Path(p).resolve() for p in self.extra_args]
def start(self):
for path in self.dio_files:
clean_drawio_file(
path,
pretty=self.pretty,
mx_attrs=self.mx_attrs,
indent=self.indent,
tabs=self.tabs,
)
class IPyDrawioApp(BaseApp):
"""ipydrawio utilities"""
name = "ipydrawio"
subcommands = dict(
clean=(CleanApp, CleanApp.__doc__.splitlines()[0]),
)
subcommands = dict()
if clean_drawio_file:
subcommands["clean"] = (CleanApp, CleanApp.__doc__.splitlines()[0])
main = launch_instance = IPyDrawioApp.launch_instance

Wyświetl plik

@ -47,6 +47,12 @@ def run_tests(attempt=0, extra_args=None):
if "--dryrun" in extra_args:
runner = ["robot"]
try:
__import__("jupyterlite")
except Exception as err:
print("skipping lite tests because", err)
extra_args += ["--exclude", "app:lite"]
args = [
*runner,
*extra_args,
@ -58,6 +64,8 @@ def run_tests(attempt=0, extra_args=None):
f"OS:{P.PLATFORM}",
"--variable",
f"PY:{P.PY_MAJOR}",
"--variable",
f"DEMO:{P.DEMO}",
"--randomize",
"all",
"--xunit",

Wyświetl plik

@ -17,9 +17,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import os
import platform
import pprint
import re
import shutil
import subprocess
@ -27,7 +29,36 @@ import sys
from collections import defaultdict
from pathlib import Path
SKIPS = ["checkpoint", "pytest_cache"]
print_ = pprint.pprint
console = None
try:
import rich.console
import rich.markdown
console = rich.console.Console()
print_ = console.print
except ImportError:
pass
LITE_PREFIX = None
SOURCE_DATE_EPOCH = None
try:
__import__("jupyterlite.manager")
LITE_PREFIX = "demo_"
SOURCE_DATE_EPOCH = (
subprocess.check_output(["git", "log", "-1", "--format=%ct"])
.decode("utf-8")
.strip()
)
except (ImportError, AttributeError, subprocess.CalledProcessError) as err:
print_(err)
pass
SKIPS = ["checkpoint", "pytest_cache", "patched-environment"]
def _clean(*paths_or_globs):
@ -59,6 +90,7 @@ ENC = dict(encoding="utf-8")
BUILDING_IN_CI = bool(json.loads(os.environ.get("BUILDING_IN_CI", "0")))
TESTING_IN_CI = bool(json.loads(os.environ.get("TESTING_IN_CI", "0")))
CI_ARTIFACT = os.environ.get("CI_ARTIFACT", "wheel")
CI = bool(json.loads(os.environ.get("CI", "0")))
# test arg pass-throughs
ATEST_ARGS = json.loads(os.environ.get("ATEST_ARGS", "[]"))
@ -119,8 +151,8 @@ DIA_URLS = {
# ci
CI = ROOT / ".github"
ENV_CI = CI / "environment.yml"
GH = ROOT / ".github"
ENV_GH = GH / "environment.yml"
# tools
PY = ["python"]
@ -298,6 +330,15 @@ SERVER_EXT = {
if sorted(v.parent.glob("src/*/serverextension.py"))
}
# demo
DEMO = ROOT / "demo"
DEMO_CONFIG = DEMO / "jupyter_config.json"
DEMO_APPS = ["lab"]
DEMO_BUILD = BUILD / "demo"
DEMO_HASHES = DEMO_BUILD / "SHA256SUMS"
DEMO_ARCHIVE = (
DEMO_BUILD / f"""ipydrawio-lite-{JS_PKG_DATA["ipydrawio"]["version"]}.tgz"""
)
# docs
SPHINX_ARGS = json.loads(os.environ.get("SPHINX_ARGS", "[]"))
@ -360,18 +401,19 @@ ALL_PY = [
POSTBUILD_PY,
DOCS_CONF,
]
ALL_YML = [
*ROOT.glob("*.yml"),
*CI.rglob("*.yml"),
*BINDER.glob("*.yml"),
*DOCS.rglob("*.yml"),
]
ALL_YML = _clean(
ROOT.glob("*.yml"),
GH.rglob("*.yml"),
BINDER.glob("*.yml"),
DOCS.rglob("*.yml"),
)
ALL_JSON = [
*ROOT.glob("*.json"),
*PACKAGES.glob("*/*.json"),
*PACKAGES.glob("*/schema/*.json"),
*ATEST.glob("fixtures/*.json"),
*BINDER.glob("*.json"),
*[p for p in DEMO.rglob("*.json") if "/_output/" not in str(p)],
]
ALL_DIO = [*DOCS_DIO, *IPJT_TMPL_DIO, *ATEST_DIO]
ALL_MD = [*ROOT.glob("*.md"), *PACKAGES.glob("*/*.md"), *DOCS_MD]
@ -391,6 +433,15 @@ ALL_HEADERS = _clean(
ALL_YML,
ALL_ROBOT,
)
ALL_DEMO_CONTENTS = [
d
for d in ALL_DIO
if "test" not in str(d).lower()
and ".doit" not in d.name
and " " not in d.name
and d.name not in ["A.dio"]
]
ESLINTRC = PACKAGES / ".eslintrc.js"
RFLINT_OPTS = sum(
@ -470,7 +521,7 @@ CMD_LAB = ["jupyter", "lab", "--no-browser", "--debug"]
# conda building
RECIPE = ROOT / "conda.recipe/meta.yaml"
CONDA_BLD = BUILD / "conda-bld"
CONDARC = CI / ".condarc"
CONDARC = GH / ".condarc"
# could be mambabuild
CONDA_BUILDERER = os.environ.get("CONDA_BUILDERER", "build")
CONDA_BUILD_ARGS = [
@ -486,7 +537,7 @@ CONDA_PKGS = {
}
# env inheritance
ENV_INHERITS = {ENV_BINDER: [ENV_CI, ENV_DOCS], ENV_DOCS: [ENV_CI]}
ENV_INHERITS = {ENV_BINDER: [ENV_GH, ENV_DOCS], ENV_DOCS: [ENV_GH]}
def get_atest_stem(attempt=1, extra_args=None, browser=None):
@ -698,6 +749,62 @@ def pip_check():
return not len(lines)
# utilities
def _echo_ok(msg):
def _echo():
print(msg, flush=True)
return True
return _echo
def _ok(task, ok):
task.setdefault("targets", []).append(ok)
task["actions"] = [
lambda: [ok.exists() and ok.unlink(), True][-1],
*task["actions"],
lambda: [ok.parent.mkdir(exist_ok=True), ok.write_text("ok"), True][-1],
]
return task
def _show(*args, **kwargs):
import rich.markdown
for arg in args:
print_(arg()) if callable(arg) else print_(arg)
for kw, kwarg in kwargs.items():
print_(rich.markdown.Markdown(f"# {kw}") if console else kw)
print_(kwarg()) if callable(kwarg) else print_(kwarg)
def _copy_one(src, dest):
if not src.exists():
return False
if not dest.parent.exists():
dest.parent.mkdir(parents=True)
if dest.exists():
if dest.is_dir():
shutil.rmtree(dest)
else:
dest.unlink()
if src.is_dir():
shutil.copytree(src, dest)
else:
shutil.copy2(src, dest)
def _build_lite():
lite = ["jupyter", "lite"]
args = ["--source-date-epoch", SOURCE_DATE_EPOCH]
for act in ["build", "check", "archive"]:
act_args = list(map(str, [*lite, act, *args]))
if subprocess.call(act_args, cwd=DEMO) == 0:
continue
return False
# Late environment hacks
os.environ.update(
CONDARC=str(CONDARC),