diff --git a/notebooks/workflow_example.ipynb b/notebooks/workflow_example.ipynb index def5e0a6..32414fef 100644 --- a/notebooks/workflow_example.ipynb +++ b/notebooks/workflow_example.ipynb @@ -238,7 +238,7 @@ "id": "58ed9b25-6dde-488d-9582-d49d405793c6", "metadata": {}, "source": [ - "This node also exploits type hinting! `run()` will check that input values conform to type hints before computing anything. Failing at this stage won't actually cause the node to have a `failed` status, so you can just re-run it once the input is fixed." + "This node also exploits type hinting! New values are checked against the node type hint, so trying to assign an incommensurate value will raise an error:" ] }, { @@ -246,27 +246,33 @@ "execution_count": 10, "id": "ac0fe993-6c82-48c8-a780-cbd0c97fc386", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(int, str)" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "adder_node.inputs.x = \"not an integer\"\n", - "adder_node.inputs.x.type_hint, type(adder_node.inputs.x.value)\n", - "# adder_node.run()" + "# adder_node.inputs.x = \"not an integer\"" + ] + }, + { + "cell_type": "markdown", + "id": "fb2eb642-4fe8-4ab7-b969-288150e43112", + "metadata": {}, + "source": [ + "We can bypass this by deactivating the `strict_hinting`, or by assigning a new value directly to the private `_value` attribute. The `run()` will also perform a check that input values conform to type hints before computing anything. Failing at this stage won't actually cause the node to have a `failed` status, so you can just re-run it once the input is fixed." ] }, { "cell_type": "code", "execution_count": 11, + "id": "a63b2cc0-9030-45ad-8d37-d11e16e61369", + "metadata": {}, + "outputs": [], + "source": [ + "adder_node.inputs.x._value = \"not an integer\"\n", + "# adder_node.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, "id": "15742a49-4c23-4d4a-84d9-9bf19677544c", "metadata": {}, "outputs": [ @@ -276,7 +282,7 @@ "3" ] }, - "execution_count": 11, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -296,7 +302,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "id": "0c8f09a7-67c4-4c6c-a021-e3fea1a16576", "metadata": {}, "outputs": [ @@ -306,7 +312,7 @@ "30" ] }, - "execution_count": 12, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -326,7 +332,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "id": "69b59737-9e09-4b4b-a0e2-76a09de02c08", "metadata": {}, "outputs": [ @@ -336,7 +342,7 @@ "31" ] }, - "execution_count": 13, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -369,7 +375,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "id": "61b43a9b-8dad-48b7-9194-2045e465793b", "metadata": {}, "outputs": [], @@ -379,7 +385,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "id": "647360a9-c971-4272-995c-aa01e5f5bb83", "metadata": {}, "outputs": [ @@ -416,7 +422,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "id": "b8c845b7-7088-43d7-b106-7a6ba1c571ec", "metadata": {}, "outputs": [ @@ -462,7 +468,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "id": "2e418abf-7059-4e1e-9b9f-b3dc0a4b5e35", "metadata": { "tags": [] @@ -484,7 +490,7 @@ "2" ] }, - "execution_count": 17, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -518,7 +524,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "id": "f3b0b700-683e-43cb-b374-48735e413bc9", "metadata": {}, "outputs": [ @@ -528,7 +534,7 @@ "4" ] }, - "execution_count": 18, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -554,7 +560,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "id": "59c29856-c77e-48a1-9f17-15d4c58be588", "metadata": {}, "outputs": [ @@ -590,7 +596,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "id": "1a4e9693-0980-4435-aecc-3331d8b608dd", "metadata": {}, "outputs": [], @@ -602,7 +608,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 22, "id": "7c4d314b-33bb-4a67-bfb9-ed77fba3949c", "metadata": {}, "outputs": [ @@ -641,7 +647,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 23, "id": "61ae572f-197b-4a60-8d3e-e19c1b9cc6e2", "metadata": {}, "outputs": [ @@ -651,7 +657,7 @@ "4" ] }, - "execution_count": 22, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -682,7 +688,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 24, "id": "6569014a-815b-46dd-8b47-4e1cd4584b3b", "metadata": {}, "outputs": [ @@ -696,7 +702,7 @@ }, { "data": { - "image/png": "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ssrKyoL2/+c//6lrr71Wa9asUUpKin75y1/qoYce0rFjx664eAAA4Hy2wsj58+dVU1Oj3NzcgPbc3FwdPnw46JicnBydOXNGFRUVsixLX375pfbs2aM777wz9KoBAEDYsBVGWltb1d3drcTExID2xMRENTc3Bx2Tk5OjXbt2KT8/X7GxsUpKStJVV12ll19+ecDX6erqUnt7e8ADAACEp5AuYHW5XAHPLcvq19arrq5Oa9as0VNPPaWamhpVVlaqoaFBBQUFA/7+kpISeTyevkdycnIoZQIAAAdwWZZlDbbz+fPnNW7cOO3evVt33313X/vatWt18uRJHThwoN+YZcuWqbOzU7t37+5rO3jwoObNm6ezZ8/K6/X2G9PV1aWurq6+5+3t7UpOTpbf71d8fPyg3xwAADCnvb1dHo/nsp/ftlZGYmNjlZGRoaqqqoD2qqoq5eTkBB3z7bffKioq8GWio6MlXVxRCSYuLk7x8fEBDwAAEJ5sb9MUFhbqjTfe0Pbt21VfX6/169ersbGxb9tl48aNWr58eV//vLw8vfvuuyorK9Pp06d16NAhrVmzRllZWZo6derQvRMAAOBIMXYH5Ofnq62tTZs3b5bP51NaWpoqKio0ffp0SZLP5ws4c+T+++9XR0eH/vznP+v3v/+9rrrqKt1222169tlnh+5dAAAAx7J1zYgpg91zAgAAo8ewXDMCAAAw1AgjAADAKMIIAAAwijACAACMIowAAACjCCMAAMAowggAADCKMAIAAIwijAAAAKMIIwAAwCjCCAAAMIowAgAAjCKMAAAAowgjAADAKMIIAAAwijACAACMIowAAACjCCMAAMAowggAADCKMAIAAIwijAAAAKMIIwAAwCjCCAAAMIowAgAAjCKMAAAAowgjAADAKMIIAAAwijACAACMIowAAACjCCMAAMAowggAADCKMAIAAIwijAAAAKMIIwAAwCjCCAAAMIowAgAAjCKMAAAAowgjAADAKMIIAAAwijACAACMIowAAACjCCMAAMAowggAADCKMAIAAIwijAAAAKMIIwAAwCjCCAAAMIowAgAAjCKMAAAAowgjAADAKMIIAAAwijACAACMIowAAACjCCMAAMAowggAADCKMAIAAIwijAAAAKMIIwAAwCjCCAAAMIowAgAAjCKMAAAAo0IKI6WlpUpJSZHb7VZGRoaq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UGjztHfvXmVmZuq5557T1Vdfreuvv16PPfaYvvvuu5EoOeKEMkc5OTk6c+aMKioqZFmWvvzyS+3Zs0d33nnnSJQ8qjnii/JaW1vV3d2txMTEgPbExEQ1NzcHHdPc3By0/4ULF9Ta2iqv1zts9UaqUObpx/70pz/pm2++0ZIlS4ajxIgXyhx99tln2rBhg6qrqxUT44h/MhwvlHk6ffq0Dh48KLfbrffee0+tra16+OGHde7cOa4bGQahzFFOTo527dql/Px8dXZ26sKFC7rrrrv08ssvj0TJo5ojVkZ6uVyugOeWZfVru1z/YO0YWnbnqdc777yjp59+WuXl5ZoyZcpwlQcNfo66u7t13333qbi4WNdff/1IlYf/svO31NPTI5fLpV27dikrK0uLFi3SCy+8oB07drA6MozszFFdXZ3WrFmjp556SjU1NaqsrFRDQ4MKCgpGotRRzRH/zZk0aZKio6P7pc2WlpZ+qbRXUlJS0P4xMTGaOHHisNUayUKZp17l5eVatWqVdu/erdtvv304y4xodueoo6NDx44d04kTJ/Too49KuvihZ1mWYmJitH//ft12220jUnskCeVvyev16uqrrw74uvZZs2bJsiydOXNG11133bDWHGlCmaOSkhLNnTtXjz/+uCTp5z//ucaPH6958+bpmWeeiegVe0esjMTGxiojI0NVVVUB7VVVVcrJyQk6Jjs7u1///fv3KzMzU2PGjBm2WiNZKPMkXVwRuf/++/X222+zdzrM7M5RfHy8Pv74Y508ebLvUVBQoJ/97Gc6efKk5syZM1KlR5RQ/pbmzp2rs2fP6uuvv+5r+/TTTxUVFaVp06YNa72RKJQ5+vbbbxUVFfixGx0dLen/Vu4jlqkrZ+3qvYVq27ZtVl1dnbVu3Tpr/Pjx1n/+8x/Lsixrw4YN1rJly/r6997au379equurs7atm0bt/aOALvz9Pbbb1sxMTHW1q1bLZ/P1/f46quvTL2FsGd3jn6Mu2lGht156ujosKZNm2b9+te/tj755BPrwIED1nXXXWc98MADpt5C2LM7R2+++aYVExNjlZaWWqdOnbIOHjxoZWZmWllZWabewqjhmDBiWZa1detWa/r06VZsbKyVnp5uHThwoO9nK1assG655ZaA/v/4xz+sX/ziF1ZsbKx17bXXWmVlZSNccWSyM0+33HKLJanfY8WKFSNfeASx+7f0Q4SRkWN3nurr663bb7/dGjt2rDVt2jSrsLDQ+vbbb0e46shid462bNlipaamWmPHjrW8Xq/1m9/8xjpz5swIVz36uCwr0teGAACASY64ZgQAAIQvwggAADCKMAIAAIwijAAAAKMIIwAAwCjCCAAAMIowAgAAjCKMAAAAowgjAADAKMIIAAAwijACAACMIowAAACj/j+E4OcogDUlLwAAAABJRU5ErkJggg==", + "image/png": "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BBcAAGANggsAALAGwQUAAFiD4AIAAKxBcAEAANYguAAAAGsQXAAAgDWCCi5lZWVKTU2V2+1WZmamampqfrD/+fPntWHDBk2ePFkJCQmaOnWqtm3bFlTBAAAgesU53aGiokKrV69WWVmZ5syZo5deekkLFy5UQ0ODfvrTn/a7z5IlS/TFF19o69at+tnPfqaWlhZdvHjxiosHAADRxWWMMU52mD17tjIyMlReXt7bNmPGDC1evFilpaV9+ldVVemee+7RyZMnNWbMmKCKbG9vl8fjkd/vV2JiYlBfAwAADK9QXL8dTRVduHBBtbW1ysvLC2jPy8vTwYMH+91n165dysrK0rPPPquJEydq+vTpWrt2rb799tsB3+f8+fNqb28P2AAAABxNFbW2tqqrq0tJSUkB7UlJSWpubu53n5MnT2r//v1yu93auXOnWltb9cgjj+jcuXMD3udSWlqqkpISJ6UBAIAoENTNuS6XK+C1MaZPW4/u7m65XC7t2LFD2dnZWrRokTZv3qzXX399wFGX9evXy+/3926nTp0KpkwAABBhHI24jBs3TrGxsX1GV1paWvqMwvTwer2aOHGiPB5Pb9uMGTNkjNHp06c1bdq0PvskJCQoISHBSWkAACAKOBpxiY+PV2ZmpqqrqwPaq6urlZub2+8+c+bM0dmzZ/XVV1/1tn366aeKiYnRpEmTgigZAABEK8dTRUVFRXr11Ve1bds2HT9+XGvWrFFTU5MKCwslXZrmWb58eW//e++9V2PHjtWKFSvU0NCgffv26YknntCvf/1rjRw5cug+CTBIXd1Gh0606e/1Z3ToRJu6uh09WAcACCPH67gUFBSora1NmzZtks/nU3p6uiorKzV58mRJks/nU1NTU2//n/zkJ6qurtZvfvMbZWVlaezYsVqyZImefvrpofsUwCBVHfOpZHeDfP7O3javx63i/DQtSPeGsTIAwGA4XsclHFjHBUOh6phPD28/qsv/w/fcVl6+NIPwAgBDKOzruAC26uo2Ktnd0Ce0SOptK9ndwLQRAFzlCC6ICocbzwVMD13OSPL5O3W48dzwFQUAcIzggqjQ0jFwaAmmHwAgPAguiArjR7uHtB8AIDwILogK2alj5PW41f/6zpdu0PV63MpODe4PgQIAhgfBBVEhNsal4vw0SeoTXnpeF+enKTZmoGgDALgaEFwQNRak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", "text/plain": [ "
" ] @@ -751,7 +757,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 25, "id": "1cd000bd-9b24-4c39-9cac-70a3291d0660", "metadata": {}, "outputs": [], @@ -778,7 +784,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 26, "id": "7964df3c-55af-4c25-afc5-9e07accb606a", "metadata": {}, "outputs": [ @@ -819,7 +825,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 27, "id": "809178a5-2e6b-471d-89ef-0797db47c5ad", "metadata": {}, "outputs": [ @@ -873,7 +879,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 28, "id": "52c48d19-10a2-4c48-ae81-eceea4129a60", "metadata": {}, "outputs": [ @@ -883,7 +889,7 @@ "{'ay': 3, 'a + b + 2': 7}" ] }, - "execution_count": 27, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -911,7 +917,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 29, "id": "bb35ba3e-602d-4c9c-b046-32da9401dd1c", "metadata": {}, "outputs": [ @@ -921,7 +927,7 @@ "(7, 3)" ] }, - "execution_count": 28, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -940,7 +946,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 30, "id": "2b0d2c85-9049-417b-8739-8a8432a1efbe", "metadata": {}, "outputs": [ @@ -959,127 +965,127 @@ "clustersimple\n", "\n", "simple: Workflow\n", + "\n", + "clustersimplesum\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "sum: AddNode\n", + "\n", + "\n", + "clustersimplesumInputs\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inputs\n", + "\n", + "\n", + "clustersimplesumOutputs\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Outputs\n", + "\n", "\n", "clustersimpleInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "Inputs\n", "\n", "\n", "clustersimpleOutputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "Outputs\n", "\n", "\n", "clustersimplea\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "a: AddOne\n", "\n", "\n", "clustersimpleaInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "Inputs\n", "\n", "\n", "clustersimpleaOutputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "Outputs\n", "\n", "\n", "clustersimpleb\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "b: AddOne\n", "\n", "\n", "clustersimplebInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "Inputs\n", "\n", "\n", "clustersimplebOutputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", + "\n", "Outputs\n", "\n", - "\n", - "clustersimplesum\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "sum: AddNode\n", - "\n", - "\n", - "clustersimplesumInputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inputs\n", - "\n", - "\n", - "clustersimplesumOutputs\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Outputs\n", - "\n", "\n", "\n", "clustersimpleInputsrun\n", @@ -1258,10 +1264,10 @@ "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 29, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -1288,14 +1294,14 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 31, "id": "ae500d5e-e55b-432c-8b5f-d5892193cdf5", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "c46776a009974c03934aeea1cb8be1ce", + "model_id": "9fad22dbcc8940cbaa936a48e84d054c", "version_major": 2, "version_minor": 0 }, @@ -1314,10 +1320,10 @@ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 30, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" }, @@ -1360,7 +1366,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 32, "id": "2114d0c3-cdad-43c7-9ffa-50c36d56d18f", "metadata": {}, "outputs": [ @@ -1568,10 +1574,10 @@ "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 31, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -1600,7 +1606,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 33, "id": "c71a8308-f8a1-4041-bea0-1c841e072a6d", "metadata": {}, "outputs": [], @@ -1610,7 +1616,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 34, "id": "2b9bb21a-73cd-444e-84a9-100e202aa422", "metadata": {}, "outputs": [ @@ -1628,7 +1634,7 @@ "13" ] }, - "execution_count": 33, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -1667,7 +1673,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 35, "id": "3668f9a9-adca-48a4-84ea-13add965897c", "metadata": {}, "outputs": [ @@ -1677,7 +1683,7 @@ "{'intermediate': 102, 'plus_three': 103}" ] }, - "execution_count": 34, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -1715,7 +1721,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "id": "9aaeeec0-5f88-4c94-a6cc-45b56d2f0111", "metadata": {}, "outputs": [], @@ -1745,7 +1751,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "id": "a832e552-b3cc-411a-a258-ef21574fc439", "metadata": {}, "outputs": [], @@ -1772,7 +1778,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 38, "id": "b764a447-236f-4cb7-952a-7cba4855087d", "metadata": {}, "outputs": [ @@ -1791,159 +1797,159 @@ "clusterphase_preference\n", "\n", "phase_preference: Workflow\n", - "\n", - "clusterphase_preferencemin_phase2\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "min_phase2: LammpsMinimize\n", - "\n", - "\n", - "clusterphase_preferencemin_phase2Inputs\n", + "\n", + "clusterphase_preferenceInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterphase_preferencemin_phase2Outputs\n", + "\n", + "clusterphase_preferenceOutputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Outputs\n", "\n", - "\n", - "clusterphase_preferencecompare\n", + "\n", + "clusterphase_preferenceelement\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "compare: PerAtomEnergyDifference\n", + "\n", + "element: UserInput\n", "\n", - "\n", - "clusterphase_preferencecompareInputs\n", + "\n", + "clusterphase_preferenceelementInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterphase_preferencecompareOutputs\n", + "\n", + "clusterphase_preferenceelementOutputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Outputs\n", "\n", - "\n", - "clusterphase_preferenceInputs\n", + "\n", + "clusterphase_preferencemin_phase1\n", "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "min_phase1: LammpsMinimize\n", + "\n", + "\n", + "clusterphase_preferencemin_phase1Inputs\n", + "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterphase_preferenceOutputs\n", + "\n", + "clusterphase_preferencemin_phase1Outputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Outputs\n", "\n", - "\n", - "clusterphase_preferenceelement\n", + "\n", + "clusterphase_preferencemin_phase2\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "element: UserInput\n", + "\n", + "min_phase2: LammpsMinimize\n", "\n", - "\n", - "clusterphase_preferenceelementInputs\n", + "\n", + "clusterphase_preferencemin_phase2Inputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterphase_preferenceelementOutputs\n", + "\n", + "clusterphase_preferencemin_phase2Outputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Outputs\n", "\n", - "\n", - "clusterphase_preferencemin_phase1\n", + "\n", + "clusterphase_preferencecompare\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", - "\n", - "min_phase1: LammpsMinimize\n", + "\n", + "compare: PerAtomEnergyDifference\n", "\n", - "\n", - "clusterphase_preferencemin_phase1Inputs\n", + "\n", + "clusterphase_preferencecompareInputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Inputs\n", + "\n", + "Inputs\n", "\n", - "\n", - "clusterphase_preferencemin_phase1Outputs\n", + "\n", + "clusterphase_preferencecompareOutputs\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "\n", - "\n", - "Outputs\n", + "\n", + "Outputs\n", "\n", "\n", "\n", @@ -2170,192 +2176,192 @@ "\n", "\n", "clusterphase_preferenceInputsphase2\n", - "\n", - "phase2\n", + "\n", + "phase2\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputscrystalstructure\n", - "\n", - "crystalstructure\n", + "\n", + "crystalstructure\n", "\n", "\n", "\n", "clusterphase_preferenceInputsphase2->clusterphase_preferencemin_phase2Inputscrystalstructure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferenceInputslattice_guess2\n", - "\n", - "lattice_guess2\n", + "\n", + "lattice_guess2\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputslattice_guess\n", - "\n", - "lattice_guess\n", + "\n", + "lattice_guess\n", "\n", "\n", "\n", "clusterphase_preferenceInputslattice_guess2->clusterphase_preferencemin_phase2Inputslattice_guess\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__structure__c\n", - "\n", - "min_phase2__structure__c\n", + "\n", + "min_phase2__structure__c\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputsstructure__c\n", - "\n", - "structure__c\n", + "\n", + "structure__c\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__structure__c->clusterphase_preferencemin_phase2Inputsstructure__c\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__structure__covera\n", - "\n", - "min_phase2__structure__covera\n", + "\n", + "min_phase2__structure__covera\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputsstructure__covera\n", - "\n", - "structure__covera\n", + "\n", + "structure__covera\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__structure__covera->clusterphase_preferencemin_phase2Inputsstructure__covera\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__structure__u\n", - "\n", - "min_phase2__structure__u\n", + "\n", + "min_phase2__structure__u\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputsstructure__u\n", - "\n", - "structure__u\n", + "\n", + "structure__u\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__structure__u->clusterphase_preferencemin_phase2Inputsstructure__u\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__structure__orthorhombic\n", - "\n", - "min_phase2__structure__orthorhombic\n", + "\n", + "min_phase2__structure__orthorhombic\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputsstructure__orthorhombic\n", - "\n", - "structure__orthorhombic\n", + "\n", + "structure__orthorhombic\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__structure__orthorhombic->clusterphase_preferencemin_phase2Inputsstructure__orthorhombic\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__structure__cubic\n", - "\n", - "min_phase2__structure__cubic\n", + "\n", + "min_phase2__structure__cubic\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputsstructure__cubic\n", - "\n", - "structure__cubic\n", + "\n", + "structure__cubic\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__structure__cubic->clusterphase_preferencemin_phase2Inputsstructure__cubic\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__calc__n_ionic_steps\n", - "\n", - "min_phase2__calc__n_ionic_steps: int\n", + "\n", + "min_phase2__calc__n_ionic_steps: int\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputscalc__n_ionic_steps\n", - "\n", - "calc__n_ionic_steps: int\n", + "\n", + "calc__n_ionic_steps: int\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__calc__n_ionic_steps->clusterphase_preferencemin_phase2Inputscalc__n_ionic_steps\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__calc__n_print\n", - "\n", - "min_phase2__calc__n_print: int\n", + "\n", + "min_phase2__calc__n_print: int\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputscalc__n_print\n", - "\n", - "calc__n_print: int\n", + "\n", + "calc__n_print: int\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__calc__n_print->clusterphase_preferencemin_phase2Inputscalc__n_print\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__calc__pressure\n", - "\n", - "min_phase2__calc__pressure\n", + "\n", + "min_phase2__calc__pressure\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Inputscalc__pressure\n", - "\n", - "calc__pressure\n", + "\n", + "calc__pressure\n", "\n", "\n", "\n", "clusterphase_preferenceInputsmin_phase2__calc__pressure->clusterphase_preferencemin_phase2Inputscalc__pressure\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -2432,74 +2438,74 @@ "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__cells\n", - "\n", - "min_phase2__calc__cells\n", + "\n", + "min_phase2__calc__cells\n", "\n", "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__displacements\n", - "\n", - "min_phase2__calc__displacements\n", + "\n", + "min_phase2__calc__displacements\n", "\n", "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__energy_tot\n", - "\n", - "min_phase2__calc__energy_tot\n", + "\n", + "min_phase2__calc__energy_tot\n", "\n", "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__force_max\n", - "\n", - "min_phase2__calc__force_max\n", + "\n", + "min_phase2__calc__force_max\n", "\n", "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__forces\n", - "\n", - "min_phase2__calc__forces\n", + "\n", + "min_phase2__calc__forces\n", "\n", "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__indices\n", - "\n", - "min_phase2__calc__indices\n", + "\n", + "min_phase2__calc__indices\n", "\n", "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__positions\n", - "\n", - "min_phase2__calc__positions\n", + "\n", + "min_phase2__calc__positions\n", "\n", "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__pressures\n", - "\n", - "min_phase2__calc__pressures\n", + "\n", + "min_phase2__calc__pressures\n", "\n", "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__steps\n", - "\n", - "min_phase2__calc__steps\n", + "\n", + "min_phase2__calc__steps\n", "\n", "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__total_displacements\n", - "\n", - "min_phase2__calc__total_displacements\n", + "\n", + "min_phase2__calc__total_displacements\n", "\n", "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__unwrapped_positions\n", - "\n", - "min_phase2__calc__unwrapped_positions\n", + "\n", + "min_phase2__calc__unwrapped_positions\n", "\n", "\n", "\n", "clusterphase_preferenceOutputsmin_phase2__calc__volume\n", - "\n", - "min_phase2__calc__volume\n", + "\n", + "min_phase2__calc__volume\n", "\n", "\n", "\n", @@ -2794,28 +2800,28 @@ "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__cells\n", - "\n", - "calc__cells\n", + "\n", + "calc__cells\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__cells->clusterphase_preferenceOutputsmin_phase2__calc__cells\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__displacements\n", - "\n", - "calc__displacements\n", + "\n", + "calc__displacements\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__displacements->clusterphase_preferenceOutputsmin_phase2__calc__displacements\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -2839,132 +2845,132 @@ "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__energy_tot\n", - "\n", - "calc__energy_tot\n", + "\n", + "calc__energy_tot\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__energy_tot->clusterphase_preferenceOutputsmin_phase2__calc__energy_tot\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__force_max\n", - "\n", - "calc__force_max\n", + "\n", + "calc__force_max\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__force_max->clusterphase_preferenceOutputsmin_phase2__calc__force_max\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__forces\n", - "\n", - "calc__forces\n", + "\n", + "calc__forces\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__forces->clusterphase_preferenceOutputsmin_phase2__calc__forces\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__indices\n", - "\n", - "calc__indices\n", + "\n", + "calc__indices\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__indices->clusterphase_preferenceOutputsmin_phase2__calc__indices\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__positions\n", - "\n", - "calc__positions\n", + "\n", + "calc__positions\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__positions->clusterphase_preferenceOutputsmin_phase2__calc__positions\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__pressures\n", - "\n", - "calc__pressures\n", + "\n", + "calc__pressures\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__pressures->clusterphase_preferenceOutputsmin_phase2__calc__pressures\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__steps\n", - "\n", - "calc__steps\n", + "\n", + "calc__steps\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__steps->clusterphase_preferenceOutputsmin_phase2__calc__steps\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__total_displacements\n", - "\n", - "calc__total_displacements\n", + "\n", + "calc__total_displacements\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__total_displacements->clusterphase_preferenceOutputsmin_phase2__calc__total_displacements\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__unwrapped_positions\n", - "\n", - "calc__unwrapped_positions\n", + "\n", + "calc__unwrapped_positions\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__unwrapped_positions->clusterphase_preferenceOutputsmin_phase2__calc__unwrapped_positions\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__volume\n", - "\n", - "calc__volume\n", + "\n", + "calc__volume\n", "\n", "\n", "\n", "clusterphase_preferencemin_phase2Outputscalc__volume->clusterphase_preferenceOutputsmin_phase2__calc__volume\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -2996,10 +3002,10 @@ "\n" ], "text/plain": [ - "" + "" ] }, - "execution_count": 37, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -3010,7 +3016,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 39, "id": "b51bef25-86c5-4d57-80c1-ab733e703caf", "metadata": {}, "outputs": [ @@ -3031,7 +3037,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 40, "id": "091e2386-0081-436c-a736-23d019bd9b91", "metadata": {}, "outputs": [ @@ -3072,7 +3078,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 41, "id": "4cdffdca-48d3-4486-9045-48102c7e5f31", "metadata": {}, "outputs": [ @@ -3110,7 +3116,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 42, "id": "ed4a3a22-fc3a-44c9-9d4f-c65bc1288889", "metadata": {}, "outputs": [ @@ -3140,7 +3146,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 43, "id": "5a985cbf-c308-4369-9223-b8a37edb8ab1", "metadata": {}, "outputs": [ @@ -3247,7 +3253,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 44, "id": "0b373764-b389-4c24-8086-f3d33a4f7fd7", "metadata": {}, "outputs": [ @@ -3261,7 +3267,7 @@ " 17.230249999999995]" ] }, - "execution_count": 43, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -3298,7 +3304,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 45, "id": "0dd04b4c-e3e7-4072-ad34-58f2c1e4f596", "metadata": {}, "outputs": [ @@ -3357,7 +3363,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 46, "id": "2dfb967b-41ac-4463-b606-3e315e617f2a", "metadata": {}, "outputs": [ @@ -3381,7 +3387,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 47, "id": "2e87f858-b327-4f6b-9237-c8a557f29aeb", "metadata": {}, "outputs": [ @@ -3389,18 +3395,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "0.639 > 0.2\n", - "0.481 > 0.2\n", - "0.582 > 0.2\n", - "0.213 > 0.2\n", - "0.829 > 0.2\n", - "0.826 > 0.2\n", - "0.401 > 0.2\n", - "0.929 > 0.2\n", - "0.251 > 0.2\n", - "0.525 > 0.2\n", - "0.087 <= 0.2\n", - "Finally 0.087\n" + "0.460 > 0.2\n", + "0.990 > 0.2\n", + "0.321 > 0.2\n", + "0.663 > 0.2\n", + "0.231 > 0.2\n", + "0.695 > 0.2\n", + "0.122 <= 0.2\n", + "Finally 0.122\n" ] } ], diff --git a/pyiron_workflow/channels.py b/pyiron_workflow/channels.py index cb3958ca..f2901ad6 100644 --- a/pyiron_workflow/channels.py +++ b/pyiron_workflow/channels.py @@ -235,15 +235,15 @@ class DataChannel(Channel, ABC): (In the future they may optionally have a storage history limit.) (In the future they may optionally have an ontological type.) - Note that for the sake of computational efficiency, assignments to the `value` - property are not type-checked; type-checking occurs only for connections where both - channels have a type hint, and when a value is being copied from another channel - with the `copy_value` method. + Note that type checking is performed on value updates. This is typically not super + expensive, but once you have a workflow you're happy with, you may wish to + deactivate `strict_hints` throughout the workflow for the sake of computational + efficiency during production runs. When type checking channel connections, we insist that the output type hint be _as or more specific_ than the input type hint, to ensure that the input always receives output of a type it expects. This behaviour can be disabled and all - connections allowed by setting `strict_connections = False` on the relevant input + connections allowed by setting `strict_hints = False` on the relevant input channel. For simple type hints like `int` or `str`, type hint comparison is trivial. @@ -271,6 +271,20 @@ class DataChannel(Channel, ABC): hint a tuple with a mixture of fixed elements of fixed type, followed by an arbitrary elements of arbitrary type. This and other complex scenarios are not yet included in our test suite and behaviour is not guaranteed. + + Attributes: + value: The actual data value held by the node. + label (str): The label for the channel. + node (pyiron_workflow.node.Node): The node to which this channel belongs. + default (typing.Any|None): The default value to initialize to. + (Default is the class `NotData`.) + type_hint (typing.Any|None): A type hint for values. (Default is None.) + strict_hints (bool): Whether to check new values, connections, and partners + when this node is a value receiver. This can potentially be expensive, so + consider deactivating strict hints everywhere for production runs. (Default + is True, raise exceptions when type hints get violated.) + value_receiver (pyiron_workflow.node.Node|None): Another node of the same class + whose value will always get updated when this node's value gets updated. """ def __init__( @@ -279,14 +293,16 @@ def __init__( node: Node, default: typing.Optional[typing.Any] = NotData, type_hint: typing.Optional[typing.Any] = None, + strict_hints: bool = True, value_receiver: typing.Optional[InputData] = None, ): super().__init__(label=label, node=node) self._value = NotData self._value_receiver = None - self.default = default - self.value = default self.type_hint = type_hint + self.strict_hints = strict_hints + self.default = default + self.value = default # Implicitly type check your default by assignment self.value_receiver = value_receiver @property @@ -295,6 +311,16 @@ def value(self): @value.setter def value(self, new_value): + if ( + self.strict_hints + and new_value is not NotData + and self._has_hint + and not valid_value(new_value, self.type_hint) + ): + raise TypeError( + f"The channel {self.label} cannot take the value `{new_value}` because " + f"it is not compliant with the type hint {self.type_hint}" + ) if self.value_receiver is not None: self.value_receiver.value = new_value self._value = new_value @@ -324,6 +350,17 @@ def value_receiver(self, new_partner: InputData | OutputData | None): f"{self.__class__.__name__} {self.label} cannot couple to itself" ) + if self._both_typed(new_partner) and new_partner.strict_hints: + if not type_hint_is_as_or_more_specific_than( + self.type_hint, new_partner.type_hint + ): + raise ValueError( + f"The channel {self.label} cannot take {new_partner.label} as " + f"a value receiver because this type hint ({self.type_hint}) " + f"is not as or more specific than the receiving type hint " + f"({new_partner.type_hint})." + ) + new_partner.value = self.value self._value_receiver = new_partner @@ -357,7 +394,7 @@ def _valid_connection(self, other) -> bool: if super()._valid_connection(other): if self._both_typed(other): out, inp = self._figure_out_who_is_who(other) - if not inp.strict_connections: + if not inp.strict_hints: return True else: return type_hint_is_as_or_more_specific_than( @@ -378,22 +415,6 @@ def _figure_out_who_is_who(self, other: DataChannel) -> (OutputData, InputData): def __str__(self): return str(self.value) - def copy_value(self, other: DataChannel) -> None: - """ - Copies the other channel's value. Unlike normal value assignment, the new value - (if it is data) must comply with this channel's type hint (if any). - """ - if ( - self._has_hint - and other._value_is_data - and not valid_value(other.value, self.type_hint) - ): - raise TypeError( - f"Channel{self.label} cannot copy value from {other.label} because " - f"value {other.value} does not match type hint {self.type_hint}" - ) - self.value = other.value - def to_dict(self) -> dict: d = super().to_dict() d["value"] = repr(self.value) @@ -401,36 +422,14 @@ def to_dict(self) -> dict: d["type_hint"] = str(self.type_hint) return d + def activate_strict_hints(self) -> None: + self.strict_hints = True -class InputData(DataChannel): - """ - `fetch()` updates input data value to the first available data among connections. - - The `strict_connections` parameter controls whether connections are subject to - type checking requirements. - I.e., they may set `strict_connections` to `False` (`True` -- default) at - instantiation or later with `(de)activate_strict_connections()` to prevent (enable) - data type checking when making connections with `OutputData` channels. - """ + def deactivate_strict_hints(self) -> None: + self.strict_hints = False - def __init__( - self, - label: str, - node: Node, - default: typing.Optional[typing.Any] = NotData, - type_hint: typing.Optional[typing.Any] = None, - value_receiver: typing.Optional[InputData] = None, - strict_connections: bool = True, - ): - super().__init__( - label=label, - node=node, - default=default, - type_hint=type_hint, - value_receiver=value_receiver, - ) - self.strict_connections = strict_connections +class InputData(DataChannel): def fetch(self) -> None: """ Sets `value` to the first value among connections that is something other than @@ -451,12 +450,6 @@ def fetch(self) -> None: self.value = out.value break - def activate_strict_connections(self) -> None: - self.strict_connections = True - - def deactivate_strict_connections(self) -> None: - self.strict_connections = False - class OutputData(DataChannel): pass diff --git a/pyiron_workflow/composite.py b/pyiron_workflow/composite.py index 861d4301..1f8376da 100644 --- a/pyiron_workflow/composite.py +++ b/pyiron_workflow/composite.py @@ -126,6 +126,16 @@ def outputs_map(self, new_map: dict | bidict | None): new_map = new_map if new_map is None else bidict(new_map) self._outputs_map = new_map + def activate_strict_hints(self): + super().activate_strict_hints() + for node in self.nodes: + node.activate_strict_hints() + + def deactivate_strict_hints(self): + super().deactivate_strict_hints() + for node in self.nodes: + node.deactivate_strict_hints() + @property def _owned_creator(self): """ diff --git a/pyiron_workflow/function.py b/pyiron_workflow/function.py index 3face365..e16cff32 100644 --- a/pyiron_workflow/function.py +++ b/pyiron_workflow/function.py @@ -157,8 +157,7 @@ class Function(Node): Note that getting "good" (i.e. dot-accessible) output labels can be achieved by using good variable names and returning those variables instead of using `output_labels`. - If we force the node to run with bad types, it will raise an - error: + If we try to assign a value of the wrong type, it will raise an error: >>> from typing import Union >>> >>> def hinted_example( @@ -168,7 +167,17 @@ class Function(Node): ... p1, m1 = x+1, y-1 ... return p1, m1 >>> - >>> plus_minus_1 = Function(hinted_example, x="not an int") + >>> plus_minus_1 = Function(hinted_example) + >>> plus_minus_1.inputs.x = "not an int or float" + TypeError: The channel x cannot take the value `not an int or float` because it + is not compliant with the type hint typing.Union[int, float] + + We can turn off type hinting with the `strict_hints` boolean property, or just + circumvent the type hinting by applying the new data directly to the private + `_value` property. + In the latter case, we'd still get a readiness error when we try to run and + the ready check sees that the data doesn't conform to the type hint: + >>> plus_minus_1.inputs.x._value = "not an int or float" >>> plus_minus_1.run() ValueError: hinted_example received a run command but is not ready. The node should be neither running nor failed, and all input values should conform to @@ -180,13 +189,9 @@ class Function(Node): Here, even though all the input has data, the node sees that some of it is the wrong type and so (by default) the run raises an error right away. - Note that the type hinting doesn't actually prevent us from assigning bad values - directly to the channel (although it will, by default, prevent connections - _between_ type-hinted channels with incompatible hints), but it _does_ stop the - node from running and throwing an error because it sees that the channel (and - thus node) is not ready - >>> plus_minus_1.inputs.x.value - 'not an int' + This causes the failure to come earlier because we stop the node from running + and throwing an error because it sees that the channel (and thus node) is not + ready: >>> plus_minus_1.ready, plus_minus_1.inputs.x.ready, plus_minus_1.inputs.y.ready (False, False, True) diff --git a/pyiron_workflow/io.py b/pyiron_workflow/io.py index f9b341db..0932a57d 100644 --- a/pyiron_workflow/io.py +++ b/pyiron_workflow/io.py @@ -191,18 +191,18 @@ def to_dict(self): d["ready"] = self.ready return d + def activate_strict_hints(self): + [c.activate_strict_hints() for c in self] + + def deactivate_strict_hints(self): + [c.deactivate_strict_hints() for c in self] + class Inputs(DataIO): @property def _channel_class(self) -> type(InputData): return InputData - def activate_strict_connections(self): - [c.activate_strict_connections() for c in self] - - def deactivate_strict_connections(self): - [c.deactivate_strict_connections() for c in self] - def fetch(self): for c in self: c.fetch() diff --git a/pyiron_workflow/macro.py b/pyiron_workflow/macro.py index b68eaaf2..f3a9edc3 100644 --- a/pyiron_workflow/macro.py +++ b/pyiron_workflow/macro.py @@ -205,9 +205,7 @@ def _get_linking_channel( composite_channel.value = child_reference_channel.value if isinstance(composite_channel, InputData): - composite_channel.strict_connections = ( - child_reference_channel.strict_connections - ) + composite_channel.strict_hints = child_reference_channel.strict_hints composite_channel.value_receiver = child_reference_channel elif isinstance(composite_channel, OutputData): child_reference_channel.value_receiver = composite_channel diff --git a/pyiron_workflow/node.py b/pyiron_workflow/node.py index 14f33aae..0a9fa5fc 100644 --- a/pyiron_workflow/node.py +++ b/pyiron_workflow/node.py @@ -600,6 +600,16 @@ def draw( """ return GraphvizNode(self, depth=depth, rankdir=rankdir).graph + def activate_strict_hints(self): + """Enable type hint checks for all data IO""" + self.inputs.activate_strict_hints() + self.outputs.activate_strict_hints() + + def deactivate_strict_hints(self): + """Disable type hint checks for all data IO""" + self.inputs.deactivate_strict_hints() + self.outputs.deactivate_strict_hints() + def __str__(self): return ( f"{self.label} ({self.__class__.__name__}):\n" @@ -760,7 +770,7 @@ def _copy_values( if to_copy.value is not NotData: try: old_value = my_panel[key].value - my_panel[key].copy_value(to_copy) + my_panel[key].value = to_copy.value # Gets hint-checked old_values.append((my_panel[key], old_value)) except Exception as e: if fail_hard: diff --git a/tests/unit/test_channels.py b/tests/unit/test_channels.py index c6e711fe..c6861954 100644 --- a/tests/unit/test_channels.py +++ b/tests/unit/test_channels.py @@ -25,9 +25,12 @@ def setUp(self) -> None: self.no = OutputData(label="numeric", node=DummyNode(), default=0, type_hint=int | float) self.no_empty = OutputData(label="not_data", node=DummyNode(), type_hint=int | float) + self.si = InputData(label="list", node=DummyNode(), type_hint=list) self.so1 = OutputData(label="list", node=DummyNode(), default=["foo"], type_hint=list) self.so2 = OutputData(label="list", node=DummyNode(), default=["foo"], type_hint=list) + self.unhinted = InputData(label="unhinted", node=DummyNode) + def test_mutable_defaults(self): self.so1.default.append("bar") self.assertEqual( @@ -138,7 +141,7 @@ def test_connection_validity_tests(self): ): self.so1.connect(self.ni2) - self.ni2.strict_connections = False + self.ni2.strict_hints = False self.so1.connect(self.ni2) self.assertIn( self.so1, @@ -171,47 +174,51 @@ def test_copy_connections(self): "state" ) - def test_copy_value(self): - self.ni1.value = 2 - self.ni2.copy_value(self.ni1) - self.assertEqual( + def test_value_receiver(self): + self.ni1.value_receiver = self.ni2 + new_value = 42 + self.assertNotEqual( self.ni2.value, - self.ni1.value, - msg="Should be able to copy values matching type hints" + 42, + msg="Sanity check that we're not starting with our target value", ) - - self.ni2.copy_value(self.no_empty) - self.assertIs( + self.ni1.value = new_value + self.assertEqual( + new_value, self.ni2.value, - NotData, - msg="Should be able to copy values that are not-data" + msg="Value-linked nodes should automatically get new values" ) + with self.assertRaises( + ValueError, + msg="Linking should obey type hint requirements", + ): + self.ni1.value_receiver = self.si + + self.si.strict_hints = False + self.ni1.value_receiver = self.si # Should work fine if the receiver is not + # strictly checking hints + + self.ni1.value_receiver = self.unhinted + self.unhinted.value_receiver = self.ni2 + # Should work fine if either is unhinted + + def test_value_assignment(self): + self.ni1.value = 2 # Should be fine when value matches hint + self.ni1.value = NotData # Should be able to clear the data + with self.assertRaises( TypeError, - msg="Should not be able to copy values of the wrong type" + msg="Should not be able to take values of the wrong type" ): - self.ni2.copy_value(self.so1) + self.ni2.value = [2] + + self.ni2.strict_hints = False + self.ni2.value = "now we can take any value" + self.ni2.strict_hints = True self.ni2.type_hint = None - self.ni2.copy_value(self.ni1) - self.assertEqual( - self.ni2.value, - self.ni1.value, - msg="Should be able to copy any data if we have no type hint" - ) - self.ni2.copy_value(self.so1) - self.assertEqual( - self.ni2.value, - self.so1.value, - msg="Should be able to copy any data if we have no type hint" - ) - self.ni2.copy_value(self.no_empty) - self.assertEqual( - self.ni2.value, - NotData, - msg="Should be able to copy not-data if we have no type hint" - ) + self.ni2.value = "Also if our hint doesn't exist" def test_ready(self): with self.subTest("Test defaults and not-data"): @@ -231,7 +238,7 @@ def test_ready(self): self.ni1.value = 1 self.assertTrue(self.ni1.ready) - self.ni1.value = "Not numeric at all" + self.ni1._value = "Not numeric at all" # Bypass type checking self.assertFalse(self.ni1.ready) def test_input_coupling(self):