diff --git a/Pipfile b/Pipfile index bca3689ed93c7f4ca004987a2136e5bf4770616f..7b54691ee6afbbc405df827687f88edc9cb2b19f 100644 --- a/Pipfile +++ b/Pipfile @@ -3,7 +3,6 @@ url = "https://pypi.python.org/simple" verify_ssl = true [packages] -steem = "0.18.2" bottle = "*" bottle-sqlalchemy = "*" bottle_errorsrest = "*" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000000000000000000000000000000000000..1689c41f2ced347831b904228830cdfffbedf646 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,621 @@ +{ + "_meta": { + "hash": { + "sha256": "53295d238744428cb8d18d5fa55051b786a833a79910960b68a8d7321a561cc5" + }, + "host-environment-markers": { + "implementation_name": "cpython", + "implementation_version": "3.6.1", + "os_name": "posix", + "platform_machine": "x86_64", + "platform_python_implementation": "CPython", + "platform_release": "16.7.0", + "platform_system": "Darwin", + "platform_version": "Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64", + "python_full_version": "3.6.1", + "python_version": "3.6", + "sys_platform": "darwin" + }, + "pipfile-spec": 3, + "requires": { + "python_version": "3.5" + }, + "sources": [ + { + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "bottle": { + "hashes": [ + "sha256:39b751aee0b167be8dffb63ca81b735bbf1dd0905b3bc42761efedee8f123355" + ], + "version": "==0.12.13" + }, + "bottle-errorsrest": { + "hashes": [ + "sha256:98a06ac885bda0e8bfcc6e9260c8fbd5a25e25a4805148c5880fa17e715386f5" + ], + "version": "==0.0.2" + }, + "bottle-sqlalchemy": { + "hashes": [ + "sha256:ba6127f3aff2b78649781adbbee65518233dc481e9f9e32e3b050d1ad9551c17" + ], + "version": "==0.4.3" + }, + "click": { + "hashes": [ + "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", + "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" + ], + "version": "==6.7" + }, + "click-spinner": { + "hashes": [ + "sha256:6e7af5fc2c7fe4a0434f4330c0075f31ea688e680760dcd14594250e8911230f", + "sha256:a0703b4a21a1ba377716c5c768928b6c7575c087af926d3d586818426982ea30" + ], + "version": "==0.1.7" + }, + "dateparser": { + "hashes": [ + "sha256:e2629d2f8361722c6047138ca085256c9f2cf5cc657fd66122aa0564afa4dc33", + "sha256:f8c24317120b06f71691d28076764ec084a132be2a250a78fdf54f6b427cac95" + ], + "version": "==0.6.0" + }, + "funcy": { + "hashes": [ + "sha256:30c0753228b4f1fceb653cccca995166da336680a556d70784dca91a388bd40f" + ], + "version": "==1.9" + }, + "humanize": { + "hashes": [ + "sha256:a43f57115831ac7c70de098e6ac46ac13be00d69abbf60bdcac251344785bb19" + ], + "version": "==0.5.1" + }, + "maya": { + "hashes": [ + "sha256:04c5d5380313d9b928746e0f8c1da914d093cd22e0324e48297347b90eb4043f", + "sha256:c1eea3fd6a63d17af5c7381c14440768f9b4ef21b3acbfd9e123c7f8ace6d174" + ], + "version": "==0.3.2" + }, + "mysqlclient": { + "hashes": [ + "sha256:b3b1a7e4468180afb79289b54069d9499242946a4cedf3928cbf6b2a13800016", + "sha256:d56e379c03efad746e84705cbb97401f60d1f98b05e11a27f2d9c2d043936974", + "sha256:371df79d000af56b4e540b7ce2120d1c9afb04b751bfce25a1eb609c50fd10ff", + "sha256:1e85e48b167e2af3bb08f273fdbd1ad6401cbe75057fa6513f97387dc7b282dc", + "sha256:2d9ec33de39f4d9c64ad7322ede0521d85829ce36a76f9dd3d6ab76a9c8648e5" + ], + "version": "==1.3.12" + }, + "pendulum": { + "hashes": [ + "sha256:8e24b9b6651402c9d5241cd09b2df16e8b8c2ee3e61ab0d243f7db81a7f4cc2b", + "sha256:dfabd6ebf87ec050214bfa41debf0985a91f98a9f808787272c5ebb56bdf743a", + "sha256:3415d9fb4d83e0a4466de24e2edcf385ce0378a5d9c06a4291d1d3411f5c9b4a", + "sha256:f47531a76ff67d21c2c0e1503c1bd8e5dbfd0ffb469c6ca8f68b6ef04ed7e8c4", + "sha256:8b52d8fc1e7a8b3025e434da351c98c34b478e8818d9fedbffed11ffb7a156cf", + "sha256:878072ac65a2d2c9abc5954bb2eda305c7ec3b5430f39cb8819cc4b676dd79a3", + "sha256:f00d45f5d4cc57829f778b024af35ce08ca0459993aff92d7606a14d7a872633", + "sha256:1f9f4cba0b21beead709a38ea9847e4324f037052bbcfd93147634e9966aaba6" + ], + "version": "==1.2.5" + }, + "prettytable": { + "hashes": [ + "sha256:853c116513625c738dc3ce1aee148b5b5757a86727e67eff6502c7ca59d43c36", + "sha256:2d5460dc9db74a32bcc8f9f67de68b2c4f4d2f01fa3bd518764c69156d9cacd9", + "sha256:a53da3b43d7a5c229b5e3ca2892ef982c46b7923b51e98f0db49956531211c4f" + ], + "version": "==0.7.2" + }, + "python-dateutil": { + "hashes": [ + "sha256:95511bae634d69bc7329ba55e646499a842bc4ec342ad54a8cdb65645a0aad3c", + "sha256:891c38b2a02f5bb1be3e4793866c8df49c7d19baabf9c1bad62547e0b4866aca" + ], + "version": "==2.6.1" + }, + "pytz": { + "hashes": [ + "sha256:c883c2d6670042c7bc1688645cac73dd2b03193d1f7a6847b6154e96890be06d", + "sha256:03c9962afe00e503e2d96abab4e8998a0f84d4230fa57afe1e0528473698cdd9", + "sha256:487e7d50710661116325747a9cd1744d3323f8e49748e287bc9e659060ec6bf9", + "sha256:43f52d4c6a0be301d53ebd867de05e2926c35728b3260157d274635a0a947f1c", + "sha256:d1d6729c85acea5423671382868627129432fba9a89ecbb248d8d1c7a9f01c67", + "sha256:54a935085f7bf101f86b2aff75bd9672b435f51c3339db2ff616e66845f2b8f9", + "sha256:39504670abb5dae77f56f8eb63823937ce727d7cdd0088e6909e6dcac0f89043", + "sha256:ddc93b6d41cfb81266a27d23a79e13805d4a5521032b512643af8729041a81b4", + "sha256:f5c056e8f62d45ba8215e5cb8f50dfccb198b4b9fbea8500674f3443e4689589" + ], + "version": "==2017.2" + }, + "pytzdata": { + "hashes": [ + "sha256:bd19fd653f89e498f1d4f9390d96456ce26ecd293a5e7405120a5de875d3314c", + "sha256:141c539a6b3f6040ca4728f8a85df598f60adccdfbab3ac1efe110934ce4a331" + ], + "version": "==2017.2.2" + }, + "regex": { + "hashes": [ + "sha256:1dad6cc413f96eddea48a50c7c039596d6a58af2110a6f19182357833ef83787", + "sha256:3f0bbff5e0bb4f9dec8dd57d85c5a4f0ead44cc31f81b104e79d42248a170343", + "sha256:c7c7cdae70faf377cc308e6389064379daf53e79c538cd9f085d7fdd754486d8", + "sha256:427908c9721581b3548dbee80dea45023db9261ea77c0bf353474d686760113d", + "sha256:159d6acb1a7bb05b128f9ca721864f5c394291c032cff6c42cbf6ecbc2d291ef", + "sha256:b6591a73146e4207e24c01edab551eb980f81cec1075bab9909cd473a3d6da5d", + "sha256:ddcf580827c8f4eb831f9bd547eb2e6b7da43823ceab0744dab3f4a5e131a014", + "sha256:f2d6b002a0d4fd4141b92d06899293b7ccab73b8043742e40d11b20c051e209a", + "sha256:0905c080f57d35fe34cd38e81f57adc4b2495be794cfa5ac8c74eb0261d84734", + "sha256:2012fbcaedf1fc496be147e3f72ca05593f3723a9e156bf170496fe15010ac1a", + "sha256:c214a7a3d742dd9e930acb5ff933264de24b2d9adcd7be46bd5baae90cdb9925", + "sha256:feca025094e4bc1b0583290b143a7b253b5f0ee21dc3f8e39a8b6c2f317a6833", + "sha256:0e529e0fef1456ed0e847b6e9d23a92f1a6c45dd7e22380cc6fa108f3474e2a7", + "sha256:f5739e8a1d83be1197363866e2a0b76ef5d472391487d53eb4cef0bcaabb41d2", + "sha256:2e6b3740fefe21becc00f3629d11fb8b239c8440c5d8d1d0fff7688a2f03fb96", + "sha256:ebc8774912557fd9ba1a2aae0c305a410ec40ae84ca08e6d2549c72ebf517ae0", + "sha256:c61a721f083824902d78d10ec109ab74ba978e3b8010a0c5fbd44dab308b9867", + "sha256:df270fcadf0872f4e599fce1d355460da0f79097c214a3a4fc09598b41f176fb", + "sha256:27ab18243b1a0aa1467027be93b118c9fcd60dd2e4020da579fad3008bc4638f" + ], + "version": "==2017.7.28" + }, + "ruamel.yaml": { + "hashes": [ + "sha256:4bdec0b1a4a12e4a35b788ef53e0b2efd1e9f815ca48615b5244ccdbc8f0b56b", + "sha256:6432088ef1c3cdc4b304dcec6044e560160c1f51dd0360ae3f0887057d310056", + "sha256:64868964b64cad9103f37d0b4fc0ec950e5b683bbc47bf4a1ea893fd70aaabf0", + "sha256:c8da0ea47df5f2dee58f6f904335ac55098b7e72303b01414ce80f37dd3091df", + "sha256:87bdfb04981f1040ec60b1d1bfa8e7e4d89f7d06c86d4b2fa935002c62e43380", + "sha256:c20e853cb585167fd5e4ce6e1b57fefe7bd4a61659f8fec5f77e12efa59a7a77", + "sha256:e884979944642b4ad3222cb102a0d4c17fed2d37abfffdf0f319fae6e41d436a", + "sha256:10493c92c0e5bd57d576d73b708ed900532846ead1b381c5236c41d0c39f5f71", + "sha256:3127a87b274e516b3bf2058f64d784089322fddd7322f8e78e9f5380bdfee064", + "sha256:530bec874292bacbbb80a9143e5182ce436c2a2434a2ea83dda24d30b8e572df", + "sha256:d92d90c9bc0945223e47223a67808dd97ac9390ed914cc6871479b7ba489e607", + "sha256:3e87114aac9553c39eea4b88e258eb7fdce39b81a2c399a775efe1f64e4f3d7b", + "sha256:dfd14829070728159d0dac55a19e4c77514cb8ad3ae3fc2ed065d7f24038b60f", + "sha256:2f28a3b6665697c20d841a4aee21cfb932bb0db91f293ff97daf845b914ddddb", + "sha256:2de7bd2d5713c46be9d1d489b028178c9497813f78bd0486a31bbe1c268d7f21", + "sha256:ddf0e1277664fafff0ae692e8ea2fca56f1ee4daf686d9be785ccdf3a9542744", + "sha256:f1e29054c6e477963e302b007b6cd1d6c7a58c38d78fabe64fde9ce170d2d1fd" + ], + "version": "==0.15.34" + }, + "six": { + "hashes": [ + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb", + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9" + ], + "version": "==1.11.0" + }, + "sqlalchemy": { + "hashes": [ + "sha256:f1191e29e35b6fe1aef7175a09b1707ebb7bd08d0b17cb0feada76c49e5a2d1e" + ], + "version": "==1.1.14" + }, + "toolz": { + "hashes": [ + "sha256:4a13c90c426001d6299c5568cf5b98e095df9c985df194008a67f84ef4fc6c50" + ], + "version": "==0.8.2" + }, + "tzlocal": { + "hashes": [ + "sha256:05a2908f7fb1ba8843f03b2360d6ad314dbf2bce4644feb702ccd38527e13059" + ], + "version": "==1.4" + }, + "ujson": { + "hashes": [ + "sha256:f66073e5506e91d204ab0c614a148d5aa938bdbf104751be66f8ad7a222f5f86" + ], + "version": "==1.35" + } + }, + "develop": { + "alabaster": { + "hashes": [ + "sha256:2eef172f44e8d301d25aff8068fddd65f767a3f04b5f15b0f4922f113aa1c732", + "sha256:37cdcb9e9954ed60912ebc1ca12a9d12178c26637abdf124e3cde2341c257fe0" + ], + "version": "==0.7.10" + }, + "appnope": { + "hashes": [ + "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", + "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71" + ], + "version": "==0.1.0" + }, + "astroid": { + "hashes": [ + "sha256:39a21dd2b5d81a6731dc0ac2884fa419532dffd465cdd43ea6c168d36b76efb3", + "sha256:492c2a2044adbf6a84a671b7522e9295ad2f6a7c781b899014308db25312dd35" + ], + "version": "==1.5.3" + }, + "attrs": { + "hashes": [ + "sha256:c59426b15b45e39a7bc408eb6ba7e7188d9532764f873cc691199ddd975c97ef", + "sha256:80203177723e36f3bbe15aa8553da6e80d47bfe53647220ccaa9ad7a5e473ccc" + ], + "version": "==16.3.0" + }, + "autopep8": { + "hashes": [ + "sha256:eb1685527355809967a0363572289303dc05f4b05edbeee4c9051762103e0ee6", + "sha256:7e82590bf366b4d891ac5c1535554c46a5f79e4400a190a8493e92e75c5037dd" + ], + "version": "==1.3.2" + }, + "babel": { + "hashes": [ + "sha256:f20b2acd44f587988ff185d8949c3e208b4b3d5d20fcab7d91fe481ffa435528", + "sha256:6007daf714d0cd5524bbe436e2d42b3c20e68da66289559341e48d2cd6d25811" + ], + "version": "==2.5.1" + }, + "certifi": { + "hashes": [ + "sha256:54a07c09c586b0e4c619f02a5e94e36619da8e2b053e20f594348c0611803704", + "sha256:40523d2efb60523e113b44602298f0960e900388cf3bb6043f645cf57ea9e3f5" + ], + "version": "==2017.7.27.1" + }, + "chardet": { + "hashes": [ + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691", + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae" + ], + "version": "==3.0.4" + }, + "commonmark": { + "hashes": [ + "sha256:34d73ec8085923c023930dfc0bcd1c4286e28a2a82de094bb72fabcc0281cbe5" + ], + "version": "==0.5.4" + }, + "decorator": { + "hashes": [ + "sha256:95a26b17806e284452bfd97fa20aa1e8cb4ee23542bda4dbac5e4562aa1642cd", + "sha256:7cb64d38cb8002971710c8899fbdfb859a23a364b7c99dab19d1f719c2ba16b5" + ], + "version": "==4.1.2" + }, + "docutils": { + "hashes": [ + "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6", + "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", + "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274" + ], + "version": "==0.14" + }, + "idna": { + "hashes": [ + "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4", + "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f" + ], + "version": "==2.6" + }, + "imagesize": { + "hashes": [ + "sha256:6ebdc9e0ad188f9d1b2cdd9bc59cbe42bf931875e829e7a595e6b3abdc05cdfb", + "sha256:0ab2c62b87987e3252f89d30b7cedbec12a01af9274af9ffa48108f2c13c6062" + ], + "version": "==0.7.1" + }, + "ipython": { + "hashes": [ + "sha256:0faac098d0e5a79272d1a8dd95d568c7ae7a823c9b21b1bca5800541aa994e6f", + "sha256:81b0d6936f87002e6972eccc7e4085f5c2d0673decff22724b53cf34809ffacf" + ], + "version": "==6.2.0" + }, + "ipython-genutils": { + "hashes": [ + "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", + "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" + ], + "version": "==0.2.0" + }, + "isort": { + "hashes": [ + "sha256:cd5d3fc2c16006b567a17193edf4ed9830d9454cbeb5a42ac80b36ea00c23db4", + "sha256:79f46172d3a4e2e53e7016e663cc7a8b538bec525c36675fcfd2767df30b3983" + ], + "version": "==4.2.15" + }, + "jedi": { + "hashes": [ + "sha256:96678411f2ffa444da3a5e7fdd4adc513b728a4a4617b30308be5c950722424b", + "sha256:7abb618cac6470ebbd142e59c23daec5e6e063bfcecc8a43a037d2ab57276f4e" + ], + "version": "==0.10.2" + }, + "jinja2": { + "hashes": [ + "sha256:2231bace0dfd8d2bf1e5d7e41239c06c9e0ded46e70cc1094a0aa64b0afeb054", + "sha256:ddaa01a212cd6d641401cb01b605f4a4d9f37bfc93043d7f760ec70fb99ff9ff" + ], + "version": "==2.9.6" + }, + "lazy-object-proxy": { + "hashes": [ + "sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019", + "sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39", + "sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c", + "sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e", + "sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b", + "sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6", + "sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b", + "sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d", + "sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff", + "sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd", + "sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f", + "sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514", + "sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92", + "sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35", + "sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff", + "sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252", + "sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7", + "sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b", + "sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f", + "sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4", + "sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577", + "sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d", + "sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109", + "sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2", + "sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33", + "sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d", + "sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5", + "sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088", + "sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a" + ], + "version": "==1.3.1" + }, + "markupsafe": { + "hashes": [ + "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" + ], + "version": "==1.0" + }, + "mccabe": { + "hashes": [ + "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", + "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + ], + "version": "==0.6.1" + }, + "mock": { + "hashes": [ + "sha256:5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1", + "sha256:b158b6df76edd239b8208d481dc46b6afd45a846b7812ff0ce58971cf5bc8bba" + ], + "version": "==2.0.0" + }, + "pbr": { + "hashes": [ + "sha256:60c25b7dfd054ef9bb0ae327af949dd4676aa09ac3a9471cdc871d8a9213f9ac", + "sha256:05f61c71aaefc02d8e37c0a3eeb9815ff526ea28b3b76324769e6158d7f95be1" + ], + "version": "==3.1.1" + }, + "pep8": { + "hashes": [ + "sha256:4fc2e478addcf17016657dff30b2d8d611e8341fac19ccf2768802f6635d7b8a", + "sha256:a113d5f5ad7a7abacef9df5ec3f2af23a20a28005921577b15dd584d099d5900" + ], + "version": "==1.7.0" + }, + "pexpect": { + "hashes": [ + "sha256:f853b52afaf3b064d29854771e2db509ef80392509bde2dd7a6ecf2dfc3f0018", + "sha256:3d132465a75b57aa818341c6521392a06cc660feb3988d7f1074f39bd23c9a92" + ], + "version": "==4.2.1" + }, + "pickleshare": { + "hashes": [ + "sha256:c9a2541f25aeabc070f12f452e1f2a8eae2abd51e1cd19e8430402bdf4c1d8b5", + "sha256:84a9257227dfdd6fe1b4be1319096c20eb85ff1e82c7932f36efccfe1b09737b" + ], + "version": "==0.7.4" + }, + "prompt-toolkit": { + "hashes": [ + "sha256:3f473ae040ddaa52b52f97f6b4a493cfa9f5920c255a12dc56a7d34397a398a4", + "sha256:1df952620eccb399c53ebb359cc7d9a8d3a9538cb34c5a1344bdbeb29fbcc381", + "sha256:858588f1983ca497f1cf4ffde01d978a3ea02b01c8a26a8bbc5cd2e66d816917" + ], + "version": "==1.0.15" + }, + "ptyprocess": { + "hashes": [ + "sha256:e8c43b5eee76b2083a9badde89fd1bbce6c8942d1045146e100b7b5e014f4f1a", + "sha256:e64193f0047ad603b71f202332ab5527c5e52aa7c8b609704fc28c0dc20c4365" + ], + "version": "==0.5.2" + }, + "py": { + "hashes": [ + "sha256:2ccb79b01769d99115aa600d7eed99f524bf752bba8f041dc1c184853514655a", + "sha256:0f2d585d22050e90c7d293b6451c83db097df77871974d90efd5a30dc12fcde3" + ], + "version": "==1.4.34" + }, + "pycodestyle": { + "hashes": [ + "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9", + "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766" + ], + "version": "==2.3.1" + }, + "pygments": { + "hashes": [ + "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", + "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc" + ], + "version": "==2.2.0" + }, + "pylint": { + "hashes": [ + "sha256:c7a3ee11db42d00334671b778f042793c837b73f5883132158284b7dbd6f8184", + "sha256:ea6afb93a9ed810cf52ff3838eb3a15e2bf6a81b80de0eaede1ce442caa5ca69" + ], + "version": "==1.7.2" + }, + "pytest": { + "hashes": [ + "sha256:b84f554f8ddc23add65c411bf112b2d88e2489fd45f753b1cae5936358bdf314", + "sha256:f46e49e0340a532764991c498244a60e3a37d7424a532b3ff1a6a7653f1a403a" + ], + "version": "==3.2.2" + }, + "pytest-console-scripts": { + "hashes": [ + "sha256:75188d816f7398956aee48dbff4ce6759d64fbf617f760c8c4cec77608afb8a3" + ], + "version": "==0.1.3" + }, + "pytest-docker": { + "hashes": [ + "sha256:f5da311c8054f6118b8d6b096c3aed7a57eb4c7c7e947a39c797b48e06412a7a", + "sha256:3affb14ddd79c68bfd95b7bbb92b331babcee36229ebe6986144e5d9b5d72155", + "sha256:b410a15b8f05a5f2f8d014610eedf4aba4601eee15a33bb9278751e83089c2de" + ], + "version": "==0.6.0" + }, + "pytest-pylint": { + "hashes": [ + "sha256:9b8ca25823b2f39e89d8170453f5282e57b973395060e838ced5f8c09271ca65", + "sha256:2efaf761472637df9a8f4a3f4fac37f8ce433d70957c5f5767c4be322a42a3d2", + "sha256:9f38725b22967a56724115c9df0a93dda37fea71dd5495fb1354b82e3d938d0d", + "sha256:85da6403c69eb715b9703df640818f337603f2cac947f932b033588851aaaf16", + "sha256:b85763dc36757bfb736b07fecb4f67a0892dcb00868e01f150c7424f608bd62e", + "sha256:ec63f7c4c05331654ab54fda8e68b8a11512009d506a8e35ee9b6d40a359356d", + "sha256:2bb26948f0355d14b274742153a6b4daa51e6d60481143bfd7f025699a27210d" + ], + "version": "==0.7.1" + }, + "pytz": { + "hashes": [ + "sha256:c883c2d6670042c7bc1688645cac73dd2b03193d1f7a6847b6154e96890be06d", + "sha256:03c9962afe00e503e2d96abab4e8998a0f84d4230fa57afe1e0528473698cdd9", + "sha256:487e7d50710661116325747a9cd1744d3323f8e49748e287bc9e659060ec6bf9", + "sha256:43f52d4c6a0be301d53ebd867de05e2926c35728b3260157d274635a0a947f1c", + "sha256:d1d6729c85acea5423671382868627129432fba9a89ecbb248d8d1c7a9f01c67", + "sha256:54a935085f7bf101f86b2aff75bd9672b435f51c3339db2ff616e66845f2b8f9", + "sha256:39504670abb5dae77f56f8eb63823937ce727d7cdd0088e6909e6dcac0f89043", + "sha256:ddc93b6d41cfb81266a27d23a79e13805d4a5521032b512643af8729041a81b4", + "sha256:f5c056e8f62d45ba8215e5cb8f50dfccb198b4b9fbea8500674f3443e4689589" + ], + "version": "==2017.2" + }, + "recommonmark": { + "hashes": [ + "sha256:cd8bf902e469dae94d00367a8197fb7b81fcabc9cfb79d520e0d22d0fbeaa8b7", + "sha256:6e29c723abcf5533842376d87c4589e62923ecb6002a8e059eb608345ddaff9d" + ], + "version": "==0.4.0" + }, + "requests": { + "hashes": [ + "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b", + "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e" + ], + "version": "==2.18.4" + }, + "simplegeneric": { + "hashes": [ + "sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173" + ], + "version": "==0.8.1" + }, + "six": { + "hashes": [ + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb", + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9" + ], + "version": "==1.11.0" + }, + "snowballstemmer": { + "hashes": [ + "sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89", + "sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128" + ], + "version": "==1.2.1" + }, + "sphinx": { + "hashes": [ + "sha256:3ea0faf3e152a0e40372d8495c8cbd59e93f89266231c367d8098ec0dfede98f", + "sha256:af8bdb8c714552b77d01d4536e3d6d2879d6cb9d25423d29163d5788e27046e6" + ], + "version": "==1.6.3" + }, + "sphinxcontrib-programoutput": { + "hashes": [ + "sha256:bd47ff0e1cddec82e1d4501f6f0fa3f77481765fcc7c58ec685ef05b44386c40", + "sha256:cbec3ee1c3abd09e105115ab69cb5ade8ca1be9811565a844f973e93e0314837" + ], + "version": "==0.11" + }, + "sphinxcontrib-restbuilder": { + "hashes": [ + "sha256:8f2d7d73930fdedc3571adab32fbe843b4716829a291dbb27bab56b7c8d1e23d" + ], + "version": "==0.1" + }, + "sphinxcontrib-websupport": { + "hashes": [ + "sha256:f4932e95869599b89bf4f80fc3989132d83c9faa5bf633e7b5e0c25dffb75da2", + "sha256:7a85961326aa3a400cd4ad3c816d70ed6f7c740acd7ce5d78cd0a67825072eb9" + ], + "version": "==1.0.1" + }, + "traitlets": { + "hashes": [ + "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9", + "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835" + ], + "version": "==4.3.2" + }, + "urllib3": { + "hashes": [ + "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", + "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" + ], + "version": "==1.22" + }, + "wcwidth": { + "hashes": [ + "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c", + "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e" + ], + "version": "==0.1.7" + }, + "wrapt": { + "hashes": [ + "sha256:d4d560d479f2c21e1b5443bbd15fe7ec4b37fe7e53d335d3b9b0a7b1226fe3c6" + ], + "version": "==1.10.11" + }, + "yapf": { + "hashes": [ + "sha256:b6a47545511839861ae92108476c119de27a4b137f380f2fd452bcc39bcd6c31", + "sha256:c703dbe4ec882061184a4ae128d2fefcae00641bb4c73df9b85fd0ea05294354" + ], + "version": "==0.18.0" + } + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 1577957a0f6d15283456567ab76dd220aace23e9..f8435839573b9484f42403e3b61599f7bdeff3c6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,7 @@ services: environment: DATABASE_URL: mysql://root:root_password@mysql:3306/testdb LOG_LEVEL: DEBUG - STEEMD_URL: https://steemd.steemit.com + STEEMD_URL: https://api.steemitdev.com links: - mysql:db ports: diff --git a/hive/indexer/http_client.py b/hive/indexer/http_client.py new file mode 100644 index 0000000000000000000000000000000000000000..47cefcfa1a17b67620984df996e25554670c920f --- /dev/null +++ b/hive/indexer/http_client.py @@ -0,0 +1,288 @@ +# coding=utf-8 +import concurrent.futures +import json +import logging +import socket +import time +from functools import partial +from http.client import RemoteDisconnected +from itertools import cycle +from urllib.parse import urlparse +import gzip + +import certifi +import urllib3 + +from urllib3.connection import HTTPConnection +from urllib3.exceptions import MaxRetryError, ReadTimeoutError, ProtocolError + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + +class RPCError(Exception): + pass + +def chunkify(iterable, chunksize=3000): + i = 0 + chunk = [] + for item in iterable: + chunk.append(item) + i += 1 + if i == chunksize: + yield chunk + i = 0 + chunk = [] + if chunk: + yield chunk + +class HttpClient(object): + """ Simple Steem JSON-HTTP-RPC API + + This class serves as an abstraction layer for easy use of the Steem API. + + Args: + nodes (list): A list of Steem HTTP RPC nodes to connect to. + + .. code-block:: python + + from steem.http_client import HttpClient + rpc = HttpClient(['https://steemd-node1.com', 'https://steemd-node2.com']) + + any call available to that port can be issued using the instance + via the syntax ``rpc.exec('command', *parameters)``. + + Example: + + .. code-block:: python + + rpc.exec( + 'get_followers', + 'furion', 'abit', 'blog', 10, + api='follow_api' + ) + + """ + + def __init__(self, nodes, **kwargs): + self.return_with_args = kwargs.get('return_with_args', False) + self.re_raise = kwargs.get('re_raise', True) + self.max_workers = kwargs.get('max_workers', None) + + num_pools = kwargs.get('num_pools', 10) + maxsize = kwargs.get('maxsize', 10) + timeout = kwargs.get('timeout', 60) + retries = kwargs.get('retries', 20) + pool_block = kwargs.get('pool_block', False) + tcp_keepalive = kwargs.get('tcp_keepalive', True) + + if tcp_keepalive: + socket_options = HTTPConnection.default_socket_options + \ + [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), ] + else: + socket_options = HTTPConnection.default_socket_options + + self.batch_size = kwargs.get('batch_size', 50) + + self.http = urllib3.poolmanager.PoolManager( + num_pools=num_pools, + maxsize=maxsize, + block=pool_block, + timeout=timeout, + retries=retries, + socket_options=socket_options, + headers={ + 'Content-Type': 'application/json', + 'accept-encoding': 'gzip' + + }, + cert_reqs='CERT_REQUIRED', + ca_certs=certifi.where()) + ''' + urlopen(method, url, body=None, headers=None, retries=None, + redirect=True, assert_same_host=True, timeout=<object object>, + pool_timeout=None, release_conn=None, chunked=False, body_pos=None, + **response_kw) + ''' + + self.nodes = cycle(nodes) + self.url = '' + self.request = None + self.next_node() + + log_level = kwargs.get('log_level', logging.DEBUG) + logger.setLevel(log_level) + + def next_node(self): + """ Switch to the next available node. + + This method will change base URL of our requests. + Use it when the current node goes down to change to a fallback node. """ + self.set_node(next(self.nodes)) + + def set_node(self, node_url): + """ Change current node to provided node URL. """ + self.url = node_url + self.request = partial(self.http.urlopen, 'POST', self.url) + + @property + def hostname(self): + return urlparse(self.url).hostname + + @staticmethod + def json_rpc_body(name, *args, api=None, as_json=True, _id=0): + """ Build request body for steemd RPC requests. + + Args: + name (str): Name of a method we are trying to call. (ie: `get_accounts`) + args: A list of arguments belonging to the calling method. + api (None, str): If api is provided (ie: `follow_api`), + we generate a body that uses `call` method appropriately. + as_json (bool): Should this function return json as dictionary or string. + _id (int): This is an arbitrary number that can be used for request/response tracking in multi-threaded + scenarios. + + Returns: + (dict,str): If `as_json` is set to `True`, we get json formatted as a string. + Otherwise, a Python dictionary is returned. + """ + headers = {"jsonrpc": "2.0", "id": _id} + if api: + body_dict = {**headers, "method": "call", "params": [api, name, args]} + else: + body_dict = {**headers, "method": name, "params": args} + if as_json: + return json.dumps(body_dict, ensure_ascii=False).encode('utf8') + else: + return body_dict + + def exec(self, name, *args, api=None, return_with_args=None, _ret_cnt=0, body=None): + """ Execute a method against steemd RPC. + + Warnings: + This command will auto-retry in case of node failure, as well as handle + node fail-over, unless we are broadcasting a transaction. + In latter case, the exception is **re-raised**. + """ + body = body or HttpClient.json_rpc_body(name, *args, api=api) + response = None + try: + response = self.request(body=body) + except (MaxRetryError, + ConnectionResetError, + ReadTimeoutError, + RemoteDisconnected, + ProtocolError) as e: + # if we broadcasted a transaction, always raise + # this is to prevent potential for double spend scenario + if api == 'network_broadcast_api': + raise e + + # try switching nodes before giving up + if _ret_cnt > 2: + time.sleep(_ret_cnt) # we should wait only a short period before trying the next node, but still slowly increase backoff + elif _ret_cnt > 10: + raise e + self.next_node() + logging.debug('Switched node to %s due to exception: %s' % + (self.hostname, e.__class__.__name__)) + return self.exec(name, *args, + return_with_args=return_with_args, + _ret_cnt=_ret_cnt + 1) + except Exception as e: + if self.re_raise: + raise e + else: + extra = dict(err=e, request=self.request) + logger.info('Request error', extra=extra) + return self._return( + response=response, + args=args, + return_with_args=return_with_args) + else: + if response.status not in tuple( + [*response.REDIRECT_STATUSES, 200]): + logger.info('non 200 response:%s', response.status) + + return self._return( + response=response, + args=args, + return_with_args=return_with_args) + + def _return(self, response=None, args=None, return_with_args=None): + return_with_args = return_with_args or self.return_with_args + result = None + + if response: + try: + response_json = json.loads(response.data.decode('utf-8')) + logger.debug(response_json) + except Exception as e: + extra = dict(response=response, request_args=args, err=e) + logger.info('failed to load response', extra=extra) + result = None + else: + if 'error' in response_json: + error = response_json['error'] + + if self.re_raise: + error_message = error.get( + 'detail', response_json['error']['message']) + raise RPCError(error_message) + + result = response_json['error'] + elif isinstance(response_json, dict): + result = response_json.get('result', None) + else: + result = response_json + if return_with_args: + return result, args + else: + return result + + def exec_multi_with_futures(self, name, params, api=None, max_workers=None): + with concurrent.futures.ThreadPoolExecutor( + max_workers=max_workers) as executor: + # Start the load operations and mark each future with its URL + def ensure_list(parameter): + return parameter if type(parameter) in (list, tuple, set) else [parameter] + + futures = (executor.submit(self.exec, name, *ensure_list(param), api=api) + for param in params) + for future in concurrent.futures.as_completed(futures): + yield future.result() + + def exec_batch(self, name, params, batch_size=None): + batch_size = batch_size or self.batch_size + + batch_requests = ({ + "method": name, + "params": [i], + "jsonrpc": "2.0", + "id": i + } for i in params) + + + for batch in chunkify(batch_requests, batch_size): + body = json.dumps(batch).encode() + batch_response = self.exec('ignore',[], body=body) + for response in batch_response: + yield response['result'] + + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser('jussi client') + + parser.add_argument('--url', type=str, default='https://api.steemitdev.com') + parser.add_argument('--start_block', type=int, default=1) + parser.add_argument('--end_block', type=int, default=15000000) + parser.add_argument('--batch_request_size', type=int, default=20) + parser.add_argument('--log_level', type=str, default='DEBUG') + + args = parser.parse_args() + + c = HttpClient(nodes=[args.url], batch_size=args.batch_request_size, re_raise=False) + + block_nums = range(args.start_block, args.end_block) + for response in c.exec_batch('get_block', block_nums): + print(json.dumps(response)) diff --git a/hive/indexer/utils.py b/hive/indexer/utils.py index c7c244f0db625d8e176b01f31025cdf064a04304..862048abb80fbe38f77fc894086322b1a8986302 100644 --- a/hive/indexer/utils.py +++ b/hive/indexer/utils.py @@ -5,7 +5,7 @@ import json from json import JSONDecodeError from toolz import update_in, assoc from datetime import datetime -from steembase.http_client import HttpClient +from .http_client import HttpClient def amount(string): return float(string.split(' ')[0]) @@ -69,11 +69,10 @@ class SteemAdapter: blocks = {} while missing: - for block in self.__exec_multi('get_block', missing): + for block in self.__exec_batch('get_block', missing): blocks[int(block['block_id'][:8], base=16)] = block - - available = set(blocks.keys()) - missing = required - available + available = set(blocks.keys()) + missing = required - available if missing: print("WARNING: API missed blocks {}".format(missing)) time.sleep(3) @@ -85,3 +84,6 @@ class SteemAdapter: def __exec(self, method, *params): return self._client.exec(method, *params) + + def __exec_batch(self, method, params): + return self._client.exec_batch(method, params) \ No newline at end of file diff --git a/setup.py b/setup.py index 2dc3ae10a377894a0ad8ad169f1b3ce4941a69f4..261b2262e2abde8b83c5c8856b782e016860ef7d 100644 --- a/setup.py +++ b/setup.py @@ -25,10 +25,10 @@ setup( 'pytest-console-scripts'], install_requires=[ - 'steem', 'bottle', 'bottle_sqlalchemy', 'bottle_errorsrest', + 'certifi', 'sqlalchemy', 'mysqlclient', 'click', @@ -36,6 +36,7 @@ setup( 'toolz', 'maya', 'ujson', + 'urllib3', 'PrettyTable', 'progressbar2', ],