From 2f320bb5d3ad315af2a0610d63136b11a32fce49 Mon Sep 17 00:00:00 2001
From: Archmonger <16909269+Archmonger@users.noreply.github.com>
Date: Fri, 3 Jan 2025 15:02:43 -0800
Subject: [PATCH 01/30] New repo structure
---
.github/FUNDING.yml | 2 +-
.gitignore | 4 +
.vscode/extensions.json | 12 -
LICENSE | 21 -
LICENSE.md | 9 +
pyproject.toml | 213 ++++++--
src/build_scripts/copy_js_output.py | 8 +
src/py/reactpy/.gitignore | 5 -
src/py/reactpy/MANIFEST.in | 3 -
src/py/reactpy/README.md | 23 -
src/py/reactpy/pyproject.toml | 158 ------
src/py/reactpy/scripts/copy_js_output.py | 8 -
src/{py/reactpy => }/reactpy/__init__.py | 0
src/{py/reactpy => }/reactpy/__main__.py | 0
.../reactpy => }/reactpy/_console/__init__.py | 0
.../reactpy/_console/ast_utils.py | 0
.../_console/rewrite_camel_case_props.py | 0
.../reactpy/_console/rewrite_keys.py | 0
src/{py/reactpy => }/reactpy/_option.py | 0
src/{py/reactpy => }/reactpy/_warnings.py | 0
.../reactpy => }/reactpy/backend/__init__.py | 0
.../reactpy => }/reactpy/backend/_common.py | 2 +-
.../reactpy => }/reactpy/backend/default.py | 0
.../reactpy => }/reactpy/backend/fastapi.py | 0
src/{py/reactpy => }/reactpy/backend/flask.py | 0
src/{py/reactpy => }/reactpy/backend/hooks.py | 0
src/{py/reactpy => }/reactpy/backend/sanic.py | 0
.../reactpy => }/reactpy/backend/starlette.py | 0
.../reactpy => }/reactpy/backend/tornado.py | 0
src/{py/reactpy => }/reactpy/backend/types.py | 0
src/{py/reactpy => }/reactpy/backend/utils.py | 0
src/{py/reactpy => }/reactpy/config.py | 0
src/{py/reactpy => }/reactpy/core/__init__.py | 0
src/{py/reactpy => }/reactpy/core/_f_back.py | 0
.../reactpy/core/_life_cycle_hook.py | 0
.../reactpy/core/_thread_local.py | 0
.../reactpy => }/reactpy/core/component.py | 0
src/{py/reactpy => }/reactpy/core/events.py | 0
src/{py/reactpy => }/reactpy/core/hooks.py | 0
src/{py/reactpy => }/reactpy/core/layout.py | 0
src/{py/reactpy => }/reactpy/core/serve.py | 0
src/{py/reactpy => }/reactpy/core/types.py | 0
src/{py/reactpy => }/reactpy/core/vdom.py | 0
src/{py/reactpy => }/reactpy/future.py | 0
src/{py/reactpy => }/reactpy/html.py | 0
src/{py/reactpy => }/reactpy/logging.py | 0
src/{py/reactpy => }/reactpy/py.typed | 0
src/{py/reactpy => }/reactpy/sample.py | 0
src/{py/reactpy => }/reactpy/svg.py | 0
.../reactpy => }/reactpy/testing/__init__.py | 0
.../reactpy => }/reactpy/testing/backend.py | 0
.../reactpy => }/reactpy/testing/common.py | 0
.../reactpy => }/reactpy/testing/display.py | 0
src/{py/reactpy => }/reactpy/testing/logs.py | 0
src/{py/reactpy => }/reactpy/types.py | 0
src/{py/reactpy => }/reactpy/utils.py | 0
src/{py/reactpy => }/reactpy/web/__init__.py | 0
src/{py/reactpy => }/reactpy/web/module.py | 0
.../reactpy/web/templates/react.js | 0
src/{py/reactpy => }/reactpy/web/utils.py | 0
src/{py/reactpy => }/reactpy/widgets.py | 0
tasks.py | 458 ------------------
{src/py/reactpy/tests => tests}/__init__.py | 0
{src/py/reactpy/tests => tests}/conftest.py | 9 +-
.../tests => tests}/test__console/__init__.py | 0
.../test_rewrite_camel_case_props.py | 0
.../test__console/test_rewrite_keys.py | 0
.../reactpy/tests => tests}/test__option.py | 0
.../asserts.py => tests/test_asserts.py | 2 +-
.../tests => tests}/test_backend/__init__.py | 0
.../test_backend/test__common.py | 0
.../tests => tests}/test_backend/test_all.py | 0
.../test_backend/test_utils.py | 0
.../py/reactpy/tests => tests}/test_client.py | 0
.../py/reactpy/tests => tests}/test_config.py | 0
.../tests => tests}/test_core/__init__.py | 0
.../test_core/test_component.py | 0
.../tests => tests}/test_core/test_events.py | 0
.../tests => tests}/test_core/test_hooks.py | 0
.../tests => tests}/test_core/test_layout.py | 0
.../tests => tests}/test_core/test_serve.py | 0
.../tests => tests}/test_core/test_vdom.py | 0
{src/py/reactpy/tests => tests}/test_html.py | 0
.../py/reactpy/tests => tests}/test_sample.py | 0
.../reactpy/tests => tests}/test_testing.py | 0
{src/py/reactpy/tests => tests}/test_utils.py | 0
.../tests => tests}/test_web/__init__.py | 0
.../js_fixtures/component-can-have-child.js | 0
.../js_fixtures/export-resolution/index.js | 0
.../js_fixtures/export-resolution/one.js | 0
.../js_fixtures/export-resolution/two.js | 0
.../test_web/js_fixtures/exports-syntax.js | 0
.../js_fixtures/exports-two-components.js | 0
.../set-flag-when-unmount-is-called.js | 0
.../test_web/js_fixtures/simple-button.js | 0
.../tests => tests}/test_web/test_module.py | 0
.../tests => tests}/test_web/test_utils.py | 0
.../reactpy/tests => tests}/test_widgets.py | 0
.../tests => tests}/tooling/__init__.py | 0
.../py/reactpy/tests => tests}/tooling/aio.py | 0
.../reactpy/tests => tests}/tooling/common.py | 0
.../reactpy/tests => tests}/tooling/hooks.py | 0
.../reactpy/tests => tests}/tooling/layout.py | 0
.../reactpy/tests => tests}/tooling/select.py | 0
104 files changed, 203 insertions(+), 734 deletions(-)
delete mode 100644 .vscode/extensions.json
delete mode 100644 LICENSE
create mode 100644 LICENSE.md
create mode 100644 src/build_scripts/copy_js_output.py
delete mode 100644 src/py/reactpy/.gitignore
delete mode 100644 src/py/reactpy/MANIFEST.in
delete mode 100644 src/py/reactpy/README.md
delete mode 100644 src/py/reactpy/pyproject.toml
delete mode 100644 src/py/reactpy/scripts/copy_js_output.py
rename src/{py/reactpy => }/reactpy/__init__.py (100%)
rename src/{py/reactpy => }/reactpy/__main__.py (100%)
rename src/{py/reactpy => }/reactpy/_console/__init__.py (100%)
rename src/{py/reactpy => }/reactpy/_console/ast_utils.py (100%)
rename src/{py/reactpy => }/reactpy/_console/rewrite_camel_case_props.py (100%)
rename src/{py/reactpy => }/reactpy/_console/rewrite_keys.py (100%)
rename src/{py/reactpy => }/reactpy/_option.py (100%)
rename src/{py/reactpy => }/reactpy/_warnings.py (100%)
rename src/{py/reactpy => }/reactpy/backend/__init__.py (100%)
rename src/{py/reactpy => }/reactpy/backend/_common.py (98%)
rename src/{py/reactpy => }/reactpy/backend/default.py (100%)
rename src/{py/reactpy => }/reactpy/backend/fastapi.py (100%)
rename src/{py/reactpy => }/reactpy/backend/flask.py (100%)
rename src/{py/reactpy => }/reactpy/backend/hooks.py (100%)
rename src/{py/reactpy => }/reactpy/backend/sanic.py (100%)
rename src/{py/reactpy => }/reactpy/backend/starlette.py (100%)
rename src/{py/reactpy => }/reactpy/backend/tornado.py (100%)
rename src/{py/reactpy => }/reactpy/backend/types.py (100%)
rename src/{py/reactpy => }/reactpy/backend/utils.py (100%)
rename src/{py/reactpy => }/reactpy/config.py (100%)
rename src/{py/reactpy => }/reactpy/core/__init__.py (100%)
rename src/{py/reactpy => }/reactpy/core/_f_back.py (100%)
rename src/{py/reactpy => }/reactpy/core/_life_cycle_hook.py (100%)
rename src/{py/reactpy => }/reactpy/core/_thread_local.py (100%)
rename src/{py/reactpy => }/reactpy/core/component.py (100%)
rename src/{py/reactpy => }/reactpy/core/events.py (100%)
rename src/{py/reactpy => }/reactpy/core/hooks.py (100%)
rename src/{py/reactpy => }/reactpy/core/layout.py (100%)
rename src/{py/reactpy => }/reactpy/core/serve.py (100%)
rename src/{py/reactpy => }/reactpy/core/types.py (100%)
rename src/{py/reactpy => }/reactpy/core/vdom.py (100%)
rename src/{py/reactpy => }/reactpy/future.py (100%)
rename src/{py/reactpy => }/reactpy/html.py (100%)
rename src/{py/reactpy => }/reactpy/logging.py (100%)
rename src/{py/reactpy => }/reactpy/py.typed (100%)
rename src/{py/reactpy => }/reactpy/sample.py (100%)
rename src/{py/reactpy => }/reactpy/svg.py (100%)
rename src/{py/reactpy => }/reactpy/testing/__init__.py (100%)
rename src/{py/reactpy => }/reactpy/testing/backend.py (100%)
rename src/{py/reactpy => }/reactpy/testing/common.py (100%)
rename src/{py/reactpy => }/reactpy/testing/display.py (100%)
rename src/{py/reactpy => }/reactpy/testing/logs.py (100%)
rename src/{py/reactpy => }/reactpy/types.py (100%)
rename src/{py/reactpy => }/reactpy/utils.py (100%)
rename src/{py/reactpy => }/reactpy/web/__init__.py (100%)
rename src/{py/reactpy => }/reactpy/web/module.py (100%)
rename src/{py/reactpy => }/reactpy/web/templates/react.js (100%)
rename src/{py/reactpy => }/reactpy/web/utils.py (100%)
rename src/{py/reactpy => }/reactpy/widgets.py (100%)
delete mode 100644 tasks.py
rename {src/py/reactpy/tests => tests}/__init__.py (100%)
rename {src/py/reactpy/tests => tests}/conftest.py (89%)
rename {src/py/reactpy/tests => tests}/test__console/__init__.py (100%)
rename {src/py/reactpy/tests => tests}/test__console/test_rewrite_camel_case_props.py (100%)
rename {src/py/reactpy/tests => tests}/test__console/test_rewrite_keys.py (100%)
rename {src/py/reactpy/tests => tests}/test__option.py (100%)
rename src/py/reactpy/tests/tooling/asserts.py => tests/test_asserts.py (83%)
rename {src/py/reactpy/tests => tests}/test_backend/__init__.py (100%)
rename {src/py/reactpy/tests => tests}/test_backend/test__common.py (100%)
rename {src/py/reactpy/tests => tests}/test_backend/test_all.py (100%)
rename {src/py/reactpy/tests => tests}/test_backend/test_utils.py (100%)
rename {src/py/reactpy/tests => tests}/test_client.py (100%)
rename {src/py/reactpy/tests => tests}/test_config.py (100%)
rename {src/py/reactpy/tests => tests}/test_core/__init__.py (100%)
rename {src/py/reactpy/tests => tests}/test_core/test_component.py (100%)
rename {src/py/reactpy/tests => tests}/test_core/test_events.py (100%)
rename {src/py/reactpy/tests => tests}/test_core/test_hooks.py (100%)
rename {src/py/reactpy/tests => tests}/test_core/test_layout.py (100%)
rename {src/py/reactpy/tests => tests}/test_core/test_serve.py (100%)
rename {src/py/reactpy/tests => tests}/test_core/test_vdom.py (100%)
rename {src/py/reactpy/tests => tests}/test_html.py (100%)
rename {src/py/reactpy/tests => tests}/test_sample.py (100%)
rename {src/py/reactpy/tests => tests}/test_testing.py (100%)
rename {src/py/reactpy/tests => tests}/test_utils.py (100%)
rename {src/py/reactpy/tests => tests}/test_web/__init__.py (100%)
rename {src/py/reactpy/tests => tests}/test_web/js_fixtures/component-can-have-child.js (100%)
rename {src/py/reactpy/tests => tests}/test_web/js_fixtures/export-resolution/index.js (100%)
rename {src/py/reactpy/tests => tests}/test_web/js_fixtures/export-resolution/one.js (100%)
rename {src/py/reactpy/tests => tests}/test_web/js_fixtures/export-resolution/two.js (100%)
rename {src/py/reactpy/tests => tests}/test_web/js_fixtures/exports-syntax.js (100%)
rename {src/py/reactpy/tests => tests}/test_web/js_fixtures/exports-two-components.js (100%)
rename {src/py/reactpy/tests => tests}/test_web/js_fixtures/set-flag-when-unmount-is-called.js (100%)
rename {src/py/reactpy/tests => tests}/test_web/js_fixtures/simple-button.js (100%)
rename {src/py/reactpy/tests => tests}/test_web/test_module.py (100%)
rename {src/py/reactpy/tests => tests}/test_web/test_utils.py (100%)
rename {src/py/reactpy/tests => tests}/test_widgets.py (100%)
rename {src/py/reactpy/tests => tests}/tooling/__init__.py (100%)
rename {src/py/reactpy/tests => tests}/tooling/aio.py (100%)
rename {src/py/reactpy/tests => tests}/tooling/common.py (100%)
rename {src/py/reactpy/tests => tests}/tooling/hooks.py (100%)
rename {src/py/reactpy/tests => tests}/tooling/layout.py (100%)
rename {src/py/reactpy/tests => tests}/tooling/select.py (100%)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index e01b3e624..74094ade3 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,6 +1,6 @@
# These are supported funding model platforms
-github: [rmorshea]
+github: [archmonger, rmorshea]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
diff --git a/.gitignore b/.gitignore
index 946bff43f..aee96f2f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+# --- Build Artifacts ---
+src/reactpy/static
+
# --- Jupyter ---
*.ipynb_checkpoints
*Untitled*.ipynb
@@ -29,6 +32,7 @@ pip-wheel-metadata
.python-version
# -- Python Tests ---
+.coverage.*
*.coverage
*.pytest_cache
*.mypy_cache
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
deleted file mode 100644
index 7471953dc..000000000
--- a/.vscode/extensions.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "recommendations": [
- "wholroyd.jinja",
- "esbenp.prettier-vscode",
- "ms-python.vscode-pylance",
- "ms-python.python",
- "charliermarsh.ruff",
- "dbaeumer.vscode-eslint",
- "ms-python.black-formatter",
- "ms-python.mypy-type-checker"
- ]
-}
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 060079c01..000000000
--- a/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2019 Ryan S. Morshead
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 000000000..f5423c3d3
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,9 @@
+## The MIT License (MIT)
+
+#### Copyright (c) Reactive Python and affiliates.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/pyproject.toml b/pyproject.toml
index 1745a3dfe..9f90c969d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,66 +1,187 @@
-# --- Project ----------------------------------------------------------------------------
+[build-system]
+build-backend = "hatchling.build"
+requires = ["hatchling", "hatch-build-scripts"]
+
+##############################
+# >>> Hatch Build Config <<< #
+##############################
[project]
-name = "scripts"
-version = "0.0.0"
-description = "Scripts for managing the ReactPy repository"
+name = "reactpy"
+description = "It's React, but in Python."
+readme = "README.md"
+keywords = ["react", "javascript", "reactpy", "component"]
+license = "MIT"
+authors = [{ name = "Ryan Morshead", email = "ryan.morshead@gmail.com" }]
+requires-python = ">=3.9"
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: Implementation :: CPython",
+ "Programming Language :: Python :: Implementation :: PyPy",
+]
+dependencies = [
+ "exceptiongroup >=1.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",
+]
+dynamic = ["version"]
+urls.Changelog = "https://reactpy.dev/docs/about/changelog.html"
+urls.Documentation = "https://reactpy.dev/"
+urls.Source = "https://github.com/reactive-python/reactpy"
+
+[tool.hatch.version]
+path = "src/reactpy/__init__.py"
+
+[tool.hatch.build.targets.sdist]
+include = ["/src"]
+artifacts = ["/src/reactpy/static/"]
-# --- Hatch ----------------------------------------------------------------------------
+[tool.hatch.build.targets.wheel]
+artifacts = ["/src/reactpy/static/"]
+
+[tool.hatch.metadata]
+license-files = { paths = ["LICENSE.md"] }
[tool.hatch.envs.default]
-detached = true
-dependencies = [
- "invoke",
- # lint
- "black==24.1.1", # Pin lint tools we don't control to avoid breaking changes
- "ruff==0.0.278", # Pin lint tools we don't control to avoid breaking changes
- "toml",
- "flake8==7.0.0", # Pin lint tools we don't control to avoid breaking changes
- "flake8-pyproject",
- # types
- "mypy",
- "types-toml",
- # publish
- "semver >=2, <3",
- "twine",
- "pre-commit",
+installer = "uv"
+
+[[tool.hatch.build.hooks.build-scripts.scripts]]
+# TODO: Nuke NPM from the repo
+commands = [
+ # TODO: Need to build JS here. Try switching to bun.
+ "cd src && cd js && npm install && npm run build",
+ "cd scripts && python copy_js_output.py",
+]
+artifacts = []
+
+[project.optional-dependencies]
+# TODO: Nuke backends from the optional deps
+all = ["reactpy[starlette,sanic,fastapi,flask,tornado,testing]"]
+starlette = ["starlette >=0.13.6", "uvicorn[standard] >=0.19.0"]
+sanic = [
+ "sanic>=21",
+ "sanic-cors",
+ "tracerite>=1.1.1",
+ "setuptools",
+ "uvicorn[standard]>=0.19.0",
]
+fastapi = ["fastapi >=0.63.0", "uvicorn[standard] >=0.19.0"]
+flask = ["flask", "markupsafe>=1.1.1,<2.1", "flask-cors", "flask-sock"]
+tornado = ["tornado"]
+testing = ["playwright"]
+
+
+#############################
+# >>> Hatch Test Runner <<< #
+#############################
+
+[tool.hatch.envs.hatch-test]
+extra-dependencies = [
+ "pytest-sugar",
+ "pytest-asyncio>=0.23",
+ "pytest-timeout",
+ "coverage[toml]>=6.5",
+ "responses",
+ "playwright",
+ # TODO: Nuke this after removing backends from deps
+ "sanic-testing",
+ # TODO: This dependency needs to be pulled out. ReactPy no longer uses this in the core logic.
+ "jsonpointer",
+]
+
+[[tool.hatch.envs.hatch-test.matrix]]
+python = ["3.9", "3.10", "3.11", "3.12"]
+
+[tool.pytest.ini_options]
+addopts = """\
+ --strict-config
+ --strict-markers
+ """
+testpaths = "tests"
+xfail_strict = true
+asyncio_mode = "auto"
+log_cli_level = "INFO"
+features = ["all"]
[tool.hatch.envs.default.scripts]
-publish = "invoke publish {args}"
-docs = "invoke docs {args}"
-check = ["lint-py", "lint-js", "test-py", "test-js", "test-docs"]
+test-cov = "playwright install && coverage run -m pytest {args:tests}"
+cov-report = [
+ # "- coverage combine",
+ "coverage report",
+]
+cov = ["test-cov {args}", "cov-report"]
-lint = ["lint-py", "lint-js"]
-lint-py = "invoke lint-py {args}"
-lint-js = "invoke lint-js {args}"
+[tool.hatch.envs.default.env-vars]
+REACTPY_DEBUG_MODE = "1"
-test = ["test-py", "test-js", "test-docs"]
-test-py = "invoke test-py {args}"
-test-js = "invoke test-js"
-test-docs = "invoke test-docs"
+################################
+# >>> Hatch Lint Scripts <<< #
+################################
-# --- Black ----------------------------------------------------------------------------
+[tool.hatch.envs.lint]
+features = ["all"]
+extra-dependencies = [
+ "black==24.1.1",
+ "ruff==0.0.278",
+ "flake8==7.0.0",
+ "toml",
+ "flake8-pyproject",
+ "mypy",
+ "types-toml",
+ "mypy==1.8",
+ "types-click",
+ "types-tornado",
+ "types-flask",
+ "types-requests",
+]
-[tool.black]
-target-version = ["py39"]
-line-length = 88
+[tool.hatch.envs.lint.scripts]
+types = "mypy --strict reactpy"
+all = ["types"]
+
+[tool.hatch.envs.publish]
+extra-dependencies = ["semver >=2, <3", "twine", "pre-commit"]
-# --- MyPy -----------------------------------------------------------------------------
+#########################
+# >>> Generic Tools <<< #
+#########################
[tool.mypy]
+incremental = false
ignore_missing_imports = true
warn_unused_configs = true
warn_redundant_casts = true
warn_unused_ignores = true
-# --- Flake8 ---------------------------------------------------------------------------
-
-[tool.flake8]
-select = ["RPY"] # only need to check with reactpy-flake8
-exclude = ["**/node_modules/*", ".eggs/*", ".tox/*", "**/venv/*"]
+[tool.coverage.run]
+source_pkgs = ["reactpy"]
+branch = false
+parallel = false
+omit = ["reactpy/__init__.py"]
-# --- Ruff -----------------------------------------------------------------------------
+[tool.coverage.report]
+fail_under = 100
+show_missing = true
+skip_covered = true
+sort = "Name"
+exclude_lines = [
+ "no ?cov",
+ '\.\.\.',
+ "if __name__ == .__main__.:",
+ "if TYPE_CHECKING:",
+]
+omit = ["reactpy/__main__.py"]
[tool.ruff]
target-version = "py39"
@@ -139,6 +260,10 @@ known-first-party = ["reactpy"]
[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "all"
+[tool.flake8]
+select = ["RPY"] # only need to check with reactpy-flake8
+exclude = ["**/node_modules/*", ".eggs/*", ".tox/*", "**/venv/*"]
+
[tool.ruff.per-file-ignores]
# Tests can use magic values, assertions, and relative imports
"**/tests/**/*" = ["PLR2004", "S101", "TID252"]
@@ -154,3 +279,7 @@ ban-relative-imports = "all"
# Allow print
"T201",
]
+
+[tool.black]
+target-version = ["py39"]
+line-length = 88
diff --git a/src/build_scripts/copy_js_output.py b/src/build_scripts/copy_js_output.py
new file mode 100644
index 000000000..23bcc29b7
--- /dev/null
+++ b/src/build_scripts/copy_js_output.py
@@ -0,0 +1,8 @@
+from pathlib import Path
+from shutil import copytree, rmtree
+
+output_dir = Path(__file__).parent.parent / "reactpy" / "static"
+source_dir = Path(__file__).parent.parent / "js" / "app" / "dist"
+rmtree(output_dir, ignore_errors=True)
+copytree(source_dir, output_dir)
+print("JavaScript output copied to reactpy/static") # noqa: T201
diff --git a/src/py/reactpy/.gitignore b/src/py/reactpy/.gitignore
deleted file mode 100644
index b4362ae8c..000000000
--- a/src/py/reactpy/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-.coverage.*
-
-# --- Build Artifacts ---
-reactpy/_static
-js
diff --git a/src/py/reactpy/MANIFEST.in b/src/py/reactpy/MANIFEST.in
deleted file mode 100644
index b989938fa..000000000
--- a/src/py/reactpy/MANIFEST.in
+++ /dev/null
@@ -1,3 +0,0 @@
-recursive-include src/reactpy/_client *
-recursive-include src/reactpy/web/templates *
-include src/reactpy/py.typed
diff --git a/src/py/reactpy/README.md b/src/py/reactpy/README.md
deleted file mode 100644
index 910a573a5..000000000
--- a/src/py/reactpy/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# ReactPy
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
zDoRQ@@BkPqI$tSBMA8JKwB6+|c^tv3G2fUi!l4>gc3 z()%Yw?Op@E6yU?Lhu2CI;**1kF9P^54}D*4{G|b3hhQH(XRdnw9RYl}e$m*iHhxWj z59i-%`VM1+`oA7L^lAe>m63B`X!*YMSu?;=zsE8qk;JSfGDT;?x-Ko#;+3MOMyiXUYY*%AC`gTxT1vk5rD4&_%IJ`!#=DeA^v-UeUx7< ze*?H_DP#O!iEY>h+4BW_O$;B!@pn3ie;e?(5cu@JBVp `J`Dm8= z5A&;;gNPpn_~`rq`N$89tN-V(h~G@G597Ad*g-+W7XU9kl|lbk+Hc5(3GrP3UlkL- z)%t%O@UiEQW%b|xME1V|J{&*j|4MB`b;OsX`|tg4r5cFuO5lTO1mE+oI3a!!;O_wb zLn3&;{vW?!{#Oa{-vd6}KjFHCE@ZtFVsr~DK z57#f;_rO$MaYFtN0lqlkL*L=~X|?wsHLz)bSM{M#Z>4>QP9po+fNuo&uxvGR5b>u0 zAMSr}?4bRXu3^OA3O;;*;|Fd3iG5eVNB4i^1CaV{LiTe2ANmh{hxS*R5Pul()d3&% z8{zMC5MLN98fCzTw&B>L@|E%r68Px+wp#uTz=!8oH2;5R7u6#>9{?Y&AL#!|`wle_ zUkH4_1kWF^Y&Bzm_$~xKS~sX1>isIAcKLvh_Fve3rF}sBA;5?6gL&vbyjGGBpO1OD z|8V?ZLj73I{qs-6_XK=6e^LIQ8UI~_+SdTSD&ViQ-$(`V@hr>h5Ay${|7L(Mh4CNh zB0K+ts9p5m`2W=Y@8yVJ1^96MU_C1Pd)xmjkJ`==_-Nc#JO1+Eri<1ew6j`%Ab}6L z@E!=);Yt$ne*o~|^Bc;ee*8`YwHIVtetv=Nq3zZBZw~k<{wwtz+Cuiv5ctq8EL*Mp zmw*rVFT`K%^8*$8^8NvRf*LFB1M*)5@V5f{@cbZ0&;an;g!ln~kNjV$Z77cTR{ a q`@d RFC*^82c+7JE(#9PXJ#TGybdfpLElI?;oq>?+1Ln ze__7>@Uih>kMD6x$#^Ls2{72>g#6#t%T^ZoR_b1RkyjHt^ z{{VawjQ!QFKX-xU_dhEgx4-*e5BQoG|55$#5cOYNaQX9R)c=+Gf%rE7AB{iK_?>N7 zkN8u7|8M-47FyoFp>3$STL0Yu|KIqZ5BS=c_^;Of6yWO;_H+{`r&n_Z;wd zW7gja&Hrcq@_?83===@suha+Re*oaC{|oz7fDhL{)LW@-*hgd^4-UUr|5tne^#FWy z|AU&Voj=up|8Mp$a>@VRe^wd;)PGID*TBS|76xYp3GrhAAH@&SR=a)&03Y2ykmd@^ z|7ZL-z{5Az{%Ygz4ftsPgLYTTzX$mH3Gsvc)y^NbE&qM~g6}X_l2EL?0pFNlAC|3l z|9=GdDE=$4i`pRjOMs8gAE<66i1_MY^F{L?&fV3 u(c^pBvyq|5w9>;)tIQ z_|Si7AC|4w{}I4P`w!Gz?fFj{eE6VFun%pomLCZC(0{mg;I-QO=X1bE_b;RYWPY1a z{3MnCdjAMXsQh;VsNFHZhy6!uf3^Lu1$={lF@F3if1Uqf{8t-4PrwJaz);v9$X{tf z{l5(OX#a)$mCj?t{{i^u`R7l@&s6m<`%rVW{VxT4ZP0(n1507W3HiTEZTbH7r~K!D z56>Te%I5=<51v2%l MEp{~hxcD-8{LC{XB*Zd{s7>E+iWNmC|jv*EMEu&AIzISUOz5?5BGmG_mN-F z#;+1;R|5F({)^V#YWJUy1p81A`moX%Ap62v%khU?m=G@K{`n{3y8=G+AC4ciy;}R% z0Uu1^pYtDDTP+_hei(mfe>DmDza8-5{DXGk7_8R52jIi`1J@7aB3 hx P z`!2u-A7O+-4jjML@+tKH`u+ @=)1o`1ODf_g}~{GI&d+;j;tX4Xa5F#`1}Fy z-SGai(uDZ003VJ&IzO(K&jnuI!1L>B?p@FZvhP6Pqx@>^7Xd!J|3V%-`>fOtWRJ)k zhf@Z8m|x8q2Juw^AKHic)x;3-qY3<#o*RIfp9%3F0X`f*)bG`v-{|)J_4hka80joq z`RAX=zB1s${-d+oYVC&rz6gPjH2>M^Up46b&I^iI0spU>mHh_S8%+Q9{J#kNR|EFp z+J%XI8Gic{^;H3Ucm$un5FgymR+JDw4e&JpAD-Rd{G(YB`>*xa3;3#p_|X9_+(%ZD zkbR#0|NZwXtDQe)fDgvt=lp?o`Bq{av_baI13or>sQh;_sNEpoZ^6VLm9Lb~Wcg=* z|BmJ_v;+41(BIkndp+{g3fSKU_>`al8@SNC{(Jks=TW;qT|dZvHn1-V?89+~WmJDR z^bdK&?*e>Tz=wILvD*2=V71JL`wla>U>sJGko}#258q$Ic@JX(`m&;g_-TNz3i!}I zEQ7d`g!o;6FHhh@?rQlP!6&=YfRAFg+V$%K_*(%V`eO(# (dI5y` zUm^4}kkAey |OXQ{aNU(}a8qK$sxH`ZEMP3lJuV zu>Co3f%^@v5nQl8FTn-dzXBIbScK&*KTDSp^4q{g39dduIYij5pOA+L+YN#XmcIoT zOc3EX41o*QkAMp%h;V*PfD8IF11`w>1TNTqj*y=R2opru{wuhCo>@U-*p8ThqyS+( zB|vCrEdgl=NCy!7hlBs$MwlQ%yDR|V9>NWf8lVV3s3QRo>PP{E=~oDwY$5Pu0Kz={ z7iA!SJE2?=Ahf3rD+vhw*COO0!e6z)4>Ev;guD?z*k4nCu>D>FS^$K8S_6dboe1Tw z0HNK303pu{Aha6*5Vj8j2z8DS%EJM|1QGrk0e-;xC<4Y1FdiV(OD13{K&Y1i5ZZ(P z9s$H`fRLX{z)JvOyFx;~gn(rPEC&evzYP%b?*N430RO!em>@#EMnWDU{I!XYhY0JN z33)8Sq8Eg6h|rH#0=5H$ye>k%kAQChg8y*uzz-O=F@W%Vx=6@FCt>{>fG{5L-*bSv z@Shuk?db^ROaNiO)&m6p;ke)r0>WQ;2>JgDq0S~kJw#|n7$7VXC6q&ieuxoJoPZL9 zdMv_rl7xCGLOm8?ku>Lmh%^E;Wq{}saVISbmsrsoKl4iM^HfQ1Bvb_zhA3gC4@IYeluf`GRP<^LB#zhHto z@PY{<1GqTAwFz7*;DQN@P*Uw@>Hp4eE`&HhgmHoAHkcs7_`-7=Ob}uIf9JR5c^n0U z0un?RxBs2rmgh%5hy_gj;DT`(1Q(n)Z@~o 8se9(ab*Tk*O?)E;@bC nRZ%CJ@lXo@UBzP+5 z``)i}@}#Oa+6!A77X-K7&=xMX`tdz_Y=6ly!vmM_XLkzGaTQXsN3lw)GRYS^?aIl# zt{IKdh4)sJ@FcHC8b^a~P(Nq$IFZP!wNGt9tluZm )|>Te)EOf`;?t-&Nqf&bm5+d68^gH zt%o@#C67atK6kSGNHfUV^V!SX!mN_4x8uAtNqauCi_si$O*tcn@U_`IdY9pzb!Tz0 zWoHjvadNLQDt+sQ(S^@WDB){T6-5u_HVBkFT2Mcw*E-fXvvbcl&ty|L>*pigcN9HY z!)3(|a>jR?-KU+q&~_kWdwLK5__=Xn#? 7isj-9NsB>DYG5 zmkdSYga;&JTcw^GeS7*{-l_Kk&t+ox?gq_Y`0foQ{N}2lUPe12`K{8D61ApxVvjD7 z@d?Y>-?qf#o@KSk)y&FwXVjz)?fK+4EUDa#SJ)*I;eE8UN3B};Y!L}@G&e>UKFgtm zj}}f`FKJWa|CRgTBmO1t%L+T5@jh%(AFZOyNsBc-S6ek~y@~Rj-i0BOGcVVqYH$5? zCp%AO9~ViCcbLWazDVr+h3^tj!haW<9_W&&CssKct$A6=Wi)c0L*Z4a@)-W;L4y%Z zt%{rqvvwhW?^>rC#z&6v7j~@k?zGI7xO&LN&!Q`tKEDX#FMKzI623R$ywN0sq}82> z9_6%~6T)%ng$9!eyzf03)f~pOUR=8OWpnDl4Q1COxNHu;TbyxMQd}+Y>`Fu%d3h;L zoRGt=3+(5Y_)&+BZ*i*jvx|755r=??fkxMPwrb}z)}_pL7v7gl6K5i$Kc2l^{kEj) zgV*-Rdkns7M9Smzqa_(PlJ_ZOzqG;l3qK!42|r@)d;3PCgL{2P*)tQKje*+s$;V zSu^9ZQKA0CwAUT+9J8oRWfQ|pb>pAkC>8roa(m;(wX*o{-wf91aWGE7=)!kWDB;OB zsjkh)s^m7 R#B$CH2BTuSs32|00R$p &GB$CVwhX)@WuLdTuHE?j!aNO-!!0i#Qg)ivncFP-& NrW;999)XLPE?Vlbtb17ikr;AZLDs(X#x?&eVpaGg+Y+ zUHI+*CH%wzx9|StY31)ah}wn2eJ|D)+il_Xi`!EfJJW%46EV1yFvck~@361A`!b(S z_eO2AQ?uJewW_II?p@y^`cehHLq_Y35s3oCQ$*pnmYulym{QEews^DXrFysG8zg-V zy78wbSlg48tq%5ExCm@bcQbn5yS{TBvAE&xrwqapL)|=Li+gRW#XT{)Ojuna5z-CK ztX?hYId}4pcl3YF)2bXh-c8dKyUvRGa#PQ-{k&?5k2OYip0N)5kq|@28tl&fBu{Ui z|Ka}3mp8M&1Y&gIJ2I5;8`CHx({QZ243(UOEYCE%)$5T`Nj4dhi#^{capFg3Jk@7q zui5GL-h`_xw1c0IcV2o-VL_P@dzkZ4cn?+SXI&3las0&ouFCsxs*aP1jm4K%UV! zy0eDcPNU9L`#!$$YSdgSRa8pj2I9T8`W=d5W)wF@_ulGS4EVZ*)<#qF!p_AFl%FxW ztXSPOW*>S~1ADYDiZ#$yv(v**tm znn*APUG1Ycl$TgY48BREiqU1m>dH n zZqW!lu)KP@-asdRl2zigQrLwn)NyXEuXnV46SfFk_!viv(Zznh0)P7J6W60kc4oIM z GMip)LS$(2!o5iv8qRgpX zFD#BSWj1~^*E;rvkL yx306>4YJDYLEjQjO9lPINtF@+82V-h{z1el3%-n2YZDR>Sy)FPzPD(RJ-r&MjMZ zq ;v`Am OC2T z3U8L2GIFA0TPksf!|T_V`lely{-L&(NyEnH;SrbT)tBhs_PB+3xuiMGdgSvT %xvoWuuOI|DR!5Pm%mY*Z(-o2aVbAG2ATRI0x-39uzFAg)WPJjM%wwIlz z@vOkHt-P=HI6k;;?)*Gxy)H(V7pq%cT1i?ulX2>is(ZYE(0skuhxq5y1`iod&7EcFM(tjTg_UNx7Uhpm-+rnVcq_ zonxVN=5_ZMA>y~&DA~ z+B#Vy_1T`y1_7g=bYnATdvTSA+^ioz^WGuWJaLZkc$@TzFR9&8I0oKmRR!vM6#7zv zcl6dQ#2cw$bm6;el<>wKPd`1mb)>mBiPt~XuvKZdK)G;q;)a1KI!{YuGCPv}AqOWv z^}abiGG{bc8!yUkm*>iB3fWW2z<9 zxJFhoy{X^FiUArfN6Go~-a%(GUg@r*?8Ea@AJfsWVO0DyFu`9?xo9#QGv<2A6aHJ` zNLK)>JNl`6fr~GSGI6A@lka=>Yhtn)<1;rM$Z{`K)?F#k-fEiDm{0Vyrbv$Ee8D*> zwz!AlLv%i)lY4T`_Kc+#s>fh-1+lvKhFDLKR@0iS5tf@iq4Lr5Y3GxDA9jbh1xj!b zotK-?mk?<$=(wIN6xdF~z`qb3T3qT~@-Ro$(9Y6ajzyLZex{226~gLXy5D5yIB|nQ z>e@Q*nRD^{7MhH$ 3;h z9h1ybgBV?5tS%dMggLv>p5~D+Yvt{VtsPJIZDmTc(;W0ObQr2~T%x;G{qiZv$NcuF z!Ldtc78;1zbz23KBpG~0Zl$fi#%2pYBSij+V0D!q*b_aTb`(Fuvz7eKUM-3>;?t1~ z;}wjt$vTW}(FONz4Lxv*KdM;Ftyi_+HY7YFt+xLb*Y?LmFM2LcG29V>pXnl9QLJuF zrup?2m&a#MuH)0>O_^D%c`o0QM36_3k$c}Jk@M#Y^lJ2s!b=AR6m&O{kaDGr$#2=^ z<9bePL|9YGk+iK$9iuCT)%`SWRyfqfvVG6&yQAh;61rZO@I`Z}H+8eWAm7@lw>~5^ z+>*QHY%-IPg_myRu}^Pmck!>uF6x?lL`q#-J=V~J(G|z)5;5FX(c+&mKj`MJaMH!l z*e0Omxc?L1Uh#Umw0*+5P1U-xpUVw-_yvNzb=R}oei7FYiy-n@q}NlY*%V5%i3y`C zfz{nQeV!p^c4_0~i)+ Tzo61Bak7Z0a?G4l_w0mL| z_I2%#kA3;B(xFK`8holUq8MFCtZw83 r>)bBH#Y`Ko%@X~pSH(L5%UE_~F&1U-{ zX^A~QY{BYYDRcXB+V+?#QREIaYc|Ktm9noR9o`G>smP+@UK{%UX ~3Hxw_Ml`o}Fe#)q>=#Tj&Nr@O9ild8_96U zX-Qc} zy^EybJlJ)-4Xb;W<=6x3*wcMvP7yT~rUzYut)tGg$4K!XFzr5lhkbWfdKS-&NRu-c zV=A500dBgl>5oEacOFkKbf}Ry^NHgF_W4)_tNXa3hC5Z&*1-6~Eqo+L&V*0M2mdLH zpc4K7>82qK!8vu#2D4~?rOoT#jeDwGd6KbY&&Nq+H&0Ca^|_q-{4Fy~d}Xn^$AXgP zBBbUNLL*r2bGNC^@25I lUpJoa*TbBsOZd&NUJ`Zij>fVbDHc~M8?6UpMF_DNnJGaj( z- A@B?CaVrZy|jKcia*FtlLX*r`GL@sMfH*`8mg6SkO%4=HgvujK4du zx^nhvA4hJPwW!W O zGzVl4b65sxgl_eHqPG*H3%=X Qc9wl{uU0!H` 6${AJlwIj7I6N_i3ulmH1yR4y;U$@xY+N&tEJMUw}4M*it4W;(o zBR)1e@^~@2idbF0@qpH;_j+||Ti$;^$3<%0-%*;OYIM5s2gQDa5}z_I?YooYcCV-c zI$y9+9-TS5`_)={GyCk>R^hnX3r9#D`7pXlSY3&y%9`)h)nw!K2h~2vt}Sx$(y~<1 zpzX&0C=`3msUz3dT|@Knqg8acy8Dqu#}>yUhF^)_%!ld8&T@{ZHh#dK&y=ycLe351 z0gD{(_T2qJ{d#fUpwvNpk%*aMZtICh6?xyj-5V7wG9eDT>t(v{g5@jYBe%#ESsGG~ z9r;qg q;0$#Um>cWxE>P+RjjV{dh!oyIi}>ymkYwhC%azV z-+nAAXwA{ Y&Zw(z(wl=WkD z)v&scM|U6OecG3A!%$%sx%bW`_b(BzpQYC3-A_J~uBtXjXGt4u`%&iI+#LJW>xyaq z2~O)A!cxJcxLk%mI54^!`y8N-)zvA_z;#fM>$k;5KXO`95Ph#BbySLjisM RK(p5cvA7CVk!Kf3PZ=o;dSL)y&BljpDKXt9;022SK){MEqf-nLAVv!Bayn`m^3 zyZq$T$x6G=9lk85`lC{!T(}KRN=DR44{pf3_G;pG8%a;S( -^R>V z?|o^$#G7wvm-EG_6TaGYb;)Qst;OhSVRfgcvzR&dKRvS0+rO{)L9JG}$0L?fGKo8m z#5rlw _*2zsQmBo9nunHI?57$MJWq2?~mK=S* zJ-_|rs|S_pqZnNstS-J>#=z3p!go6L-o`$mTlQD@7D@yk29>ZfGbiiDRRp{_ex3NM zz}G8GH RKqo0nR>l9!a Sk}7A%bPa$du=i0c6MJlvpJ+)gd|rlMu;k7A*Swf Wu=m4lS0oYlIfbPg9)jW6W*#$0qhJ(^Qo79_*g ztbE3-Y6DBFcK>D1b&(F%_g}@3PZTxZR=<8z%j6x#Uwy1@(V&r)$txqfIgw+#cLmAs zp<*3uU_Z|)Fmio&dk@)%^^OGCTAH12*{9bNhZjt0QI1eE;Z}?9lg1uN);+D6!{B z_&sfu@FOusQ+6qjS=gk@Y1}z^n{RtxE849cN$rryBJX_Sl|x&B-3i{n&(vgVa}+9e z`WblLJ-2jsR>A44^Xu|`PFZspf8qDOQNpj?I5iSAP*^WAxlPyJ`{U)1s-kmy-}9NL z-;ARv&s@itVCldq#iYPT+iZ7truOTlxh>HRoYei#MBT(}bK2z(VRVg UKbWc zH~4gq-Yr);L5%J$tgh9bl$>Ud2aaR$ZY-|q9Yk(TUS@|sWz!KKoPKI5JWRhnmZ?r@ znkv(%Uo*|5Z)eHao1y@V4NPCFU8g9gTMB|Py1TKu)mp@gH_Y=)*hFtHs9#IWr+s}c zV*5}L^F9_*!5>}k#~hP2i>-C;H8;3Y6bGgo7wvvrSl4wfbh1rLsGU>f%@9V{7^{o# zsUS;Dm#9%NYP2tLJ{+EU+-6Z#eyh}r-Fw_)*$p$>pGX?`sNe3{K+F(ic4{B ;v z2G }-WP$~1&TkiNCiS?W^V)LcLeD_aFKFH{oP_;eci`{q3 zu)6fGu3nhB_=xZO-YM^xb5Z(fyPMeV?7gwrbz_X`>luw6GNA)^w}zbe@ZI}p` BNtt&G z)Y&}$bj6H#i?YW4z_$vnX3i!ylbTunsnIkp_u?#{rWYi^@7kjMcQ01=l*RhO!?LnM zK_55y1!&_1_g4L=%j?spByp%OK9czTHJ7>cRpEHEdii}RJyx1Fa?Wjx-)?@Vy=JEC zcto32atxzuj@6xRZnjkLN-3`mldrvghrhVpw|VCq+KJ(>ccm_BRUYw^q;z&F{8&Oa zE1AxdtQ_ANXL_XV=I-)O?j ~i@-1I}-BI*XyPL?fX-DqyhyDEupXP&1K2;n~>@IdT z2{F#L*COF*GsftGKQH@F!i(nF-(*T5Nvv+-a%TAC J)MQ_8A)N5y=syKtbjJ6tU1q^g|=j(ax@px>o$G7;T z2F73b_bMpiOU*^9>z`4)I+nXGbDWj7mOmtj<@l4j*e&(fDh(%>?9UXWUR8X1Go5c# z&yP p%>B^5>OSdjTZTK+6G&?;X@q$8I~@5k{y__)Ylqck5Tr1i-6|yg+NaYsG?eEE z|BV}$JRG$LLK{9$>7K5#-#9`x-K|Q0=KYSsGh1@J9_y`XSt~}pJ?y?SlMMZW9`2 z9;^H8>X8yYWy5`42^-ZsT@DEBdS-LiroxbV*Lw5rNH1FNpl#h2mMKK%{c>AxOMVF* zInnZ-Ijf?JL|G)-${~fG9^ 6EsOB%DI7j2#n+O965wF9oZ& zD5Y< Xf#f+H3d;RsSiP)Q0Z{Ap@Uy`krV} NrorxV65@iF!8S$-lvYp1=Qv}?dzYGGCQwwOe=e5V?<7jTydE9 $P>6SAux&)Ls7&j;lEFC@9deE9QB$E2-WJ<`x(xBj`?^daa`z(7|wHRsD zQZK6fFgf+3-Mv44=dPa9xg?Xx*!{s7t9w~=M@qazA|25;8E#>dDvrv~Z6}9MW-{;! z_bACJM9D gw3$$3j ipoI|5<`_aSq_KdE-B73OvgaV_;#;O^iO_5s#1a&WA=P&%8 zG)j0!)l*Sr?~L~0PD+UChj(la^qMF3e%h~%AIUqv>z-mz{ngF%3D3M5c^2=c@3U*) z+_b&Hma*uVZ^9@`=X{8pCdOYkBnl9pkk2i4ekiZ2uAb~$gGR;utBUQtbn*S?`Ln~D z+r|&@-;AO7_J(|r` e|X29ba>4oAF28vBAlN)S1`Y0$gu)sF96dbQP$xvo4xAoylSqz~in( zbE}?j=aFMXG9!B9HCGL<&IA~VQhdTbcY9)WO+6Bya0s7P4c%TWvscS6*=UgE$C)WS z%|)vA*IP}#T^n*ydZ#=k*A 2yEKV@`>^L_?)H|IZg?(q8-N`ZjdSP|HotX4o zv*DdzbDf@);L#j*O_oQ^&K`=DWFwbM(rUhQg}pkpz;WV`r>?16buFo)XfmC0{#kG+ zTx7ZQe)ihbYmBZpR`(UX$!DI#YWpobEauF4hL%oM0R_S4FK4}uD>U|(?`(L|>+|IU z%f`|&t#bvYrYbWE_FC@~KGNdWOy26(m1C5|==xxFC%vCNIyz?EtvO^T@kHArZM)>R znsr-iFVNgIyV@9Vk#erbc66lSWlHa35?)uz^ral*8!XkbC$-moa;=yASfP#4^~LI5 zlg=jH&O(~4uxW;pcX+J!<~H)p8P}eFN#3;mAbDgiQE3iu#_?_VZ=Or3T_oL)PsqPf z;BBCx3J(v->3d{e_zI)zht-ul&!?8_wZ7jdzQ8$=^mto_HvU*eE^B9o1Wj*bly`vG z<*I9Omf5lEqZVjBSD%Oq93}58YsoxY@a^&&n@BV4_aFSRx{ga)?IrGAtS$G-J(Zly zL{y0$(c~@CZQ3HWu_3ea@dKOJGiS276)Z$a-Ni2+OO>G5rSa^=i73Zc9Pcjk94Z(sJtvcRQtTLBTK)k(Vy`{3x_jUZ#@|4!Zt&rkN6A{9W1g|z zyVXg;V{MQmBXYjkfHFYxAj@EjW_W7)^$Uy_`upj!%D3L7_gnltmSy95>S89Jf*Ezm zrn9{mUHJE~DB&*@?q+ ru;X<|10ymA6K(>139nA3aG<+xjI5aVzGjHI;p>56?BI4k^9 MB)^RPgo>Bq|F(!CwQH&+;FmoRkh>6nStF!qiGqD z*L#nI?Hn%l6tlaSYI6mn8-mp>VG!q+b0uzEWO#SdDMs;&7i&??crs0s&J15>?c+#S zfijvm3oWT8DIyE_#d*n#O- *%j}huQ9;|;^A`O&UlbHxPVw?Y?j(6sYd*C$Dtg*zTORg% zSMcwYQNka(DX}o*ayIG)omZV*RJA*enU#0k_Ox6FlPO7?tNHSulI~qLkfDsGkd5SK zNboKXcKEP&RAyIjrt{>uht0l^m^d6mq5$z(c1K!ed$()leV35G
hsqYeZzkOZoW*LgI|Sek_@%a^gLAaLmoil}P#}x97*g*vlo^ zHm{}2;{--?-0Hwh{y+EmFs$y}nrkBtlN7`|x>+}IlJwEoO&*nPF_JFNW32HwZ&nd~ zjfkcHs%-JzC?>TTviuo2GqS_6GlyBO9`WjXea5T?dyYDe)qVQocypw1jDw`gQ;K70 zG3(0NTf2KH7_{GMZTvpE=}csLdaAz2+@(Ey>nTp_bNh;?U+@UCGM0?ob6>~4+;rnX zjKASnU8!v%0=~C*+&*6Jp!wLDY9Gsqf joM zPh6bs8=$|-V!XeUY3zl}6|(Et`%nZ{m-Ho ALGJA%^O?2RUWT-6!i# zHeK=BX{q`6a07*xuXp*bxd%s6&eN*syfwBkYAqcm9VlG%KO!D>Jh&G7zAqZ9s}*FC zuSH(;Ju%~uNey{-inM9k0ppGC;a*MMeCt>kI9;MXL`V!ZW%$IV(HjJO(VI#TQ)c^6 z*5IpNTC&kj2)m!1#OiimtneJAn*1tudj6#IHM#)rO{$SOd5epJTG> 2k3pm! n&}fRtU^2GN}4aHq>_iP|S3>?8%GadQUwRcS;Xa z-#2-LlTB-3zSYkqgnb_sht(C1ZflTx7(mtiDStbuW~_tSkv_kxHsfK;T27?X=0~+~ z?rjzqBZg^Qrk&@K;+bAtSyES2AEvdCnRUr6yxmWQ@i!i;n>H`unY%@eKgOV0mV#J# z2T5fZ8M|IT^JcwI8w3N)`D;sEm3ByU@;RR1K4fpjJU2+gb^EooDEk(oPfu#2j-0^g zCSY}i)A!hn-mA&qs!2X79>9=f`Mo0Wre#4ZpXOD)n*y_%>ZU3-2hY(fp9;xrz2i(| z)Z$j%78-9`%lpNLyDdEe`@ObAtZwuN{-COfV2>XSYhs_$Mv!ptJN{$u*kG^iZoND2 zs3~K_Hd}wbBD#tF7W1VKMNApjn>k-6bW3s=hFFh&+5XN!5#w(XR=15TtL5W(r{}q1 zwx*UBdBo1gV)NOyehkt&unspeKl+|?XHoQ?FCOxzche_lRHa^JZce+J=3Oq^?EXy2 z^>dXtM)wp}_fg;g&Ao1E`Ws|2CL8(RI0YLSa}PaubX$uCH)UKuuki#o6|McFSAL5} z-p+x7A(I8=g-tP>4|s4nDV*JVLWME9$yi+-UwN6e<<|>a-rFzcTNpejDHXMPTu8$h zv+1LmV>Mlj2xtBAA{~CS4PEc~-LA?xQ1Y^>e?NAqEk)3xgY@7!?E9V5Sl!IeYld_x zUzJ&X)Oz+kb=$z;`)94$v%?K!JYKVWoV8(}kMlPLQq%Bcbop=)-8nfL5})MtSmmni zn)Ug?4{p3p!}yzm)qR~*CDU+r ``wssGWY&z^S-J zPBk?msG#^= _Q@2APJ;G+2SGGWFsujlHvshhL zJ>S_zpGY$w%i|xP@7|SoCWYLf>W!@0L9@*VvyRK0f3T@3KdOJ)GH~YJyWHH1rw(zn zmcGiWzToef$|O|lYL3xO#p(v1yV?+Cz{T8D_AS2jc^`FHWszCk-2|Hh^4Apu7W71< zA|(W$`|-9ikmOEV^r@e}mmm|$r)#}gfSk3-qw(xZjP5zC?&JD3yFyO$UHm@PS@G0n zt>?(r@qmR5`%CQfB?n9N|3CJ=1Rkp`?Dr+Jj42gmip-fckU~O+GN)8#%8;2T5>XK< zq-0D9No1Cxlp;kTQ<;+~LeixBtk>&n-*ejM-gEA`-*@ltvwy$8z3peO|9aN5p7pG? z*Iw`5>{MOK8sRpfQESSxl%y1YP -PJ~1~GZ4 ztX%f+d~iiFrC{=fewiJjN>akdc*4wj9*2JTeER36hpUq5uRp&yM4{^>rk96ZzY~w; zb=nY8pf9j~N@ars≫z168lfF) z1+%Y>*!655+~@qsKMdP%Uc>SV27dEd(zS*{d1_#wI7MQVrRgEvxz{ZXiJ@+C)JMMU zXHV9q6W*Fx{H^GNiOJ}Vr#lao=-!F&=W}Pto`_mkehXuV1S~IQTKDE&$rn$*Uq4&u zn
dIE< z7{aA{-0^{eCT`y~^Ils@O0#XcpW0%r=c7xZpK9bb8!4o78asYS#`1FHUGEnUn%l~{ z>iao5Q+A!=?rlMAou6*+KO)w}w}tZE{pHJ9{0)lQq6Vz5I>==vwCa0q4dyu>-74Cg z!&<%uyRIz-%d7Uhgk9xl%g$>T<6JFyeiU(j2#x5MU6r$r;&?`HiNT0^+O^YPm#y;Z zX>cr^>3>4LB%R{;*QM5B);b~Hduxth=lfEzysDSX%7iy_)4uSmd-1cl#Hm_X&gT;w zxADM5N45$-Df1ma!?tl>dUDE$V~vvvy>+qr4bz02l5;0V1#X!}Fuk$G*!Mb?H#AxC z;f!y!Xp_Y0!|ohH!%@r?^g%v%XYaRL9+kaWCl@**bZkrG&ike|tA?DJ-5iq7=d3EW zdbBpKa6`|l9VyuPk~AzYnX1je557Yxx$Y~!8PXb-ZSM0n5et%6Wsmyg{ {w zwLfUr5^ATmIc=_MQnOd0Fudtl-gNz3k&At2Y|Oq;(1eUx2DkhWkJo3{%o_btmU@ZW z{MypDlNs=zbu!s-aN489jP?iHB_e7Is6FLljw%Q3#QN_oEN}enGLzx0+d``%<(;%n zsdmRv{!rEsSwF)Z@4W72$ED4#r&PDzPzjNhnpRm_A~P2pq+6XR>)Xlca_fPqS%hgK zM&Ar9Z=}R&o-!*2{^~a)XRM>A+%oOPCNc%x)aTful--?Fx{bp0-E3)s*=zcKX2=zq z-=CA|o0twYmwzBOw99H|)O8GRCYG0n|B749k&4r5{^V)zp4xLd>r_rT)`dPdnN|;4 z?Xo#w?sUqsbMz_K`{f)u%$Fz-I@faXN*BrP{nq`4^;22K1co;Y%d47c#9pfww_(e< zLtg{0i_aaXHeY4v@%`;(UhVHcnl``qSz|!)!M3MD@(FpQ{*Jl!$J1YzR^{v>7#~_U zeQn}+4TkqNmUpRqrL1}ihXZ-bjU1)yjOZ;%{JmeeM4zR7{eCI8Jgk~G%b`B`4cTj3 zO6s47r~Fw;okpptC&pilcm{7#Rdsuh;myYK_EP&AMTc)|@ZOyg$Y!|z=I-ScJzAUT z6=kyeI5V@{p1hJ`_wl~3eT(k=b#1z5H4alcN>6+a#a#RFnAT}?5)*bjc?ZjD?SI@i zb5GSU+kxvR3|5cEu$vC*oA*~YJUCZe;IZUnsjKwP?SXcxjLoN&zV8!0k$h{aaqasi z`K4d{H%vGd+1|wHn}g*wnXXv-u|fMx&DjU )DG~ELn$0)0?KFc125UQS@N5e+yL#u)x>gMDT`aGG-~-v&rFY8;^X2Vc*>{|O zrQ}Q3J}g~x #R6D~(Fi9jcCc;X2x}@==d>X^vy=@x7%q66+k)FP{lH z^I%(6-t11T*q-{cba^7!dHXypFXM$;hvtp@gzT&1uPeXrqJ6f~l;2-qRdK^%!B=$6 z+<GNoRiQ3i+bywUsT9>F@&@W$u@QTRDcZDHWCs2AVTq^u34Web~J8mIH0In@;&D zH}z}j3I~qf580qp)O0_r=VtaKD_igD0u>78{1@%|?nm;%l!DmKp3?|A>T`Mc1mBd3 zPLLCZH~)X){a~}+C7Z1xJba%<%=c@WtAm>Ryh7YM_$t1L(yv_ib0(uB?0|e-YspTH z)#*LuYt;{EewX~j?|O;gezI*xF?PPX0LvSZ*;4=YvEldF$JFIpw0v^c##b;4o>Hsm zVK-~N{q-5&@`2tHKSy-Py;-)@FfM&) P sy^t`<4? zF5g&xw|WrYjxx1f-MTtf{wqQ{UJZm64~fowetN3pb$H~s6~mE7cfT_#W5;JDSYC>D za?9(n`=_@An5ADoHM^2!>p{k*uTjqqW#*kHgm*gd+6B3P+Obw>_P~>~ljHZqvbe43 zmCwG+_SaJN&kyy&K7T94@`}1_o#2QXTS2unb1>UhT=VuA!-G51J*sB({%d2}g3mV( zuaS=4@uA@8k}2~PcCI6y>jR}WZOE@ lMk3VyZsA*-Yust90eujM>TaM+`&re;o=YEdASh;MmrukLnk(BW$i&_cxar4)& zBPS?0DL=ik*nVj@Ig@SttVvyeQ@r+8!`iWek$S#b)<>~|*mX=5Sl*O3E3UcUKS6C3 z5VBVBLlkG5tO?WBD}C)l)u}#fnkR0TW;SeZxmW#Wjs2+a^a~?bvKOL5`^G1vr1s90 zHC2ke!q}k_%e&|6S_#HP?nDXgld5|1Y1fiNM@qlWz8&5lwL!U;>PVM$9|w7M|K&{v zkuUoy cdj%$UyF*ubye^v~1RXS-K6>#OJowM2iZQ1K5jKFRm#q}#Ii z&*sO+XVN9VxMK8uh~@1FDXDdl x#*Zjbnn;0==%uE z8!K{G;qcGBvqz$b3TP^tTs=5n%L@yV3s5M0h&tcee{fQn&+Nka^5d*;%lFH!T_UxM zv1`Q4fako-#4eW|!gOyiypOTGJ6;-`Fs69(NLrXrSec%@R3~`rxPg11PGjL^huNL- zXEq Iaqik0y2bN^W>yWsUhK6AS6Z-gYt zwTE_BSY-}2 Badrl8u?#-HY^sHNJS750r@bKec(sLW3 zJ@JCPCNnM=-a0Jr9kPn>+qS;E!*o4y1E)ovAKl!#RlwR?=A1E2t}>gDU 2DiO_0s4w%^ivp-HG9?$MT+S ziMd_Ckr5ReX}4FwT0EAv$8c`vO)-~H9*5Ogp{r^4QLj4vZaVc5_06tNr#DLv$?W>1 z*04)t#XCcvvb7iV@-e*6vAji_=Ge4dvlJ%jHI-A}=2KfxJHAq=n;^G&aH^-V#5AMN z_*xZV {b9+rmJnz5aaPR=Io8J}J+!eW2C`CX zDZHLF2J2r{f8%FYPaeMBj`GA5c0G6_miK0|%f#A_vCF&WXfNd~y~lLG==4OV8^gPJ z%Y8PN3vbw-@!~MbRZL|`*=U&|_{KZLf6Ik{?{scDOD*1>4V )R1J9$q z^^FrRJ{3BT;eCbWrQP@D{hsdTYcDoO?yCL}G_rZ_HgAy1%dfYiD(%k?h|Ox=wPB-J zmMvAkb|$ 0?kr1)AWlMpN+RbhY#%iX$jX@aRW-KqA zE%WfZEzX@KpU-G|F#ECXOdE}9`WeovzU2Lt@p4V6X9*1_S_`k#Sv()!usl9)(?p%> z=PwQFdep<4U3WNK#*QCau)ME|Cui?A)>FQ{u{~*oL16iL+2e)`RJk*1p+CQUXm07$ zETQhZI*~GWJ)B8 GH5*5%lOD?XzS%I?GVvrwLD_ZXr6t(; z$yO}yW!JNB`Pny&HL+*ydZ*3A${MwsHG)Fq=a*F!n=d!+<8 luSo1s+9J0vFMlIO-!?4oxvR&9SUxg-((c>d#qdTqN3~~V+kgb$vbWM~ zC85WEs 9K|1#94z+x*Tiyb-T0w=`W$v%qaDk8z`L;jW#0KE zB2^=kIqnb0OLs9|9Dh14ZTy3xTkCRS_>1wm;rmOOLK@T}y>cAt(u{ofou!S}EqQbB zsCH^63--BQ2bMS4__~rY-MxrFtt~ZsPZ}Jpa}fC<^WYGLzwlYV*W1*b#a}Nm3>I8{ zFh{{FSe)08W?+0{s21bt=WU8XHubs&*!%fTEbn<%cDvEZmdD@uggFnF#%LbbKW|@U z^ny^!E;V)RUG+=9%p);^+ S-B+|A^se(&85XM;@LmM*=w82i4#@^U)d zP3mve=MEdLGw@BO4fOJ64appOSn%yk&^$ywBdt%u_$AebRo4{HXRe&AyjYG1PE| zGkiOR5=G%k( `oj0QRgGoF z?)g@$Zk>HSY-?M|P@yf;UA?Q%olaqs=BKJVatYzqHU?BikIlkd5-_|2Sl-F(X4WON z`vU#)YnqidaKC!BUxmudnM^f iIL9xF}5-CfZ) zqZEIy@q*4~4DUNEFQJ!PgOG58*QD}CrJBp#Z_&0oV BjQ>2HDb2S@by_6SVb*@b-c$_lAePruv*l)ERCijJppEp#;)_Mr7R@_? z{psb$i+{L0_nB+?$~c_Mp0Qsgk|WebG&pI kSoO7d7%vNmo{2^c}+To@$Z}zInCo#CZ*oqt0i>Xv|OBWv(_9Y#6DjdK|=A(&lqq z%(*z+kIRp>(EHJoeMc^Goqe&xgX8(O?q>n+(!AK`yu(;tL*F|_GY7V<;~2Qi)qPab zO-J~hQcPBZm>ic*r3iW8g$x$HWu?A5<~Hu_Vkyx*wYSRtSpP}|;r@c6T|F9;1K9i6 z5iGBcfEy*R<&nvbpjYD;v*H`By#917Sw-C}BvmeVhAYP 4|u zejA$vToWfx^F3wzS|;gz#-QkWuH{)xDSnUmexI_MBAz^7jD5$jyot4v;qSiKoqoZ# z%SMH5<;Gl@b8TOqa;sX-sB(H97NQ7FT$$cd`qi`5^lpu!dudMAeHQw-2zqAU@}tXF zQmr7v@P5GZ4i3JU__>s=p?ryeGSlj+_PAASAM Pc^l S#5;k;&o^^2is7$l5k_ zN9)= t2)FvdF=1|S7y3K*;BWjQOF74hp$|D+97ES0V61?KU`+&-2 zhQ>&^M8wA#!}}4-+toHG^tt85Gwr73N^Q@7B(_yeADv3o=rz~8JHnZv=a)|IF7@=V zj&_UcG4A+~%I&4sd ltQ#*&aAr;cWpNN;rlD>9qZh` z>Zh9DX5C-B&ZotA_F#n--_It^91B+Emp$!aUa?UG4n~C!tEzm)M7b00VBhOAh2i}_ zKE&8z8bd}=mLD&acjbV7`T1$r-c++MQnK4s+Uh4QTiP|=POvkr=NWK4_}ObLO*!G+ zg}^%sp?oxC{14-44Gd0PXX=Paq{i@m!t!2B4p^y>AvUgI|L#G~?)2hUFZRwR3y%xc z8nLUHMl>b7%k#I`8Z>s%c09C|pQoRqs@2_*`s{k`y-Fgv%9D8yFuXHZ-mgp)vh2Bc z9*b^zo>(zuC{TZAd1F=lGBZCN#q1|XbR}9g8%lN`E*0!ASgEu&gR>x_hcDClghp=2 zlwd!3+Hewv_cNCF(Je39=uZbsl`aMdRq`BJvzz?f>#FpG=C2MsRCS3z^%#Y6Twirj z`f|k_ncFS(ZsJwDp6$0XmIx+O>N^6DhOoasp2hMODvU x_qaPnx0+^N5;uHYKk?x4I&tF1S!V2zXKr*m~yzI|JG zd^FJPqfs2^SakMwj2*sWd5cb}JkHo-Wphkt+4ZSV)e~vyTx+f!o8D1=sm(?5!=2~O zyteI+)L!jtat#T%Mb|y3b1I^VM{IuzcWq;pqH$M0hW8tm_oaM!`eX=mEzkXFeHu-9 zy1d6jmC|IP0?)GUstK^FZ(<$lIpf8zaW((SAx2f&q$QuptJ=0wu#DbV5)*9rn4Jd0 z`yI>MWY|Bg9dfl_+M01{wuUy;byn7KN2}Je(pQIP#HPrSGtLww-U=f()QNd|Nc<_q zo@YZYDdUpXqOqbXeo5&w*!PV5!18_@qh8^2W9R#&*SBcucognWkg8}~zFO>FV&B21 zlqYAhXz$B~?v1dba(H{uhHYE+HbohwCYg}~_C?B#x^d2XUSRZ{!}8Wl`?d+iysZva zb8^eh xbTE< z3-2%uirdMJnp{&KhB)1nvqrDTcoa5~Pc@sYXFl*$|COEZNs0BTv2GQQiM#>C@4_s> z@;dM6mp{AflePGz>GmIbAvF&4bo5WiiX;duHw7QdezMcFnx32BG`DWgd#}DOop-=1 z|4Rn<<<^58C!*P6T=sq@@=_4LiGl7l7B-5VUzgt*_^hLTVwdji4dF*sAHOkeH==%` zN~mMpzvYI8KuP)~zfan%-@ooUa5s3&UTM7$SDK|=q^iO7^&yN0wp5aMQJqm@c{#$I zrW*X%MB6Kxn$3OKqo5_PO}o8>?2) zq?TsQd^zAGQ?`l3O96^fVR`SRzI;^uw0|hv*=Wp2h@h3oaH7ps&RBs`-|?wZ6M1>T zI{oBIAKRp_J?0M6pKg(fx+=zN>@fUvL3^FU?aQa;pTEsJG3q-OHi~|$xu`n1y?auP zp3ga*zHKMO*l!|JcZNJ-6WKrx@6Efj;a{Hb^4j)3wf7;DQBx0{*3AHy9p)ZUcLL5A zZ}Pb`zn*|V&J2VoRxE53h9zpnF8uwwvtARLZ@zi{QCT|H^GKk{dJU$K(uzYLRyYm6 zF8Oh?+aO+9+A6~@H0_A8*TtQpC%nEJD8_skyIxPS1LCE{@(!NOoK#mD7TW))>z=Xq z@obsd47<*T^_-H1nPELk=b||ExA+~r9pcB9U_Cv _?hgY6IX=fT6ZI!(;`Aj597ypobv|W?(-BP zq@TT6v*(8BK+kDH$TGg@Z`&j;U(qM>(h%DlJ(f3hFjssmEPU_eobQQcyCUK_O^Twb z%2zAgKSJJac&v$WqDnUK?Iwl}hOg%vw$}bIIC+LzO;ER(p1#A|f%atlygv|VxIy2g zSl%bu+O$=lo7ECtl?OZ}uzSak&3#u-?Mjckq#0pqTCz%>XG;Lnmh&|iJ&!kz5K=$N zkJfZC9V^mHvn&fujBqE#9U2 Bn&YFJIk+poF;2t&OcXz@-|TZt$7c6u$J>&hz@-S5`b{C1y( zqO-uW`S}L|4Qj_MSl+fn8}i;dZS~OD`i@R%yKqhSz?|IWyYE!@sD8@bs5mBN+8f~L zKz?(pkC*dB#n@~9*UEgq5GV>pZd{HrdReJ@g=B|qz{`r|-F&=2^sY5!Z+*lJWv9WM zi =Lz43`3g@i9iN{! zBG91rwhYS~)Rds~_DMPSw^Y{J74K8tRlZo?PkoF>pRI)cYTjODlcbUgp`=$aD?^1; z?r}}5NOQhpe&f}u7knqVznQ#hjI$)_OGC6T8 LO%w@E%968C*=aHE-Mm801s42G&yZ!;KrCiu3 zD(siAeDIAPTvlRqinmW+NvyFhlzwGGT+`V2vb`cFJ+-r|w~87(UbEjY{LzcN19ia# zhN}j*3H)TY-RB$;<*Z56cm6p8diJugQSisp7TNCkpsBFownx_u=_|S8qeaU?V!oRt zFYTP0p0VhAN*}x4uSQFDT(4pKVK&Rv#w+(}Jq`;EG8KKVz5T2ohL;`7YfNoMHMHdU zhfm3$E4Op`z8k9IKAb3YMeEyRkvn4K8Mbi462wc-BS~O zqm9wFpFa#^cvoV16S~6NI-is@@2RG6Y4PS!NWSx$r8i2aM(B0B-z|EXqMM$UO+TJ^ z=^xkL$a8whXB)+*SDW|R`HgLePvXpV6#RkVU4`Yn8E$uKeVAUEe7G(D8mop=du94% zqW4dB8nfwJJZif5&Mv7tPONriz>Gg<#l!Di+1sXWyI!;LHB6>Zi+yFEY>MGsjpgl` zxIW$~8dT#ZbuE)iC&M&XwO5!wy-0bt|M|1M-1_5D)VlY(+f+8xo8J5I_+Wz5?#zVL zT`rs*&dYc=Gv!~C#_*ywr3)K{`4xGQ`U_0=ENaClnpBxmq^3S-9=PEi$@fx9@ovp2 z!`Egb9Rn}bKeW5bA6imv%k1D%djDC}T5V2ex_ITHO9dESbPv9;QMjGlb6lgsqi}_7 z{?_Kh_m&WH*(wSX-dtVAu&L_1-iz**v?II)v=>gE$!%*|vyV6M^&M~KykV&qt@Wzm zsZwJT7~VBl-n+@0KKR~xtaGQ+Q-1LM6gAcAm$?_}b~H|0x@+~FTfBTSPnqmF%bidL zf9>sO#I2$;UI;litS<|QW$g 0rCLU7_t5uicDVp;-Paq%T@TpqwG?wdF}74_F!5>`87(n3J{WXm zjjW%~aI5X1I~ZPWEU&qG;$4pq7hDGPo~E4=kWt9$Rv7;F-Q ;e2<;wD?Kgx zf>k6LfBIQkg@^wPH`AgYQ;v&i=h~9=wBPXthL;D+%l<><+V+T)i%~XW>(6!H$r7Yi ziE?EoYZT)aDh>|2Qt zJK_Gd4a19`9sX+@hIbu?m%?@|+9mjD<#6P9%FP{jhLq`9 zWJZ{7?``JZ%&M;a^9Dg7crM|-PQ3{kSM{v3F>}4Z8rSF@x6QUSj{1t8l9$gW^Lnj< z;YH7M7B-52R~uwbC@xKS&VO!Bko;!jG4FSYOyURfQf=0kNpWburF~z;(z+@}K0=?P z_=orxsk5vMk9uRK#yXCR uZ+lLpkK@Da5|y=IvYJHhpv!qpu*AS9)J$ z GJQ8AY~ zZhEKA(?P8~bB0i$B&6_JJ#6AJh17Dn+J~Rg>P<6+_IwC an2@80IG5z;T*Kpv>NPSfVqMI&*s5gOnA-9xK>gwGT(CQ5!E?V;S(5?q&IHh+IH z$Areel33os)k}{1CvGTgC)?B>XdRJu*YN>Y<9PlJiP{WKtCD`P@v=25*A`LEt}%g` z+`fHU5piGNYRkR0u1|`Wj$!XoA>P8yw__ *XC5u-?z-$ zR}RZ-^0I4Sy KV;Yh$c`bLV^MrnkB8yiGqg=UaGo7d%V9 z$M2l9=d6RO+PCg#)qS7hMo%x*){{B6dr9Syc+o8yHQRz&z0iEr?>2A~!ckXicXxYh zSG0Z%?rQ?@uCsP?uy=BY?|nle2Au1Dmv-^o|Ct3)9l6>&x|-V%2p0BE;)mV-&-fNo z|JPXn)#YI~F&}eB2f`Q=%>Dcqwj-pAxuvtK6YMish~L}}fWyV@{~-&YI#OVVIau%$ zHQ3|D=l_RPUQ}h#0*e+{w7{YT7A>%7fkg}as}?}xAvbGBPit2K;mVfz`%rYw)!NP3 z!PA=10q5xdi^tR_6yX^(=wRt!zj&|quh#zGCqwgUXj|CN|M%y=zw@gs}|JwMspy%R#(E|TF7C^ph zXFY! R1r{x^Xn{owELvdE0*e+{ zw7{YT7A>%7fkg`}T42!vixyb4z@h~fEwE^TMGGuiV9^4L7Fe{vq6HQ$uxNor3oKe- z(E^JWShT>R1r{x^Xn{owELvdE0*e;-Z()JOCFuVxs(G=Hixyb4z@h~fEwE^TMGO40 zz~3JJ{Ott4@>)Fl|H=aY_iOt9eh&X%8T`LaS#HvcQJYA6It4dZOL2QAH+ORf2XO~y z%Of`S4%XrZuGZFDxuqny-RzH9JKKnI%W#`J*xNce+rZ1|7Ph&c^Bejfy)PJB$qWCZ z-wrRFNALbc$4dZI0JNd^_M&UhyAQ6R1K7~JanW^@fX$?H=)Jb+97?AK=a6jv{q_U` zH5~hqu0!vWMc1Kkc&sCxLq$dB2&9*{wxSPQz(%LwAuY8PiiT52bQPjN`Xc>Mo+y8m z-xUyC1;hi8-id$!z)3(LAOx@*um_+AFaXE^WC3yjdB8@%CIBkq7Jvdk5x@di2H=2v zI00(_TmT*bFMtoQ4!{o(0IUZH0fYe}08xM#Kpe0EAOVmBNCBh)G5}eC96%nh5wHoc z833=;A;2qb2wMS4fNcO|OIiS`dU)RsdLJx$mnHfgIPn`6tgv4OU;``%tN^S8tO7(p zIU)g3fONnuKn5TS5Cez>TmTpW+yL$X4}d4Y3*Zg#0T=^J0DA$Z01e3;Ue_b$~Qr z1;7Q)qgSTFt7{1%fKWgb-~s@>NdvwA@*E%x-~c!VI08@u1OiS0P6Gk}z5qYKaeybl z24D-=3or#}0(Jn ziaRB gF`;*+ ze*ts?-T mRsd^&4Zs#)2e1bm1{?u6 z02~2M0B68afD6DCPyi?d6aij9KEAL=?P?!@8^8 >3aAC3?-c0*oB`f|2tXtt z3UC6-EC~<>M8mb`0fzx*026=?U 7EKhzIs4#-f 08RszLK=8|C?OM$ zQLNMj_yYt1H0Yznuubfk-~X>-Aj$*fKLu$}Jv9MPJOnCYOs$9gE5J)YBcK8B9Drhn z3jo=a4uI^7?2PRF08kFN4?uN<+7;qNF$vk^5P%UN1&{ 4$Ve zdC>ys0So|SYZM3B0BZp&0LuZ${%ZgnfYkt$ZWUl90G;O~feZHh03HAzfETb1AOJx2 zz}f+o9hDn#2>}EFA^>54C_o$_0gwSm1CZY6coP7XTOJ?>*bLYPKy`$G?5PUS0H_1h z06PJi0BwLCzyP2R*aJXyg!~tsNB^Tesvp$GO#%A>W`F|##BT{O2Uq~C0M>vwfGwaD zPy#p(@BughiUCD{LV!2G1K C>S@5x& zaJ=9rsBDN2r9ovvKEJ?&(xLni7m^{}5YV*?x-a~X_>qn70MKy;AQx~KkOM&1q4Ov$ zlA-f|JhzYz<%@g^D@V4E1|XkY;6e3<@?5ws>o1N=L59kOxRD(fIBvr+N{{wPj*hX{ zpz jbFMx9>Mj-w30H_S8t`}k?D$5H1igm~z7Wj~#pt7PengFnQ zBD*17u;>2xKe`5Sq4SJ@*MMO_Ghhh-o$Ca208{|20CY_YfSPpN4*NF15I_3U SV2{dy zS3Yzt`u5X$fB*oER~Om_;#@dh*kifTH+RsO7L9H3@}GjdQU2(=DadAME&$CDpt%Fm zw8Wowm2h1Z0JT*#6@cai(0l<(!>tB?V5111-_t4Vlae#@*a(v#a{n 4A+CDLwN$YkprNSU%)2VGr``b+1Dwk;&v)G6%U+Q31<$GBpy`Bfhxi2;oMZ} zBnfJv+;GK{XU~UM?>-<-Lv}z`Ty~?l1hFq$h1lAZat*~c4*ZfVe4(Y-Ai(zybNVbu zHi}E5H0i*$4AS&Y2)q%NckTd*ytuTKxYU1q{iXAOgkG*laX1ZGDO?La9&iYXjrPk| zUK3qGUcZ%`?0~emr1(aNDj-4H$MPwOwmT`V0*R!!l(;O66+prclCkakOk{Q0HW4M# z;?fXdNNLKmrr&ire$gjNB*kT5{O4@x?rQI3eb!J&O|0SJ2arfXJ`ympgEUC% ?NFm79KDTds^6Yk@p_h^Wno$NP|iWY0y15vcczc3$36wt^lG2 zL>o|%6YmfL@}lmNGekazmIYc%AnU9I3Cc%={piL0`aY;U$U2}kx Vh*K^i$IDX^irA>;=sRlPm} zug=SYL p> *5oaXG|><`P{&GOYLbX{+^(!yu6b zyAic^@vwIFA+$(;de4w2XblqZoOw16cY6mng12xthhJs26G%`kKt4A1PF7;@dP2r` z_Kd6Z_qY++h&HfwaJDdqG~(q&;>uEjFA*CUbd$J*y_*;k61;+J-ihmaELapYSomhk z`L=URUsAKkAq`jn>Gm7%1wTZR7O*Vxs_MNGpXtI1B2Ye{HAwLNtqam14_QIEca6@P zQ%}jsHj2y2BkQ0qt0M`wr)G{Tn`R}jNr-PkEo+J-S;O1IS4qoT3=%oy2hhWiB!+E0 zBCK4yjuF!kElLfY89lJ=KH*T-c{eW$B=YliV P|IRF*WPRR z>`@v>klmm>Iv_#Oa@CV4+Bd;DnM8>sl9-Vs%bD+%9*c0g3=)z>9YDeiX{=8SdAaWG zxd{>!Lm;~Nkt7wOSKGH3uGA!^kwWb>j3kkA$eXHtVP^{xlHKA#g5u6#wojCCuM4zB z&`kzua0eu8AUU6$Yxv{%3qz2gHV1Y35G1J0<*wq|Rke(dnv@Sn+ChTaX-{Z}`(2${ z8e$rv$4`NT10;$%M}5`=KTrS()Ef$8j1 A*yXnknjVW(Q5TZu8(=(I|~y0^87g-gi_Aygwui{pWk|~1d9IKp~vF{4Yr2v zN%?Zn!4EV*tr6_@9wewv_f~wMjDI^ONYqVITpng(V0ef8z<5uSw%O#<9+K8zH!YAL zkAE8O$H-3J>`hJvhDHS?IJ&_f*skBJZq9!lp-#+)=pk0n;82 b0IgJOt-xx2fy6`?Vyt0}vH zD}yMJ5{K@b7$!gi8{_Z~tM}^c0132G#OCZ|2{8 o=2P3@=O+*P%H+v^LYgc=BH ?^yPz!M>J;`UoxOClqvLNQSbrW}lxbGn?p zl*9)4=z@e9B#-Kb^G_Vqa``2}*Uih-{OD01xKD|1d%oV!^BXxCvNPK7B}0%s(!fz@ ztwqKq3te(DWNMVo#@x-_1@@l3ny(~u!p(>pK-+@*6$lf{AWi*J?IzRwaUm!vmtS;RxYk%5>(h%K^U