From 6bbb76f6bbf370ac477c9fe862fd116d94eba1f8 Mon Sep 17 00:00:00 2001 From: Selene Chew Date: Thu, 22 Apr 2021 12:17:50 -0400 Subject: [PATCH 1/8] add query planning typedefs --- Pipfile | 3 +- Pipfile.lock | 313 +++++++++++++------- graphql_compiler/query_planning/typedefs.py | 295 ++++++++++++++++++ setup.py | 3 +- 4 files changed, 499 insertions(+), 115 deletions(-) create mode 100644 graphql_compiler/query_planning/typedefs.py diff --git a/Pipfile b/Pipfile index 6d4960e61..ec6afa9ab 100755 --- a/Pipfile +++ b/Pipfile @@ -40,10 +40,11 @@ sphinx = ">=1.8,<2" [packages] # Make sure to keep in sync with setup.py requirements. ciso8601 = ">=2.1.3,<3" +dataclasses-json = ">=0.5.2,<0.6" funcy = ">=1.7.3,<2" graphql-core = ">=3.1.2,<3.2" # minor versions sometimes contain breaking changes six = ">=1.10.0" -sqlalchemy = ">=1.3.0,<2" +sqlalchemy = ">=1.3.0,<1.4" # The below is necessary to make a few pylint passes work properly, since pylint expects to be able # to run "import graphql_compiler" in the environment in which it runs. diff --git a/Pipfile.lock b/Pipfile.lock index d5dcc5d8a..f15774490 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "3cb98c3dd25e1ed97827a7776f87def23c3112f56b8e5a6cb06119e7559ed6b6" + "sha256": "cccd3a7f57d64a4d4e40bcc9881b91001c99832d42b31d9f5c0ea5a6aa9ec5a8" }, "pipfile-spec": 6, "requires": { @@ -23,6 +23,14 @@ "index": "pypi", "version": "==2.1.3" }, + "dataclasses-json": { + "hashes": [ + "sha256:56ec931959ede74b5dedf65cf20772e6a79764d20c404794cce0111c88c085ff", + "sha256:b746c48d9d8e884e2a0ffa59c6220a1b21f94d4f9f12c839da0a8a0efd36dc19" + ], + "index": "pypi", + "version": "==0.5.2" + }, "funcy": { "hashes": [ "sha256:65b746fed572b392d886810a98d56939c6e0d545abb750527a717c21ced21008", @@ -37,11 +45,33 @@ }, "graphql-core": { "hashes": [ - "sha256:b1826fbd1c6c290f7180d758ecf9c3859a46574cff324bf35a10167533c0e463", - "sha256:c056424cbdaa0ff67446e4379772f43746bad50a44ec23d643b9bdcd052f5b3a" + "sha256:2ae62ed27eb5c629fdfd06f89bec36a427bef97d37aef41e8f195c5352f22bea", + "sha256:972eff18416ad90815c95914b754486270f13fbe1a24e010c7eedbaa903b9386" ], "index": "pypi", - "version": "==3.1.2" + "version": "==3.1.4" + }, + "marshmallow": { + "hashes": [ + "sha256:0dd42891a5ef288217ed6410917f3c6048f585f8692075a0052c24f9bfff9dfd", + "sha256:16e99cb7f630c0ef4d7d364ed0109ac194268dde123966076ab3dafb9ae3906b" + ], + "markers": "python_version >= '3.5'", + "version": "==3.11.1" + }, + "marshmallow-enum": { + "hashes": [ + "sha256:38e697e11f45a8e64b4a1e664000897c659b60aa57bfa18d44e226a9920b6e58", + "sha256:57161ab3dbfde4f57adeb12090f39592e992b9c86d206d02f6bd03ebec60f072" + ], + "version": "==1.5.1" + }, + "mypy-extensions": { + "hashes": [ + "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", + "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" + ], + "version": "==0.4.3" }, "six": { "hashes": [ @@ -53,47 +83,65 @@ }, "sqlalchemy": { "hashes": [ - "sha256:04f995fcbf54e46cddeb4f75ce9dfc17075d6ae04ac23b2bacb44b3bc6f6bf11", - "sha256:0c6406a78a714a540d980a680b86654feadb81c8d0eecb59f3d6c554a4c69f19", - "sha256:0c72b90988be749e04eff0342dcc98c18a14461eb4b2ad59d611b57b31120f90", - "sha256:108580808803c7732f34798eb4a329d45b04c562ed83ee90f09f6a184a42b766", - "sha256:1418f5e71d6081aa1095a1d6b567a562d2761996710bdce9b6e6ba20a03d0864", - "sha256:17610d573e698bf395afbbff946544fbce7c5f4ee77b5bcb1f821b36345fae7a", - "sha256:216ba5b4299c95ed179b58f298bda885a476b16288ab7243e89f29f6aeced7e0", - "sha256:2ff132a379838b1abf83c065be54cef32b47c987aedd06b82fc76476c85225eb", - "sha256:314f5042c0b047438e19401d5f29757a511cfc2f0c40d28047ca0e4c95eabb5b", - "sha256:318b5b727e00662e5fc4b4cd2bf58a5116d7c1b4dd56ffaa7d68f43458a8d1ed", - "sha256:3ab5b44a07b8c562c6dcb7433c6a6c6e03266d19d64f87b3333eda34e3b9936b", - "sha256:426ece890153ccc52cc5151a1a0ed540a5a7825414139bb4c95a868d8da54a52", - "sha256:491fe48adc07d13e020a8b07ef82eefc227003a046809c121bea81d3dbf1832d", - "sha256:4a84c7c7658dd22a33dab2e2aa2d17c18cb004a42388246f2e87cb4085ef2811", - "sha256:54da615e5b92c339e339fe8536cce99fe823b6ed505d4ea344852aefa1c205fb", - "sha256:5a7f224cdb7233182cec2a45d4c633951268d6a9bcedac37abbf79dd07012aea", - "sha256:61628715931f4962e0cdb2a7c87ff39eea320d2aa96bd471a3c293d146f90394", - "sha256:62285607a5264d1f91590abd874d6a498e229d5840669bd7d9f654cfaa599bd0", - "sha256:62fb881ba51dbacba9af9b779211cf9acff3442d4f2993142015b22b3cd1f92a", - "sha256:68428818cf80c60dc04aa0f38da20ad39b28aba4d4d199f949e7d6e04444ea86", - "sha256:6aaa13ee40c4552d5f3a59f543f0db6e31712cc4009ec7385407be4627259d41", - "sha256:70121f0ae48b25ef3e56e477b88cd0b0af0e1f3a53b5554071aa6a93ef378a03", - "sha256:715b34578cc740b743361f7c3e5f584b04b0f1344f45afc4e87fbac4802eb0a0", - "sha256:758fc8c4d6c0336e617f9f6919f9daea3ab6bb9b07005eda9a1a682e24a6cacc", - "sha256:7d4b8de6bb0bc736161cb0bbd95366b11b3eb24dd6b814a143d8375e75af9990", - "sha256:81d8d099a49f83111cce55ec03cc87eef45eec0d90f9842b4fc674f860b857b0", - "sha256:888d5b4b5aeed0d3449de93ea80173653e939e916cc95fe8527079e50235c1d2", - "sha256:95bde07d19c146d608bccb9b16e144ec8f139bcfe7fd72331858698a71c9b4f5", - "sha256:9bf572e4f5aa23f88dd902f10bb103cb5979022a38eec684bfa6d61851173fec", - "sha256:bab5a1e15b9466a25c96cda19139f3beb3e669794373b9ce28c4cf158c6e841d", - "sha256:bd4b1af45fd322dcd1fb2a9195b4f93f570d1a5902a842e3e6051385fac88f9c", - "sha256:bde677047305fe76c7ee3e4492b545e0018918e44141cc154fe39e124e433991", - "sha256:c389d7cc2b821853fb018c85457da3e7941db64f4387720a329bc7ff06a27963", - "sha256:d055ff750fcab69ca4e57b656d9c6ad33682e9b8d564f2fbe667ab95c63591b0", - "sha256:d53f59744b01f1440a1b0973ed2c3a7de204135c593299ee997828aad5191693", - "sha256:f115150cc4361dd46153302a640c7fa1804ac207f9cc356228248e351a8b4676", - "sha256:f1e88b30da8163215eab643962ae9d9252e47b4ea53404f2c4f10f24e70ddc62", - "sha256:f8191fef303025879e6c3548ecd8a95aafc0728c764ab72ec51a0bdf0c91a341" + "sha256:014ea143572fee1c18322b7908140ad23b3994036ef4c0d630110faf942652f8", + "sha256:0172423a27fbcae3751ef016663b72e1a516777de324a76e30efa170dbd3dd2d", + "sha256:01aa5f803db724447c1d423ed583e42bf5264c597fd55e4add4301f163b0be48", + "sha256:0352db1befcbed2f9282e72843f1963860bf0e0472a4fa5cf8ee084318e0e6ab", + "sha256:09083c2487ca3c0865dc588e07aeaa25416da3d95f7482c07e92f47e080aa17b", + "sha256:0d5d862b1cfbec5028ce1ecac06a3b42bc7703eb80e4b53fceb2738724311443", + "sha256:14f0eb5db872c231b20c18b1e5806352723a3a89fb4254af3b3e14f22eaaec75", + "sha256:1e2f89d2e5e3c7a88e25a3b0e43626dba8db2aa700253023b82e630d12b37109", + "sha256:26155ea7a243cbf23287f390dba13d7927ffa1586d3208e0e8d615d0c506f996", + "sha256:2ed6343b625b16bcb63c5b10523fd15ed8934e1ed0f772c534985e9f5e73d894", + "sha256:34fcec18f6e4b24b4a5f6185205a04f1eab1e56f8f1d028a2a03694ebcc2ddd4", + "sha256:4d0e3515ef98aa4f0dc289ff2eebb0ece6260bbf37c2ea2022aad63797eacf60", + "sha256:5de2464c254380d8a6c20a2746614d5a436260be1507491442cf1088e59430d2", + "sha256:6607ae6cd3a07f8a4c3198ffbf256c261661965742e2b5265a77cd5c679c9bba", + "sha256:8110e6c414d3efc574543109ee618fe2c1f96fa31833a1ff36cc34e968c4f233", + "sha256:816de75418ea0953b5eb7b8a74933ee5a46719491cd2b16f718afc4b291a9658", + "sha256:861e459b0e97673af6cc5e7f597035c2e3acdfb2608132665406cded25ba64c7", + "sha256:87a2725ad7d41cd7376373c15fd8bf674e9c33ca56d0b8036add2d634dba372e", + "sha256:a006d05d9aa052657ee3e4dc92544faae5fcbaafc6128217310945610d862d39", + "sha256:bce28277f308db43a6b4965734366f533b3ff009571ec7ffa583cb77539b84d6", + "sha256:c10ff6112d119f82b1618b6dc28126798481b9355d8748b64b9b55051eb4f01b", + "sha256:d375d8ccd3cebae8d90270f7aa8532fe05908f79e78ae489068f3b4eee5994e8", + "sha256:d37843fb8df90376e9e91336724d78a32b988d3d20ab6656da4eb8ee3a45b63c", + "sha256:e47e257ba5934550d7235665eee6c911dc7178419b614ba9e1fbb1ce6325b14f", + "sha256:e98d09f487267f1e8d1179bf3b9d7709b30a916491997137dd24d6ae44d18d79", + "sha256:ebbb777cbf9312359b897bf81ba00dae0f5cb69fba2a18265dcc18a6f5ef7519", + "sha256:ee5f5188edb20a29c1cc4a039b074fdc5575337c9a68f3063449ab47757bb064", + "sha256:f03bd97650d2e42710fbe4cf8a59fae657f191df851fc9fc683ecef10746a375", + "sha256:f1149d6e5c49d069163e58a3196865e4321bad1803d7886e07d8710de392c548", + "sha256:f3c5c52f7cb8b84bfaaf22d82cb9e6e9a8297f7c2ed14d806a0f5e4d22e83fb7", + "sha256:f597a243b8550a3a0b15122b14e49d8a7e622ba1c9d29776af741f1845478d79", + "sha256:fc1f2a5a5963e2e73bac4926bdaf7790c4d7d77e8fc0590817880e22dd9d0b8b", + "sha256:fc4cddb0b474b12ed7bdce6be1b9edc65352e8ce66bc10ff8cbbfb3d4047dbf4", + "sha256:fcb251305fa24a490b6a9ee2180e5f8252915fb778d3dafc70f9cc3f863827b9" ], "index": "pypi", - "version": "==1.3.22" + "version": "==1.3.24" + }, + "stringcase": { + "hashes": [ + "sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008" + ], + "version": "==1.2.0" + }, + "typing-extensions": { + "hashes": [ + "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", + "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", + "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" + ], + "version": "==3.7.4.3" + }, + "typing-inspect": { + "hashes": [ + "sha256:3b98390df4d999a28cf5b35d8b333425af5da2ece8a4ea9e98f71e7591347b4f", + "sha256:8f1b1dd25908dbfd81d3bebc218011531e7ab614ba6e5bf7826d887c834afab7", + "sha256:de08f50a22955ddec353876df7b2545994d6df08a2f45d54ac8c05e530372ca0" + ], + "version": "==0.6.0" } }, "develop": { @@ -116,6 +164,7 @@ "sha256:71ea07f44df9568a75d0f354c49143a4575d90645e9fead6dfb52c26a85ed13a", "sha256:840947ebfa8b58f318d42301cf8c0a20fd794a33b61cc4638e28e9e61ba32f42" ], + "markers": "python_version >= '3.5'", "version": "==2.3.3" }, "attrs": { @@ -123,6 +172,7 @@ "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==20.3.0" }, "babel": { @@ -130,6 +180,7 @@ "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5", "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.9.0" }, "bandit": { @@ -159,6 +210,7 @@ "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==4.0.0" }, "click": { @@ -166,12 +218,14 @@ "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==7.1.2" }, "codecov": { "hashes": [ "sha256:6cde272454009d27355f9434f4e49f238c0273b216beda8472a65dc4957f473b", - "sha256:ba8553a82942ce37d4da92b70ffd6d54cf635fc1793ab0a7dc3fecd6ebfb3df8" + "sha256:ba8553a82942ce37d4da92b70ffd6d54cf635fc1793ab0a7dc3fecd6ebfb3df8", + "sha256:e95901d4350e99fc39c8353efa450050d2446c55bac91d90fcfd2354e19a6aef" ], "index": "pypi", "version": "==2.1.11" @@ -231,14 +285,16 @@ "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d", "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", "version": "==5.5" }, "docutils": { "hashes": [ - "sha256:a71042bb7207c03d5647f280427f14bfbd1a65c9eb84f4b341d85fafb6bb4bdf", - "sha256:e2ffeea817964356ba4470efba7c2f42b6b0de0b04e66378507e3e2504bbff4c" + "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af", + "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc" ], - "version": "==0.17" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==0.16" }, "fastdiff": { "hashes": [ @@ -248,19 +304,19 @@ }, "flake8": { "hashes": [ - "sha256:749dbbd6bfd0cf1318af27bf97a14e28e5ff548ef8e5b1566ccfb25a11e7c839", - "sha256:aadae8761ec651813c24be05c6f7b4680857ef6afaae4651a4eccaef97ce6c3b" + "sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378", + "sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a" ], "index": "pypi", - "version": "==3.8.4" + "version": "==3.9.1" }, "flake8-bugbear": { "hashes": [ - "sha256:528020129fea2dea33a466b9d64ab650aa3e5f9ffc788b70ea4bc6cf18283538", - "sha256:f35b8135ece7a014bc0aee5b5d485334ac30a6da48494998cc1fabf7ec70d703" + "sha256:2346c81f889955b39e4a368eb7d508de723d9de05716c287dc860a4073dc57e7", + "sha256:4f305dca96be62bf732a218fe6f1825472a621d3452c5b994d8f89dae21dbafa" ], "index": "pypi", - "version": "==20.11.1" + "version": "==21.4.3" }, "flake8-print": { "hashes": [ @@ -274,6 +330,7 @@ "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0", "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005" ], + "markers": "python_version >= '3.4'", "version": "==4.0.7" }, "gitpython": { @@ -281,6 +338,7 @@ "sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b", "sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61" ], + "markers": "python_version >= '3.4'", "version": "==3.1.14" }, "idna": { @@ -288,6 +346,7 @@ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.10" }, "imagesize": { @@ -295,6 +354,7 @@ "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1", "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.2.0" }, "isort": { @@ -310,6 +370,7 @@ "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==2.11.3" }, "lazy-object-proxy": { @@ -336,6 +397,7 @@ "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.4.3" }, "markupsafe": { @@ -393,6 +455,7 @@ "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.1.1" }, "mccabe": { @@ -407,27 +470,36 @@ "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced", "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713" ], + "markers": "python_version >= '3.5'", "version": "==8.7.0" }, "mypy": { "hashes": [ - "sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324", - "sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc", - "sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802", - "sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122", - "sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975", - "sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7", - "sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666", - "sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669", - "sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178", - "sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01", - "sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea", - "sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de", - "sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1", - "sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c" + "sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e", + "sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064", + "sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c", + "sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4", + "sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97", + "sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df", + "sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8", + "sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a", + "sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56", + "sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7", + "sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6", + "sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5", + "sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a", + "sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521", + "sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564", + "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49", + "sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66", + "sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a", + "sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119", + "sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506", + "sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c", + "sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb" ], "index": "pypi", - "version": "==0.790" + "version": "==0.812" }, "mypy-extensions": { "hashes": [ @@ -470,6 +542,7 @@ "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5", "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==20.9" }, "parameterized": { @@ -492,6 +565,7 @@ "sha256:5fad80b613c402d5b7df7bd84812548b2a61e9977387a80a5fc5c396492b13c9", "sha256:b236cde0ac9a6aedd5e3c34517b423cd4fd97ef723849da6b0d2231142d89c00" ], + "markers": "python_version >= '2.6'", "version": "==5.5.1" }, "pluggy": { @@ -499,6 +573,7 @@ "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.13.1" }, "psycopg2-binary": { @@ -550,14 +625,16 @@ "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3", "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.10.0" }, "pycodestyle": { "hashes": [ - "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367", - "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e" + "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068", + "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef" ], - "version": "==2.6.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.7.0" }, "pydocstyle": { "hashes": [ @@ -569,16 +646,18 @@ }, "pyflakes": { "hashes": [ - "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", - "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" + "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3", + "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db" ], - "version": "==2.2.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.3.1" }, "pygments": { "hashes": [ "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94", "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8" ], + "markers": "python_version >= '3.5'", "version": "==2.8.1" }, "pylint": { @@ -621,6 +700,7 @@ "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==2.4.7" }, "pytest": { @@ -633,11 +713,11 @@ }, "pytest-cov": { "hashes": [ - "sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191", - "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e" + "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7", + "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da" ], "index": "pypi", - "version": "==2.10.1" + "version": "==2.11.1" }, "pytz": { "hashes": [ @@ -678,6 +758,7 @@ "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6", "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", "version": "==5.4.1" }, "redis": { @@ -746,6 +827,7 @@ "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==2.25.1" }, "six": { @@ -761,6 +843,7 @@ "sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182", "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2" ], + "markers": "python_version >= '3.5'", "version": "==4.0.0" }, "snapshottest": { @@ -788,17 +871,18 @@ }, "sphinx-rtd-theme": { "hashes": [ - "sha256:eda689eda0c7301a80cf122dad28b1861e5605cbf455558f3775e1e8200e83a5", - "sha256:fa6bebd5ab9a73da8e102509a86f3fcc36dec04a0b52ea80e5a033b2aba00113" + "sha256:32bd3b5d13dc8186d7a42fc816a23d32e83a4827d7d9882948e7b837c232da5a", + "sha256:4a05bdbe8b1446d77a01e20a23ebc6777c74f43237035e76be89699308987d6f" ], "index": "pypi", - "version": "==0.5.1" + "version": "==0.5.2" }, "sphinxcontrib-serializinghtml": { "hashes": [ "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc", "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a" ], + "markers": "python_version >= '3.5'", "version": "==1.1.4" }, "sphinxcontrib-websupport": { @@ -806,6 +890,7 @@ "sha256:4edf0223a0685a7c485ae5a156b6f529ba1ee481a1417817935b20bde1956232", "sha256:6fc9287dfc823fe9aa432463edd6cea47fa9ebbf488d7f289b322ffcfca075c7" ], + "markers": "python_version >= '3.5'", "version": "==1.2.4" }, "stevedore": { @@ -813,6 +898,7 @@ "sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee", "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a" ], + "markers": "python_version >= '3.6'", "version": "==3.3.0" }, "termcolor": { @@ -826,42 +912,43 @@ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.10.2" }, "typed-ast": { "hashes": [ - "sha256:07d49388d5bf7e863f7fa2f124b1b1d89d8aa0e2f7812faff0a5658c01c59aa1", - "sha256:14bf1522cdee369e8f5581238edac09150c765ec1cb33615855889cf33dcb92d", - "sha256:240296b27397e4e37874abb1df2a608a92df85cf3e2a04d0d4d61055c8305ba6", - "sha256:36d829b31ab67d6fcb30e185ec996e1f72b892255a745d3a82138c97d21ed1cd", - "sha256:37f48d46d733d57cc70fd5f30572d11ab8ed92da6e6b28e024e4a3edfb456e37", - "sha256:4c790331247081ea7c632a76d5b2a265e6d325ecd3179d06e9cf8d46d90dd151", - "sha256:5dcfc2e264bd8a1db8b11a892bd1647154ce03eeba94b461effe68790d8b8e07", - "sha256:7147e2a76c75f0f64c4319886e7639e490fee87c9d25cb1d4faef1d8cf83a440", - "sha256:7703620125e4fb79b64aa52427ec192822e9f45d37d4b6625ab37ef403e1df70", - "sha256:8368f83e93c7156ccd40e49a783a6a6850ca25b556c0fa0240ed0f659d2fe496", - "sha256:84aa6223d71012c68d577c83f4e7db50d11d6b1399a9c779046d75e24bed74ea", - "sha256:85f95aa97a35bdb2f2f7d10ec5bbdac0aeb9dafdaf88e17492da0504de2e6400", - "sha256:8db0e856712f79c45956da0c9a40ca4246abc3485ae0d7ecc86a20f5e4c09abc", - "sha256:9044ef2df88d7f33692ae3f18d3be63dec69c4fb1b5a4a9ac950f9b4ba571606", - "sha256:963c80b583b0661918718b095e02303d8078950b26cc00b5e5ea9ababe0de1fc", - "sha256:987f15737aba2ab5f3928c617ccf1ce412e2e321c77ab16ca5a293e7bbffd581", - "sha256:9ec45db0c766f196ae629e509f059ff05fc3148f9ffd28f3cfe75d4afb485412", - "sha256:9fc0b3cb5d1720e7141d103cf4819aea239f7d136acf9ee4a69b047b7986175a", - "sha256:a2c927c49f2029291fbabd673d51a2180038f8cd5a5b2f290f78c4516be48be2", - "sha256:a38878a223bdd37c9709d07cd357bb79f4c760b29210e14ad0fb395294583787", - "sha256:b4fcdcfa302538f70929eb7b392f536a237cbe2ed9cba88e3bf5027b39f5f77f", - "sha256:c0c74e5579af4b977c8b932f40a5464764b2f86681327410aa028a22d2f54937", - "sha256:c1c876fd795b36126f773db9cbb393f19808edd2637e00fd6caba0e25f2c7b64", - "sha256:c9aadc4924d4b5799112837b226160428524a9a45f830e0d0f184b19e4090487", - "sha256:cc7b98bf58167b7f2db91a4327da24fb93368838eb84a44c472283778fc2446b", - "sha256:cf54cfa843f297991b7388c281cb3855d911137223c6b6d2dd82a47ae5125a41", - "sha256:d003156bb6a59cda9050e983441b7fa2487f7800d76bdc065566b7d728b4581a", - "sha256:d175297e9533d8d37437abc14e8a83cbc68af93cc9c1c59c2c292ec59a0697a3", - "sha256:d746a437cdbca200622385305aedd9aef68e8a645e385cc483bdc5e488f07166", - "sha256:e683e409e5c45d5c9082dc1daf13f6374300806240719f95dc783d1fc942af10" - ], - "version": "==1.4.2" + "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace", + "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff", + "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266", + "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528", + "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6", + "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808", + "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4", + "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363", + "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341", + "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04", + "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41", + "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e", + "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3", + "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899", + "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805", + "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c", + "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c", + "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39", + "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a", + "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3", + "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7", + "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f", + "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075", + "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0", + "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40", + "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428", + "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927", + "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3", + "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f", + "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65" + ], + "version": "==1.4.3" }, "typing-copilot": { "hashes": [ @@ -885,7 +972,7 @@ "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df", "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937" ], - "index": "pypi", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", "version": "==1.26.4" }, "wasmer": { diff --git a/graphql_compiler/query_planning/typedefs.py b/graphql_compiler/query_planning/typedefs.py new file mode 100644 index 000000000..30a964308 --- /dev/null +++ b/graphql_compiler/query_planning/typedefs.py @@ -0,0 +1,295 @@ +# Copyright 2021-present Kensho Technologies, LLC. +from abc import ABCMeta +import copy +from dataclasses import dataclass, field +from typing import Any, Callable, Dict, Iterable, Mapping, Optional + +from dataclasses_json import DataClassJsonMixin, config +from graphql import ( + GraphQLList, + GraphQLNonNull, + GraphQLScalarType, + GraphQLType, + ListTypeNode, + NamedTypeNode, + NonNullTypeNode, + TypeNode, + parse_type, + specified_scalar_types, +) + +from .. import GraphQLDate, GraphQLDateTime, GraphQLDecimal +from ..compiler.compiler_frontend import OutputMetadata +from ..global_utils import is_same_type +from ..typedefs import QueryArgumentGraphQLType + + +QueryExecutionFunc = Callable[ + [str, Dict[str, QueryArgumentGraphQLType], Dict[str, Any]], Iterable[Mapping[str, Any]] +] + +# Custom scalar types. +CUSTOM_SCALAR_TYPES = { + GraphQLDate.name: GraphQLDate, + GraphQLDateTime.name: GraphQLDateTime, + GraphQLDecimal.name: GraphQLDecimal, +} + +# Custom scalar types must not have name conflicts with builtin scalar types. +if set(CUSTOM_SCALAR_TYPES).intersection(specified_scalar_types): + raise AssertionError( + f"Custom scalar types must have different names than builtin scalar types. Received " + f"overlapping type(s) {set(CUSTOM_SCALAR_TYPES).intersection(specified_scalar_types)}. " + f"Custom scalar types: {set(CUSTOM_SCALAR_TYPES)}. Builtin scalar types: " + f"{set(specified_scalar_types)}." + ) + +# Custom scalar types combined with builtin scalar types represent all allowable scalar types. +ALL_SCALAR_TYPES = copy.copy(CUSTOM_SCALAR_TYPES) +ALL_SCALAR_TYPES.update(specified_scalar_types) + + +def _type_from_scalar_type_dictionary( + scalar_types: Dict[str, GraphQLScalarType], type_node: TypeNode +) -> GraphQLType: + """Get the GraphQL type definition from an AST node. + + Given a scalar type dictionary and an AST node describing a type, return a GraphQLType + definition, which applies to that type. For example, if provided the parsed AST node for + `[Date]`, a GraphQLList instance will be returned, containing the type called + "Date" found in the scalar type dictionary. If a type called "Date" is not found in the scalar + type dictionary, then None will be returned. + + Note: this is very similar to GraphQL's type_from_ast. However, instead of requiring a GraphQL + schema this function requires a dictionary of the scalar types. This simplifies deserialization + and allows for custom scalar types without constructing an entire schema. + + Args: + scalar_types: dictionary mapping type name to GraphQLScalarType + type_node: AST node describing a type + + Returns: + GraphQLType that applies to the type specified in type_node. + + Raises: + AssertionError: if an invalid type node is given. + """ + if isinstance(type_node, ListTypeNode): + inner_type = _type_from_scalar_type_dictionary(scalar_types, type_node.type) + if inner_type: + return GraphQLList(inner_type) + else: + raise AssertionError( + f"Invalid type node. ListTypeNode contained inner type {inner_type}." + ) + elif isinstance(type_node, NonNullTypeNode): + inner_type = _type_from_scalar_type_dictionary(scalar_types, type_node.type) + if inner_type: + return GraphQLNonNull(inner_type) + else: + raise AssertionError( + f"Invalid type node. NonNullTypeNone contained inner type {inner_type}." + ) + elif isinstance(type_node, NamedTypeNode): + return scalar_types[type_node.name.value] + + # Not reachable. All possible type nodes have been considered. + raise AssertionError(f"Unexpected type node: {type_node}.") + + +def _serialize_output_metadata_field( + output_metadata_dictionary: Optional[Dict[str, OutputMetadata]] +) -> Optional[Dict[str, Dict[str, Any]]]: + """Serialize OutputMetadata into a dictionary.""" + if not output_metadata_dictionary: + return None + dictionary_value = {} + for output_name, output_metadata in output_metadata_dictionary.items(): + dictionary_value[output_name] = { + "type": str(output_metadata.type), + "optional": output_metadata.optional, + "folded": output_metadata.folded, + } + return dictionary_value + + +def _deserialize_output_metadata_field( + dict_value: Optional[Dict[str, Dict[str, Any]]] +) -> Optional[Dict[str, OutputMetadata]]: + """Deserialize the dictionary representation of OutputMetadata.""" + if not dict_value: + return None + output_metadata_dictionary = {} + for output_name, output_metadata in dict_value.items(): + output_metadata_dictionary[output_name] = OutputMetadata( + type=_type_from_scalar_type_dictionary( + ALL_SCALAR_TYPES, parse_type(output_metadata["type"]) + ), + optional=output_metadata["optional"], + folded=output_metadata["folded"], + ) + return output_metadata_dictionary + + +def _serialize_input_metadata_field( + input_metadata_dictionary: Optional[Dict[str, Any]] +) -> Optional[Dict[str, str]]: + """Serialize input metadata, converting GraphQLTypes to strings.""" + # It is possible to have an empty input metadata dictionary (i.e. no inputs for the query). + # Note that this is different than "None", which means no metadata was provided. + if input_metadata_dictionary == {}: + return {} + if not input_metadata_dictionary: + return None + dictionary_value = {} + for input_name, input_type in input_metadata_dictionary.items(): + dictionary_value[input_name] = str(input_type) + return dictionary_value + + +def _deserialize_input_metadata_field( + dict_value: Optional[Dict[str, str]] +) -> Optional[Dict[str, GraphQLType]]: + """Deserialize input metadata, converting strings to GraphQLTypes.""" + # It is possible to have an empty input metadata dictionary (i.e. no inputs for the query). + # Note that this is different than "None", which means no metadata was provided. + if dict_value == {}: + return {} + if not dict_value: + return None + input_metadata_dictionary = {} + for input_name, input_type in dict_value.items(): + input_metadata_dictionary[input_name] = _type_from_scalar_type_dictionary( + ALL_SCALAR_TYPES, parse_type(input_type) + ) + return input_metadata_dictionary + + +def _compare_input_metadata_field( + left_input_metadata: Optional[Dict[str, QueryArgumentGraphQLType]], + right_input_metadata: Optional[Dict[str, QueryArgumentGraphQLType]], +) -> bool: + """Check input_metadata SimpleExecute field equality, comparing GraphQLTypes appropriately.""" + if left_input_metadata is None: + # Since left_input_metadata is None, checking for equality requires determining whether or + # not right_metadata is also None. If right_metadata is None, left and right metadata + # are equal and True is returned. + return right_input_metadata is None + + # right_input_metadata is None, but left_input_metadata is not. + if right_input_metadata is None: + return False + + # Neither left_input_metadata nor right_input_metadata is None. + input_metadata_keys = left_input_metadata.keys() + + # Check if input_metadata keys match. + if input_metadata_keys != right_input_metadata.keys(): + return False + # Check if input_metadata values match for all keys. + for key in input_metadata_keys: + if not is_same_type(left_input_metadata[key], right_input_metadata[key]): + return False + + # All keys and values match so return True. + return True + + +def _deserialize_independent_query_plan_field(dict_value: Dict[str, Any]) -> "IndependentQueryPlan": + """Deserialize the dict representation of IndependentQueryPlan.""" + # Note: there will be more types of IndependentQueryPlans that will require different + # deserialization shortly. + return SimpleExecute.from_dict(dict_value) + + +# ############ +# Public API # +# ############ + + +@dataclass(init=True, repr=True, eq=True, frozen=True) +class ProviderMetadata(DataClassJsonMixin): + """Metadata about the provider.""" + + # Name of the type of provider (ex. PostgreSQL, Cypher, etc). + backend_type: str + + # Whether this backend requires MSSQL fold postprocessing for folded outputs. + requires_fold_postprocessing: bool + + +@dataclass(init=True, repr=True, eq=True, frozen=True) +class QueryPlanNode(DataClassJsonMixin, metaclass=ABCMeta): + """Abstract query plan node. May or may not contain other nodes, depending on its type.""" + + # Unique ID of the plan node. + # Note: do not compare "uuid" values to determine whether query plan nodes are equal, since two + # plans with different identifiers might still be semantically equivalent and therefore equal. + uuid: str = field(compare=False) + + +@dataclass(init=True, repr=True, eq=False, frozen=True) +class SimpleExecute(QueryPlanNode): + """Just give the specified query and args to the provider, it'll execute it for you as-is.""" + + provider_id: str + provider_metadata: ProviderMetadata + query: str # in whatever query language the provider will accept (not necessarily GraphQL) + arguments: Dict[str, Any] + + # Input and output metadata of the query. + output_metadata: Dict[str, OutputMetadata] = field( + metadata=config( + encoder=_serialize_output_metadata_field, decoder=_deserialize_output_metadata_field + ) + ) + input_metadata: Dict[str, QueryArgumentGraphQLType] = field( + metadata=config( + encoder=_serialize_input_metadata_field, decoder=_deserialize_input_metadata_field + ) + ) + + def __eq__(self, other: Any) -> bool: + """Check equality between an object and this SimpleExecute.""" + if not isinstance(other, SimpleExecute): + return False + + # Perform special check for input_metadata since GraphQLTypes don't have equality, and + # check all other fields in a straight forward manner. + return ( + self.provider_id == other.provider_id + and self.query == other.query + and self.arguments == other.arguments + and self.output_metadata == other.output_metadata + and _compare_input_metadata_field(self.input_metadata, other.input_metadata) + ) + + +# More types of IndependentQueryPlans will be added in the future. +IndependentQueryPlan = SimpleExecute + + +@dataclass(init=True, repr=True, eq=True, frozen=True) +class QueryPlan(DataClassJsonMixin): + """A description of the execution of a GraphQL query, including pagination and joins.""" + + # Version number, so we can make breaking changes without requiring lock-step upgrades. + # Clients should report supported version ranges when requesting a plan, and the server + # should pick the highest version that is supported by both client and server. + version: int + + # Metadata on which provider produced the plan, and for what inputs. + provider_id: str + input_graphql_query: str + input_parameters: Dict[str, Any] + desired_page_size: Optional[int] + output_metadata: Dict[str, OutputMetadata] = field( + metadata=config( + encoder=_serialize_output_metadata_field, decoder=_deserialize_output_metadata_field + ) + ) + + # The actual query plan. + plan_root_node: IndependentQueryPlan = field( + metadata=config(decoder=_deserialize_independent_query_plan_field) + ) diff --git a/setup.py b/setup.py index 26e84f88a..8b2e35700 100755 --- a/setup.py +++ b/setup.py @@ -56,10 +56,11 @@ def find_long_description() -> str: packages=find_packages(exclude=["tests*"]), install_requires=[ # Make sure to keep in sync with Pipfile requirements. "ciso8601>=2.1.3,<3", + "dataclasses-json>=0.5.2,<0.6", "funcy>=1.7.3,<2", "graphql-core>=3.1.2,<3.2", "six>=1.10.0", - "sqlalchemy>=1.3.0,<2", + "sqlalchemy>=1.3.0,<1.4", ], extras_require={ ':python_version<"3.7"': ["dataclasses>=0.7,<1"], From b4b7fb28c6b2ee17add91edc5ef6d0d82bab4a5b Mon Sep 17 00:00:00 2001 From: Selene Chew Date: Thu, 22 Apr 2021 13:58:34 -0400 Subject: [PATCH 2/8] update pipfile lock --- Pipfile.lock | 84 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index d8f8bab8f..5f4c91e6e 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "bc634468beba3b9a3df60a32beadbcb92d0dc46476868a86cc304d1d5093f28a" + "sha256": "91599e4b711c9bcae2f412debca81714239825a5bdf9fab2dee3ce5f92c661b1" }, "pipfile-spec": 6, "requires": { @@ -51,6 +51,28 @@ "index": "pypi", "version": "==3.1.4" }, + "marshmallow": { + "hashes": [ + "sha256:0dd42891a5ef288217ed6410917f3c6048f585f8692075a0052c24f9bfff9dfd", + "sha256:16e99cb7f630c0ef4d7d364ed0109ac194268dde123966076ab3dafb9ae3906b" + ], + "markers": "python_version >= '3.5'", + "version": "==3.11.1" + }, + "marshmallow-enum": { + "hashes": [ + "sha256:38e697e11f45a8e64b4a1e664000897c659b60aa57bfa18d44e226a9920b6e58", + "sha256:57161ab3dbfde4f57adeb12090f39592e992b9c86d206d02f6bd03ebec60f072" + ], + "version": "==1.5.1" + }, + "mypy-extensions": { + "hashes": [ + "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", + "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8" + ], + "version": "==0.4.3" + }, "six": { "hashes": [ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", @@ -98,6 +120,28 @@ ], "index": "pypi", "version": "==1.3.24" + }, + "stringcase": { + "hashes": [ + "sha256:48a06980661908efe8d9d34eab2b6c13aefa2163b3ced26972902e3bdfd87008" + ], + "version": "==1.2.0" + }, + "typing-extensions": { + "hashes": [ + "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918", + "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c", + "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f" + ], + "version": "==3.7.4.3" + }, + "typing-inspect": { + "hashes": [ + "sha256:3b98390df4d999a28cf5b35d8b333425af5da2ece8a4ea9e98f71e7591347b4f", + "sha256:8f1b1dd25908dbfd81d3bebc218011531e7ab614ba6e5bf7826d887c834afab7", + "sha256:de08f50a22955ddec353876df7b2545994d6df08a2f45d54ac8c05e530372ca0" + ], + "version": "==0.6.0" } }, "develop": { @@ -431,31 +475,23 @@ }, "mypy": { "hashes": [ - "sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e", - "sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064", - "sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c", - "sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4", - "sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97", - "sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df", - "sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8", - "sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a", - "sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56", - "sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7", - "sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6", - "sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5", - "sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a", - "sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521", - "sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564", - "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49", - "sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66", - "sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a", - "sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119", - "sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506", - "sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c", - "sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb" + "sha256:0a0d102247c16ce93c97066443d11e2d36e6cc2a32d8ccc1f705268970479324", + "sha256:0d34d6b122597d48a36d6c59e35341f410d4abfa771d96d04ae2c468dd201abc", + "sha256:2170492030f6faa537647d29945786d297e4862765f0b4ac5930ff62e300d802", + "sha256:2842d4fbd1b12ab422346376aad03ff5d0805b706102e475e962370f874a5122", + "sha256:2b21ba45ad9ef2e2eb88ce4aeadd0112d0f5026418324176fd494a6824b74975", + "sha256:72060bf64f290fb629bd4a67c707a66fd88ca26e413a91384b18db3876e57ed7", + "sha256:af4e9ff1834e565f1baa74ccf7ae2564ae38c8df2a85b057af1dbbc958eb6666", + "sha256:bd03b3cf666bff8d710d633d1c56ab7facbdc204d567715cb3b9f85c6e94f669", + "sha256:c614194e01c85bb2e551c421397e49afb2872c88b5830e3554f0519f9fb1c178", + "sha256:cf4e7bf7f1214826cf7333627cb2547c0db7e3078723227820d0a2490f117a01", + "sha256:da56dedcd7cd502ccd3c5dddc656cb36113dd793ad466e894574125945653cea", + "sha256:e86bdace26c5fe9cf8cb735e7cedfe7850ad92b327ac5d797c656717d2ca66de", + "sha256:e97e9c13d67fbe524be17e4d8025d51a7dca38f90de2e462243ab8ed8a9178d1", + "sha256:eea260feb1830a627fb526d22fbb426b750d9f5a47b624e8d5e7e004359b219c" ], "index": "pypi", - "version": "==0.812" + "version": "==0.790" }, "mypy-extensions": { "hashes": [ From 4ad499cc408925cc312256649a3f66dbc62a75df Mon Sep 17 00:00:00 2001 From: Selene Chew Date: Thu, 22 Apr 2021 15:39:04 -0400 Subject: [PATCH 3/8] make backend type an enum --- graphql_compiler/query_planning/typedefs.py | 39 ++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/graphql_compiler/query_planning/typedefs.py b/graphql_compiler/query_planning/typedefs.py index 30a964308..004c42ba3 100644 --- a/graphql_compiler/query_planning/typedefs.py +++ b/graphql_compiler/query_planning/typedefs.py @@ -1,13 +1,14 @@ # Copyright 2021-present Kensho Technologies, LLC. from abc import ABCMeta -import copy from dataclasses import dataclass, field -from typing import Any, Callable, Dict, Iterable, Mapping, Optional +from enum import Enum, unique +from typing import Any, Callable, Dict, Iterable, Mapping, Optional, cast from dataclasses_json import DataClassJsonMixin, config from graphql import ( GraphQLList, GraphQLNonNull, + GraphQLNullableType, GraphQLScalarType, GraphQLType, ListTypeNode, @@ -45,11 +46,11 @@ ) # Custom scalar types combined with builtin scalar types represent all allowable scalar types. -ALL_SCALAR_TYPES = copy.copy(CUSTOM_SCALAR_TYPES) +ALL_SCALAR_TYPES = CUSTOM_SCALAR_TYPES.copy() ALL_SCALAR_TYPES.update(specified_scalar_types) -def _type_from_scalar_type_dictionary( +def _get_type_from_scalar_type_dictionary( scalar_types: Dict[str, GraphQLScalarType], type_node: TypeNode ) -> GraphQLType: """Get the GraphQL type definition from an AST node. @@ -75,7 +76,7 @@ def _type_from_scalar_type_dictionary( AssertionError: if an invalid type node is given. """ if isinstance(type_node, ListTypeNode): - inner_type = _type_from_scalar_type_dictionary(scalar_types, type_node.type) + inner_type = _get_type_from_scalar_type_dictionary(scalar_types, type_node.type) if inner_type: return GraphQLList(inner_type) else: @@ -83,8 +84,9 @@ def _type_from_scalar_type_dictionary( f"Invalid type node. ListTypeNode contained inner type {inner_type}." ) elif isinstance(type_node, NonNullTypeNode): - inner_type = _type_from_scalar_type_dictionary(scalar_types, type_node.type) + inner_type = _get_type_from_scalar_type_dictionary(scalar_types, type_node.type) if inner_type: + inner_type = cast(GraphQLNullableType, inner_type) return GraphQLNonNull(inner_type) else: raise AssertionError( @@ -122,7 +124,7 @@ def _deserialize_output_metadata_field( output_metadata_dictionary = {} for output_name, output_metadata in dict_value.items(): output_metadata_dictionary[output_name] = OutputMetadata( - type=_type_from_scalar_type_dictionary( + type=_get_type_from_scalar_type_dictionary( ALL_SCALAR_TYPES, parse_type(output_metadata["type"]) ), optional=output_metadata["optional"], @@ -139,7 +141,7 @@ def _serialize_input_metadata_field( # Note that this is different than "None", which means no metadata was provided. if input_metadata_dictionary == {}: return {} - if not input_metadata_dictionary: + if input_metadata_dictionary is None: return None dictionary_value = {} for input_name, input_type in input_metadata_dictionary.items(): @@ -155,11 +157,11 @@ def _deserialize_input_metadata_field( # Note that this is different than "None", which means no metadata was provided. if dict_value == {}: return {} - if not dict_value: + if dict_value is None: return None input_metadata_dictionary = {} for input_name, input_type in dict_value.items(): - input_metadata_dictionary[input_name] = _type_from_scalar_type_dictionary( + input_metadata_dictionary[input_name] = _get_type_from_scalar_type_dictionary( ALL_SCALAR_TYPES, parse_type(input_type) ) return input_metadata_dictionary @@ -207,12 +209,25 @@ def _deserialize_independent_query_plan_field(dict_value: Dict[str, Any]) -> "In # ############ +@unique +class BackendType(Enum): + # N.B.: The values of the enums are the "human-friendly" display names. Since they are shown + # to humans, they are subject to change if said humans find a friendlier name. + # Don't assume they are immutable! + cypher = "Cypher" + gremlin = "Gremlin" + interpreter = "InterpreterAdapter" + match = "Match" + mssql = "MSSQL" + postgresql = "PostgreSQL" + + @dataclass(init=True, repr=True, eq=True, frozen=True) class ProviderMetadata(DataClassJsonMixin): """Metadata about the provider.""" - # Name of the type of provider (ex. PostgreSQL, Cypher, etc). - backend_type: str + # Name of the type of provider. + backend_type: BackendType # Whether this backend requires MSSQL fold postprocessing for folded outputs. requires_fold_postprocessing: bool From 0b9be1da5814d4a6d06ab6e9c60064b77ad4e270 Mon Sep 17 00:00:00 2001 From: Selene Chew Date: Thu, 22 Apr 2021 16:18:20 -0400 Subject: [PATCH 4/8] typing copilot tighten --- mypy.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy.ini b/mypy.ini index 6d34e87db..21ab52826 100644 --- a/mypy.ini +++ b/mypy.ini @@ -160,7 +160,7 @@ disallow_untyped_calls = False [mypy-graphql_compiler.query_pagination.query_parameterizer.*] disallow_untyped_calls = False -[mypy-graphql_compiler.query_planning.*] +[mypy-graphql_compiler.query_planning.make_query_plan.*] disallow_untyped_calls = False disallow_untyped_defs = False From f564021c0ef6e95f2b5ceb5343d2a3d77b7b9320 Mon Sep 17 00:00:00 2001 From: chewselene <52711428+chewselene@users.noreply.github.com> Date: Mon, 26 Apr 2021 13:04:14 -0400 Subject: [PATCH 5/8] update match typedef name Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> --- graphql_compiler/query_planning/typedefs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphql_compiler/query_planning/typedefs.py b/graphql_compiler/query_planning/typedefs.py index 004c42ba3..9da980b77 100644 --- a/graphql_compiler/query_planning/typedefs.py +++ b/graphql_compiler/query_planning/typedefs.py @@ -217,7 +217,7 @@ class BackendType(Enum): cypher = "Cypher" gremlin = "Gremlin" interpreter = "InterpreterAdapter" - match = "Match" + match = "OrientDB MATCH" mssql = "MSSQL" postgresql = "PostgreSQL" From 442c8f42e484875d5db7504cfee4bf2af802a89b Mon Sep 17 00:00:00 2001 From: chewselene <52711428+chewselene@users.noreply.github.com> Date: Mon, 26 Apr 2021 13:04:29 -0400 Subject: [PATCH 6/8] typo Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> --- graphql_compiler/query_planning/typedefs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphql_compiler/query_planning/typedefs.py b/graphql_compiler/query_planning/typedefs.py index 9da980b77..b03667e3f 100644 --- a/graphql_compiler/query_planning/typedefs.py +++ b/graphql_compiler/query_planning/typedefs.py @@ -270,7 +270,7 @@ def __eq__(self, other: Any) -> bool: return False # Perform special check for input_metadata since GraphQLTypes don't have equality, and - # check all other fields in a straight forward manner. + # check all other fields in a straightforward manner. return ( self.provider_id == other.provider_id and self.query == other.query From 69b83d3feb756b04476185a1bd5b3252305717b0 Mon Sep 17 00:00:00 2001 From: chewselene <52711428+chewselene@users.noreply.github.com> Date: Mon, 26 Apr 2021 13:04:52 -0400 Subject: [PATCH 7/8] update interpreter typedef name Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com> --- graphql_compiler/query_planning/typedefs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphql_compiler/query_planning/typedefs.py b/graphql_compiler/query_planning/typedefs.py index b03667e3f..7e576a148 100644 --- a/graphql_compiler/query_planning/typedefs.py +++ b/graphql_compiler/query_planning/typedefs.py @@ -216,7 +216,7 @@ class BackendType(Enum): # Don't assume they are immutable! cypher = "Cypher" gremlin = "Gremlin" - interpreter = "InterpreterAdapter" + interpreter = "interpreter" match = "OrientDB MATCH" mssql = "MSSQL" postgresql = "PostgreSQL" From c0b0ca79c81e197b4f04f80421c46bc76803350d Mon Sep 17 00:00:00 2001 From: Selene Chew Date: Mon, 26 Apr 2021 13:10:49 -0400 Subject: [PATCH 8/8] document deserialization limitation --- graphql_compiler/query_planning/typedefs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/graphql_compiler/query_planning/typedefs.py b/graphql_compiler/query_planning/typedefs.py index 7e576a148..ce10bb6cb 100644 --- a/graphql_compiler/query_planning/typedefs.py +++ b/graphql_compiler/query_planning/typedefs.py @@ -63,7 +63,8 @@ def _get_type_from_scalar_type_dictionary( Note: this is very similar to GraphQL's type_from_ast. However, instead of requiring a GraphQL schema this function requires a dictionary of the scalar types. This simplifies deserialization - and allows for custom scalar types without constructing an entire schema. + and allows for custom scalar types without constructing an entire schema. Unfortunately, this + means that user-defined custom scalars that are not known to the compiler cannot be used. Args: scalar_types: dictionary mapping type name to GraphQLScalarType