{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "de1360fc-f524-4f7d-878a-202b762a000e", "metadata": {}, "outputs": [], "source": [ "%run ../src/ipyautoui/__init__.py\n", "%load_ext lab_black" ] }, { "cell_type": "code", "execution_count": 9, "id": "5a531914-e287-4ef1-9896-c51a2937b375", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "' NOT YET IMPLEMENTED'" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\" not yet implemented\".upper()" ] }, { "cell_type": "markdown", "id": "170ab4fa-63f5-40ce-bcf6-1724caa0cac2", "metadata": {}, "source": [ "# Validation - NOTE: NOT YET IMPLEMENTED\n", "\n", "- jsonschema validation occurs on-change by default (TODO: implement!)\n", " - review https://github.com/pydantic/pydantic-core\n", "- the top-level pydantic model can also be validated on request (slows things down...?)\n", " - possible to implement recursive validation with pydantic? " ] }, { "cell_type": "code", "execution_count": 2, "id": "3a6be2ea-a222-4b35-b4ca-4e391781e3c2", "metadata": {}, "outputs": [ { "ename": "ValidationError", "evalue": "'Invalid' is not of type 'number'\n\nFailed validating 'type' in schema['properties']['price']:\n {'type': 'number'}\n\nOn instance['price']:\n 'Invalid'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)", "Input \u001b[0;32mIn [2]\u001b[0m, in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[38;5;66;03m# If no exception is raised by validate(), the instance is valid.\u001b[39;00m\n\u001b[1;32m 13\u001b[0m validate(instance\u001b[38;5;241m=\u001b[39m{\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mname\u001b[39m\u001b[38;5;124m\"\u001b[39m : \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mEggs\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mprice\u001b[39m\u001b[38;5;124m\"\u001b[39m : \u001b[38;5;241m34.99\u001b[39m}, schema\u001b[38;5;241m=\u001b[39mschema)\n\u001b[0;32m---> 15\u001b[0m \u001b[43mvalidate\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m 16\u001b[0m \u001b[43m \u001b[49m\u001b[43minstance\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mname\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m \u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mEggs\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mprice\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m \u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mInvalid\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mschema\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mschema\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m 17\u001b[0m \u001b[43m)\u001b[49m\n", "File \u001b[0;32m~/miniconda3/envs/ipyautoui-dev/lib/python3.9/site-packages/jsonschema/validators.py:1022\u001b[0m, in \u001b[0;36mvalidate\u001b[0;34m(instance, schema, cls, *args, **kwargs)\u001b[0m\n\u001b[1;32m 1020\u001b[0m error \u001b[38;5;241m=\u001b[39m exceptions\u001b[38;5;241m.\u001b[39mbest_match(validator\u001b[38;5;241m.\u001b[39miter_errors(instance))\n\u001b[1;32m 1021\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m error \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m-> 1022\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m error\n", "\u001b[0;31mValidationError\u001b[0m: 'Invalid' is not of type 'number'\n\nFailed validating 'type' in schema['properties']['price']:\n {'type': 'number'}\n\nOn instance['price']:\n 'Invalid'" ] } ], "source": [ "from jsonschema import validate, Draft202012Validator, ErrorTree\n", "\n", "# A sample schema, like what we'd get from json.load()\n", "schema = {\n", " \"type\" : \"object\",\n", " \"properties\" : {\n", " \"price\" : {\"type\" : \"number\"},\n", " \"name\" : {\"type\" : \"string\"},\n", " },\n", "}\n", "\n", "# If no exception is raised by validate(), the instance is valid.\n", "validate(instance={\"name\" : \"Eggs\", \"price\" : 34.99}, schema=schema)\n", "\n", "validate(\n", " instance={\"name\" : \"Eggs\", \"price\" : \"Invalid\"}, schema=schema,\n", ") " ] }, { "cell_type": "code", "execution_count": 3, "id": "32b78cb3-7b1c-4fc7-bb9a-17cd51ae4d50", "metadata": {}, "outputs": [], "source": [ "schema = {\n", " \"items\": {\n", " \"anyOf\": [{\"type\": \"string\", \"maxLength\": 2}, {\"type\": \"integer\", \"minimum\": 5}]\n", " }\n", "}\n", "instance = [{}, 3, \"foo\"]\n", "v = Draft202012Validator(schema)\n", "errors = sorted(v.iter_errors(instance), key=lambda e: e.path)" ] }, { "cell_type": "code", "execution_count": 4, "id": "e39f8063-acb2-43aa-86a9-4a08d271550e", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "35b949f30a5546168a865aa03577a4af", "version_major": 2, "version_minor": 0 }, "text/plain": [ "HBox(children=(Button(disabled=True, icon='check', layout=Layout(height='25px', width='44px'), style=ButtonSty…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from ipyautoui.constants import TRUE_BUTTON_KWARGS, BUTTON_WIDTH_MIN, BUTTON_HEIGHT_MIN\n", "import ipyw as w\n", "\n", "BLANK_BUTTON_KWARGS = dict(\n", " icon=\"times\",\n", " style={\"button_color\": \"red\"},\n", " layout={\"width\": BUTTON_WIDTH_MIN, \"height\": BUTTON_HEIGHT_MIN},\n", " disabled=True,\n", ")\n", "\n", "BLANK_BUTTON_KWARGS = dict(\n", " icon=\"check\",\n", " style={\"button_color\": \"white\"},\n", " layout={\"width\": BUTTON_WIDTH_MIN, \"height\": BUTTON_HEIGHT_MIN},\n", " disabled=True,\n", ")\n", "w.HBox([w.Button(**BLANK_BUTTON_KWARGS), w.Text(\"asdfasdf\")])" ] }, { "cell_type": "code", "execution_count": 5, "id": "97a51b55-ddea-4af0-a37f-59f99a2553e0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 'type'], {} is not of type 'string'\n", "[1, 'type'], {} is not of type 'integer'\n", "[0, 'type'], 3 is not of type 'string'\n", "[1, 'minimum'], 3 is less than the minimum of 5\n", "[0, 'maxLength'], 'foo' is too long\n", "[1, 'type'], 'foo' is not of type 'integer'\n" ] } ], "source": [ "for error in errors:\n", " for suberror in sorted(error.context, key=lambda e: e.schema_path):\n", " print(list(suberror.schema_path), suberror.message, sep=\", \")" ] }, { "cell_type": "code", "execution_count": 6, "id": "acb5746f-6be6-4630-a8e7-0ded0bd00ff2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "'spam' is not of type 'number'\n", "'spam' is not one of [1, 2, 3]\n", "['spam', 2] is too short\n" ] } ], "source": [ "schema = {\n", " \"type\": \"array\",\n", " \"items\": {\"type\": \"number\", \"enum\": [1, 2, 3]},\n", " \"minItems\": 3,\n", "}\n", "instance = [\"spam\", 2]\n", "\n", "v = Draft202012Validator(schema)\n", "for error in sorted(v.iter_errors([\"spam\", 2]), key=str):\n", " print(error.message)" ] }, { "cell_type": "code", "execution_count": 7, "id": "ff3af45b-6046-4357-adfc-80d1bf28be34", "metadata": {}, "outputs": [], "source": [ "tree = ErrorTree(v.iter_errors(instance))" ] }, { "cell_type": "code", "execution_count": 8, "id": "4081eded-f153-4846-a14d-2dd91a5d660b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "username='scolvin' password1='zxcvbn' password2='zxcvbn'\n", "1 validation error for UserModel\n", "__root__\n", " passwords do not match (type=value_error)\n" ] } ], "source": [ "from pydantic import BaseModel, ValidationError, root_validator\n", "\n", "\n", "class UserModel(BaseModel):\n", " username: str\n", " password1: str\n", " password2: str\n", "\n", " @root_validator(pre=True)\n", " def check_card_number_omitted(cls, values):\n", " assert \"card_number\" not in values, \"card_number should not be included\"\n", " return values\n", "\n", " @root_validator\n", " def check_passwords_match(cls, values):\n", " pw1, pw2 = values.get(\"password1\"), values.get(\"password2\")\n", " if pw1 is not None and pw2 is not None and pw1 != pw2:\n", " raise ValueError(\"passwords do not match\")\n", " return values\n", "\n", "\n", "print(UserModel(username=\"scolvin\", password1=\"zxcvbn\", password2=\"zxcvbn\"))\n", "# > username='scolvin' password1='zxcvbn' password2='zxcvbn'\n", "try:\n", " UserModel(username=\"scolvin\", password1=\"zxcvbn\", password2=\"zxcvbn2\")\n", "except ValidationError as e:\n", " print(e)" ] }, { "cell_type": "code", "execution_count": null, "id": "42cbb5b2-4d4f-44b4-b66e-932b807dae12", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "24f35a58-e4d5-4012-bf29-c0ec3e713195", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "ea279190-8152-474b-a829-277efb2144a9", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 28, "id": "9ee7f460-11bb-41e2-b136-77b67c1d24f6", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": 33, "id": "2ade04b5-4d59-417f-b355-25b25e4c7fae", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "2d140e33133c455d8c20d5b37926442f", "version_major": 2, "version_minor": 0 }, "text/plain": [ "VBox(children=(HBox(children=(Text(value=''), Label(value='schedule name in plain english (with spaces)'))), H…" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import stringcase as sc\n", "\n", "name = w.Text()\n", "name_ = w.Text(disabled=True)\n", "vbox = w.VBox(\n", " [\n", " w.HBox([name, w.Label(\"schedule name in plain english (with spaces)\")]),\n", " w.HBox([name_, w.Label(\"schedule name for machines\")]),\n", " ]\n", ")\n", "\n", "\n", "def on_name_change(on_change):\n", " name_.value = sc.pascalcase(sc.snakecase(name.value.lower())).strip(\"_\")\n", "\n", "\n", "name.observe(on_name_change, \"value\")\n", "\n", "vbox" ] }, { "cell_type": "code", "execution_count": null, "id": "02c53e29-c7aa-4a31-97b4-c3774ad1b595", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.13" } }, "nbformat": 4, "nbformat_minor": 5 }