diff --git a/Pipfile b/Pipfile index 5420701f..aabe3351 100644 --- a/Pipfile +++ b/Pipfile @@ -5,10 +5,9 @@ name = "pypi" [packages] python-dateutil = "==2.6.0" -pygeodiff = "==0.8.6" +pygeodiff = "==1.0.0" pytz = "==2019.3" - [dev-packages] pytest = "==3.10.1" pytest-cov = "==2.6.1" diff --git a/Pipfile.lock b/Pipfile.lock index fc2bcd4e..3c5bdbfd 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ef6e760041680b6221ed53cb42f4c4b9d64e079510a9eb349810e4dc86f938b2" + "sha256": "8c84f91fc345e65a58856963069400907395e5342b9c4bb7808758b9d15e8a2a" }, "pipfile-spec": 6, "requires": { @@ -18,20 +18,40 @@ "default": { "pygeodiff": { "hashes": [ - "sha256:015554b1849f98233e4edf4bb2fafa003f6f5443d9a2447fb894f943ca7e2137", - "sha256:025ac9d8db9d144ffbf20dadc15e429e8cb526fc14277c7072cc9802fdf878a5", - "sha256:0df51544a5133e608a5541ac8b904058f1c511828f08441291ffccab96e9f132", - "sha256:4da89ba9d9836e708819343cefe383b77a9d61808fac58b4cb0b3a157c03671a", - "sha256:87614130d5762ec90cf2d55b9634d15a85d67febd352066cf4fac91f33aad19d", - "sha256:950f0eef75869dbe6dc987b6a858cdc54dcd9fab3cc300528ad529c41abcdbfc", - "sha256:9c6b10c6fc0d490ce7ecd65ff1828d775065ac67382faa739dbb0f14dec0bea6", - "sha256:b0d05552df55da1a619bf694a99ad7602b83195a0a2157434604fe8c1eced632", - "sha256:b60f3ed72779600d3a93e2709211dedc2ab76f79f200091b827cf7d785871300", - "sha256:d69085feddac198206eaa877755e601b67935a1eff2dde7f3701084c665d04bb", - "sha256:dcf550e3492a3de4652737ecf4f43b6ed418b1f7f2f4f607fbc0a2a62177777c" + "sha256:03ea2c6e3d46f865a8751ce12fc3c009d39f94ee8a7ddf255409a7f69ecbe54e", + "sha256:211543eda7ff5faf6a373fd0e38db6c00c17370e9bbcaab1cdd390cf860e9379", + "sha256:24257ad5e408a85ca27020b53b082ebd219901549c9bdf76f812b0b5275f9546", + "sha256:2fc97adf58bfae278c6c0084e4dabc6ccf1c5845d37ddbb4a76ba08f2f7881c5", + "sha256:4df23f4a835d8122529edbf501299fec0bc68307669d9f57d91ab47024c0f35b", + "sha256:54c23d03b30f4d62203f055d92be05560b5756d3ca96815b7a3f36074f4f6412", + "sha256:56a69ee4fa433dcc9bf454d87779e926e536d00f3e22663957165f77ef8443f8", + "sha256:63e87ee4c02709902b17ae133fdc54e663a5a190fdf8d2f6ce3e56f058320fbd", + "sha256:688cda686a478c40b8131859be1aea86675792ac19e520d769edf99c05511979", + "sha256:6de9a49e028eb581e84d1da1ba6abbd55a566bc16815ea355c3f0f80ee237e33", + "sha256:77e4e01fd907a5aa00a5cac992be82aeb93615455cec52d6cd28f00fc425ba1a", + "sha256:77f97f2f7d9e2de1c152fa3958822ef072b47aebbeaca6333da5558ad8d4af56", + "sha256:7a291bed50a117020f357d0eb9b148544042b2f45de2afd8da6030929967e296", + "sha256:7ce565fe3994396010e149ddb7b9d8ed4490930b1133d2df068dccfcbe2ae014", + "sha256:8360e2801f85453006abfc4f68ec9d6216308f75bc975a4b93f2870b4a0c2483", + "sha256:8c1a212543a814e39961e2d85502198fb4bf1ab7233c937b8d5c765cf9035176", + "sha256:ab5e35c34299aa8892f612347755bb4103b704f7e127ded0b862bc15c454d552", + "sha256:b8ac352589f525166d0d407e65ed732d33505add586de452385bec30ab78d2a2", + "sha256:beef3d020c7e6c4c5ff74dd3ae91d436ca006a28e5dbbde2edd8a10413b28946", + "sha256:c96330a10eed427a7c18420cdc4a7c80424b87d2f040dc1fb53c57d93a92e3eb", + "sha256:ddf756c02e04ddd99507a8401a444aedf57f425b0bdd882f5d5592353f073d42", + "sha256:deb550753b4f9d0775a27defeb06938689a456df26c0cdaa24a0c0782e4206c0", + "sha256:df13c37055076325bf0c3e34f1796ad8fc7f95abba70ce5ddc3d97c10a0f193a", + "sha256:e908dae8962c4f829bbf487fd01be0de8ab1b4eb47c079df95be5e2cef8da60a", + "sha256:e9adb6676b5a2f47080bf7b31d890eada8da5f02d53ec425cdb994dbd608eb24", + "sha256:eabc20dc0505f086ce5c782d631c58299f1c8f56c3bf52b6105d8faed30d9783", + "sha256:ef6edd73559b4b81de05a54ef009efdf8e7942f66ce86056aac187abe9e1306f", + "sha256:f1223a672f2cd91dbd605051d676bbb36bed08b3d67c1af75b60546d3daf88d6", + "sha256:f3e99f0af7ab26fbcc06f1944daebe970aec15de4600f9abbf859fb64b40c989", + "sha256:f53fac110172f26ba7ba147985a1718da0e153f922674ff4fa2005621839a735", + "sha256:f5f3747554c849b35626a4c60cc5b10ee5133b942c92d42347d0bdad5165fb98" ], "index": "pypi", - "version": "==0.8.6" + "version": "==1.0.0" }, "python-dateutil": { "hashes": [ @@ -52,19 +72,19 @@ }, "six": { "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], - "version": "==1.15.0" + "version": "==1.16.0" } }, "develop": { "astroid": { "hashes": [ - "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703", - "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386" + "sha256:2476b7f0d6cec13f4c1f53b54bea2ce072310ac9fc7acb669d5270190c748042", + "sha256:f0c8bfebc3da61bde8e3e3df1e879dc16e6d26d6078ddc34813fcb07045782d3" ], - "version": "==2.4.2" + "version": "==2.5.8" }, "atomicwrites": { "hashes": [ @@ -75,64 +95,67 @@ }, "attrs": { "hashes": [ - "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", - "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" + "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", + "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" ], - "version": "==20.3.0" + "version": "==21.2.0" }, "coverage": { "hashes": [ - "sha256:03ed2a641e412e42cc35c244508cf186015c217f0e4d496bf6d7078ebe837ae7", - "sha256:04b14e45d6a8e159c9767ae57ecb34563ad93440fc1b26516a89ceb5b33c1ad5", - "sha256:0cdde51bfcf6b6bd862ee9be324521ec619b20590787d1655d005c3fb175005f", - "sha256:0f48fc7dc82ee14aeaedb986e175a429d24129b7eada1b7e94a864e4f0644dde", - "sha256:107d327071061fd4f4a2587d14c389a27e4e5c93c7cba5f1f59987181903902f", - "sha256:1375bb8b88cb050a2d4e0da901001347a44302aeadb8ceb4b6e5aa373b8ea68f", - "sha256:14a9f1887591684fb59fdba8feef7123a0da2424b0652e1b58dd5b9a7bb1188c", - "sha256:16baa799ec09cc0dcb43a10680573269d407c159325972dd7114ee7649e56c66", - "sha256:1b811662ecf72eb2d08872731636aee6559cae21862c36f74703be727b45df90", - "sha256:1ccae21a076d3d5f471700f6d30eb486da1626c380b23c70ae32ab823e453337", - "sha256:2f2cf7a42d4b7654c9a67b9d091ec24374f7c58794858bff632a2039cb15984d", - "sha256:322549b880b2d746a7672bf6ff9ed3f895e9c9f108b714e7360292aa5c5d7cf4", - "sha256:32ab83016c24c5cf3db2943286b85b0a172dae08c58d0f53875235219b676409", - "sha256:3fe50f1cac369b02d34ad904dfe0771acc483f82a1b54c5e93632916ba847b37", - "sha256:4a780807e80479f281d47ee4af2eb2df3e4ccf4723484f77da0bb49d027e40a1", - "sha256:4a8eb7785bd23565b542b01fb39115a975fefb4a82f23d407503eee2c0106247", - "sha256:5bee3970617b3d74759b2d2df2f6a327d372f9732f9ccbf03fa591b5f7581e39", - "sha256:60a3307a84ec60578accd35d7f0c71a3a971430ed7eca6567399d2b50ef37b8c", - "sha256:6625e52b6f346a283c3d563d1fd8bae8956daafc64bb5bbd2b8f8a07608e3994", - "sha256:66a5aae8233d766a877c5ef293ec5ab9520929c2578fd2069308a98b7374ea8c", - "sha256:68fb816a5dd901c6aff352ce49e2a0ffadacdf9b6fae282a69e7a16a02dad5fb", - "sha256:6b588b5cf51dc0fd1c9e19f622457cc74b7d26fe295432e434525f1c0fae02bc", - "sha256:6c4d7165a4e8f41eca6b990c12ee7f44fef3932fac48ca32cecb3a1b2223c21f", - "sha256:6d2e262e5e8da6fa56e774fb8e2643417351427604c2b177f8e8c5f75fc928ca", - "sha256:6d9c88b787638a451f41f97446a1c9fd416e669b4d9717ae4615bd29de1ac135", - "sha256:755c56beeacac6a24c8e1074f89f34f4373abce8b662470d3aa719ae304931f3", - "sha256:7e40d3f8eb472c1509b12ac2a7e24158ec352fc8567b77ab02c0db053927e339", - "sha256:812eaf4939ef2284d29653bcfee9665f11f013724f07258928f849a2306ea9f9", - "sha256:84df004223fd0550d0ea7a37882e5c889f3c6d45535c639ce9802293b39cd5c9", - "sha256:859f0add98707b182b4867359e12bde806b82483fb12a9ae868a77880fc3b7af", - "sha256:87c4b38288f71acd2106f5d94f575bc2136ea2887fdb5dfe18003c881fa6b370", - "sha256:89fc12c6371bf963809abc46cced4a01ca4f99cba17be5e7d416ed7ef1245d19", - "sha256:9564ac7eb1652c3701ac691ca72934dd3009997c81266807aef924012df2f4b3", - "sha256:9754a5c265f991317de2bac0c70a746efc2b695cf4d49f5d2cddeac36544fb44", - "sha256:a565f48c4aae72d1d3d3f8e8fb7218f5609c964e9c6f68604608e5958b9c60c3", - "sha256:a636160680c6e526b84f85d304e2f0bb4e94f8284dd765a1911de9a40450b10a", - "sha256:a839e25f07e428a87d17d857d9935dd743130e77ff46524abb992b962eb2076c", - "sha256:b62046592b44263fa7570f1117d372ae3f310222af1fc1407416f037fb3af21b", - "sha256:b7f7421841f8db443855d2854e25914a79a1ff48ae92f70d0a5c2f8907ab98c9", - "sha256:ba7ca81b6d60a9f7a0b4b4e175dcc38e8fef4992673d9d6e6879fd6de00dd9b8", - "sha256:bb32ca14b4d04e172c541c69eec5f385f9a075b38fb22d765d8b0ce3af3a0c22", - "sha256:c0ff1c1b4d13e2240821ef23c1efb1f009207cb3f56e16986f713c2b0e7cd37f", - "sha256:c669b440ce46ae3abe9b2d44a913b5fd86bb19eb14a8701e88e3918902ecd345", - "sha256:c67734cff78383a1f23ceba3b3239c7deefc62ac2b05fa6a47bcd565771e5880", - "sha256:c6809ebcbf6c1049002b9ac09c127ae43929042ec1f1dbd8bb1615f7cd9f70a0", - "sha256:cd601187476c6bed26a0398353212684c427e10a903aeafa6da40c63309d438b", - "sha256:ebfa374067af240d079ef97b8064478f3bf71038b78b017eb6ec93ede1b6bcec", - "sha256:fbb17c0d0822684b7d6c09915677a32319f16ff1115df5ec05bdcaaee40b35f3", - "sha256:fff1f3a586246110f34dc762098b5afd2de88de507559e63553d7da643053786" - ], - "version": "==5.4" + "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c", + "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6", + "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45", + "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a", + "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03", + "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529", + "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a", + "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a", + "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2", + "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6", + "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759", + "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53", + "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a", + "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4", + "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff", + "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502", + "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793", + "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb", + "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905", + "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821", + "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b", + "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81", + "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0", + "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b", + "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3", + "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184", + "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701", + "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a", + "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82", + "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638", + "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5", + "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083", + "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6", + "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90", + "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465", + "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a", + "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3", + "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e", + "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066", + "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf", + "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b", + "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae", + "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669", + "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873", + "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b", + "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6", + "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb", + "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160", + "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c", + "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079", + "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d", + "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6" + ], + "version": "==5.5" }, "isort": { "hashes": [ @@ -143,29 +166,30 @@ }, "lazy-object-proxy": { "hashes": [ - "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d", - "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449", - "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08", - "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a", - "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50", - "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd", - "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239", - "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb", - "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea", - "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e", - "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156", - "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142", - "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442", - "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62", - "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db", - "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531", - "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383", - "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a", - "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357", - "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4", - "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0" - ], - "version": "==1.4.3" + "sha256:17e0967ba374fc24141738c69736da90e94419338fd4c7c7bef01ee26b339653", + "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61", + "sha256:22ddd618cefe54305df49e4c069fa65715be4ad0e78e8d252a33debf00f6ede2", + "sha256:24a5045889cc2729033b3e604d496c2b6f588c754f7a62027ad4437a7ecc4837", + "sha256:410283732af311b51b837894fa2f24f2c0039aa7f220135192b38fcc42bd43d3", + "sha256:4732c765372bd78a2d6b2150a6e99d00a78ec963375f236979c0626b97ed8e43", + "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726", + "sha256:4f60460e9f1eb632584c9685bccea152f4ac2130e299784dbaf9fae9f49891b3", + "sha256:5743a5ab42ae40caa8421b320ebf3a998f89c85cdc8376d6b2e00bd12bd1b587", + "sha256:85fb7608121fd5621cc4377a8961d0b32ccf84a7285b4f1d21988b2eae2868e8", + "sha256:9698110e36e2df951c7c36b6729e96429c9c32b3331989ef19976592c5f3c77a", + "sha256:9d397bf41caad3f489e10774667310d73cb9c4258e9aed94b9ec734b34b495fd", + "sha256:b579f8acbf2bdd9ea200b1d5dea36abd93cabf56cf626ab9c744a432e15c815f", + "sha256:b865b01a2e7f96db0c5d12cfea590f98d8c5ba64ad222300d93ce6ff9138bcad", + "sha256:bf34e368e8dd976423396555078def5cfc3039ebc6fc06d1ae2c5a65eebbcde4", + "sha256:c6938967f8528b3668622a9ed3b31d145fab161a32f5891ea7b84f6b790be05b", + "sha256:d1c2676e3d840852a2de7c7d5d76407c772927addff8d742b9808fe0afccebdf", + "sha256:d7124f52f3bd259f510651450e18e0fd081ed82f3c08541dffc7b94b883aa981", + "sha256:d900d949b707778696fdf01036f58c9876a0d8bfe116e8d220cfd4b15f14e741", + "sha256:ebfd274dcd5133e0afae738e6d9da4323c3eb021b3e13052d8cbd0e457b1256e", + "sha256:ed361bb83436f117f9917d282a456f9e5009ea12fd6de8742d1a4752c3017e93", + "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b" + ], + "version": "==1.6.0" }, "mccabe": { "hashes": [ @@ -176,10 +200,10 @@ }, "more-itertools": { "hashes": [ - "sha256:8e1a2a43b2f2727425f2b5839587ae37093f19153dc26c0927d1048ff6557330", - "sha256:b3a9005928e5bed54076e6e549c792b306fddfe72b2d1d22dd63d42d5d3899cf" + "sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d", + "sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a" ], - "version": "==8.6.0" + "version": "==8.8.0" }, "pluggy": { "hashes": [ @@ -221,10 +245,10 @@ }, "six": { "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], - "version": "==1.15.0" + "version": "==1.16.0" }, "wrapt": { "hashes": [ diff --git a/mergin/client_pull.py b/mergin/client_pull.py index 8ecc27e0..c1e94b40 100644 --- a/mergin/client_pull.py +++ b/mergin/client_pull.py @@ -247,8 +247,7 @@ def apply(self, directory, mp): file_to_merge.merge() if mp.is_versioned_file(self.file_path): - shutil.copy(mp.fpath(self.file_path), mp.fpath_meta(self.file_path)) - + mp.geodiff.make_copy_sqlite(mp.fpath(self.file_path), mp.fpath_meta(self.file_path)) class DownloadQueueItem: diff --git a/mergin/merginproject.py b/mergin/merginproject.py index ad404a6b..372747c0 100644 --- a/mergin/merginproject.py +++ b/mergin/merginproject.py @@ -9,7 +9,7 @@ from datetime import datetime from dateutil.tz import tzlocal -from .common import UPLOAD_CHUNK_SIZE, InvalidProject +from .common import UPLOAD_CHUNK_SIZE, InvalidProject, ClientError from .utils import generate_checksum, move_file, int_version, find, do_sqlite_checkpoint @@ -22,10 +22,7 @@ try: from .deps import pygeodiff except ImportError: - try: - import pygeodiff - except ImportError: - os.environ['GEODIFF_ENABLED'] = 'False' + import pygeodiff class MerginProject: @@ -38,33 +35,31 @@ def __init__(self, directory): if not os.path.exists(self.dir): raise InvalidProject('Project directory does not exist') - # make sure we can load correct pygeodiff - if os.environ.get('GEODIFF_ENABLED', 'True').lower() == 'true': - try: - self.geodiff = pygeodiff.GeoDiff() - except pygeodiff.geodifflib.GeoDiffLibVersionError: - self.geodiff = None - else: - self.geodiff = None - self.meta_dir = os.path.join(self.dir, '.mergin') if not os.path.exists(self.meta_dir): os.mkdir(self.meta_dir) self.setup_logging(directory) + # make sure we can load correct pygeodiff + try: + self.geodiff = pygeodiff.GeoDiff() + except pygeodiff.geodifflib.GeoDiffLibVersionError: + # this is a fatal error, we can't live without geodiff + self.log.error("Unable to load geodiff! (lib version error)") + raise ClientError("Unable to load geodiff library!") + # redirect any geodiff output to our log file - if self.geodiff: - def _logger_callback(level, text_bytes): - text = text_bytes.decode() # convert bytes to str - if level == pygeodiff.GeoDiff.LevelError: - self.log.error("GEODIFF: " + text) - elif level == pygeodiff.GeoDiff.LevelWarning: - self.log.warning("GEODIFF: " + text) - else: - self.log.info("GEODIFF: " + text) - self.geodiff.set_logger_callback(_logger_callback) - self.geodiff.set_maximum_logger_level(pygeodiff.GeoDiff.LevelDebug) + def _logger_callback(level, text_bytes): + text = text_bytes.decode() # convert bytes to str + if level == pygeodiff.GeoDiff.LevelError: + self.log.error("GEODIFF: " + text) + elif level == pygeodiff.GeoDiff.LevelWarning: + self.log.warning("GEODIFF: " + text) + else: + self.log.info("GEODIFF: " + text) + self.geodiff.set_logger_callback(_logger_callback) + self.geodiff.set_maximum_logger_level(pygeodiff.GeoDiff.LevelDebug) def setup_logging(self, logger_name): """Setup logging into project directory's .mergin/client-log.txt file.""" @@ -127,8 +122,6 @@ def is_versioned_file(self, file): :returns: if file is compatible with geodiff lib :rtype: bool """ - if not self.geodiff: - return False diff_extensions = ['.gpkg', '.sqlite'] f_extension = os.path.splitext(file)[1] return f_extension in diff_extensions @@ -247,14 +240,13 @@ def get_pull_changes(self, server_files): :returns: changes metadata for files to be pulled from server :rtype: dict """ + + # first let's have a look at the added/updated/removed files changes = self.compare_file_sets(self.metadata['files'], server_files) - if not self.geodiff: - self.log.warning("geodiff is not available!") - return changes + # then let's inspect our versioned files (geopackages) if there are any relevant changes not_updated = [] for file in changes['updated']: - # for small geodiff files it does not make sense to download diff and then apply it (slow) if not self.is_versioned_file(file["path"]): continue @@ -310,10 +302,6 @@ def get_push_changes(self): file["checksum"] = checksum file['chunks'] = [str(uuid.uuid4()) for i in range(math.ceil(file["size"] / UPLOAD_CHUNK_SIZE))] - if not self.geodiff: - self.log.warning("geodiff is not available!") - return changes - # need to check for for real changes in geodiff files using geodiff tool (comparing checksum is not enough) not_updated = [] for file in changes['updated']: @@ -374,7 +362,7 @@ def apply_pull_changes(self, changes, temp_dir): Update project files according to file changes. Apply changes to geodiff basefiles as well so they are up to date with server. In case of conflicts create backups from locally modified versions. - .. seealso:: self.pull_changes + .. seealso:: self.get_pull_changes :param changes: metadata for pulled files :type changes: dict[str, list[dict]] @@ -410,18 +398,17 @@ def apply_pull_changes(self, changes, temp_dir): # temporary backup of file pulled from server for recovery f_server_backup = self.fpath(f'{path}-server_backup', temp_dir) - shutil.copy(src, f_server_backup) + self.geodiff.make_copy_sqlite(src, f_server_backup) # create temp backup (ideally with geodiff) of locally modified file if needed later f_conflict_file = self.fpath(f'{path}-local_backup', temp_dir) try: self.geodiff.create_changeset(basefile, dest, local_diff) - shutil.copy(basefile, f_conflict_file) + self.geodiff.make_copy_sqlite(basefile, f_conflict_file) self.geodiff.apply_changeset(f_conflict_file, local_diff) except (pygeodiff.GeoDiffLibError, pygeodiff.GeoDiffLibConflictError): - self.log.warning("backup of local file with geodiff failed - need to do hard copy (dangerous!)") - # FIXME hard copy can lead to data loss if changes from -wal file were not flushed !!! - shutil.copy(dest, f_conflict_file) + self.log.info("backup of local file with geodiff failed - need to do hard copy") + self.geodiff.make_copy_sqlite(dest, f_conflict_file) # in case there will be any conflicting operations found during rebase, # they will be stored in a JSON file - if there are no conflicts, the file @@ -438,17 +425,12 @@ def apply_pull_changes(self, changes, temp_dir): except (pygeodiff.GeoDiffLibError, pygeodiff.GeoDiffLibConflictError) as err: self.log.warning("rebase failed! going to create conflict file") # it would not be possible to commit local changes, they need to end up in new conflict file - shutil.copy(f_conflict_file, dest) # revert file + self.geodiff.make_copy_sqlite(f_conflict_file, dest) conflict = self.backup_file(path) conflicts.append(conflict) # original file synced with server - shutil.copy(f_server_backup, basefile) - shutil.copy(f_server_backup, dest) - # changes in -wal have been already applied in conflict file or LOST (see above) - if os.path.exists(f'{dest}-wal'): - os.remove(f'{dest}-wal') - if os.path.exists(f'{dest}-shm'): - os.remove(f'{dest}-shm') + self.geodiff.make_copy_sqlite(f_server_backup, basefile) + self.geodiff.make_copy_sqlite(f_server_backup, dest) else: # The local file is not modified -> no rebase needed. # We just apply the diff between our copy and server to both the local copy and its basefile @@ -464,12 +446,11 @@ def apply_pull_changes(self, changes, temp_dir): self.geodiff.apply_changeset(basefile, server_diff) self.log.info("update successful") except (pygeodiff.GeoDiffLibError, pygeodiff.GeoDiffLibConflictError): - self.log.warning("update failed! going to copy file (dangerous!)") + self.log.warning("update failed! going to copy file") # something bad happened and we have failed to patch our local files - this should not happen if there # wasn't a schema change or something similar that geodiff can't handle. - # FIXME: this is a last resort and may corrupt data! (we should warn user) - shutil.copy(src, dest) - shutil.copy(src, basefile) + self.geodiff.make_copy_sqlite(src, dest) + self.geodiff.make_copy_sqlite(src, basefile) else: # backup if needed if path in modified and item['checksum'] != local_files_map[path]['checksum']: @@ -485,9 +466,11 @@ def apply_pull_changes(self, changes, temp_dir): if self.is_versioned_file(path): os.remove(basefile) else: - shutil.copy(src, dest) if self.is_versioned_file(path): - shutil.copy(src, basefile) + self.geodiff.make_copy_sqlite(src, dest) + self.geodiff.make_copy_sqlite(src, basefile) + else: + shutil.copy(src, dest) return conflicts @@ -498,8 +481,6 @@ def apply_push_changes(self, changes): :param changes: metadata for pulled files :type changes: dict[str, list[dict]] """ - if not self.geodiff: - return for k, v in changes.items(): for item in v: path = item['path'] @@ -510,12 +491,12 @@ def apply_push_changes(self, changes): if k == 'removed': os.remove(basefile) elif k == 'added': - shutil.copy(self.fpath(path), basefile) + self.geodiff.make_copy_sqlite(self.fpath(path), basefile) elif k == 'updated': # in case for geopackage cannot be created diff (e.g. forced update with committed changes from wal file) if "diff" not in item: self.log.info("updating basefile (copy) for: " + path) - shutil.copy(self.fpath(path), basefile) + self.geodiff.make_copy_sqlite(self.fpath(path), basefile) else: self.log.info("updating basefile (diff) for: " + path) # better to apply diff to previous basefile to avoid issues with geodiff tmp files @@ -545,7 +526,10 @@ def backup_file(self, file): while os.path.exists(backup_path): backup_path = self.fpath(f'{file}_conflict_copy{index}') index += 1 - shutil.copy(src, backup_path) + if self.is_versioned_file(file): + self.geodiff.make_copy_sqlite(src, backup_path) + else: + shutil.copy(src, backup_path) return backup_path def apply_diffs(self, basefile, diffs): diff --git a/mergin/test/test_client.py b/mergin/test/test_client.py index 70fac54e..709eb124 100644 --- a/mergin/test/test_client.py +++ b/mergin/test/test_client.py @@ -20,10 +20,6 @@ CHANGED_SCHEMA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'modified_schema') -def toggle_geodiff(enabled): - os.environ['GEODIFF_ENABLED'] = str(enabled) - - @pytest.fixture(scope='function') def mc(): return create_client(API_USER, USER_PWD) @@ -282,19 +278,9 @@ def test_ignore_files(mc): assert not next((f for f in project_info['files'] if f['path'] == '.directory'), None) -# (diffs size limit, push geodiff enabled, pull geodiff enabled) -diff_test_scenarios = [ - (True, True), - (True, False), - (False, True), - (False, False), -] - +def test_sync_diff(mc): -@pytest.mark.parametrize("push_geodiff_enabled, pull_geodiff_enabled", diff_test_scenarios) -def test_sync_diff(mc, push_geodiff_enabled, pull_geodiff_enabled): - - test_project = f'test_sync_diff_push{int(push_geodiff_enabled)}_pull{int(pull_geodiff_enabled)}' + test_project = f'test_sync_diff' project = API_USER + '/' + test_project project_dir = os.path.join(TMP_DIR, test_project) # primary project dir for updates project_dir_2 = os.path.join(TMP_DIR, test_project + '_2') # concurrent project dir with no changes @@ -302,32 +288,24 @@ def test_sync_diff(mc, push_geodiff_enabled, pull_geodiff_enabled): cleanup(mc, project, [project_dir, project_dir_2, project_dir_3]) # create remote project - toggle_geodiff(push_geodiff_enabled) shutil.copytree(TEST_DATA_DIR, project_dir) mc.create_project_and_push(test_project, project_dir) # make sure we have v1 also in concurrent project dirs - toggle_geodiff(pull_geodiff_enabled) mc.download_project(project, project_dir_2) mc.download_project(project, project_dir_3) # test push changes with diffs: - toggle_geodiff(push_geodiff_enabled) mp = MerginProject(project_dir) f_updated = 'base.gpkg' # step 1) base.gpkg updated to inserted_1_A (inserted A feature) - if push_geodiff_enabled: - shutil.move(mp.fpath(f_updated), mp.fpath_meta(f_updated)) # make local copy for changeset calculation + shutil.move(mp.fpath(f_updated), mp.fpath_meta(f_updated)) # make local copy for changeset calculation shutil.copy(mp.fpath('inserted_1_A.gpkg'), mp.fpath(f_updated)) mc.push_project(project_dir) - if push_geodiff_enabled: - mp.geodiff.create_changeset(mp.fpath(f_updated), mp.fpath_meta(f_updated), mp.fpath_meta('push_diff')) - assert not mp.geodiff.has_changes(mp.fpath_meta('push_diff')) - else: - assert not os.path.exists(mp.fpath_meta(f_updated)) + mp.geodiff.create_changeset(mp.fpath(f_updated), mp.fpath_meta(f_updated), mp.fpath_meta('push_diff')) + assert not mp.geodiff.has_changes(mp.fpath_meta('push_diff')) # step 2) base.gpkg updated to inserted_1_A_mod (modified 2 features) - if push_geodiff_enabled: - shutil.move(mp.fpath(f_updated), mp.fpath_meta(f_updated)) + shutil.move(mp.fpath(f_updated), mp.fpath_meta(f_updated)) shutil.copy(mp.fpath('inserted_1_A_mod.gpkg'), mp.fpath(f_updated)) # introduce some other changes f_removed = 'inserted_1_B.gpkg' @@ -343,48 +321,27 @@ def test_sync_diff(mc, push_geodiff_enabled, pull_geodiff_enabled): assert next((f for f in project_info['files'] if f['path'] == 'renamed.gpkg'), None) assert not next((f for f in project_info['files'] if f['path'] == f_removed), None) assert not os.path.exists(mp.fpath_meta(f_removed)) - if push_geodiff_enabled: - assert 'diff' in f_remote - assert os.path.exists(mp.fpath_meta('renamed.gpkg')) - else: - assert 'diff' not in f_remote - assert not os.path.exists(mp.fpath_meta('renamed.gpkg')) + assert 'diff' in f_remote + assert os.path.exists(mp.fpath_meta('renamed.gpkg')) # pull project in different directory - toggle_geodiff(pull_geodiff_enabled) mp2 = MerginProject(project_dir_2) mc.pull_project(project_dir_2) - if pull_geodiff_enabled: - mp2.geodiff.create_changeset(mp.fpath(f_updated), mp2.fpath(f_updated), mp2.fpath_meta('diff')) - assert not mp2.geodiff.has_changes(mp2.fpath_meta('diff')) - else: - server_file_checksum = next((f['checksum'] for f in project_info['files'] if f['path'] == f_updated), '') - assert server_file_checksum == generate_checksum(mp2.fpath(f_updated)) + mp2.geodiff.create_changeset(mp.fpath(f_updated), mp2.fpath(f_updated), mp2.fpath_meta('diff')) + assert not mp2.geodiff.has_changes(mp2.fpath_meta('diff')) # introduce conflict local change (inserted B feature to base) mp3 = MerginProject(project_dir_3) shutil.copy(mp3.fpath('inserted_1_B.gpkg'), mp3.fpath(f_updated)) checksum = generate_checksum(mp3.fpath('inserted_1_B.gpkg')) mc.pull_project(project_dir_3) - if pull_geodiff_enabled: - assert not os.path.exists(mp3.fpath('base.gpkg_conflict_copy')) - else: - assert os.path.exists(mp3.fpath('base.gpkg_conflict_copy')) # - assert generate_checksum(mp3.fpath('base.gpkg_conflict_copy')) == checksum + assert not os.path.exists(mp3.fpath('base.gpkg_conflict_copy')) # push new changes from project_3 and pull in original project - toggle_geodiff(push_geodiff_enabled) mc.push_project(project_dir_3) - toggle_geodiff(pull_geodiff_enabled) mc.pull_project(project_dir) - if pull_geodiff_enabled: - mp3.geodiff.create_changeset(mp.fpath(f_updated), mp3.fpath(f_updated), mp.fpath_meta('diff')) - assert not mp3.geodiff.has_changes(mp.fpath_meta('diff')) - else: - assert os.path.exists(mp.fpath('base.gpkg_conflict_copy')) - - # make sure that we leave geodiff enabled for further tests - toggle_geodiff(True) + mp3.geodiff.create_changeset(mp.fpath(f_updated), mp3.fpath(f_updated), mp.fpath_meta('diff')) + assert not mp3.geodiff.has_changes(mp.fpath_meta('diff')) def test_list_of_push_changes(mc): @@ -396,7 +353,6 @@ def test_list_of_push_changes(mc): cleanup(mc, project, [project_dir]) shutil.copytree(TEST_DATA_DIR, project_dir) - toggle_geodiff(True) mc.create_project_and_push(test_project, project_dir) f_updated = 'base.gpkg' diff --git a/setup.py b/setup.py index 6c9b4a67..8066f54a 100644 --- a/setup.py +++ b/setup.py @@ -27,8 +27,6 @@ 'console_scripts': ['mergin=mergin.cli:cli'], }, - test_suite='nose.collector', - tests_require=['nose'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers',