Poetry+Hatch Monorepo (#1002)
* re-organize dirs + start using hatch * setup root pyproject.toml + basic invoke tasks * add publish task * more ruff fixes * get workflows to run * split up script runs * rename to check * change matrix order * make ruff happy * get tests to pass * check semver * more fixes * ignore missing coverage * fix cov * fix import sort * try build in env-js * try latest hatch-build-scripts * misc fixes * try to fix npm in gh action * do not set registry url by default * allow re-runs * no need for extra build * fix doc build and tests * remove scripts * fix tests * update contributor guide
This commit is contained in:
parent
cf7950d468
commit
ac582666eb
198 changed files with 3856 additions and 1913 deletions
|
@ -1,4 +1,4 @@
|
|||
name: Nox Session
|
||||
name: hatch-run
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
@ -6,12 +6,9 @@ on:
|
|||
job-name:
|
||||
required: true
|
||||
type: string
|
||||
nox-args:
|
||||
hatch-run:
|
||||
required: true
|
||||
type: string
|
||||
nox-session-args:
|
||||
required: false
|
||||
type: string
|
||||
runs-on-array:
|
||||
required: false
|
||||
type: string
|
||||
|
@ -20,6 +17,10 @@ on:
|
|||
required: false
|
||||
type: string
|
||||
default: '["3.x"]'
|
||||
node-registry-url:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
secrets:
|
||||
node-auth-token:
|
||||
required: false
|
||||
|
@ -29,19 +30,19 @@ on:
|
|||
required: false
|
||||
|
||||
jobs:
|
||||
nox-session:
|
||||
hatch:
|
||||
name: ${{ format(inputs.job-name, matrix.python-version, matrix.runs-on) }}
|
||||
strategy:
|
||||
matrix:
|
||||
runs-on: ${{ fromJson(inputs.runs-on-array) }}
|
||||
python-version: ${{ fromJson(inputs.python-version-array) }}
|
||||
runs-on: ${{ fromJson(inputs.runs-on-array) }}
|
||||
runs-on: ${{ matrix.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: "14.x"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
registry-url: ${{ inputs.node-registry-url }}
|
||||
- name: Pin NPM Version
|
||||
run: npm install -g npm@8.19.3
|
||||
- name: Use Python ${{ matrix.python-version }}
|
||||
|
@ -49,10 +50,10 @@ jobs:
|
|||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Python Dependencies
|
||||
run: pip install -r requirements/nox-deps.txt
|
||||
- name: Run Sessions
|
||||
run: pip install hatch poetry
|
||||
- name: Run Scripts
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.node-auth-token }}
|
||||
PYPI_USERNAME: ${{ secrets.pypi-username }}
|
||||
PYPI_PASSWORD: ${{ secrets.pypi-password }}
|
||||
run: nox ${{ inputs.nox-args }} --stop-on-first-error -- ${{ inputs.nox-session-args }}
|
||||
run: hatch run ${{ inputs.hatch-run }}
|
45
.github/workflows/check.yml
vendored
Normal file
45
.github/workflows/check.yml
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
name: check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
|
||||
jobs:
|
||||
test-py-cov:
|
||||
uses: ./.github/workflows/.hatch-run.yml
|
||||
with:
|
||||
job-name: "python-{0}"
|
||||
hatch-run: "test-py"
|
||||
lint-py:
|
||||
uses: ./.github/workflows/.hatch-run.yml
|
||||
with:
|
||||
job-name: "python-{0}"
|
||||
hatch-run: "lint-py"
|
||||
test-py-matrix:
|
||||
uses: ./.github/workflows/.hatch-run.yml
|
||||
with:
|
||||
job-name: "python-{0} {1}"
|
||||
hatch-run: "test-py --no-cov"
|
||||
runs-on-array: '["ubuntu-latest", "macos-latest", "windows-latest"]'
|
||||
python-version-array: '["3.9", "3.10", "3.11"]'
|
||||
test-docs:
|
||||
uses: ./.github/workflows/.hatch-run.yml
|
||||
with:
|
||||
job-name: "python-{0}"
|
||||
hatch-run: "test-docs"
|
||||
test-js:
|
||||
uses: ./.github/workflows/.hatch-run.yml
|
||||
with:
|
||||
job-name: "{1}"
|
||||
hatch-run: "test-js"
|
||||
lint-js:
|
||||
uses: ./.github/workflows/.hatch-run.yml
|
||||
with:
|
||||
job-name: "{1}"
|
||||
hatch-run: "lint-js"
|
5
.github/workflows/publish.yml
vendored
5
.github/workflows/publish.yml
vendored
|
@ -9,10 +9,11 @@ on:
|
|||
|
||||
jobs:
|
||||
publish:
|
||||
uses: ./.github/workflows/.nox-session.yml
|
||||
uses: ./.github/workflows/.hatch-run.yml
|
||||
with:
|
||||
job-name: "publish"
|
||||
nox-args: "-s publish"
|
||||
hatch-run: "publish"
|
||||
node-registry-url: "https://registry.npmjs.org"
|
||||
secrets:
|
||||
node-auth-token: ${{ secrets.NODE_AUTH_TOKEN }}
|
||||
pypi-username: ${{ secrets.PYPI_USERNAME }}
|
||||
|
|
37
.github/workflows/test.yml
vendored
37
.github/workflows/test.yml
vendored
|
@ -1,37 +0,0 @@
|
|||
name: test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
|
||||
jobs:
|
||||
python-exhaustive:
|
||||
uses: ./.github/workflows/.nox-session.yml
|
||||
with:
|
||||
job-name: "python-{0}"
|
||||
nox-args: "-t check-python"
|
||||
nox-session-args: "--pytest --maxfail=3 --reruns 3"
|
||||
python-environments:
|
||||
uses: ./.github/workflows/.nox-session.yml
|
||||
with:
|
||||
job-name: "python-{0} {1}"
|
||||
nox-args: "-s check-python-tests"
|
||||
nox-session-args: "--no-cov --pytest --maxfail=3 --reruns 3"
|
||||
runs-on-array: '["ubuntu-latest", "macos-latest", "windows-latest"]'
|
||||
python-version-array: '["3.7", "3.8", "3.9", "3.10", "3.11"]'
|
||||
docs:
|
||||
uses: ./.github/workflows/.nox-session.yml
|
||||
with:
|
||||
job-name: "python-{0}"
|
||||
nox-args: "-s check-docs"
|
||||
javascript:
|
||||
uses: ./.github/workflows/.nox-session.yml
|
||||
with:
|
||||
job-name: "{1}"
|
||||
nox-args: "-t check-javascript"
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,6 +1,3 @@
|
|||
# --- Build Artifacts ---
|
||||
src/reactpy/_client
|
||||
|
||||
# --- Jupyter ---
|
||||
*.ipynb_checkpoints
|
||||
*Untitled*.ipynb
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
repos:
|
||||
- repo: https://github.com/ambv/black
|
||||
rev: 23.1.0
|
||||
hooks:
|
||||
- id: black
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
name: isort
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v3.0.0-alpha.6
|
||||
hooks:
|
||||
- id: prettier
|
|
@ -8,42 +8,26 @@ RUN curl -sL https://deb.nodesource.com/setup_14.x | bash -
|
|||
RUN apt-get install -yq nodejs build-essential
|
||||
RUN npm install -g npm@8.5.0
|
||||
|
||||
# Create Python Venv
|
||||
# ------------------
|
||||
ENV VIRTUAL_ENV=/opt/venv
|
||||
RUN python3 -m venv $VIRTUAL_ENV
|
||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
RUN pip install --upgrade pip
|
||||
|
||||
# Install ReactPy
|
||||
# Install Pipx
|
||||
# ------------
|
||||
COPY requirements ./requirements
|
||||
RUN pip install -r requirements/build-docs.txt
|
||||
RUN pip install pipx
|
||||
|
||||
# Copy Files
|
||||
# ----------
|
||||
COPY LICENSE ./
|
||||
COPY src ./src
|
||||
COPY scripts ./scripts
|
||||
COPY setup.py ./
|
||||
COPY pyproject.toml ./
|
||||
COPY MANIFEST.in ./
|
||||
COPY README.md ./
|
||||
RUN pip install .[all]
|
||||
|
||||
# COPY License
|
||||
# -----------
|
||||
COPY LICENSE /app/
|
||||
|
||||
# Build the Docs
|
||||
# --------------
|
||||
COPY docs/__init__.py ./docs/
|
||||
COPY docs/app.py ./docs/
|
||||
COPY docs/examples.py ./docs/
|
||||
COPY docs/source ./docs/source
|
||||
COPY docs ./docs
|
||||
COPY branding ./branding
|
||||
RUN sphinx-build -v -W -b html docs/source docs/build
|
||||
|
||||
# Install and Build Docs
|
||||
# ----------------------
|
||||
WORKDIR /app/docs
|
||||
RUN pipx run poetry install
|
||||
RUN pipx run poetry run sphinx-build -v -W -b html source build
|
||||
|
||||
# Define Entrypoint
|
||||
# -----------------
|
||||
ENV PORT 5000
|
||||
ENV REACTPY_DEBUG_MODE=1
|
||||
ENV REACTPY_CHECK_VDOM_SPEC=0
|
||||
CMD python scripts/run_docs.py
|
||||
CMD pipx run poetry run python main.py
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@ -3,14 +3,15 @@ from pathlib import Path
|
|||
|
||||
from sanic import Sanic, response
|
||||
|
||||
from docs_app.examples import get_normalized_example_name, load_examples
|
||||
from reactpy import component
|
||||
from reactpy.backend.sanic import Options, configure, use_request
|
||||
from reactpy.core.types import ComponentConstructor
|
||||
|
||||
from .examples import get_normalized_example_name, load_examples
|
||||
THIS_DIR = Path(__file__).parent
|
||||
DOCS_DIR = THIS_DIR.parent
|
||||
DOCS_BUILD_DIR = DOCS_DIR / "build"
|
||||
|
||||
|
||||
HERE = Path(__file__).parent
|
||||
REACTPY_MODEL_SERVER_URL_PREFIX = "/_reactpy"
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
@ -40,13 +41,13 @@ def reload_examples():
|
|||
_EXAMPLES: dict[str, ComponentConstructor] = {}
|
||||
|
||||
|
||||
def make_app():
|
||||
app = Sanic("docs_app")
|
||||
def make_app(name: str):
|
||||
app = Sanic(name)
|
||||
|
||||
app.static("/docs", str(HERE / "build"))
|
||||
app.static("/docs", str(DOCS_BUILD_DIR))
|
||||
|
||||
@app.route("/")
|
||||
async def forward_to_index(request):
|
||||
async def forward_to_index(_):
|
||||
return response.redirect("/docs/index.html")
|
||||
|
||||
configure(
|
|
@ -13,22 +13,19 @@ from sphinx_autobuild.cli import (
|
|||
get_parser,
|
||||
)
|
||||
|
||||
from docs.app import make_app, reload_examples
|
||||
from docs_app.app import make_app, reload_examples
|
||||
from reactpy.backend.sanic import serve_development_app
|
||||
from reactpy.testing import clear_reactpy_web_modules_dir
|
||||
|
||||
|
||||
# these environment variable are used in custom Sphinx extensions
|
||||
os.environ["REACTPY_DOC_EXAMPLE_SERVER_HOST"] = "127.0.0.1:5555"
|
||||
os.environ["REACTPY_DOC_STATIC_SERVER_HOST"] = ""
|
||||
|
||||
_running_reactpy_servers = []
|
||||
|
||||
|
||||
def wrap_builder(old_builder):
|
||||
# This is the bit that we're injecting to get the example components to reload too
|
||||
|
||||
app = make_app()
|
||||
app = make_app("docs_dev_app")
|
||||
|
||||
thread_started = threading.Event()
|
||||
|
||||
|
@ -87,8 +84,8 @@ def main():
|
|||
ignore_handler = _get_ignore_handler(args)
|
||||
server.watch(srcdir, builder, ignore=ignore_handler)
|
||||
for dirpath in args.additional_watched_dirs:
|
||||
dirpath = os.path.realpath(dirpath)
|
||||
server.watch(dirpath, builder, ignore=ignore_handler)
|
||||
real_dirpath = os.path.realpath(dirpath)
|
||||
server.watch(real_dirpath, builder, ignore=ignore_handler)
|
||||
server.watch(outdir, ignore=ignore_handler)
|
||||
|
||||
if not args.no_initial_build:
|
||||
|
@ -100,12 +97,8 @@ def main():
|
|||
|
||||
def opener():
|
||||
time.sleep(args.delay)
|
||||
webbrowser.open("http://%s:%s/index.html" % (args.host, args.port))
|
||||
webbrowser.open(f"http://{args.host}:{args.port}/index.html")
|
||||
|
||||
threading.Thread(target=opener, daemon=True).start()
|
||||
|
||||
server.serve(port=portn, host=args.host, root=outdir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,16 +1,16 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Iterator
|
||||
from io import StringIO
|
||||
from pathlib import Path
|
||||
from traceback import format_exc
|
||||
from typing import Callable, Iterator
|
||||
from typing import Callable
|
||||
|
||||
import reactpy
|
||||
from reactpy.types import ComponentType
|
||||
|
||||
|
||||
HERE = Path(__file__)
|
||||
SOURCE_DIR = HERE.parent / "source"
|
||||
SOURCE_DIR = HERE.parent.parent / "source"
|
||||
CONF_FILE = SOURCE_DIR / "conf.py"
|
||||
RUN_ReactPy = reactpy.run
|
||||
|
||||
|
@ -148,7 +148,6 @@ class _PrintBuffer:
|
|||
|
||||
def set_callback(self, function: Callable[[str], None]) -> None:
|
||||
self._callback = function
|
||||
return None
|
||||
|
||||
def getvalue(self) -> str:
|
||||
return "".join(self._lines)
|
14
docs/docs_app/prod.py
Normal file
14
docs/docs_app/prod.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
import os
|
||||
|
||||
from docs_app.app import make_app
|
||||
|
||||
app = make_app("docs_prod_app")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
app.run(
|
||||
host="0.0.0.0", # noqa: S104
|
||||
port=int(os.environ.get("PORT", 5000)),
|
||||
workers=int(os.environ.get("WEB_CONCURRENCY", 1)),
|
||||
debug=bool(int(os.environ.get("DEBUG", "0"))),
|
||||
)
|
9
docs/main.py
Normal file
9
docs/main.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
import sys
|
||||
|
||||
from docs_app import dev, prod
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) == 1:
|
||||
prod.main()
|
||||
else:
|
||||
dev.main()
|
|
@ -1,35 +0,0 @@
|
|||
@ECHO OFF
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
REM Command file for Sphinx documentation
|
||||
|
||||
if "%SPHINXBUILD%" == "" (
|
||||
set SPHINXBUILD=sphinx-build
|
||||
)
|
||||
set SOURCEDIR=source
|
||||
set BUILDDIR=build
|
||||
|
||||
if "%1" == "" goto help
|
||||
|
||||
%SPHINXBUILD% >NUL 2>NUL
|
||||
if errorlevel 9009 (
|
||||
echo.
|
||||
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
|
||||
echo.installed, then set the SPHINXBUILD environment variable to point
|
||||
echo.to the full path of the 'sphinx-build' executable. Alternatively you
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
goto end
|
||||
|
||||
:help
|
||||
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
|
||||
|
||||
:end
|
||||
popd
|
2269
docs/poetry.lock
generated
Normal file
2269
docs/poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
23
docs/pyproject.toml
Normal file
23
docs/pyproject.toml
Normal file
|
@ -0,0 +1,23 @@
|
|||
[tool.poetry]
|
||||
name = "docs"
|
||||
version = "0.0.0"
|
||||
description = "docs"
|
||||
authors = ["rmorshea <ryan.morshead@gmail.com>"]
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.9"
|
||||
reactpy = { path = "../src/py/reactpy", extras = ["starlette", "sanic", "fastapi", "flask", "tornado", "testing"], develop = false }
|
||||
furo = "2022.04.07"
|
||||
sphinx = "*"
|
||||
sphinx-autodoc-typehints = "*"
|
||||
sphinx-copybutton = "*"
|
||||
sphinx-autobuild = "*"
|
||||
sphinx-reredirects = "*"
|
||||
sphinx-design = "*"
|
||||
sphinx-resolve-py-references = "*"
|
||||
sphinxext-opengraph = "*"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
25
docs/source/_custom_js/package-lock.json
generated
25
docs/source/_custom_js/package-lock.json
generated
|
@ -8,7 +8,7 @@
|
|||
"name": "reactpy-docs-example-loader",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@reactpy/client": "file:../../../src/client/packages/@reactpy/client"
|
||||
"@reactpy/client": "file:../../../src/js/packages/@reactpy/client"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^21.0.1",
|
||||
|
@ -21,6 +21,7 @@
|
|||
"../../../src/client/packages/@reactpy/client": {
|
||||
"version": "0.3.1",
|
||||
"integrity": "sha512-pIK5eNwFSHKXg7ClpASWFVKyZDYxz59MSFpVaX/OqJFkrJaAxBuhKGXNTMXmuyWOL5Iyvb/ErwwDRxQRzMNkfQ==",
|
||||
"extraneous": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"event-to-object": "^0.1.2",
|
||||
|
@ -58,8 +59,26 @@
|
|||
"react-dom": ">=16 <18"
|
||||
}
|
||||
},
|
||||
"../../../src/js/packages/@reactpy/client": {
|
||||
"version": "0.3.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"event-to-object": "^0.1.2",
|
||||
"json-pointer": "^0.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/json-pointer": "^1.0.31",
|
||||
"@types/react": "^17.0",
|
||||
"@types/react-dom": "^17.0",
|
||||
"typescript": "^4.9.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16 <18",
|
||||
"react-dom": ">=16 <18"
|
||||
}
|
||||
},
|
||||
"node_modules/@reactpy/client": {
|
||||
"resolved": "../../../src/client/packages/@reactpy/client",
|
||||
"resolved": "../../../src/js/packages/@reactpy/client",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@rollup/plugin-commonjs": {
|
||||
|
@ -434,7 +453,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@reactpy/client": {
|
||||
"version": "file:../../../src/client/packages/@reactpy/client",
|
||||
"version": "file:../../../src/js/packages/@reactpy/client",
|
||||
"requires": {
|
||||
"@types/json-pointer": "^1.0.31",
|
||||
"@types/react": "^17.0",
|
||||
|
|
|
@ -15,6 +15,6 @@
|
|||
"rollup": "^2.35.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@reactpy/client": "file:../../../src/client/packages/@reactpy/client"
|
||||
"@reactpy/client": "file:../../../src/js/packages/@reactpy/client"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ from sphinx.application import Sphinx
|
|||
from sphinx.ext.doctest import DocTestBuilder
|
||||
from sphinx.ext.doctest import setup as doctest_setup
|
||||
|
||||
|
||||
test_template = """
|
||||
import asyncio as __test_template_asyncio
|
||||
|
||||
|
@ -41,10 +40,8 @@ class AsyncDoctestBuilder(DocTestBuilder):
|
|||
@test_runner.setter
|
||||
def test_runner(self, value: DocTestRunner) -> None:
|
||||
self._test_runner = TestRunnerWrapper(value)
|
||||
return None
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> None:
|
||||
doctest_setup(app)
|
||||
app.add_builder(AsyncDoctestBuilder, override=True)
|
||||
return None
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from collections.abc import Collection, Iterator
|
||||
from pathlib import Path
|
||||
from typing import Collection, Iterator
|
||||
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
|
||||
HERE = Path(__file__).parent
|
||||
SRC = HERE.parent.parent.parent / "src"
|
||||
PYTHON_PACKAGE = SRC / "reactpy"
|
||||
PYTHON_PACKAGE = SRC / "py" / "reactpy" / "reactpy"
|
||||
|
||||
AUTO_DIR = HERE.parent / "_auto"
|
||||
AUTO_DIR.mkdir(exist_ok=True)
|
||||
|
@ -82,9 +81,12 @@ def get_module_name(path: Path) -> str:
|
|||
|
||||
|
||||
def get_section_symbol(path: Path) -> str:
|
||||
rel_path_parts = path.relative_to(PYTHON_PACKAGE).parts
|
||||
assert len(rel_path_parts) < len(SECTION_SYMBOLS), "package structure is too deep"
|
||||
return SECTION_SYMBOLS[len(rel_path_parts)]
|
||||
rel_path = path.relative_to(PYTHON_PACKAGE)
|
||||
rel_path_parts = rel_path.parts
|
||||
if len(rel_path_parts) > len(SECTION_SYMBOLS):
|
||||
msg = f"package structure is too deep - ran out of section symbols: {rel_path}"
|
||||
raise RuntimeError(msg)
|
||||
return SECTION_SYMBOLS[len(rel_path_parts) - 1]
|
||||
|
||||
|
||||
def walk_python_files(root: Path, ignore_dirs: Collection[str]) -> Iterator[Path]:
|
||||
|
|
|
@ -3,11 +3,10 @@ from pathlib import Path
|
|||
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
|
||||
SOURCE_DIR = Path(__file__).parent.parent
|
||||
CUSTOM_JS_DIR = SOURCE_DIR / "_custom_js"
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> None:
|
||||
subprocess.run("npm install", cwd=CUSTOM_JS_DIR, shell=True)
|
||||
subprocess.run("npm run build", cwd=CUSTOM_JS_DIR, shell=True)
|
||||
subprocess.run("npm install", cwd=CUSTOM_JS_DIR, shell=True) # noqa S607
|
||||
subprocess.run("npm run build", cwd=CUSTOM_JS_DIR, shell=True) # noqa S607
|
||||
|
|
|
@ -17,7 +17,6 @@ from sphinx.locale import __
|
|||
from sphinx.util import logging
|
||||
from sphinx.util.nodes import clean_astext
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
|
|
@ -4,18 +4,17 @@ import re
|
|||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from docs_app.examples import (
|
||||
SOURCE_DIR,
|
||||
get_example_files_by_name,
|
||||
get_normalized_example_name,
|
||||
)
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils.statemachine import StringList
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from sphinx_design.tabs import TabSetDirective
|
||||
|
||||
from docs.examples import (
|
||||
SOURCE_DIR,
|
||||
get_example_files_by_name,
|
||||
get_normalized_example_name,
|
||||
)
|
||||
|
||||
|
||||
class WidgetExample(SphinxDirective):
|
||||
has_content = False
|
||||
|
@ -41,10 +40,8 @@ class WidgetExample(SphinxDirective):
|
|||
ex_files = get_example_files_by_name(example_name)
|
||||
if not ex_files:
|
||||
src_file, line_num = self.get_source_info()
|
||||
raise ValueError(
|
||||
f"Missing example named {example_name!r} "
|
||||
f"referenced by document {src_file}:{line_num}"
|
||||
)
|
||||
msg = f"Missing example named {example_name!r} referenced by document {src_file}:{line_num}"
|
||||
raise ValueError(msg)
|
||||
|
||||
labeled_tab_items: list[tuple[str, Any]] = []
|
||||
if len(ex_files) == 1:
|
||||
|
@ -114,7 +111,8 @@ def _literal_include(path: Path, linenos: bool):
|
|||
".json": "json",
|
||||
}[path.suffix]
|
||||
except KeyError:
|
||||
raise ValueError(f"Unknown extension type {path.suffix!r}")
|
||||
msg = f"Unknown extension type {path.suffix!r}"
|
||||
raise ValueError(msg) from None
|
||||
|
||||
return _literal_include_template.format(
|
||||
name=str(path.relative_to(SOURCE_DIR)),
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
print(sys.path)
|
||||
|
||||
from docs_app.examples import get_normalized_example_name
|
||||
from docutils.nodes import raw
|
||||
from docutils.parsers.rst import directives
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
|
||||
from docs.examples import get_normalized_example_name
|
||||
|
||||
|
||||
_REACTPY_EXAMPLE_HOST = os.environ.get("REACTPY_DOC_EXAMPLE_SERVER_HOST", "")
|
||||
_REACTPY_STATIC_HOST = os.environ.get("REACTPY_DOC_STATIC_SERVER_HOST", "/docs").rstrip(
|
||||
"/"
|
||||
|
|
|
@ -80,12 +80,18 @@ In order to develop ReactPy locally you'll first need to install the following:
|
|||
* - What to Install
|
||||
- How to Install
|
||||
|
||||
* - Python >= 3.9
|
||||
- https://realpython.com/installing-python/
|
||||
|
||||
* - Hatch
|
||||
- https://hatch.pypa.io/latest/install/
|
||||
|
||||
* - Poetry
|
||||
- https://python-poetry.org/docs/#installation
|
||||
|
||||
* - Git
|
||||
- https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
|
||||
|
||||
* - Python >= 3.7
|
||||
- https://realpython.com/installing-python/
|
||||
|
||||
* - NodeJS >= 14
|
||||
- https://nodejs.org/en/download/package-manager/
|
||||
|
||||
|
@ -106,57 +112,38 @@ Once done, you can clone a local copy of this repository:
|
|||
git clone https://github.com/reactive-python/reactpy.git
|
||||
cd reactpy
|
||||
|
||||
Then, you should be able to run the command below to:
|
||||
|
||||
- Install an editable version of the Python code
|
||||
|
||||
- Download, build, and install Javascript dependencies
|
||||
|
||||
- Install some pre-commit_ hooks for Git
|
||||
Then, you should be able to activate your development environment with:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install -e . -r requirements.txt && pre-commit install
|
||||
|
||||
If you modify any Javascript, you'll need to re-install ReactPy:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install -e .
|
||||
|
||||
However you may also ``cd`` to the ``src/client`` directory which contains a
|
||||
``package.json`` that you can use to run standard ``npm`` commands from.
|
||||
hatch shell
|
||||
|
||||
|
||||
Running The Tests
|
||||
-----------------
|
||||
|
||||
The test suite for ReactPy is executed with Nox_, which should already be installed if you
|
||||
followed the `earlier instructions <Development Environment>`_. The suite covers:
|
||||
|
||||
1. Server-side Python code with PyTest_
|
||||
|
||||
2. The end-to-end application using Playwright_ in Python
|
||||
|
||||
3. Client-side Javascript code with UVU_
|
||||
|
||||
Once you've installed them you'll be able to run:
|
||||
Tests exist for both Python and Javascript. These can be run with the following:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
nox -s check-python-tests
|
||||
hatch run test-py
|
||||
hatch run test-js
|
||||
|
||||
You can observe the browser as the tests are running by passing an extra flag:
|
||||
If you want to run tests for individual packages you'll need to ``cd`` into the
|
||||
package directory and run the tests from there. For example, to run the tests just for
|
||||
the ``reactpy`` package you'd do:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
nox -s check-python-tests -- --headed
|
||||
cd src/py/reactpy
|
||||
hatch run test --headed # run the tests in a browser window
|
||||
|
||||
To see a full list of available commands (e.g. ``nox -s <your-command>``) run:
|
||||
For Javascript, you'd do:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
nox -l
|
||||
cd src/js/packages/event-to-object
|
||||
npm run check:tests
|
||||
|
||||
|
||||
Code Quality Checks
|
||||
|
@ -172,8 +159,9 @@ The following are currently being used:
|
|||
- MyPy_ - a static type checker
|
||||
- Black_ - an opinionated code formatter
|
||||
- Flake8_ - a style guide enforcement tool
|
||||
- ISort_ - a utility for alphabetically sorting imports
|
||||
- Ruff_ - An extremely fast Python linter, written in Rust.
|
||||
- Prettier_ - a tool for automatically formatting various file types
|
||||
- EsLint_ - A Javascript linter
|
||||
|
||||
The most strict measure of quality enforced on the codebase is 100% test coverage in
|
||||
Python files. This means that every line of coded added to ReactPy requires a test case
|
||||
|
@ -186,10 +174,10 @@ your :ref:`Pull Request <Making a Pull Request>`.
|
|||
|
||||
.. note::
|
||||
|
||||
You can manually run ``nox -s format`` to auto format your code without having to
|
||||
do so via ``pre-commit``. However, many IDEs have ways to automatically format upon
|
||||
saving a file
|
||||
(e.g.`VSCode <https://code.visualstudio.com/docs/python/editing#_formatting>`__)
|
||||
You can manually run ``hatch run lint --fix`` to auto format your code without
|
||||
having to do so via ``pre-commit``. However, many IDEs have ways to automatically
|
||||
format upon saving a file (e.g.
|
||||
`VSCode <https://code.visualstudio.com/docs/python/editing#_formatting>`__)
|
||||
|
||||
|
||||
Building The Documentation
|
||||
|
@ -199,7 +187,7 @@ To build and display the documentation locally run:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
nox -s docs
|
||||
hatch run docs
|
||||
|
||||
This will compile the documentation from its source files into HTML, start a web server,
|
||||
and open a browser to display the now generated documentation. Whenever you change any
|
||||
|
@ -211,14 +199,14 @@ To run some of the examples in the documentation as if they were tests run:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
nox -s test_docs
|
||||
hatch run test-docs
|
||||
|
||||
Building the documentation as it's deployed in production requires Docker_. Once you've
|
||||
installed Docker, you can run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
nox -s docs_in_docker
|
||||
hatch run docs --docker
|
||||
|
||||
Where you can then navigate to http://localhost:5000..
|
||||
|
||||
|
@ -329,7 +317,6 @@ you should refer to their respective documentation in the links below:
|
|||
.. _pip: https://pypi.org/project/pip/
|
||||
.. _PyTest: pytest <https://docs.pytest.org
|
||||
.. _Playwright: https://playwright.dev/python/
|
||||
.. _Nox: https://nox.thea.codes/en/stable/#
|
||||
.. _React: https://reactjs.org/
|
||||
.. _Heroku: https://www.heroku.com/what
|
||||
.. _GitHub Actions: https://github.com/features/actions
|
||||
|
@ -338,6 +325,7 @@ you should refer to their respective documentation in the links below:
|
|||
.. _MyPy: http://mypy-lang.org/
|
||||
.. _Black: https://github.com/psf/black
|
||||
.. _Flake8: https://flake8.pycqa.org/en/latest/
|
||||
.. _ISort: https://pycqa.github.io/isort/
|
||||
.. _Ruff: https://github.com/charliermarsh/ruff
|
||||
.. _UVU: https://github.com/lukeed/uvu
|
||||
.. _Prettier: https://prettier.io/
|
||||
.. _ESLint: https://eslint.org/
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
|
@ -10,16 +9,14 @@ import sys
|
|||
from doctest import DONT_ACCEPT_TRUE_FOR_1, ELLIPSIS, NORMALIZE_WHITESPACE
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
# -- Path Setup --------------------------------------------------------------
|
||||
|
||||
THIS_DIR = Path(__file__).parent
|
||||
ROOT_DIR = THIS_DIR.parent.parent
|
||||
|
||||
# project path
|
||||
sys.path.insert(0, str(ROOT_DIR))
|
||||
DOCS_DIR = THIS_DIR.parent
|
||||
|
||||
# extension path
|
||||
sys.path.insert(0, str(DOCS_DIR))
|
||||
sys.path.insert(0, str(THIS_DIR / "_exts"))
|
||||
|
||||
|
||||
|
@ -32,7 +29,7 @@ description = (
|
|||
"a single line of Javascript. It can be run standalone, in a Jupyter Notebook, or "
|
||||
"as part of an existing application."
|
||||
)
|
||||
copyright = "2023, Ryan Morshead"
|
||||
copyright = "2023, Ryan Morshead" # noqa: A001
|
||||
author = "Ryan Morshead"
|
||||
|
||||
# -- Common External Links ---------------------------------------------------
|
||||
|
@ -40,23 +37,23 @@ author = "Ryan Morshead"
|
|||
extlinks = {
|
||||
"issue": (
|
||||
"https://github.com/reactive-python/reactpy/issues/%s",
|
||||
"#",
|
||||
"#%s",
|
||||
),
|
||||
"pull": (
|
||||
"https://github.com/reactive-python/reactpy/pull/%s",
|
||||
"#",
|
||||
"#%s",
|
||||
),
|
||||
"discussion": (
|
||||
"https://github.com/reactive-python/reactpy/discussions/%s",
|
||||
"#",
|
||||
"#%s",
|
||||
),
|
||||
"discussion-type": (
|
||||
"https://github.com/reactive-python/reactpy/discussions/categories/%s",
|
||||
"",
|
||||
"%s",
|
||||
),
|
||||
"commit": (
|
||||
"https://github.com/reactive-python/reactpy/commit/%s",
|
||||
"",
|
||||
"%s",
|
||||
),
|
||||
}
|
||||
extlinks_detect_hardcoded_links = True
|
||||
|
@ -320,7 +317,7 @@ epub_exclude_files = ["search.html"]
|
|||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
intersphinx_mapping = {
|
||||
"https://docs.python.org/": None,
|
||||
"python": ("https://docs.python.org/3", None),
|
||||
"pyalect": ("https://pyalect.readthedocs.io/en/latest", None),
|
||||
"sanic": ("https://sanic.readthedocs.io/en/latest/", None),
|
||||
"tornado": ("https://www.tornadoweb.org/en/stable/", None),
|
||||
|
|
|
@ -3,7 +3,6 @@ from pathlib import Path
|
|||
|
||||
from reactpy import component, hooks, html, run
|
||||
|
||||
|
||||
HERE = Path(__file__)
|
||||
DATA_PATH = HERE.parent / "data.json"
|
||||
sculpture_data = json.loads(DATA_PATH.read_text())
|
||||
|
|
|
@ -3,7 +3,6 @@ from pathlib import Path
|
|||
|
||||
from reactpy import component, hooks, html, run
|
||||
|
||||
|
||||
HERE = Path(__file__)
|
||||
DATA_PATH = HERE.parent / "data.json"
|
||||
sculpture_data = json.loads(DATA_PATH.read_text())
|
||||
|
|
|
@ -3,7 +3,6 @@ from pathlib import Path
|
|||
|
||||
from reactpy import component, hooks, html, run
|
||||
|
||||
|
||||
HERE = Path(__file__)
|
||||
DATA_PATH = HERE.parent / "data.json"
|
||||
sculpture_data = json.loads(DATA_PATH.read_text())
|
||||
|
|
|
@ -8,7 +8,7 @@ def ArtistList():
|
|||
)
|
||||
|
||||
def handle_sort_click(event):
|
||||
set_artists(list(sorted(artists)))
|
||||
set_artists(sorted(artists))
|
||||
|
||||
def handle_reverse_click(event):
|
||||
set_artists(list(reversed(artists)))
|
||||
|
|
|
@ -6,7 +6,7 @@ def DataList(items, filter_by_priority=None, sort_by_priority=False):
|
|||
if filter_by_priority is not None:
|
||||
items = [i for i in items if i["priority"] <= filter_by_priority]
|
||||
if sort_by_priority:
|
||||
items = list(sorted(items, key=lambda i: i["priority"]))
|
||||
items = sorted(items, key=lambda i: i["priority"])
|
||||
list_item_elements = [html.li(i["text"]) for i in items]
|
||||
return html.ul(list_item_elements)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ def DataList(items, filter_by_priority=None, sort_by_priority=False):
|
|||
if filter_by_priority is not None:
|
||||
items = [i for i in items if i["priority"] <= filter_by_priority]
|
||||
if sort_by_priority:
|
||||
items = list(sorted(items, key=lambda i: i["priority"]))
|
||||
items = sorted(items, key=lambda i: i["priority"])
|
||||
list_item_elements = [html.li({"key": i["id"]}, i["text"]) for i in items]
|
||||
return html.ul(list_item_elements)
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
from reactpy import component, run, web
|
||||
|
||||
|
||||
mui = web.module_from_template(
|
||||
"react@^17.0.0",
|
||||
"@material-ui/core@4.12.4",
|
||||
|
|
|
@ -2,7 +2,6 @@ import json
|
|||
|
||||
import reactpy
|
||||
|
||||
|
||||
mui = reactpy.web.module_from_template(
|
||||
"react@^17.0.0",
|
||||
"@material-ui/core@4.12.4",
|
||||
|
|
|
@ -2,7 +2,6 @@ from pathlib import Path
|
|||
|
||||
from reactpy import component, run, web
|
||||
|
||||
|
||||
file = Path(__file__).parent / "super-simple-chart.js"
|
||||
ssc = web.module_from_file("super-simple-chart", file, fallback="⌛")
|
||||
SuperSimpleChart = web.export(ssc, "SuperSimpleChart")
|
||||
|
|
|
@ -13,7 +13,8 @@ def GoodComponent():
|
|||
|
||||
@component
|
||||
def BadComponent():
|
||||
raise RuntimeError("This component raised an error")
|
||||
msg = "This component raised an error"
|
||||
raise RuntimeError(msg)
|
||||
|
||||
|
||||
run(App)
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
from reactpy import run
|
||||
from reactpy.backend import fastapi as fastapi_server
|
||||
|
||||
|
||||
# the run() function is the entry point for examples
|
||||
fastapi_server.configure = lambda _, cmpt: run(cmpt)
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
from reactpy import run
|
||||
from reactpy.backend import flask as flask_server
|
||||
|
||||
|
||||
# the run() function is the entry point for examples
|
||||
flask_server.configure = lambda _, cmpt: run(cmpt)
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
from reactpy import run
|
||||
from reactpy.backend import sanic as sanic_server
|
||||
|
||||
|
||||
# the run() function is the entry point for examples
|
||||
sanic_server.configure = lambda _, cmpt: run(cmpt)
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
from reactpy import run
|
||||
from reactpy.backend import starlette as starlette_server
|
||||
|
||||
|
||||
# the run() function is the entry point for examples
|
||||
starlette_server.configure = lambda _, cmpt: run(cmpt)
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
from reactpy import run
|
||||
from reactpy.backend import tornado as tornado_server
|
||||
|
||||
|
||||
# the run() function is the entry point for examples
|
||||
tornado_server.configure = lambda _, cmpt: run(cmpt)
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import reactpy
|
||||
|
||||
|
||||
reactpy.run(reactpy.sample.SampleApp)
|
||||
|
|
|
@ -4,7 +4,6 @@ from sanic.response import file
|
|||
from reactpy import component, html
|
||||
from reactpy.backend.sanic import Options, configure
|
||||
|
||||
|
||||
app = Sanic("MyApp")
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ from pathlib import Path
|
|||
|
||||
from reactpy import component, hooks, html, run
|
||||
|
||||
|
||||
HERE = Path(__file__)
|
||||
DATA_PATH = HERE.parent / "data.json"
|
||||
food_data = json.loads(DATA_PATH.read_text())
|
||||
|
@ -33,11 +32,10 @@ def Table(value, set_value):
|
|||
name = html.td(row["name"])
|
||||
descr = html.td(row["description"])
|
||||
tr = html.tr(name, descr, value)
|
||||
if value == "":
|
||||
if not value:
|
||||
rows.append(tr)
|
||||
elif value.lower() in row["name"].lower():
|
||||
rows.append(tr)
|
||||
else:
|
||||
if value.lower() in row["name"].lower():
|
||||
rows.append(tr)
|
||||
headers = html.tr(html.td(html.b("name")), html.td(html.b("description")))
|
||||
table = html.table(html.thead(headers), html.tbody(rows))
|
||||
return table
|
||||
|
|
|
@ -4,7 +4,6 @@ from typing import NamedTuple
|
|||
from reactpy import component, html, run, use_state
|
||||
from reactpy.widgets import image
|
||||
|
||||
|
||||
HERE = Path(__file__)
|
||||
CHARACTER_IMAGE = (HERE.parent / "static" / "bunny.png").read_bytes()
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import reactpy
|
||||
|
||||
|
||||
mui = reactpy.web.module_from_template("react", "@material-ui/core@^5.0", fallback="⌛")
|
||||
Switch = reactpy.web.export(mui, "Switch")
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ from reactpy.widgets import image
|
|||
def PolynomialPlot():
|
||||
coefficients, set_coefficients = reactpy.hooks.use_state([0])
|
||||
|
||||
x = [n for n in linspace(-1, 1, 50)]
|
||||
x = list(linspace(-1, 1, 50))
|
||||
y = [polynomial(value, coefficients) for value in x]
|
||||
|
||||
return reactpy.html.div(
|
||||
|
@ -31,7 +31,7 @@ def ExpandableNumberInputs(values, set_values):
|
|||
inputs.append(poly_coef_input(i + 1, set_value_at_index))
|
||||
|
||||
def add_input():
|
||||
set_values(values + [0])
|
||||
set_values([*values, 0])
|
||||
|
||||
def del_input():
|
||||
set_values(values[:-1])
|
||||
|
@ -62,7 +62,7 @@ def poly_coef_input(index, callback):
|
|||
reactpy.html.label(
|
||||
"C",
|
||||
reactpy.html.sub(index),
|
||||
" × X",
|
||||
" x X",
|
||||
reactpy.html.sup(index),
|
||||
),
|
||||
reactpy.html.input({"type": "number", "on_change": callback}),
|
||||
|
|
|
@ -2,7 +2,6 @@ import random
|
|||
|
||||
import reactpy
|
||||
|
||||
|
||||
react_cytoscapejs = reactpy.web.module_from_template(
|
||||
"react",
|
||||
"react-cytoscapejs",
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import reactpy
|
||||
|
||||
|
||||
pigeon_maps = reactpy.web.module_from_template("react", "pigeon-maps", fallback="⌛")
|
||||
Map, Marker = reactpy.web.export(pigeon_maps, ["Map", "Marker"])
|
||||
|
||||
|
@ -9,18 +8,16 @@ Map, Marker = reactpy.web.export(pigeon_maps, ["Map", "Marker"])
|
|||
def MapWithMarkers():
|
||||
marker_anchor, add_marker_anchor, remove_marker_anchor = use_set()
|
||||
|
||||
markers = list(
|
||||
map(
|
||||
lambda anchor: Marker(
|
||||
{
|
||||
"anchor": anchor,
|
||||
"onClick": lambda: remove_marker_anchor(anchor),
|
||||
},
|
||||
key=str(anchor),
|
||||
),
|
||||
marker_anchor,
|
||||
markers = [
|
||||
Marker(
|
||||
{
|
||||
"anchor": anchor,
|
||||
"onClick": lambda event, a=anchor: remove_marker_anchor(a),
|
||||
},
|
||||
key=str(anchor),
|
||||
)
|
||||
)
|
||||
for anchor in marker_anchor
|
||||
]
|
||||
|
||||
return Map(
|
||||
{
|
||||
|
|
|
@ -5,7 +5,6 @@ import time
|
|||
import reactpy
|
||||
from reactpy.widgets import Input
|
||||
|
||||
|
||||
victory = reactpy.web.module_from_template(
|
||||
"react",
|
||||
"victory-line",
|
||||
|
|
|
@ -107,7 +107,7 @@ def GameLoop(grid_size, block_scale, set_game_state):
|
|||
|
||||
if snake[-1] == food:
|
||||
set_food()
|
||||
new_snake = snake + [new_snake_head]
|
||||
new_snake = [*snake, new_snake_head]
|
||||
else:
|
||||
new_snake = snake[1:] + [new_snake_head]
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ def Todo():
|
|||
|
||||
async def add_new_task(event):
|
||||
if event["key"] == "Enter":
|
||||
set_items(items + [event["target"]["value"]])
|
||||
set_items([*items, event["target"]["value"]])
|
||||
|
||||
tasks = []
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ def reducer(count, action):
|
|||
elif action == "reset":
|
||||
return 0
|
||||
else:
|
||||
raise ValueError(f"Unknown action '{action}'")
|
||||
msg = f"Unknown action '{action}'"
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
@reactpy.component
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import reactpy
|
||||
|
||||
|
||||
victory = reactpy.web.module_from_template("react", "victory-bar", fallback="⌛")
|
||||
VictoryBar = reactpy.web.export(victory, "VictoryBar")
|
||||
|
||||
|
|
553
noxfile.py
553
noxfile.py
|
@ -1,553 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from argparse import REMAINDER
|
||||
from dataclasses import replace
|
||||
from pathlib import Path
|
||||
from shutil import rmtree
|
||||
from typing import TYPE_CHECKING, Callable, NamedTuple, Sequence, cast
|
||||
|
||||
from noxopt import Annotated, NoxOpt, Option, Session
|
||||
|
||||
|
||||
# --- Typing Preamble ------------------------------------------------------------------
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# not available in typing module until Python 3.8
|
||||
# not available in typing module until Python 3.10
|
||||
from typing import Literal, Protocol, TypeAlias
|
||||
|
||||
class ReleasePrepFunc(Protocol):
|
||||
def __call__(
|
||||
self, session: Session, package: PackageInfo
|
||||
) -> Callable[[bool], None]:
|
||||
...
|
||||
|
||||
|
||||
LanguageName: TypeAlias = "Literal['py', 'js']"
|
||||
|
||||
|
||||
# --- Constants ------------------------------------------------------------------------
|
||||
|
||||
|
||||
ROOT_DIR = Path(__file__).parent.resolve()
|
||||
SRC_DIR = ROOT_DIR / "src"
|
||||
CLIENT_DIR = SRC_DIR / "client"
|
||||
REACTPY_DIR = SRC_DIR / "reactpy"
|
||||
TAG_PATTERN = re.compile(
|
||||
# start
|
||||
r"^"
|
||||
# package name
|
||||
r"(?P<name>[0-9a-zA-Z-@/]+)-"
|
||||
# package version
|
||||
r"v(?P<version>[0-9][0-9a-zA-Z-\.\+]*)"
|
||||
# end
|
||||
r"$"
|
||||
)
|
||||
REMAINING_ARGS = Option(nargs=REMAINDER, type=str)
|
||||
|
||||
|
||||
# --- Session Setup --------------------------------------------------------------------
|
||||
|
||||
|
||||
group = NoxOpt(auto_tag=True)
|
||||
|
||||
|
||||
@group.setup
|
||||
def setup_checks(session: Session) -> None:
|
||||
session.install("--upgrade", "pip")
|
||||
session.run("pip", "--version")
|
||||
session.run("npm", "--version", external=True)
|
||||
|
||||
|
||||
@group.setup("check-javascript")
|
||||
def setup_javascript_checks(session: Session) -> None:
|
||||
session.chdir(CLIENT_DIR)
|
||||
session.run("npm", "ci", external=True)
|
||||
|
||||
|
||||
# --- Session Definitions --------------------------------------------------------------
|
||||
|
||||
|
||||
@group.session
|
||||
def format(session: Session) -> None:
|
||||
"""Auto format Python and Javascript code"""
|
||||
# format Python
|
||||
install_requirements_file(session, "check-style")
|
||||
session.run("black", ".")
|
||||
session.run("isort", ".")
|
||||
|
||||
# format client Javascript
|
||||
session.chdir(CLIENT_DIR)
|
||||
session.run("npm", "run", "format", external=True)
|
||||
|
||||
# format docs Javascript
|
||||
session.chdir(ROOT_DIR / "docs" / "source" / "_custom_js")
|
||||
session.run("npm", "run", "format", external=True)
|
||||
|
||||
|
||||
@group.session
|
||||
def tsc(session: Session) -> None:
|
||||
session.chdir(CLIENT_DIR)
|
||||
session.run("npx", "tsc", "-b", "-w", "packages/app", external=True)
|
||||
|
||||
|
||||
@group.session
|
||||
def example(session: Session) -> None:
|
||||
"""Run an example"""
|
||||
session.install("matplotlib")
|
||||
install_reactpy_dev(session)
|
||||
session.run(
|
||||
"python",
|
||||
"scripts/one_example.py",
|
||||
*session.posargs,
|
||||
env=get_reactpy_script_env(),
|
||||
)
|
||||
|
||||
|
||||
@group.session
|
||||
def docs(session: Session) -> None:
|
||||
"""Build and display documentation in the browser (automatically reloads on change)"""
|
||||
install_requirements_file(session, "build-docs")
|
||||
install_reactpy_dev(session)
|
||||
session.run(
|
||||
"python",
|
||||
"scripts/live_docs.py",
|
||||
"--open-browser",
|
||||
# watch python source too
|
||||
"--watch=src/reactpy",
|
||||
# for some reason this matches absolute paths
|
||||
"--ignore=**/_auto/*",
|
||||
"--ignore=**/_static/custom.js",
|
||||
"--ignore=**/node_modules/*",
|
||||
"--ignore=**/package-lock.json",
|
||||
"-a",
|
||||
"-E",
|
||||
"-b",
|
||||
"html",
|
||||
"docs/source",
|
||||
"docs/build",
|
||||
env={**os.environ, **get_reactpy_script_env()},
|
||||
)
|
||||
|
||||
|
||||
@group.session
|
||||
def docs_in_docker(session: Session) -> None:
|
||||
"""Build a docker image for the documentation and run it to mimic production"""
|
||||
session.run(
|
||||
"docker",
|
||||
"build",
|
||||
".",
|
||||
"--file",
|
||||
"docs/Dockerfile",
|
||||
"--tag",
|
||||
"reactpy-docs:latest",
|
||||
external=True,
|
||||
)
|
||||
session.run(
|
||||
"docker",
|
||||
"run",
|
||||
"-it",
|
||||
"-p",
|
||||
"5000:5000",
|
||||
"-e",
|
||||
"DEBUG=1",
|
||||
"--rm",
|
||||
"reactpy-docs:latest",
|
||||
external=True,
|
||||
)
|
||||
|
||||
|
||||
@group.session
|
||||
def check_python_tests(
|
||||
session: Session,
|
||||
no_cov: Annotated[bool, Option(help="turn off coverage checks")] = False,
|
||||
headed: Annotated[bool, Option(help="run tests with a headed browser")] = False,
|
||||
pytest: Annotated[Sequence[str], replace(REMAINING_ARGS, help="pytest args")] = (),
|
||||
) -> None:
|
||||
"""Run the Python-based test suite"""
|
||||
session.env["REACTPY_DEBUG_MODE"] = "1"
|
||||
install_requirements_file(session, "test-env")
|
||||
session.run("playwright", "install", "chromium")
|
||||
|
||||
args = ["pytest", *pytest]
|
||||
if headed:
|
||||
args.append("--headed")
|
||||
|
||||
if no_cov:
|
||||
session.log("Coverage won't be checked")
|
||||
session.install(".[all]")
|
||||
else:
|
||||
args = ["coverage", "run", "--source=src/reactpy", "--module", *args]
|
||||
install_reactpy_dev(session)
|
||||
|
||||
session.run(*args)
|
||||
|
||||
if not no_cov:
|
||||
session.run("coverage", "report")
|
||||
|
||||
|
||||
@group.session
|
||||
def check_python_types(session: Session) -> None:
|
||||
"""Perform a static type analysis of the Python codebase"""
|
||||
install_requirements_file(session, "check-types")
|
||||
install_requirements_file(session, "pkg-deps")
|
||||
install_requirements_file(session, "pkg-extras")
|
||||
session.run("mypy", "--version")
|
||||
session.run("mypy", "--show-error-codes", "--strict", "src/reactpy")
|
||||
session.run("mypy", "--show-error-codes", "noxfile.py")
|
||||
|
||||
|
||||
@group.session
|
||||
def check_python_format(session: Session) -> None:
|
||||
"""Check that Python style guidelines are being followed"""
|
||||
install_requirements_file(session, "check-style")
|
||||
session.run("flake8", "src/reactpy", "tests", "docs")
|
||||
session.run("black", ".", "--check")
|
||||
session.run("isort", ".", "--check-only")
|
||||
|
||||
|
||||
@group.session
|
||||
def check_python_build(session: Session) -> None:
|
||||
"""Test whether the Python package can be build for distribution"""
|
||||
install_requirements_file(session, "build-pkg")
|
||||
session.run("python", "-m", "build", "--sdist", "--wheel", "--outdir", "dist", ".")
|
||||
|
||||
|
||||
@group.session
|
||||
def check_docs(session: Session) -> None:
|
||||
"""Verify that the docs build and that doctests pass"""
|
||||
install_requirements_file(session, "build-docs")
|
||||
install_reactpy_dev(session)
|
||||
session.run(
|
||||
"sphinx-build",
|
||||
"-a", # re-write all output files
|
||||
"-T", # show full tracebacks
|
||||
"-W", # turn warnings into errors
|
||||
"--keep-going", # complete the build, but still report warnings as errors
|
||||
"-b",
|
||||
"html",
|
||||
"docs/source",
|
||||
"docs/build",
|
||||
)
|
||||
session.run("sphinx-build", "-b", "doctest", "docs/source", "docs/build")
|
||||
# ensure docker image build works too
|
||||
session.run("docker", "build", ".", "--file", "docs/Dockerfile", external=True)
|
||||
|
||||
|
||||
@group.session
|
||||
def check_javascript_tests(session: Session) -> None:
|
||||
session.run("npm", "run", "check:tests", external=True)
|
||||
|
||||
|
||||
@group.session
|
||||
def check_javascript_format(session: Session) -> None:
|
||||
session.run("npm", "run", "check:format", external=True)
|
||||
|
||||
|
||||
@group.session
|
||||
def check_javascript_types(session: Session) -> None:
|
||||
session.run("npm", "run", "build", external=True)
|
||||
session.run("npm", "run", "check:types", external=True)
|
||||
|
||||
|
||||
@group.session
|
||||
def check_javascript_build(session: Session) -> None:
|
||||
session.run("npm", "run", "build", external=True)
|
||||
|
||||
|
||||
@group.session
|
||||
def build_javascript(session: Session) -> None:
|
||||
"""Build javascript client code"""
|
||||
session.chdir(CLIENT_DIR)
|
||||
session.run("npm", "run", "build", external=True)
|
||||
|
||||
|
||||
@group.session
|
||||
def build_python(session: Session) -> None:
|
||||
"""Build python package dist"""
|
||||
rmtree(str(ROOT_DIR / "build"))
|
||||
rmtree(str(ROOT_DIR / "dist"))
|
||||
install_requirements_file(session, "build-pkg")
|
||||
session.run("python", "-m", "build", "--sdist", "--wheel", "--outdir", "dist", ".")
|
||||
|
||||
|
||||
@group.session
|
||||
def publish(
|
||||
session: Session,
|
||||
publish_dry_run: Annotated[
|
||||
bool,
|
||||
Option(help="whether to test the release process"),
|
||||
] = False,
|
||||
publish_fake_tags: Annotated[
|
||||
Sequence[str],
|
||||
Option(nargs="*", type=str, help="fake tags to use for a dry run release"),
|
||||
] = (),
|
||||
) -> None:
|
||||
packages = get_packages(session)
|
||||
|
||||
release_prep: dict[LanguageName, ReleasePrepFunc] = {
|
||||
"js": prepare_javascript_release,
|
||||
"py": prepare_python_release,
|
||||
}
|
||||
|
||||
if publish_fake_tags and not publish_dry_run:
|
||||
session.error("Cannot specify --publish-fake-tags without --publish-dry-run")
|
||||
|
||||
parsed_tags: list[TagInfo] = []
|
||||
for tag in publish_fake_tags or get_current_tags(session):
|
||||
tag_info = parse_tag(tag)
|
||||
if tag_info is None:
|
||||
session.error(
|
||||
f"Invalid tag {tag} - must be of the form <package>-<language>-<version>"
|
||||
)
|
||||
parsed_tags.append(tag_info) # type: ignore
|
||||
|
||||
publishers: list[tuple[Path, Callable[[bool], None]]] = []
|
||||
for tag, tag_pkg, tag_ver in parsed_tags:
|
||||
if tag_pkg not in packages:
|
||||
session.error(f"Tag {tag} references package {tag_pkg} that does not exist")
|
||||
|
||||
pkg_name, pkg_path, pkg_lang, pkg_ver = pkg_info = packages[tag_pkg]
|
||||
if pkg_ver != tag_ver:
|
||||
session.error(
|
||||
f"Tag {tag} references version {tag_ver} of package {tag_pkg}, "
|
||||
f"but the current version is {pkg_ver}"
|
||||
)
|
||||
|
||||
session.chdir(pkg_path)
|
||||
session.log(f"Preparing {tag_pkg} for release...")
|
||||
publishers.append((pkg_path, release_prep[pkg_lang](session, pkg_info)))
|
||||
|
||||
for pkg_path, publish in publishers:
|
||||
session.log(f"Publishing {pkg_path}...")
|
||||
session.chdir(pkg_path)
|
||||
publish(publish_dry_run)
|
||||
|
||||
|
||||
# --- Utilities ------------------------------------------------------------------------
|
||||
|
||||
|
||||
def install_requirements_file(session: Session, name: str) -> None:
|
||||
file_path = ROOT_DIR / "requirements" / (name + ".txt")
|
||||
assert file_path.exists(), f"requirements file {file_path} does not exist"
|
||||
session.install("-r", str(file_path))
|
||||
|
||||
|
||||
def install_reactpy_dev(session: Session, extras: str = "all") -> None:
|
||||
if "--no-install" not in session.posargs:
|
||||
session.install("-e", f".[{extras}]")
|
||||
else:
|
||||
session.posargs.remove("--no-install")
|
||||
|
||||
|
||||
def get_reactpy_script_env() -> dict[str, str]:
|
||||
return {
|
||||
"PYTHONPATH": os.getcwd(),
|
||||
"REACTPY_DEBUG_MODE": os.environ.get("REACTPY_DEBUG_MODE", "1"),
|
||||
"REACTPY_TESTING_DEFAULT_TIMEOUT": os.environ.get(
|
||||
"REACTPY_TESTING_DEFAULT_TIMEOUT", "6.0"
|
||||
),
|
||||
"REACTPY_CHECK_VDOM_SPEC": os.environ.get("REACTPY_CHECK_VDOM_SPEC", "0"),
|
||||
}
|
||||
|
||||
|
||||
def prepare_javascript_release(
|
||||
session: Session, package: PackageInfo
|
||||
) -> Callable[[bool], None]:
|
||||
node_auth_token = session.env.get("NODE_AUTH_TOKEN")
|
||||
if node_auth_token is None:
|
||||
session.error("NODE_AUTH_TOKEN environment variable must be set")
|
||||
|
||||
session.run("npm", "ci", external=True)
|
||||
session.run("npm", "run", "build", external=True)
|
||||
|
||||
def publish(dry_run: bool) -> None:
|
||||
if dry_run:
|
||||
session.run(
|
||||
"npm",
|
||||
"--workspace",
|
||||
package.name,
|
||||
"pack",
|
||||
"--dry-run",
|
||||
external=True,
|
||||
)
|
||||
return
|
||||
session.run(
|
||||
"npm",
|
||||
"--workspace",
|
||||
package.name,
|
||||
"publish",
|
||||
"--access",
|
||||
"public",
|
||||
external=True,
|
||||
env={"NODE_AUTH_TOKEN": node_auth_token},
|
||||
)
|
||||
|
||||
return publish
|
||||
|
||||
|
||||
def prepare_python_release(
|
||||
session: Session, package: PackageInfo
|
||||
) -> Callable[[bool], None]:
|
||||
twine_username = session.env.get("PYPI_USERNAME")
|
||||
twine_password = session.env.get("PYPI_PASSWORD")
|
||||
|
||||
if not (twine_password and twine_username):
|
||||
session.error(
|
||||
"PYPI_USERNAME and PYPI_PASSWORD environment variables must be set"
|
||||
)
|
||||
|
||||
for build_dir_name in ["build", "dist"]:
|
||||
build_dir_path = Path.cwd() / build_dir_name
|
||||
if build_dir_path.exists():
|
||||
rmtree(str(build_dir_path))
|
||||
|
||||
install_requirements_file(session, "build-pkg")
|
||||
session.run("python", "-m", "build", "--sdist", "--wheel", "--outdir", "dist", ".")
|
||||
|
||||
def publish(dry_run: bool):
|
||||
if dry_run:
|
||||
session.run("twine", "check", "dist/*")
|
||||
return
|
||||
|
||||
session.run(
|
||||
"twine",
|
||||
"upload",
|
||||
"dist/*",
|
||||
env={"TWINE_USERNAME": twine_username, "TWINE_PASSWORD": twine_password},
|
||||
)
|
||||
|
||||
return publish
|
||||
|
||||
|
||||
def get_packages(session: Session) -> dict[str, PackageInfo]:
|
||||
packages: dict[str, PackageInfo] = {
|
||||
"reactpy": PackageInfo(
|
||||
"reactpy", ROOT_DIR, "py", get_reactpy_package_version(session)
|
||||
)
|
||||
}
|
||||
|
||||
# collect javascript packages
|
||||
js_package_paths: list[Path] = []
|
||||
for maybed_pkg in (CLIENT_DIR / "packages").glob("*"):
|
||||
if not (maybed_pkg / "package.json").exists():
|
||||
for nmaybe_namespaced_pkg in maybed_pkg.glob("*"):
|
||||
if (nmaybe_namespaced_pkg / "package.json").exists():
|
||||
js_package_paths.append(nmaybe_namespaced_pkg)
|
||||
else:
|
||||
js_package_paths.append(maybed_pkg)
|
||||
|
||||
# get javascript package info
|
||||
for pkg in js_package_paths:
|
||||
pkg_json_file = pkg / "package.json" # we already know this exists
|
||||
|
||||
pkg_json = json.loads(pkg_json_file.read_text())
|
||||
|
||||
pkg_name = pkg_json.get("name")
|
||||
pkg_version = pkg_json.get("version")
|
||||
|
||||
if pkg_version is None or pkg_name is None:
|
||||
session.log(f"Skipping - {pkg_name} has no name/version in package.json")
|
||||
continue
|
||||
|
||||
if pkg_name in packages:
|
||||
session.error(f"Duplicate package name {pkg_name}")
|
||||
|
||||
packages[pkg_name] = PackageInfo(pkg_name, CLIENT_DIR, "js", pkg_version)
|
||||
|
||||
return packages
|
||||
|
||||
|
||||
class PackageInfo(NamedTuple):
|
||||
name: str
|
||||
path: Path
|
||||
language: LanguageName
|
||||
version: str
|
||||
|
||||
|
||||
def get_current_tags(session: Session) -> list[str]:
|
||||
"""Get tags for the current commit"""
|
||||
# check if unstaged changes
|
||||
try:
|
||||
session.run(
|
||||
"git",
|
||||
"diff",
|
||||
"--cached",
|
||||
"--exit-code",
|
||||
silent=True,
|
||||
external=True,
|
||||
)
|
||||
session.run(
|
||||
"git",
|
||||
"diff",
|
||||
"--exit-code",
|
||||
silent=True,
|
||||
external=True,
|
||||
)
|
||||
except Exception:
|
||||
session.error("Cannot create a tag - there are uncommited changes")
|
||||
|
||||
tags_per_commit: dict[str, list[str]] = {}
|
||||
for commit, tag in map(
|
||||
str.split,
|
||||
cast(
|
||||
str,
|
||||
session.run(
|
||||
"git",
|
||||
"for-each-ref",
|
||||
"--format",
|
||||
r"%(objectname) %(refname:short)",
|
||||
"refs/tags",
|
||||
silent=True,
|
||||
external=True,
|
||||
),
|
||||
).splitlines(),
|
||||
):
|
||||
tags_per_commit.setdefault(commit, []).append(tag)
|
||||
|
||||
current_commit = cast(
|
||||
str, session.run("git", "rev-parse", "HEAD", silent=True, external=True)
|
||||
).strip()
|
||||
tags = tags_per_commit.get(current_commit, [])
|
||||
|
||||
if not tags:
|
||||
session.error("No tags found for current commit")
|
||||
|
||||
session.log(f"Found tags: {tags}")
|
||||
|
||||
return tags
|
||||
|
||||
|
||||
def parse_tag(tag: str) -> TagInfo | None:
|
||||
match = TAG_PATTERN.match(tag)
|
||||
if not match:
|
||||
return None
|
||||
return TagInfo(tag, match["name"], match["version"])
|
||||
|
||||
|
||||
class TagInfo(NamedTuple):
|
||||
tag: str
|
||||
package: str
|
||||
version: str
|
||||
|
||||
|
||||
def get_reactpy_package_version(session: Session) -> str: # type: ignore[return]
|
||||
pkg_root_init_file = REACTPY_DIR / "__init__.py"
|
||||
for line in pkg_root_init_file.read_text().split("\n"):
|
||||
if line.startswith('__version__ = "') and line.endswith('" # DO NOT MODIFY'):
|
||||
return (
|
||||
line
|
||||
# get assignment value
|
||||
.split("=", 1)[1]
|
||||
# remove "DO NOT MODIFY" comment
|
||||
.split("#", 1)[0]
|
||||
# clean up leading/trailing space
|
||||
.strip()
|
||||
# remove the quotes
|
||||
[1:-1]
|
||||
)
|
||||
session.error(f"No version found in {pkg_root_init_file}")
|
177
pyproject.toml
177
pyproject.toml
|
@ -1,61 +1,132 @@
|
|||
[build-system]
|
||||
requires = ["setuptools>=42", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
# --- Project ----------------------------------------------------------------------------
|
||||
|
||||
[tool.isort]
|
||||
multi_line_output = 3
|
||||
force_grid_wrap = 0
|
||||
use_parentheses = "True"
|
||||
ensure_newline_before_comments = "True"
|
||||
include_trailing_comma = "True"
|
||||
line_length = 88
|
||||
lines_after_imports = 2
|
||||
[project]
|
||||
name = "scripts"
|
||||
version = "0.0.0"
|
||||
description = "Scripts for managing the ReactPy respository"
|
||||
|
||||
[tool.mypy]
|
||||
incremental = false
|
||||
ignore_missing_imports = true
|
||||
warn_unused_configs = true
|
||||
warn_redundant_casts = true
|
||||
warn_unused_ignores = true
|
||||
# --- Hatch ----------------------------------------------------------------------------
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = "tests"
|
||||
xfail_strict = true
|
||||
markers = ["slow: marks tests as slow (deselect with '-m \"not slow\"')"]
|
||||
python_files = "*asserts.py test_*.py"
|
||||
asyncio_mode = "auto"
|
||||
|
||||
[tool.coverage.report]
|
||||
fail_under = 100
|
||||
show_missing = true
|
||||
skip_covered = true
|
||||
sort = "Name"
|
||||
exclude_lines = [
|
||||
# These are regex patterns
|
||||
'pragma: no cover',
|
||||
'\.\.\.',
|
||||
'raise NotImplementedError',
|
||||
'if TYPE_CHECKING[\s:]',
|
||||
]
|
||||
omit = [
|
||||
"src/reactpy/__main__.py",
|
||||
[tool.hatch.envs.default]
|
||||
detached = true
|
||||
dependencies = [
|
||||
"invoke",
|
||||
# lint
|
||||
"black",
|
||||
"ruff",
|
||||
"toml",
|
||||
"flake8",
|
||||
"flake8-pyproject",
|
||||
"reactpy-flake8 >=0.7",
|
||||
# publish
|
||||
"semver >=2, <3",
|
||||
"twine",
|
||||
]
|
||||
|
||||
[tool.pydocstyle]
|
||||
inherit = false
|
||||
match = '.*\.py'
|
||||
convention = "google"
|
||||
add_ignore = ["D100", "D101", "D102", "D103", "D104", "D105", "D107", "D412", "D415"]
|
||||
[tool.hatch.envs.default.scripts]
|
||||
publish = "invoke publish {args}"
|
||||
docs = "invoke docs {args}"
|
||||
|
||||
lint-py = "invoke lint-py {args}"
|
||||
lint-js = "invoke lint-js {args}"
|
||||
|
||||
test-py = "invoke test-py {args}"
|
||||
test-js = "invoke test-js"
|
||||
test-docs = "invoke test-docs"
|
||||
|
||||
# --- Black ----------------------------------------------------------------------------
|
||||
|
||||
[tool.black]
|
||||
target-version = ["py39"]
|
||||
line-length = 88
|
||||
|
||||
# --- Flake8 ----------------------------------------------------------------------------
|
||||
|
||||
[tool.flake8]
|
||||
ignore = ["E203", "E266", "E501", "W503", "F811", "N802", "N806"]
|
||||
per-file-ignores = [
|
||||
# sometimes this is required in order to hide setup for an example
|
||||
"docs/*/_examples/*.py:E402",
|
||||
select = ["RPY"] # only need to check with reactpy-flake8
|
||||
exclude = ["**/node_modules/*", ".eggs/*", ".tox/*", "**/venv/*"]
|
||||
|
||||
# --- Ruff -----------------------------------------------------------------------------
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py39"
|
||||
line-length = 88
|
||||
select = [
|
||||
"A",
|
||||
"ARG",
|
||||
"B",
|
||||
"C",
|
||||
"DTZ",
|
||||
"E",
|
||||
# error message linting is overkill
|
||||
# "EM",
|
||||
"F",
|
||||
# TODO: turn this on later
|
||||
# "FBT",
|
||||
"I",
|
||||
"ICN",
|
||||
"ISC",
|
||||
"N",
|
||||
"PLC",
|
||||
"PLE",
|
||||
"PLR",
|
||||
"PLW",
|
||||
"Q",
|
||||
"RUF",
|
||||
"S",
|
||||
"T",
|
||||
"TID",
|
||||
"UP",
|
||||
"W",
|
||||
"YTT",
|
||||
]
|
||||
ignore = [
|
||||
# TODO: turn this on later
|
||||
"N802", "N806", # allow TitleCase functions/variables
|
||||
# We're not any cryptography
|
||||
"S311",
|
||||
# For loop variable re-assignment seems like an uncommon mistake
|
||||
"PLW2901",
|
||||
# Let Black deal with line-length
|
||||
"E501",
|
||||
# Allow args/attrs to shadow built-ins
|
||||
"A002", "A003",
|
||||
# Allow unused args (useful for documenting what the parameter is for later)
|
||||
"ARG001", "ARG002", "ARG005",
|
||||
# Allow non-abstract empty methods in abstract base classes
|
||||
"B027",
|
||||
# Allow boolean positional values in function calls, like `dict.get(... True)`
|
||||
"FBT003",
|
||||
# If we're making an explicit comparison to a falsy value it was probably intentional
|
||||
"PLC1901",
|
||||
# Ignore checks for possible passwords
|
||||
"S105", "S106", "S107",
|
||||
# Ignore complexity
|
||||
"C901", "PLR0911", "PLR0912", "PLR0913", "PLR0915",
|
||||
]
|
||||
unfixable = [
|
||||
# Don't touch unused imports
|
||||
"F401",
|
||||
]
|
||||
|
||||
[tool.ruff.isort]
|
||||
known-first-party = ["reactpy"]
|
||||
|
||||
[tool.ruff.flake8-tidy-imports]
|
||||
ban-relative-imports = "all"
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
# Tests can use magic values, assertions, and relative imports
|
||||
"**/tests/**/*" = ["PLR2004", "S101", "TID252"]
|
||||
"docs/**/*.py" = [
|
||||
# Examples require some extra setup before import
|
||||
"E402",
|
||||
# Allow exec
|
||||
"S102",
|
||||
# Allow print
|
||||
"T201",
|
||||
]
|
||||
"scripts/**/*.py" = [
|
||||
# Allow print
|
||||
"T201",
|
||||
]
|
||||
max-line-length = 88
|
||||
max-complexity = 20
|
||||
select = ["B", "C", "E", "F", "W", "T4", "B9", "N", "RPY"]
|
||||
exclude = ["**/node_modules/*", ".eggs/*", ".tox/*"]
|
||||
# -- flake8-tidy-imports --
|
||||
ban-relative-imports = "true"
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
sphinx
|
||||
sphinx-autodoc-typehints
|
||||
furo ==2022.04.07
|
||||
sphinx-copybutton
|
||||
sphinx-autobuild
|
||||
sphinx-reredirects
|
||||
sphinx-design
|
||||
sphinx-resolve-py-references
|
||||
sphinxext-opengraph
|
|
@ -1,3 +0,0 @@
|
|||
twine
|
||||
wheel
|
||||
build
|
|
@ -1,10 +0,0 @@
|
|||
black[jupyter]
|
||||
flake8
|
||||
flake8-pyproject
|
||||
reactpy-flake8 >=0.7
|
||||
flake8-print
|
||||
flake8-tidy-imports
|
||||
isort >=5.7.0
|
||||
pep8-naming
|
||||
# pydocstyle
|
||||
git+https://github.com/PyCQA/pydocstyle.git@bd4993345a241bd0d5128f21de56394b1cde3714#egg=pydocstyle
|
|
@ -1,6 +0,0 @@
|
|||
mypy
|
||||
types-click
|
||||
types-tornado
|
||||
types-pkg-resources
|
||||
types-flask
|
||||
types-requests
|
|
@ -1 +0,0 @@
|
|||
semver >=2, <3
|
|
@ -1,2 +0,0 @@
|
|||
nox
|
||||
noxopt
|
|
@ -1,9 +0,0 @@
|
|||
typing-extensions >=3.10
|
||||
mypy-extensions >=0.4.3
|
||||
anyio >=3
|
||||
jsonpatch >=1.32
|
||||
fastjsonschema >=2.14.5
|
||||
requests >=2
|
||||
colorlog >=6
|
||||
asgiref >=3
|
||||
lxml >=4
|
|
@ -1,24 +0,0 @@
|
|||
# extra=starlette
|
||||
starlette >=0.13.6
|
||||
uvicorn[standard] >=0.19.0
|
||||
|
||||
# extra=sanic
|
||||
sanic >=21
|
||||
sanic-cors
|
||||
uvicorn[standard] >=0.19.0
|
||||
|
||||
# extra=fastapi
|
||||
fastapi >=0.63.0
|
||||
uvicorn[standard] >=0.19.0
|
||||
|
||||
# extra=flask
|
||||
flask
|
||||
markupsafe>=1.1.1,<2.1
|
||||
flask-cors
|
||||
flask-sock
|
||||
|
||||
# extra=tornado
|
||||
tornado
|
||||
|
||||
# extra=testing
|
||||
playwright
|
|
@ -1,15 +0,0 @@
|
|||
pytest
|
||||
pytest-asyncio>=0.17
|
||||
pytest-mock
|
||||
pytest-rerunfailures
|
||||
pytest-timeout
|
||||
|
||||
coverage
|
||||
responses
|
||||
playwright
|
||||
|
||||
# I'm not quite sure why this needs to be installed for tests with Sanic to pass
|
||||
sanic-testing
|
||||
|
||||
# Used to generate model changes from layout update messages
|
||||
jsonpointer
|
|
@ -1,5 +0,0 @@
|
|||
# Scripts
|
||||
|
||||
All scripts should be run from the repository root (typically using
|
||||
[`nox`](https://nox.thea.codes/en/stable/)). Use `nox --list` to see the list of
|
||||
available scripts.
|
|
@ -1,103 +0,0 @@
|
|||
import sys
|
||||
import time
|
||||
from os.path import getmtime
|
||||
from threading import Event, Thread
|
||||
|
||||
import reactpy
|
||||
from docs.examples import all_example_names, get_example_files_by_name, load_one_example
|
||||
from reactpy.widgets import _hotswap
|
||||
|
||||
|
||||
EXAMPLE_NAME_SET = all_example_names()
|
||||
EXAMPLE_NAME_LIST = tuple(sorted(EXAMPLE_NAME_SET))
|
||||
|
||||
|
||||
def on_file_change(path, callback):
|
||||
did_call_back_once = Event()
|
||||
|
||||
def watch_for_change():
|
||||
last_modified = 0
|
||||
while True:
|
||||
modified_at = getmtime(path)
|
||||
if modified_at != last_modified:
|
||||
callback()
|
||||
did_call_back_once.set()
|
||||
last_modified = modified_at
|
||||
time.sleep(1)
|
||||
|
||||
Thread(target=watch_for_change, daemon=True).start()
|
||||
did_call_back_once.wait()
|
||||
|
||||
|
||||
def main():
|
||||
ex_name = _example_name_input()
|
||||
|
||||
mount, component = _hotswap()
|
||||
|
||||
def update_component():
|
||||
print(f"Loading example: {ex_name!r}")
|
||||
mount(load_one_example(ex_name))
|
||||
|
||||
for file in get_example_files_by_name(ex_name):
|
||||
on_file_change(file, update_component)
|
||||
|
||||
reactpy.run(component)
|
||||
|
||||
|
||||
def _example_name_input() -> str:
|
||||
if len(sys.argv) == 1:
|
||||
_print_error(
|
||||
"No example argument given. Provide an example's number from above."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
ex_num = sys.argv[1]
|
||||
|
||||
try:
|
||||
ex_num = int(ex_num)
|
||||
except ValueError:
|
||||
_print_error(
|
||||
f"No example {ex_num!r} exists. Provide an example's number as an integer."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
ex_index = ex_num - 1
|
||||
try:
|
||||
return EXAMPLE_NAME_LIST[ex_index]
|
||||
except IndexError:
|
||||
_print_error(f"No example #{ex_num} exists. Choose from an option above.")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _print_error(*args) -> None:
|
||||
_print_available_options()
|
||||
print(*args)
|
||||
|
||||
|
||||
def _print_available_options():
|
||||
examples_by_path = {}
|
||||
for i, name in enumerate(EXAMPLE_NAME_LIST):
|
||||
if "/" not in name:
|
||||
path = ""
|
||||
else:
|
||||
path, name = name.rsplit("/", 1)
|
||||
examples_by_path.setdefault(path, []).append(name)
|
||||
|
||||
number = 1
|
||||
print()
|
||||
for path, names in examples_by_path.items():
|
||||
title = " > ".join(
|
||||
section.replace("-", " ").replace("_", " ").title()
|
||||
for section in path.split("/")
|
||||
if not section.startswith("_")
|
||||
)
|
||||
print(title)
|
||||
print("-" * len(title))
|
||||
for name in names:
|
||||
print(f"{number}. ", name)
|
||||
number += 1
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,20 +0,0 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
|
||||
# all scripts should be run from the repository root so we need to insert cwd to path
|
||||
# to import docs
|
||||
sys.path.insert(0, os.getcwd())
|
||||
|
||||
from docs.app import make_app
|
||||
|
||||
|
||||
app = make_app()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(
|
||||
host="0.0.0.0",
|
||||
port=int(os.environ.get("PORT", 5000)),
|
||||
workers=int(os.environ.get("WEB_CONCURRENCY", 1)),
|
||||
debug=bool(int(os.environ.get("DEBUG", "0"))),
|
||||
)
|
203
setup.py
203
setup.py
|
@ -1,203 +0,0 @@
|
|||
from __future__ import print_function
|
||||
|
||||
import pipes
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import traceback
|
||||
from logging import StreamHandler, getLogger
|
||||
from pathlib import Path
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
from setuptools.command.develop import develop
|
||||
from setuptools.command.sdist import sdist
|
||||
|
||||
|
||||
if sys.platform == "win32":
|
||||
from subprocess import list2cmdline
|
||||
else:
|
||||
|
||||
def list2cmdline(cmd_list):
|
||||
return " ".join(map(pipes.quote, cmd_list))
|
||||
|
||||
|
||||
log = getLogger()
|
||||
log.addHandler(StreamHandler(sys.stdout))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Basic Constants
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# the name of the project
|
||||
NAME = "reactpy"
|
||||
|
||||
# basic paths used to gather files
|
||||
ROOT_DIR = Path(__file__).parent
|
||||
SRC_DIR = ROOT_DIR / "src"
|
||||
PKG_DIR = SRC_DIR / NAME
|
||||
JS_DIR = SRC_DIR / "client"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Package Definition
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
package = {
|
||||
"name": NAME,
|
||||
"python_requires": ">=3.7",
|
||||
"packages": find_packages(str(SRC_DIR)),
|
||||
"package_dir": {"": "src"},
|
||||
"description": "It's React, but in Python",
|
||||
"author": "Ryan Morshead",
|
||||
"author_email": "ryan.morshead@gmail.com",
|
||||
"url": "https://github.com/reactive-python/reactpy",
|
||||
"license": "MIT",
|
||||
"platforms": "Linux, Mac OS X, Windows",
|
||||
"keywords": ["interactive", "widgets", "DOM", "React"],
|
||||
"include_package_data": True,
|
||||
"zip_safe": False,
|
||||
"classifiers": [
|
||||
"Environment :: Web Environment",
|
||||
"Framework :: AsyncIO",
|
||||
"Framework :: Flask",
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: Science/Research",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Topic :: Software Development :: User Interfaces",
|
||||
"Topic :: Software Development :: Widget Sets",
|
||||
"Typing :: Typed",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Library Version
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
pkg_root_init_file = PKG_DIR / "__init__.py"
|
||||
for line in pkg_root_init_file.read_text().split("\n"):
|
||||
if line.startswith('__version__ = "') and line.endswith('" # DO NOT MODIFY'):
|
||||
package["version"] = (
|
||||
line
|
||||
# get assignment value
|
||||
.split("=", 1)[1]
|
||||
# remove "DO NOT MODIFY" comment
|
||||
.split("#", 1)[0]
|
||||
# clean up leading/trailing space
|
||||
.strip()
|
||||
# remove the quotes
|
||||
[1:-1]
|
||||
)
|
||||
break
|
||||
else:
|
||||
print(f"No version found in {pkg_root_init_file}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Requirements
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
requirements = []
|
||||
with (ROOT_DIR / "requirements" / "pkg-deps.txt").open() as f:
|
||||
for line in map(str.strip, f):
|
||||
if not line.startswith("#"):
|
||||
requirements.append(line)
|
||||
package["install_requires"] = requirements
|
||||
|
||||
_current_extras = []
|
||||
extra_requirements = {"all": []} # type: ignore
|
||||
extra_requirements_path = ROOT_DIR / "requirements" / "pkg-extras.txt"
|
||||
with extra_requirements_path.open() as f:
|
||||
for line in map(str.strip, f):
|
||||
if line.startswith("#") and line[1:].strip().startswith("extra="):
|
||||
_current_extras = [e.strip() for e in line.split("=", 1)[1].split(",")]
|
||||
if "all" in _current_extras:
|
||||
raise ValueError("%r uses the reserved extra name 'all'")
|
||||
for e in _current_extras:
|
||||
extra_requirements[e] = []
|
||||
elif _current_extras:
|
||||
for e in _current_extras:
|
||||
extra_requirements[e].append(line)
|
||||
extra_requirements["all"].append(line)
|
||||
elif line:
|
||||
raise ValueError(
|
||||
f"No '# extra=<name>' header before requirements in {extra_requirements_path}"
|
||||
)
|
||||
package["extras_require"] = extra_requirements
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Library Description
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
with (ROOT_DIR / "README.md").open() as f:
|
||||
long_description = f.read()
|
||||
|
||||
package["long_description"] = long_description
|
||||
package["long_description_content_type"] = "text/markdown"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Command Line Interface
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
package["entry_points"] = {"console_scripts": ["reactpy=reactpy.__main__:app"]}
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Build Javascript
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def build_javascript_first(cls):
|
||||
class Command(cls):
|
||||
def run(self):
|
||||
log.info("Installing Javascript...")
|
||||
try:
|
||||
npm = shutil.which("npm") # this is required on windows
|
||||
if npm is None:
|
||||
raise RuntimeError("NPM is not installed.")
|
||||
for args in (f"{npm} ci", f"{npm} run build"):
|
||||
args_list = args.split()
|
||||
log.info(f"> {list2cmdline(args_list)}")
|
||||
subprocess.run(args_list, cwd=str(JS_DIR), check=True)
|
||||
except Exception:
|
||||
log.error("Failed to install Javascript")
|
||||
log.error(traceback.format_exc())
|
||||
raise
|
||||
else:
|
||||
log.info("Successfully installed Javascript")
|
||||
super().run()
|
||||
|
||||
return Command
|
||||
|
||||
|
||||
package["cmdclass"] = {
|
||||
"sdist": build_javascript_first(sdist),
|
||||
"develop": build_javascript_first(develop),
|
||||
}
|
||||
|
||||
if sys.version_info < (3, 10, 6):
|
||||
from distutils.command.build import build
|
||||
|
||||
package["cmdclass"]["build"] = build_javascript_first(build)
|
||||
else:
|
||||
from setuptools.command.build_py import build_py
|
||||
|
||||
package["cmdclass"]["build_py"] = build_javascript_first(build_py)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
# Install It
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup(**package)
|
1
src/client/.gitignore → src/js/.gitignore
vendored
1
src/client/.gitignore → src/js/.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
tsconfig.tsbuildinfo
|
||||
packages/**/package-lock.json
|
||||
dist
|
|
@ -22,7 +22,6 @@
|
|||
"build": "vite build",
|
||||
"format": "prettier --write . && eslint --fix .",
|
||||
"test": "npm run check:tests",
|
||||
"check:format": "prettier --check .",
|
||||
"check:tests": "echo 'no tests'",
|
||||
"check:types": "tsc --noEmit"
|
||||
}
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
@ -1,7 +1,7 @@
|
|||
import { defineConfig } from "vite";
|
||||
|
||||
export default defineConfig({
|
||||
build: { outDir: "../../reactpy/_client", emptyOutDir: true },
|
||||
build: { emptyOutDir: true },
|
||||
resolve: {
|
||||
alias: {
|
||||
react: "preact/compat",
|
214
src/client/package-lock.json → src/js/package-lock.json
generated
214
src/client/package-lock.json → src/js/package-lock.json
generated
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "client",
|
||||
"name": "js",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
@ -8,7 +8,7 @@
|
|||
"workspaces": [
|
||||
"packages/event-to-object",
|
||||
"packages/@reactpy/client",
|
||||
"ui"
|
||||
"app"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||
|
@ -18,6 +18,45 @@
|
|||
"prettier": "^3.0.0-alpha.6"
|
||||
}
|
||||
},
|
||||
"app": {
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@reactpy/client": "^0.2.0",
|
||||
"preact": "^10.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^17.0",
|
||||
"@types/react-dom": "^17.0",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^3.1.8"
|
||||
}
|
||||
},
|
||||
"app/node_modules/@reactpy/client": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@reactpy/client/-/client-0.2.1.tgz",
|
||||
"integrity": "sha512-9sgGH+pJ2BpLT+QSVe7FQLS2VQ9acHgPlO8X3qiTumGw43O0X82sm8pzya8H8dAew463SeGza/pZc0mpUBHmqA==",
|
||||
"dependencies": {
|
||||
"event-to-object": "^0.1.2",
|
||||
"json-pointer": "^0.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16 <18",
|
||||
"react-dom": ">=16 <18"
|
||||
}
|
||||
},
|
||||
"app/node_modules/typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
},
|
||||
"apps/ui": {
|
||||
"extraneous": true,
|
||||
"license": "MIT",
|
||||
|
@ -514,6 +553,10 @@
|
|||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/app": {
|
||||
"resolved": "app",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
|
@ -2386,10 +2429,16 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
||||
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
|
@ -2663,9 +2712,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.21",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
|
||||
"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
|
||||
"version": "8.4.24",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
|
||||
"integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -2675,10 +2724,14 @@
|
|||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.4",
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
},
|
||||
|
@ -2687,9 +2740,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/preact": {
|
||||
"version": "10.13.1",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.13.1.tgz",
|
||||
"integrity": "sha512-KyoXVDU5OqTpG9LXlB3+y639JAGzl8JSBXLn1J9HTSB3gbKcuInga7bZnXLlxmK94ntTs1EFeZp0lrja2AuBYQ==",
|
||||
"version": "10.15.1",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.15.1.tgz",
|
||||
"integrity": "sha512-qs2ansoQEwzNiV5eAcRT1p1EC/dmEzaATVDJNiB3g2sRDWdA7b7MurXdJjB2+/WQktGWZwxvDrnuRFbWuIr64g==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/preact"
|
||||
|
@ -2810,12 +2863,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
||||
"version": "1.22.2",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
|
||||
"integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.9.0",
|
||||
"is-core-module": "^2.11.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
|
@ -3232,10 +3285,6 @@
|
|||
"node": ">=12.20"
|
||||
}
|
||||
},
|
||||
"node_modules/ui": {
|
||||
"resolved": "ui",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/unbox-primitive": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
|
||||
|
@ -3279,9 +3328,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.5.tgz",
|
||||
"integrity": "sha512-4mVEpXpSOgrssFZAOmGIr85wPHKvaDAcXqxVxVRZhljkJOMZi1ibLibzjLHzJvcok8BMguLc7g1W6W/GqZbLdQ==",
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.7.tgz",
|
||||
"integrity": "sha512-29pdXjk49xAP0QBr0xXqu2s5jiQIXNvE/xwd0vUizYT2Hzqe4BksNNoWllFVXJf4eLZ+UlVQmXfB4lWrc+t18g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.15.9",
|
||||
|
@ -3458,7 +3507,7 @@
|
|||
}
|
||||
},
|
||||
"packages/@reactpy/client": {
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"event-to-object": "^0.1.2",
|
||||
|
@ -3556,6 +3605,7 @@
|
|||
"extraneous": true
|
||||
},
|
||||
"ui": {
|
||||
"extraneous": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@reactpy/client": "^0.2.0",
|
||||
|
@ -3567,32 +3617,6 @@
|
|||
"typescript": "^4.9.5",
|
||||
"vite": "^3.1.8"
|
||||
}
|
||||
},
|
||||
"ui/node_modules/@reactpy/client": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@reactpy/client/-/client-0.2.1.tgz",
|
||||
"integrity": "sha512-9sgGH+pJ2BpLT+QSVe7FQLS2VQ9acHgPlO8X3qiTumGw43O0X82sm8pzya8H8dAew463SeGza/pZc0mpUBHmqA==",
|
||||
"dependencies": {
|
||||
"event-to-object": "^0.1.2",
|
||||
"json-pointer": "^0.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16 <18",
|
||||
"react-dom": ">=16 <18"
|
||||
}
|
||||
},
|
||||
"ui/node_modules/typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.2.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -3923,6 +3947,34 @@
|
|||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"app": {
|
||||
"version": "file:app",
|
||||
"requires": {
|
||||
"@reactpy/client": "^0.2.0",
|
||||
"@types/react": "^17.0",
|
||||
"@types/react-dom": "^17.0",
|
||||
"preact": "^10.7.0",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^3.1.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@reactpy/client": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@reactpy/client/-/client-0.2.1.tgz",
|
||||
"integrity": "sha512-9sgGH+pJ2BpLT+QSVe7FQLS2VQ9acHgPlO8X3qiTumGw43O0X82sm8pzya8H8dAew463SeGza/pZc0mpUBHmqA==",
|
||||
"requires": {
|
||||
"event-to-object": "^0.1.2",
|
||||
"json-pointer": "^0.6.2"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
|
@ -5233,9 +5285,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"nanoid": {
|
||||
"version": "3.3.4",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
|
||||
"integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
|
||||
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
|
||||
"dev": true
|
||||
},
|
||||
"natural-compare": {
|
||||
|
@ -5424,20 +5476,20 @@
|
|||
"dev": true
|
||||
},
|
||||
"postcss": {
|
||||
"version": "8.4.21",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
|
||||
"integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
|
||||
"version": "8.4.24",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz",
|
||||
"integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"nanoid": "^3.3.4",
|
||||
"nanoid": "^3.3.6",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"preact": {
|
||||
"version": "10.13.1",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.13.1.tgz",
|
||||
"integrity": "sha512-KyoXVDU5OqTpG9LXlB3+y639JAGzl8JSBXLn1J9HTSB3gbKcuInga7bZnXLlxmK94ntTs1EFeZp0lrja2AuBYQ=="
|
||||
"version": "10.15.1",
|
||||
"resolved": "https://registry.npmjs.org/preact/-/preact-10.15.1.tgz",
|
||||
"integrity": "sha512-qs2ansoQEwzNiV5eAcRT1p1EC/dmEzaATVDJNiB3g2sRDWdA7b7MurXdJjB2+/WQktGWZwxvDrnuRFbWuIr64g=="
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
|
@ -5513,12 +5565,12 @@
|
|||
}
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.22.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
||||
"integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
|
||||
"version": "1.22.2",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz",
|
||||
"integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-core-module": "^2.9.0",
|
||||
"is-core-module": "^2.11.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
}
|
||||
|
@ -5802,34 +5854,6 @@
|
|||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"ui": {
|
||||
"version": "file:ui",
|
||||
"requires": {
|
||||
"@reactpy/client": "^0.2.0",
|
||||
"@types/react": "^17.0",
|
||||
"@types/react-dom": "^17.0",
|
||||
"preact": "^10.7.0",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^3.1.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@reactpy/client": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@reactpy/client/-/client-0.2.1.tgz",
|
||||
"integrity": "sha512-9sgGH+pJ2BpLT+QSVe7FQLS2VQ9acHgPlO8X3qiTumGw43O0X82sm8pzya8H8dAew463SeGza/pZc0mpUBHmqA==",
|
||||
"requires": {
|
||||
"event-to-object": "^0.1.2",
|
||||
"json-pointer": "^0.6.2"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.9.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"unbox-primitive": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
|
||||
|
@ -5864,9 +5888,9 @@
|
|||
}
|
||||
},
|
||||
"vite": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.5.tgz",
|
||||
"integrity": "sha512-4mVEpXpSOgrssFZAOmGIr85wPHKvaDAcXqxVxVRZhljkJOMZi1ibLibzjLHzJvcok8BMguLc7g1W6W/GqZbLdQ==",
|
||||
"version": "3.2.7",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-3.2.7.tgz",
|
||||
"integrity": "sha512-29pdXjk49xAP0QBr0xXqu2s5jiQIXNvE/xwd0vUizYT2Hzqe4BksNNoWllFVXJf4eLZ+UlVQmXfB4lWrc+t18g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esbuild": "^0.15.9",
|
|
@ -4,15 +4,17 @@
|
|||
"publish": "npm --workspaces publish",
|
||||
"test": "npm --workspaces test",
|
||||
"build": "npm --workspaces run build",
|
||||
"format": "prettier --write . && eslint --fix .",
|
||||
"check:format": "prettier --check . && eslint .",
|
||||
"format": "npm run prettier -- --write && npm run eslint -- --fix",
|
||||
"check:format": "npm run prettier -- --check && npm run eslint",
|
||||
"check:tests": "npm --workspaces run check:tests",
|
||||
"check:types": "npm --workspaces run check:types"
|
||||
"check:types": "npm --workspaces run check:types",
|
||||
"prettier": "prettier --ignore-path .gitignore .",
|
||||
"eslint": "eslint --ignore-path .gitignore ."
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/event-to-object",
|
||||
"packages/@reactpy/client",
|
||||
"ui"
|
||||
"app"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue