From 2d23493c1d912932e256e02997b089eb00f4325f Mon Sep 17 00:00:00 2001 From: Alex Kanitz Date: Thu, 9 Feb 2023 16:40:48 +0100 Subject: [PATCH 1/3] feat: support Python 3.7+; drop Python 2.7,<3.7 --- .coverage | 1 - .github/workflows/tests.yml | 37 +++++--- .gitignore | 177 +++++++++++++++++++++++++++++++++++- .travis.yml | 22 ----- requirements.txt | 7 +- setup.py | 21 ++--- tes/__init__.py | 34 ++++--- tes/client.py | 112 +++++++++++------------ tes/models.py | 176 +++++++++++++++++------------------ tes/utils.py | 16 ++-- tests/requirements.txt | 10 +- 11 files changed, 380 insertions(+), 233 deletions(-) delete mode 100644 .coverage delete mode 100644 .travis.yml diff --git a/.coverage b/.coverage deleted file mode 100644 index 39575c3..0000000 --- a/.coverage +++ /dev/null @@ -1 +0,0 @@ -!coverage.py: This is a private format, don't read it directly!{"lines":{"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/utils.py":[9,11,12,13,14,15,16,17,18,19,20,21,534,23,24,26,27,28,32,33,34,37,39,42,561,521,572,583,524,695,90,607,784,99,100,101,103,104,615,620,622,157,616,629,532,635,533,640,641,130,619,644,154,538,160,674,163,164,166,168,169,170,683,686,642,177,692,182,183,698,546,211,724,730,219,733,737,738,739,740,741,745,244,680,639,259,262,265,266,779,780,781,272,816,304,833,839,841,842,843,844,846,850,851,854,861,863,864,867,868,875,364,268,379,892,390,410,417,419,420,431,449,459,675,676,496,497,339,500,506,507],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/sessions.py":[517,9,10,11,12,13,14,16,17,18,20,21,22,23,24,27,28,30,35,38,551,41,47,562,56,57,59,60,573,64,65,69,70,583,74,75,78,591,592,81,597,87,88,601,602,603,606,96,528,98,612,615,616,619,622,111,113,114,628,117,631,120,634,637,645,651,652,654,656,663,665,666,667,672,673,674,539,677,678,679,680,682,683,685,699,701,702,704,709,710,712,715,719,724,221,245,286,50,309,326,329,330,331,334,339,343,348,351,356,359,362,366,372,609,376,382,385,386,387,389,390,392,393,395,405,408,409,412,413,416,417,418,420,421,422,423,424,425,426,427,428,429,430,431,433,436,437,438,100,590,476,477,478,479,480,481,482,483,484,485,486,593,488,490,492,493,497,498,499,501,502,504,506,119],"/Users/strucka/Projects/tes-python-packages/py-tes/tes/__init__.py":[1,3,4,8,9,10,11,12,13,14,15,16,17,18],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/fields.py":[1,2,3,5,71,8,105,138,50,116,158,22,157,62,63],"/Users/strucka/Projects/tes-python-packages/py-tes/tests/test_client.py":[1,2,3,4,7,8,11,12,14,15,16,20,21,22,24,25,26,27,28,29,31,32,33,35,36,37,39,40,42,43,44,45,46,47,48,49,52,53,54,55,57,59,60,61,63,64,66,67,68,69,70,71,72,75,76,77,78,80,82,83,84,86,87,89,90,91,92,93,94,96,97,98,99,101,103,104,105,107,108,110,111,112,113,114,115,117,118,119,120,122,124,125,126,128,129],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/adapters.py":[9,11,12,14,15,16,17,18,19,20,21,22,23,24,25,26,27,29,30,31,34,35,36,38,40,41,42,43,46,47,48,49,52,53,55,56,58,59,76,81,106,107,108,110,111,112,113,114,117,118,120,122,123,124,126,128,132,144,157,158,159,161,162,164,201,253,263,266,269,272,273,274,276,277,282,285,286,288,290,313,319,320,323,352,366,388],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/sbcsgroupprober.py":[34,35,37,38,39,40,43,44,29,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/events.py":[4,5,6,7,8,15,16,21,22,23,24,25,26,27,28,29,31,32,36,37,38,39,40,42,43,45,46,47,48,49,50,51,52,54,55,56,57,58,59,61,62,64,66,67,68,69,70,71,72,73,75,76,78,79,81,82,84,85],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/skip.py":[57,59,60,61],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/proxy.py":[30,163,164,165,168,169,170,43,45,46,47,176,177,178,57,58,59,60,62,64,78,80,81,82,83,102,103,104,110,111,112,116,117,118],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/langturkishmodel.py":[192,52,183,186,187,188,189,190,191],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/version.py":[8,9,6],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/__init__.py":[3,5,6,8,14,15,16,17,18,19,20,21,25,26,27,33,34,35,51,54,57,76,83,85,87,88,90,93],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/six.py":[1,23,25,26,27,28,29,31,32,36,37,38,40,49,50,51,52,53,55,60,62,63,64,65,71,72,75,77,80,82,83,86,88,89,91,92,93,94,97,100,103,105,106,107,112,114,115,117,124,126,127,128,130,136,139,141,142,143,154,155,156,157,159,160,161,164,171,173,174,175,177,178,179,181,182,184,185,186,187,189,190,191,195,196,198,199,200,201,202,205,206,207,209,218,224,226,229,231,232,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,303,308,309,310,311,312,314,316,317,320,322,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,350,351,352,354,356,357,360,362,366,367,368,370,371,372,374,376,377,380,382,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,420,421,422,424,426,427,430,432,436,437,438,439,441,442,443,445,447,448,451,453,457,459,460,461,463,465,466,469,471,472,473,474,475,476,477,479,482,483,486,491,502,511,512,514,515,516,517,520,521,525,528,529,535,546,549,552,555,557,560,561,562,565,566,567,568,569,570,573,592,595,598,599,601,604,606,608,610,611,612,613,614,615,618,642,643,646,648,649,651,654,656,657,658,659,660,661,662,663,666,670,674,678,689,691,692,693,694,695,696,699,701,703,706,712,717,721,722,776,777,779,786,788,789,790,800,812,828,849,850,851,856,857,862,863,866,868],"/Users/strucka/Projects/tes-python-packages/py-tes/tes/models.py":[1,3,5,6,9,10,11,13,17,18,19,21,22,23,24,25,28,35,36,39,40,41,42,43,44,45,48,51,52,54,55,56,57,60,61,62,66,67,68,69,71,72,74,75,77,78,80,81,83,84,88,89,90,91,93,94,96,97,99,100,102,103,107,108,109,110,113,114,115,116,118,119,121,122,124,125,127,128,130,131,133,134,136,137,141,142,143,144,146,147,149,150,152,153,155,156,158,159,161,162,166,167,168,169,171,172,174,175,179,180,181,182,184,185,187,188,190,191,193,194,198,199,200,201,203,204,205,206,207,210,211,213,214,216,217,219,220,222,223,225,226,228,229,231,232,234,235,237,238,241,242,245,246,253,254,255,256,257,268,278,281,282,283,284,286,287,291,292,293,294,298,299,300,303,304,305,306,308,309,311,312,316,317,318,319,323,324,325,328,329,330,331,333,334,336,337,339,340,342,343,347,348,349,350,352,353],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/error.py":[2,4,37,6,7,8,9,10,11,12,45,14,48,50,51,46,58],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/capture.py":[64,96,98,69,102,97,58,59,101],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/attr/validators.py":[128,130,3,5,134,7,137,138,11,12,13,14,15,144,146,19,20,21,23,27,133,36,166,39,43,151,56,59,60,61,63,147,75,82,135,98,99,100,38,102,103,104,106,108,110,111,115],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/suite.py":[539,540,543,544,545,546,547,548,551,552,554,563,52,53,567,68,72,73,75,76,79,80,81,94,95,96,97,98,99,100,103,104,105,106,107,113,114,148,149,150,151,153,154,155,156,157,158,173,177,201,204,205,208,209,216,217,218,224,226,227,228,269,270,274,277,278,279,282,283,285,286,287,288,289,290,291,292,293,297,298,301,302,303,304,308,309,310,312,564,315,323,324,325,326,327,328,329,330,331,337,338,339,340,341,342,313,345,346,347,348,349,350,351,356,357,358,360,361,362,364,365,367,368,372,373,374,394,396,397,401,402,403,404,405,406,407,418,419,420,421,422,423,424,427,435,436,441,443,445,446,447,448,451,452,453,454,457,459,460,462,463,464,465,466,467,471,474,475,476,477,478,479,480,481,482,483,484,485,486],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/testid.py":[137,138,142,143,144,145,148,149,150,151,154,155],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/packages/ssl_match_hostname/_implementation.py":[96,1,6,7,14,15,16,17,19,84,22,23,26,79],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/utf8prober.py":[35,36,38,76,44,49,53,57,28,29,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/resolver.py":[2,4,5,7,9,10,23,12,14,15,16,164,18,19,21,22,217,25,26,27,28,154,30,31,160,33,34,35,162,37,167,168,170,171,172,174,175,177,178,179,219,183,184,116,186,187,188,192,193,195,196,197,198,161,200,201,202,143,205,207,208,209,163,213,214,216,89,218,91,92,93,223,224,225,226,165,144,114,115,204,120],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/gb2312prober.py":[33,34,40,44,28,29,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/idna/__init__.py":[1],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/packages.py":[1,6,7,10,11,12],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/util/timeout.py":[1,99,4,5,7,103,11,140,15,18,195,171,182,88,91,156,93,213],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/enums.py":[5,8,11,12,13,14,17,21,22,23,24,25,26,27,28,29,32,35,36,37,38,41,44,45,46,47,50,53,54,55,56,57,59,65,71,72,73,74,75,76],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/xunit.py":[192,193,191],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/sjisprober.py":[32,33,36,37,44,48,52,56,89,28,29,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/api.py":[129,11,101,71,72,112,75,13,143,16,115,88,57,58,61],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/mbcsgroupprober.py":[32,33,34,35,36,37,38,41,42,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/attr/_config.py":[1,19,4,6,9],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/__init__.py":[41,43,44,45,46,49,50,51,54,58,59,61,62,63,66,67,69,70,71,75,76,83,84,86,87,90,91,93,94,95,97,98,99,100,101,102,103,110,111,112,118,121],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/contrib/pyopenssl.py":[43,44,46],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/poolmanager.py":[1,2,3,4,6,7,8,9,10,11,12,13,16,402,19,149,22,151,152,281,410,155,154,159,160,162,165,425,170,301,49,54,439,57,266,147,197,204,206,377,352,229,110,111,112,242,115,116,117,153,121,379,380],"/Users/strucka/Projects/tes-python-packages/py-tes/tests/test_utils.py":[1,2,4,5,8,10,11,12,13,14,15,16,18,19,20,21,22,24,25,26,27,28,29,30,31,33,34,36,37,38,39,43,44,45,49,50,51,52,53,54,55,56],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/pkg_resources/_vendor/six.py":[185,187],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/hebrewprober.py":[128,130,131,132,133,134,135,136,137,138,139,144,149,151,152,154,28,29,286,164,174,178,182,196,282,255],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests_mock/exceptions.py":[14,15,18,19,21,24,29,30],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/representer.py":[2,3,5,6,8,10,12,13,15,17,18,20,27,34,40,74,75,76,77,78,80,81,82,83,84,86,94,112,136,139,141,149,153,169,172,179,182,185,186,187,189,209,224,227,233,237,241,248,251,252,254,255,257,258,260,261,263,264,266,267,269,270,272,273,275,276,278,279,281,282,284,285,287,288,290,291,293,295,311,320,326,337,340,344,348,389,451,452,454,455,457,458,460,461,463,464,466,467,469,470,472,473,475,476,478,479,481,482,484,485],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests_mock/compat.py":[32,34,60,42,44,13,47,16,17,20,23,25,26,31,28,29,30,52],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/idna/core.py":[1,2,3,4,5,6,8,9,10,12,258,16,17,18,131,21,22,23,26,27,28,286,31,32,33,36,39,42,45,49,307,56,190,63,140,335,231,364,146,124],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/constructor.py":[2,3,516,5,6,8,10,12,13,15,17,18,20,21,22,535,24,26,30,35,37,38,551,42,43,44,45,46,47,48,50,51,52,565,55,56,58,61,574,64,65,66,67,68,633,524,106,98,87,88,100,91,92,93,94,101,609,668,99,612,613,614,103,616,105,618,620,621,622,112,625,626,628,629,630,617,120,532,634,636,637,638,127,640,641,642,644,645,646,135,648,137,650,23,652,653,654,656,657,658,660,149,150,151,152,153,666,155,156,157,158,159,672,161,674,163,164,113,168,170,171,172,173,174,175,117,197,201,202,205,206,207,208,632,210,214,215,216,217,218,219,220,223,227,39,110,125,126,255,256,257,258,260,129,128,121,670,284,133,292,134,302,304,649,624,53,334,356,377,383,384,385,386,662,390,391,392,393,395,396,397,398,399,661,401,665,411,416,417,418,420,421,422,424,425,426,428,429,430,669,432,433,434,436,437,438,440,441,442,444,445,446,448,449,450,452,453,454,673,456,457,458,460,461,462,464,465,467,469,472,475,118,478,481,484,664,495],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests_mock/response.py":[13,15,16,17,18,19,20,22,23,25,26,28,29,32,34,60,62,64,68,70,75,76,78,83,87,96,97,98,102,106,112,114,115,119,122,139,141,143,144,145,146,147,148,150,152,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,171,172,173,175,177,180,181,183,184,185,186,187,190,192,193,198,201,202,207,208,210,214,218,220,225,226,229,230,231,232,235,236,238,239,240,241,242,243,244,245,246,247],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/idna/intranges.py":[34,6,38,8,10,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/failuredetail.py":[33,35,36],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/logcapture.py":[34,38,39,40,41,44,178,179,193,194,195,196,198,199,76,204,77,78,207,80,209,86,217,79,222,208],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/packages/__init__.py":[1,3,5],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/compat.py":[22,25,26,27,28,29],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/pyversion.py":[70,136,49,50,51,52,53,54,56,58],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/packages/ssl_match_hostname/__init__.py":[1,3,6,7,10,11,13,14,16,19],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/util/request.py":[1,2,4,5,7,8,11,12,77,95],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/__init__.py":[128,129,2,4,5,6,8,9,11,13,14,15,19,276,279,280,281,282,156,30,69,295,290,163,164,165,166,167,168,41,298,299,301,93,306,52,286,308,314,296,64,267,197,70,71,73,258,75,204,212,87,292,220,221,95,293,103,104,105,231,241,249,125,126,127],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/models.py":[513,515,516,517,8,521,10,11,12,526,17,530,19,20,21,22,849,25,26,27,29,30,31,34,35,548,39,42,43,559,48,49,50,51,52,565,55,56,57,60,61,574,575,578,581,609,584,585,588,589,591,592,593,82,596,601,91,92,93,606,95,96,97,98,99,612,101,102,746,104,105,103,620,109,623,531,617,532,635,637,640,643,534,654,877,662,665,675,170,171,685,174,689,179,180,182,704,194,709,711,177,631,716,721,726,216,219,220,223,224,225,226,227,229,230,743,233,234,235,236,237,238,239,240,241,243,894,246,763,765,767,770,772,774,264,300,779,781,776,280,282,284,286,520,288,560,291,293,295,297,812,301,402,304,305,306,307,308,309,822,564,315,317,830,832,331,333,334,335,568,850,853,825,857,858,347,354,355,868,870,360,745,365,573,370,371,375,381,524,917,896,320,747,388,393,397,398,400,401,914,405,918,408,409,410,411,412,413,414,415,928,417,930,931,420,933,934,423,424,937,939,428,430,431,433,436,437,438,440,441,442,444,816,100,451,452,748,454,462,463,464,467,468,846,472,847,337,827,495,936,498,499,500,501,425,505,818,508,511],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/request.py":[1,3,4,37,7,72,41,10,39,44,45,50,89,90,42],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/cyaml.py":[2,3,5,7,9,10,12,14,16,21,23,28,30,35,38,39,40,41,42,52,55,56,57,58,59,69,72,73,74,75,76],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/composer.py":[129,2,4,5,6,135,8,9,138,11,13,14,16,24,29,31,133,34,35,36,39,46,48,136,50,52,55,58,60,61,63,64,71,72,73,78,79,80,81,82,83,84,85,86,137,88,89,90,91,92,93,94,95,97,99,100,101,102,103,104,105,106,107,109,110,111,112,113,114,115,117,118,119,120,121,122,123,124,125,127],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests_mock/adapter.py":[13,15,16,17,19,20,21,23,26,28,29,31,32,34,37,38,42,46,50,55,62,65,66,68,76,78,79,80,81,82,83,84,87,88,89,90,91,92,94,95,96,104,105,108,109,113,114,118,122,126,129,133,134,136,143,148,150,151,171,173,174,177,180,182,183,184,185,186,188,189,194,197,200,202,203,206,209,210,211,212,213,215,216,217,218,219,221,222,223,228,229,230,231,235,238,244,245,246,247,249,252,255,256,264,265,266,267,268,269,270,271,272,273,274,276,284,287],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/charsetgroupprober.py":[32,33,65,39,49,85,57,28,29],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/charsetprober.py":[32,66,35,37,39,103,44,61,47,51,54,58,29,30],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/contrib/socks.py":[32,33,37,39,23,24,26,27,28,29,30],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/case.py":[128,129,130,131,132,133,140,147,148,149,151,29,33,34,36,37,38,39,40,41,42,45,59,60,64,69,70,74,99,100,101,102,103,104],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/auth.py":[8,10,11,12,13,14,15,17,19,20,21,22,24,25,28,286,292,266,72,73,75,79,80,82,86,217,92,222,95,100,101,103,108,109,111,117,127],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/langhebrewmodel.py":[193,194,195,196,197,198,199,53,190],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/packages/six.py":[1,2,4,6,8,10,12,13,20,23,24,25,26,27,28,29,31,32,35,36,37,38,39,40,41,45,47,49,50,51,52,53,55,57,59,60,61,62,63,64,65,67,69,71,72,73,75,77,79,80,81,82,83,85,86,87,88,89,91,92,93,94,97,100,103,105,106,107,112,114,115,117,124,126,127,128,130,136,139,141,142,143,154,155,156,157,159,160,161,164,171,173,174,175,177,178,179,181,182,184,185,186,187,189,190,191,195,196,198,199,200,201,202,203,205,206,207,209,218,224,226,229,231,232,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,303,308,309,310,311,312,314,316,317,320,322,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,350,351,352,354,356,357,360,362,366,367,368,370,371,372,374,376,377,380,382,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,420,421,422,424,426,427,430,432,436,437,438,439,441,442,443,445,447,448,451,453,457,459,460,461,463,465,466,469,471,472,473,474,475,476,477,479,482,483,486,491,502,511,512,514,515,516,517,520,521,525,528,529,535,546,549,552,555,557,560,561,562,565,566,567,568,569,570,573,592,595,596,598,601,604,606,608,610,611,612,613,614,615,618,642,646,648,649,651,654,656,657,658,659,660,661,662,663,666,670,674,678,689,691,692,693,694,695,696,699,701,703,706,712,717,721,722,776,777,779,786,788,789,790,800,812,828,849,850,851,856,857,862,863,866,868],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/attr/_make.py":[512,1,514,3,4,1029,6,519,8,9,10,523,524,526,941,344,18,19,20,21,22,345,536,25,540,541,30,31,545,34,37,40,553,554,43,44,557,46,349,560,50,563,513,565,566,1033,56,57,58,571,572,1034,693,352,579,326,694,1036,586,695,588,590,591,569,594,568,799,699,1041,617,716,530,570,531,717,532,635,637,126,533,130,131,132,645,134,135,136,137,138,142,365,803,707,789,662,755,152,154,155,157,158,159,160,161,164,165,166,167,170,199,177,178,179,180,542,182,183,696,185,186,543,671,714,373,193,195,708,197,516,712,201,202,715,204,546,718,207,208,209,210,211,724,377,217,218,719,220,638,549,720,587,379,230,564,237,238,239,754,723,756,757,758,761,765,766,575,790,642,558,333,794,559,796,797,798,133,800,801,802,219,804,805,806,812,813,814,815,818,825,223,828,906,832,181,834,323,836,837,838,839,840,841,842,843,844,845,334,847,336,337,850,1030,340,853,854,855,856,857,346,347,860,861,350,864,356,358,359,872,361,363,364,402,573,371,372,885,374,887,376,889,890,891,380,381,895,384,388,389,902,391,904,905,394,395,908,909,911,912,401,914,403,916,918,919,920,580,922,925,926,415,928,929,930,931,933,422,426,427,428,429,942,431,945,947,437,438,585,441,927,445,446,448,672,331,452,965,454,968,969,458,459,322,463,419,982,983,984,473,986,482,995,491,500,509,510,511],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/packages/ordered_dict.py":[128,257,132,5,6,137,10,11,142,16,17,151,154,156,28,218,158,159,160,161,34,36,37,38,39,40,41,42,44,173,175,48,177,50,51,52,54,169,244,190,63,65,66,67,68,197,71,79,69,210,212,213,214,215,216,164,49,91,220,224,225,235,168,116,170,120,249,122,124,253,126],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/serializer.py":[2,4,5,6,8,9,74,11,13,46,15,16,78,36,27,60],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/contrib/__init__.py":[1],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/euctwfreq.py":[385,44,47],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/core.py":[65,34,36,37,41,42,43,44,50,51,55,56,59,188,61,62,193,66,199,200,201,202,203,204,205,207,187,60],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/mbcharsetprober.py":[34,37,39,45,53,57,90,61,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/attr/__init__.py":[1,3,10,20,24,25,26,27,30,32,33,34,35,37,38,40,41,44,45,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/euckrprober.py":[34,35,41,45,28,29,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/loader.py":[522,523,540,541,542,547,550,551,559,564,566,567,569,570,79,81,82,83,84,85,86,89,90,91,92,93,94,95,97,99,105,108,109,110,112,113,114,116,119,121,122,123,128,131,134,135,143,144,145,146,147,149,150,151,154,156,157,158,159,160,161,163,164,167,168,170,177,178,179,180,181,182,183,186,196,197,200,201,209,210,211,212,314,315,316,317,321,322,323,325,326,327,328,330,331,332,333,338,340,341,343,356,359,369,371,374,375,378,379,404,405,406,409,410,416,417,418,420,421,428,431,432,433,434,435,436,447,454,455,473,474,475,476,481,486,487,488,493,494],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/langgreekmodel.py":[224,69,50,206,209,210,211,212,213,214,215,218,219,220,221,222,223],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/cover.py":[164,263,173,271,182,183],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/certifi/__init__.py":[1,3],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/cp949prober.py":[34,35,43,47,28,29,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/_collections.py":[1,2,3,4,133,262,135,136,265,138,139,268,14,15,18,276,21,150,151,24,281,154,27,157,261,160,237,294,39,168,41,43,172,173,47,175,48,177,50,180,182,137,57,87,288,224,72,202,247,45,79,208,83,142,86,185,89,90,143,92,93,229,96,263,101,232,171,44,146,238,297,250,234],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/attr/converters.py":[8,3,5],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/selector.py":[149,129,130,131,134,135,140,141,144,148,171,153,152,68,154,156,157,69,162,35,37,167,40,41,42,43,44,45,174,175,176,178,179,53,54,57,187,188,191,193,194,196,197,72,73,74,76,77,80,81,163,169,222,224,225,226,227,228,229,232,233,234,235,236,237,238,239,240,241,116,117,118,119,123,170,126,127],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/universaldetector.py":[36,39,40,41,43,44,45,46,47,48,51,66,68,69,70,71,72,73,74,75,76,77,78,79,81,220,94,111],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/util/retry.py":[1,2,3,4,5,6,7,9,394,279,273,142,144,145,147,20,150,23,24,153,154,155,285,158,159,160,162,27,294,167,168,169,170,171,257,173,175,172,310,189,190,319,320,203,401,217,152,166,17,233,157,243,251],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/latin1prober.py":[130,29,30,32,34,35,36,37,38,39,40,41,42,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,92,96,97,103,108,112,116],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/result.py":[38,39,40,41,106,43,44,109,110,104,105,103],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/escprober.py":[35,69,40,73,42,77,83,58,28,29,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/jisfreq.py":[322,44,47],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/codingstatemachine.py":[33,66,86,80,83,54,55,28,30,63],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/util/__init__.py":[1,3,4,5,6,16,21,22,28,53],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/scanner.py":[514,375,1149,1197,1177,120,27,29,542,1055,32,33,35,548,549,38,39,40,41,42,43,44,46,48,561,121,1204,60,181,64,67,70,1095,465,73,76,79,593,594,595,596,1109,598,478,609,1124,101,620,109,1137,115,116,117,118,631,1144,1145,634,191,1148,637,1150,1151,1152,311,130,132,1157,134,1159,648,1161,1162,651,1164,1165,1166,1167,1168,1169,1170,1171,1172,1173,1174,1175,1176,665,1178,156,1181,1182,1183,1184,1187,1189,166,113,680,169,1194,1195,1196,173,1198,687,177,773,30,695,654,703,192,195,708,199,200,204,782,1228,718,207,208,721,722,211,1230,365,728,219,220,122,223,227,550,1254,231,1201,235,749,125,979,247,1272,127,769,771,772,261,774,775,778,215,269,270,271,784,273,274,276,196,657,1156,283,284,285,286,555,803,292,163,146,1319,470,298,302,303,304,136,306,307,309,137,824,314,138,318,123,322,1163,1153,652,843,1356,551,337,338,142,857,346,143,144,867,356,1382,145,361,364,877,368,371,374,887,376,660,379,382,149,1408,385,387,901,422,151,312,399,402,1427,405,153,239,1436,1437,150,1443,421,1446,424,937,427,430,433,243,436,439,440,441,442,159,444,445,447,448,450,453,456,129,459,663,305,462,463,464,248,467,203,473,476,477,1158,479,481,1190,1191,272,1192,545,425,1193,170,341],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/sbcharsetprober.py":[33,34,35,36,37,70,39,77,53,124,29,30,63],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/deprecated.py":[40,42,43,44],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/attr/_compat.py":[1,3,4,7,10,11,15,16,19,21,24,28,31,33,38,43,48,53,58,63,68,70,72,73,74,75],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/big5prober.py":[34,35,41,45,28,29,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/compat.py":[9,11,13,20,23,26,28,29,30,33,39,40,43,44,45,46,47,49,51,52,53,54,55,56],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/gb2312freq.py":[281,42,44],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/structures.py":[8,10,12,15,40,42,43,44,45,46,48,51,53,54,56,59,60,62,63,65,73,82,85,89,90,92,93,94,96,99,104],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/nodes.py":[2,3,8,25,26,28,29,30,31,32,33,35,37,38,39,40,41,42,44,45,47,48],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/attr/_funcs.py":[1,3,5,6,7,10,11,142,151,154,39,40,41,42,43,45,46,49,50,51,52,55,57,187,64,67,70,71],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/debug.py":[40,41,42,43],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/langcyrillicmodel.py":[321,141,302,278,281,282,283,284,285,286,287,290,291,292,293,294,295,296,299,300,301,46,303,304,305,308,309,310,311,312,313,314,317,318,319,320,65,322,323,326,327,328,329,330,331,332,84,103,122],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/__init__.py":[24,19,20,21],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/jpcntx.py":[31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,116,117,118,119,120,121,123,131,143,170,173,180,183,184,188,192,212,213],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/mbcssm.py":[534,538,539,28,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,566,568,569,570,571,572,64,68,69,70,73,75,76,77,78,79,99,104,105,106,107,108,109,110,113,115,116,117,118,119,155,159,160,161,162,163,166,168,169,170,171,172,208,212,213,216,218,219,220,221,222,258,262,263,264,265,266,267,270,272,273,274,275,276,312,316,317,318,319,320,321,329,331,332,333,334,335,540,373,377,378,379,382,384,385,386,387,388,424,428,429,430,431,432,433,434,437,439,440,441,442,443,479,483,484,485,486,487,488,489,492,494,495,496,497,498],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/euckrfreq.py":[41,43,193],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/errorclass.py":[148,150,151,152,153,154],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/euctwprober.py":[33,34,40,44,28,29,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/connectionpool.py":[1,2,3,4,5,7,8,777,11,155,448,282,409,26,27,28,29,158,159,214,161,162,35,36,38,39,40,41,42,43,812,794,46,157,48,50,883,52,54,58,757,62,406,64,65,67,836,292,201,74,759,78,288,81,163,86,855,164,731,94,97,321,449,747,304,749,750,752,753,754,755,756,446,758,425,447,252],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/base.py":[98,100,101,102],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/certifi/core.py":[35,9,10,11,14,18,21,22,24,27],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/prof.py":[71,74,75,76,80,81,82,83,84,57],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/hooks.py":[34,13,14,17,18,23,25,26,27],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/exceptions.py":[1,2,8,9,10,13,14,15,18,19,20,24,29,30,31,35,40,41,42,45,46,47,50,51,52,55,56,57,61,66,74,76,85,86,88,94,95,96,99,104,105,108,109,110,115,116,117,120,121,122,125,126,127,130,131,132,135,136,137,140,141,143,150,151,152,153,156,157,158,161,162,163,166,167,168,171,172,173,176,177,178,181,182,183,186,190,191,194,195,196,199,203,204,207,214,215,218,223,224,225,228,229,232,237,238,239,244,245,246],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/util/selectors.py":[256,130,131,133,8,9,10,11,12,13,14,16,17,18,19,21,22,150,24,25,26,539,29,30,287,288,289,34,164,37,294,389,41,534,278,172,302,136,308,286,312,58,543,266,565,192,139,71,456,457,458,459,206,463,336,466,270,165,226,484,146,245,505,250,281,127],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/emitter.py":[516,9,11,12,14,15,17,18,1047,540,31,33,34,35,38,39,922,559,1082,582,106,619,111,629,120,133,146,160,170,175,178,546,215,227,233,234,261,267,275,790,281,794,799,803,293,470,816,311,317,840,334,847,856,355,360,829,369,374,377,389,393,396,910,911,912,913,914,915,916,917,918,919,920,921,410,923,924,925,415,928,423,427,431,438,460,982,993,495],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/polling.py":[1,3,5,6,7,44,39,12,13,14,45,19,20,23,24,33,27],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/response.py":[1,2,3,4,5,6,7,9,10,14,15,16,17,19,22,24,29,32,545,513,55,57,60,522,63,69,76,108,110,111,113,114,531,116,118,121,122,123,124,125,126,127,128,130,131,132,133,134,136,139,140,142,143,146,147,148,150,151,155,158,161,174,115,181,190,194,202,206,208,219,239,240,245,248,250,256,257,260,264,265,273,276,278,289,298,300,301,302,324,328,567,343,346,367,368,371,374,375,377,378,383,384,385,393,394,395,403,404,405,408,410,413,415,431,435,436,438,439,441,471,474,478,485,487,489,491,492,496,505,509],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/util/ssl_.py":[1,2,3,4,6,7,9,215,12,13,14,15,18,19,20,21,278,279,280,25,38,39,42,43,44,45,176,50,51,70,71,72,73,74,75,76,77,78,79,80,81,82,83,86,87,216,199,281,149],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/util/wait.py":[1,36,29,9],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/exceptions.py":[8,9,12,15,17,19,20,21,22,23,24,25,28,29,32,33,36,37,40,41,44,50,53,57,60,61,64,65,68,69,72,73,76,77,80,81,84,85,88,89,92,93,96,97,100,101,104,105,110,111,112,115,116,117,120,121,122],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/attr/exceptions.py":[1,4,33,39,12,13,14,17,22,25,30],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/connection.py":[1,2,3,4,5,6,7,8,9,10,11,13,14,15,282,259,23,25,26,27,28,154,31,37,39,169,257,48,50,52,54,55,56,62,65,66,67,70,258,260,208,209,211,213,214,215,95,97,354,228,101,165,104,106,255,368,370,371,246,250,251,252,253,254,127],"/Users/strucka/Projects/tes-python-packages/py-tes/tes/utils.py":[1,3,4,5,6,7,8,11,12,13,15,16,17,18,20,21,22,23,24,25,28,29,30,31,32,35,36,37,38,39,40,41,42,43,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,87,88,89,90,91,92,93],"/Users/strucka/Projects/tes-python-packages/py-tes/tes/client.py":[1,3,4,6,7,8,10,11,14,15,16,17,19,21,22,27,28,29,30,31,32,33,34,35,36,37,38,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/langbulgarianmodel.py":[224,225,226,227,72,209,212,53,214,215,216,217,218,223,221,222,213],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/manager.py":[128,262,263,264,265,272,273,274,149,167,166,295,168,301,302,177,178,184,249,88,89,93,94,95,96,99,252,106,107,111,114,123,118,105,120,121,250,251,124,253,254],"/Users/strucka/Projects/tes-python-packages/py-tes/tests/test_models.py":[1,2,4,7,8,10,11,12,17,19,20,21,26,27,29,30,32,33,35,36,37,39,41,43],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests_mock/__init__.py":[32,33,34,35,36,13,14,15,16,17,20,21,22,23,24,25,26,27,28,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/cookies.py":[512,514,515,516,520,10,523,12,13,14,15,17,18,20,21,535,536,537,26,542,36,38,39,40,41,43,46,49,52,66,69,72,75,79,82,83,85,89,93,98,529,103,105,110,112,113,115,119,532,126,127,128,136,142,143,144,147,166,169,172,188,190,202,219,228,236,245,253,262,271,279,287,300,316,322,331,338,344,349,351,352,357,377,402,409,415,422,437,472,503,511],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/util.py":[320,446,278,263,520,521,266,267,140,397,270,271,272,273,403,660,405,662,663,408,409,410,411,312,393,163,164,406,470,321,264,306,307,308,309,310,265,184,313,187,188,189,190,319,192,279,195,407,471,311,337,338,339,340,398,342,343,399,479,323,481,483,484,485,486,449,404,448,318,502,503,504,505,506,447,276],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/isolate.py":[61,62],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/langthaimodel.py":[192,193,194,195,196,197,198,52,189],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/multiprocess.py":[224,225,226,227,231,233,234,235,238,223],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/importer.py":[143,144,146,147,148,149,151,152,153,154,155,156,157,158,32,161,165,166,167,40,41,42,44,45,47,30,54,59,62,53,65,66,67,68,70,71,72,75,76,77,78,79,80,81,94,96,97,98,99,101,103,104,63],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/reader.py":[129,133,134,135,137,138,139,140,175,173,152,146,147,20,149,22,151,24,26,155,156,154,150,33,169,170,171,172,45,174,157,177,59,60,61,62,63,64,65,66,67,68,69,70,71,72,76,77,78,79,87,88,89,94,95,97,99,100,101,102,103,104,105,106,107,18,110,111,112,114,115,116,117,148,122,123,125,126],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/idna/idnadata.py":[3,37,54,65,71,81,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,480,481,482,483,484,485,486,487,488,489,490,491,492,493,494,495,496,497,498,499,500,501,502,503,504,505,506,507,508,509,510,511,512,513,514,515,516,517,518,519,520,521,522,523,524,525,526,527,528,529,530,531,532,533,534,535,536,537,538,539,540,541,542,543,544,545,546,547,548,549,550,551,552,553,554,555,556,557,558,559,560,561,562,563,564,565,566,567,568,569,570,571,572,573,574,575,576,577,578,579,580,581,582,583,584,585,586,587,588,590,1571,1574,1582],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/pkg_resources/extern/__init__.py":[28,29,30],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/eucjpprober.py":[32,33,36,37,44,48,52,56,89,28,29,30,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/chardistribution.py":[171,132,133,139,151,152,28,30,32,34,36,40,41,42,43,44,46,177,158,61,192,193,70,199,84,217,218,224,100,105,113,114,120,170],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/dumper.py":[2,4,5,6,7,9,12,13,14,15,16,27,30,31,32,33,34,45,48,49,50,51,52],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/certs.py":[17,14,15],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/util/url.py":[1,2,201,4,7,11,14,19,20,22,23,24,132,154,27,28,26,158,159,160,33,162,163,164,38,167,168,176,172,174,29,48,179,30,55,184,31,189,190,192,193,195,198,161,200,129,211,215,219,222,95,225,99,115,116,117,118,119,120,122,123,124,126],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/util/connection.py":[96,1,2,3,4,37,7,107,130,110,109,112,120,121,118,119,88,36,125,126,127],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/__version__.py":[5,6,7,8,9,10,11,12,13,14],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/util/response.py":[1,2,4,69,38,7,15,18,19,20,22,24],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/_internal_utils.py":[37,38,39,40,9,11,14,19,20,27,30],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/tokens.py":[2,3,4,5,6,17,18,19,25,26,28,29,31,32,33,34,35,36,37,39,40,42,43,45,46,48,49,51,52,54,55,57,58,60,61,63,64,66,67,69,70,72,73,75,76,77,82,83,84,89,90,91,96,97,98,99,100,101,102,103],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/attr/filters.py":[3,5,38,7,8,11,21],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/big5freq.py":[384,43,46],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/loader.py":[33,2,4,5,6,7,8,9,11,13,21,23,24,25,26,27,28,29,31],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/yaml/parser.py":[512,526,537,538,539,540,542,543,544,545,546,551,552,553,554,555,556,563,564,565,566,567,569,570,571,572,573,62,64,65,66,67,69,70,583,72,587,76,77,78,81,82,83,84,85,86,87,89,91,92,94,96,97,98,99,100,102,103,104,105,107,109,112,114,116,117,118,119,120,121,127,130,131,132,135,137,139,142,143,144,145,146,147,148,151,152,154,159,162,166,183,184,185,186,187,188,190,193,194,195,196,200,201,204,206,208,217,264,265,267,268,270,273,274,279,280,281,282,292,301,315,316,317,318,319,325,326,327,328,330,331,334,335,336,337,338,339,340,341,342,343,344,345,346,372,574,376,381,402,422,427,446,471,472,473,474,476,477,478,479,480,486,493,494,495,496,497,498,499,500,502],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests_mock/request.py":[130,134,136,138,139,13,14,143,16,17,18,21,26,28,29,30,31,32,36,37,38,39,40,44,46,47,49,51,52,54,55,57,59,61,63,65,67,69,76,141,95,97,99,101,103,146,110,112,114,118,122,126],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/urllib3/filepost.py":[1,2,4,5,7,8,9,11,14,21,41,59],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/nose/plugins/doctests.py":[192,193,194,195,188,189,190,191],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests/status_codes.py":[3,5,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,25,26,27,28,29,30,31,32,34,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,72,73,74,75,76,77,78,79,80,81,82,85,87,88,89,90,91],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/requests_mock/mocker.py":[68,128,134,137,138,87,140,13,15,144,17,18,20,21,22,23,24,25,26,155,28,158,31,161,36,165,38,39,40,41,42,43,172,45,180,181,183,184,244,187,188,190,63,64,65,67,196,70,73,205,78,141,81,83,84,86,185,88,152,143,223,123,100,101,167,146,110,114,116,170,153,121,122,127,125,126,149],"/Users/strucka/Projects/tes-python-packages/venv/lib/python2.7/site-packages/chardet/escsm.py":[129,131,132,133,134,135,136,28,243,170,174,175,176,177,178,179,180,181,182,185,187,188,189,62,191,192,66,67,68,69,70,71,74,76,77,78,79,80,81,226,230,231,232,233,234,237,239,240,241,242,115,244,190,119,120,121,122,123,124,125,126],"/home/kellrott/workspaces/py-tes/tes/__init__.py":[1,3,4,5,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,22,41],"/home/kellrott/workspaces/py-tes/tes/client.py":[1,3,4,5,7,8,9,10,12,15,18,22,23,24,25,26,27,28,29,30,31,32,33,34,36,37,45,53,67,77,86,103,123,19,38,39,78,79,124,126,128,130,134,135,137,80,81,82,83,84,40,41,42,54,55,59,127,60,61,62,64,65,46,47,48,49,50,51,68,69,70,129,71,72,73,74,75,87,88,89,90,91,92,94,96,97,98,99,100,101,104,107,110,111,115,116,105,119,121,120,117],"/home/kellrott/workspaces/py-tes/tes/models.py":[1,3,4,5,6,8,9,10,11,14,15,16,18,33,40,44,56,70,76,82,88,89,91,97,104,105,106,107,109,110,112,113,115,116,118,119,121,122,126,127,128,129,131,132,134,135,137,138,140,141,145,146,147,148,150,151,153,154,156,157,159,160,41,164,165,166,167,169,170,172,173,175,176,178,179,181,182,184,185,189,190,191,192,193,194,196,197,198,199,201,202,204,205,207,208,212,213,214,215,217,218,220,221,225,226,227,228,229,230,232,233,234,235,237,238,240,241,243,244,246,247,251,252,253,254,256,257,258,259,260,263,264,266,267,269,270,272,273,275,276,278,279,281,282,284,285,287,288,290,291,292,293,296,361,362,363,364,366,367,371,372,373,374,378,379,380,383,384,385,386,388,389,391,392,396,397,398,399,403,404,405,408,409,410,411,413,414,416,417,419,420,422,423,427,428,429,430,432,433,57,62,63,58,59,65,22,77,79,98,99,92,93,94,45,47,48,50,49,53,46,100,297,298,301,302,304,306,309,312,315,322,333,342,348,355,358,323,324,325,326,328,330,356,334,335,337,339,23,24,26,27,28,29,30,78,71,72],"/home/kellrott/workspaces/py-tes/tes/utils.py":[1,3,4,6,7,11,12,15,20,21,25,26,30,90,91,92,93,94,95,96,31,33,34,38,39,40,41,16,17,46,47,48,49,50,51,45,54,63,64,65,66,77,79,80,87,67,75,76,55,56,57,61,27,81,82,83,84,85,22,32,58,68,69,70,71,72,73]}} \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 68873ce..e01138f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,26 +1,35 @@ name: py-test_file -on: [ pull_request ] +on: [pull_request] jobs: - test: runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + steps: - - name: Check out code - uses: actions/checkout@v2 + - name: Check out code + uses: actions/checkout@v3 - - name: Requirements - run: pip install -r requirements.txt + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.version }} - - name: Test Requirements - run: pip install -r tests/requirements.txt + - name: Install requirements + run: | + pip install -r requirements.txt + pip install -r tests/requirements.txt - - name: Install - run: python setup.py install --user + - name: Install app + run: pip install . - - name: Flake - run: flake8 . + - name: Lint with Flake8 + run: flake8 . - - name: Test - run: python -m nose tests --with-coverage --cover-package tes --cover-min-percentage 80 + - name: Run unit tests + run: coverage run --source tes -m pytest -W ignore::DeprecationWarning diff --git a/.gitignore b/.gitignore index d22d113..18dd7bf 100644 --- a/.gitignore +++ b/.gitignore @@ -16,5 +16,180 @@ eggs/ \#*\# .desktop +# Misc test_tmp -*venv* \ No newline at end of file +*venv* + +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9623fa9..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -language: python -python: - - 2.7 - - 3.5 - - 3.6 - -install: - - pip install pip --upgrade - - pip install setuptools --upgrade - - pip install -r tests/requirements.txt - - pip install -r requirements.txt - - python setup.py install - -script: - - flake8 . - - python -m nose tests --with-coverage --cover-package tes --cover-min-percentage 80 - -after_success: - - coveralls - -notifications: - email: false diff --git a/requirements.txt b/requirements.txt index 631e096..907fd76 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,3 @@ -attrs>=17.4.0 -future>=0.16.0 -python-dateutil>=2.6.1 -requests>=2.18.2 +attrs>=22.2.0 +python-dateutil>=2.8.2 +requests>=2.28.2 diff --git a/setup.py b/setup.py index b75e855..6a8542a 100644 --- a/setup.py +++ b/setup.py @@ -37,25 +37,20 @@ def find_version(*file_paths): url="https://github.com/ohsu-comp-bio/py-tes", license="MIT", packages=find_packages(), - python_requires=">=2.7, <4", - install_requires=[ - "attrs>=17.4.0", - "future>=0.16.0", - "python-dateutil>=2.6.1", - "requests>=2.18.1" - ], + python_requires=">=3.7, <4", + install_requires=read("requirements.txt").splitlines(), zip_safe=True, classifiers=[ - "Development Status :: 3 - Alpha", + "Development Status :: 4 - Beta", "Intended Audience :: Developers", "Natural Language :: English", "License :: OSI Approved :: MIT License", "Topic :: Software Development :: Libraries", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6" + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9" + "Programming Language :: Python :: 3.10" + "Programming Language :: Python :: 3.11" ], ) diff --git a/tes/__init__.py b/tes/__init__.py index 95b0822..d48b5ce 100644 --- a/tes/__init__.py +++ b/tes/__init__.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - from tes.client import HTTPClient from tes.utils import unmarshal from tes.models import ( @@ -20,22 +18,22 @@ ) __all__ = [ - HTTPClient, - unmarshal, - Input, - Output, - Resources, - Executor, - Task, - ExecutorLog, - TaskLog, - OutputFileLog, - CreateTaskResponse, - GetTaskRequest, - ListTasksRequest, - ListTasksResponse, - ServiceInfoRequest, - ServiceInfo + "HTTPClient", + "unmarshal", + "Input", + "Output", + "Resources", + "Executor", + "Task", + "ExecutorLog", + "TaskLog", + "OutputFileLog", + "CreateTaskResponse", + "GetTaskRequest", + "ListTasksRequest", + "ListTasksResponse", + "ServiceInfoRequest", + "ServiceInfo" ] __version__ = "0.4.2" diff --git a/tes/client.py b/tes/client.py index 6c2fdf7..2cf8d81 100644 --- a/tes/client.py +++ b/tes/client.py @@ -1,13 +1,11 @@ -from __future__ import absolute_import, print_function, unicode_literals - import re import requests import time from attr import attrs, attrib from attr.validators import instance_of, optional -from builtins import str -from requests.utils import urlparse +from urllib.parse import urlparse +from typing import Any, Dict, Optional from tes.models import (Task, ListTasksRequest, ListTasksResponse, ServiceInfo, GetTaskRequest, CancelTaskRequest, CreateTaskResponse, @@ -21,19 +19,16 @@ def process_url(value): @attrs class HTTPClient(object): - url = attrib(converter=process_url) - timeout = attrib(default=10, validator=instance_of(int)) - user = attrib(default=None, - converter=strconv, - validator=optional(instance_of(str))) - password = attrib(default=None, - converter=strconv, - validator=optional(instance_of(str))) - token = attrib(default=None, - converter=strconv, - validator=optional(instance_of(str))) - - @url.validator + url: str = attrib(converter=process_url) + timeout: int = attrib(default=10, validator=instance_of(int)) + user: Optional[str] = attrib( + default=None, converter=strconv, validator=optional(instance_of(str))) + password: Optional[str] = attrib( + default=None, converter=strconv, validator=optional(instance_of(str))) + token: Optional[str] = attrib( + default=None, converter=strconv, validator=optional(instance_of(str))) + + @url.validator # type: ignore def __check_url(self, attribute, value): u = urlparse(value) if u.scheme not in ["http", "https"]: @@ -42,48 +37,51 @@ def __check_url(self, attribute, value): % ("http", "https") ) - def get_service_info(self): - kwargs = self._request_params() - response = requests.get( - "%s/v1/tasks/service-info" % (self.url), + def get_service_info(self) -> ServiceInfo: + kwargs: Dict[str, Any] = self._request_params() + response: requests.Response = requests.get( + f"{self.url}/v1/tasks/service-info", **kwargs) response.raise_for_status() return unmarshal(response.json(), ServiceInfo) - def create_task(self, task): + def create_task(self, task: Task) -> CreateTaskResponse: if isinstance(task, Task): msg = task.as_json() else: raise TypeError("Expected Task instance") - kwargs = self._request_params(data=msg) - response = requests.post( - "%s/v1/tasks" % (self.url), + kwargs: Dict[str, Any] = self._request_params(data=msg) + response: requests.Response = requests.post( + f"{self.url}/v1/tasks", **kwargs ) response.raise_for_status() return unmarshal(response.json(), CreateTaskResponse).id - def get_task(self, task_id, view="BASIC"): - req = GetTaskRequest(task_id, view) - payload = {"view": req.view} - kwargs = self._request_params(params=payload) - response = requests.get( - "%s/v1/tasks/%s" % (self.url, req.id), + def get_task(self, task_id: str, view: str = "BASIC") -> Task: + req: GetTaskRequest = GetTaskRequest(task_id, view) + payload: Dict[str, Optional[str]] = {"view": req.view} + kwargs: Dict[str, Any] = self._request_params(params=payload) + response: requests.Response = requests.get( + f"{self.url}/v1/tasks/{req.id}", **kwargs) response.raise_for_status() return unmarshal(response.json(), Task) - def cancel_task(self, task_id): - req = CancelTaskRequest(task_id) - kwargs = self._request_params() - response = requests.post( - "%s/v1/tasks/%s:cancel" % (self.url, req.id), + def cancel_task(self, task_id: str) -> None: + req: CancelTaskRequest = CancelTaskRequest(task_id) + kwargs: Dict[str, Any] = self._request_params() + response: requests.Response = requests.post( + f"{self.url}/v1/tasks/{req.id}:cancel", **kwargs) response.raise_for_status() - return + return None - def list_tasks(self, view="MINIMAL", page_size=None, page_token=None): + def list_tasks( + self, view: str = "MINIMAL", page_size: Optional[int] = None, + page_token: Optional[str] = None + ) -> ListTasksResponse: req = ListTasksRequest( view=view, page_size=page_size, @@ -91,47 +89,49 @@ def list_tasks(self, view="MINIMAL", page_size=None, page_token=None): name_prefix=None, project=None ) - msg = req.as_dict() + msg: Dict = req.as_dict() - kwargs = self._request_params(params=msg) - response = requests.get( - "%s/v1/tasks" % (self.url), + kwargs: Dict[str, Any] = self._request_params(params=msg) + response: requests.Response = requests.get( + f"{self.url}/v1/tasks", **kwargs) response.raise_for_status() return unmarshal(response.json(), ListTasksResponse) - def wait(self, task_id, timeout=None): - def check_success(data): + def wait(self, task_id: str, timeout=None) -> Task: + def check_success(data: Task) -> bool: return data.state not in ["QUEUED", "RUNNING", "INITIALIZING"] max_time = time.time() + timeout if timeout else None + response: Optional[Task] = None while True: try: response = self.get_task(task_id, "MINIMAL") except Exception: - response = None + pass if response is not None: if check_success(response): return response - if max_time is not None and time.time() >= max_time: - raise TimeoutError("last_response: %s" % (response.as_dict())) + if max_time is not None and time.time() >= max_time: + raise TimeoutError("last_response: {response.as_dict()}") time.sleep(0.5) - def _request_params(self, data=None, params=None): - kwargs = {'timeout': self.timeout} - + def _request_params( + self, data: Optional[str] = None, + params: Optional[Dict] = None + ) -> Dict[str, Any]: + kwargs: Dict[str, Any] = {} + kwargs['timeout'] = self.timeout + kwargs['headers'] = {} + kwargs['headers']['Content-type'] = 'application/json' + kwargs['auth'] = (self.user, self.password) if data: kwargs['data'] = data if params: kwargs['params'] = params if self.token: - kwargs['headers'] = {'Content-type': 'application/json', - 'Authorization': 'Bearer ' + self.token} - else: - kwargs['headers'] = {'Content-type': 'application/json'} - kwargs['auth'] = (self.user, self.password) - + kwargs['headers']['Authorization'] = f"Bearer {self.token}" return kwargs diff --git a/tes/models.py b/tes/models.py index f0f50c2..30a38f5 100644 --- a/tes/models.py +++ b/tes/models.py @@ -3,17 +3,16 @@ import dateutil.parser import json import os -import six from attr import asdict, attrs, attrib from attr.validators import instance_of, optional, in_ -from builtins import str from datetime import datetime +from typing import Any, Dict, List, Optional, Tuple, Type, Union @attrs class _ListOfValidator(object): - type = attrib() + type: Type = attrib() def __call__(self, inst, attr, value): """ @@ -21,27 +20,20 @@ def __call__(self, inst, attr, value): """ if not all([isinstance(n, self.type) for n in value]): raise TypeError( - "'{name}' must be a list of {type!r} (got {value!r} that is a " - "list of {actual!r})." - .format(name=attr.name, - type=self.type, - actual=value[0].__class__, - value=value), + "'{attr.name}' must be a list of {self.type!r} (got {value!r} " + "that is a list of {values[0].__class__!r}).", attr, self.type, value, ) - def __repr__(self): - return ( - "" - .format(type=self.type) - ) + def __repr__(self) -> str: + return f"" -def list_of(type): - return _ListOfValidator(type) +def list_of(_type: Any) -> _ListOfValidator: + return _ListOfValidator(_type) -def _drop_none(obj): +def _drop_none(obj: Any) -> Any: if isinstance(obj, (list, tuple, set)): return type(obj)(_drop_none(x) for x in obj if x is not None) elif isinstance(obj, dict): @@ -53,13 +45,13 @@ def _drop_none(obj): return obj -def strconv(value): +def strconv(value: Any) -> Any: if isinstance(value, (tuple, list)): - if all([isinstance(n, six.string_types) for n in value]): + if all([isinstance(n, str) for n in value]): return [str(n) for n in value] else: return value - elif isinstance(value, six.string_types): + elif isinstance(value, str): return str(value) else: return value @@ -67,19 +59,19 @@ def strconv(value): # since an int64 value is encoded as a string in json we need to handle # conversion -def int64conv(value): +def int64conv(value: Optional[str]) -> Optional[int]: if value is not None: return int(value) return value -def timestampconv(value): - if isinstance(value, six.string_types): +def timestampconv(value: Optional[str]) -> Optional[datetime]: + if value is not None: return dateutil.parser.parse(value) return value -def datetime_json_handler(x): +def datetime_json_handler(x: Any) -> str: if isinstance(x, datetime): return x.isoformat() raise TypeError("Unknown type") @@ -88,13 +80,13 @@ def datetime_json_handler(x): @attrs class Base(object): - def as_dict(self, drop_empty=True): + def as_dict(self, drop_empty: bool = True) -> Dict[str, Any]: obj = asdict(self) if drop_empty: return _drop_none(obj) return obj - def as_json(self, drop_empty=True): + def as_json(self, drop_empty: bool = True) -> str: return json.dumps( self.as_dict(drop_empty), default=datetime_json_handler @@ -103,197 +95,197 @@ def as_json(self, drop_empty=True): @attrs class Input(Base): - url = attrib( + url: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - path = attrib( + path: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - type = attrib( + type: str = attrib( default="FILE", validator=in_(["FILE", "DIRECTORY"]) ) - name = attrib( + name: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - description = attrib( + description: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - content = attrib( + content: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) @attrs class Output(Base): - url = attrib( + url: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - path = attrib( + path: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - type = attrib( + type: str = attrib( default="FILE", validator=in_(["FILE", "DIRECTORY"]) ) - name = attrib( + name: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - description = attrib( + description: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) @attrs class Resources(Base): - cpu_cores = attrib( + cpu_cores: Optional[int] = attrib( default=None, validator=optional(instance_of(int)) ) - ram_gb = attrib( + ram_gb: Optional[Union[float, int]] = attrib( default=None, validator=optional(instance_of((float, int))) ) - disk_gb = attrib( + disk_gb: Optional[Union[float, int]] = attrib( default=None, validator=optional(instance_of((float, int))) ) - preemptible = attrib( + preemptible: Optional[bool] = attrib( default=None, validator=optional(instance_of(bool)) ) - zones = attrib( + zones: Optional[List[str]] = attrib( default=None, converter=strconv, validator=optional(list_of(str)) ) @attrs class Executor(Base): - image = attrib( + image: str = attrib( converter=strconv, validator=instance_of(str) ) - command = attrib( + command: List[str] = attrib( converter=strconv, validator=list_of(str) ) - workdir = attrib( + workdir: str = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - stdin = attrib( + stdin: str = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - stdout = attrib( + stdout: str = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - stderr = attrib( + stderr: str = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - env = attrib( + env: Optional[Dict] = attrib( default=None, validator=optional(instance_of(dict)) ) @attrs class ExecutorLog(Base): - start_time = attrib( + start_time: datetime = attrib( default=None, converter=timestampconv, validator=optional(instance_of(datetime)) ) - end_time = attrib( + end_time: datetime = attrib( default=None, converter=timestampconv, validator=optional(instance_of(datetime)) ) - stdout = attrib( + stdout: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - stderr = attrib( + stderr: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - exit_code = attrib( + exit_code: Optional[int] = attrib( default=None, validator=optional(instance_of(int)) ) @attrs class OutputFileLog(Base): - url = attrib( + url: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - path = attrib( + path: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - size_bytes = attrib( + size_bytes: Optional[int] = attrib( default=None, converter=int64conv, validator=optional(instance_of(int)) ) @attrs class TaskLog(Base): - start_time = attrib( + start_time: datetime = attrib( default=None, converter=timestampconv, validator=optional(instance_of(datetime)) ) - end_time = attrib( + end_time: datetime = attrib( default=None, converter=timestampconv, validator=optional(instance_of(datetime)) ) - metadata = attrib( + metadata: Optional[Dict] = attrib( default=None, validator=optional(instance_of(dict)) ) - logs = attrib( + logs: Optional[List[ExecutorLog]] = attrib( default=None, validator=optional(list_of(ExecutorLog)) ) - outputs = attrib( + outputs: Optional[List[OutputFileLog]] = attrib( default=None, validator=optional(list_of(OutputFileLog)) ) - system_logs = attrib( + system_logs: Optional[List[str]] = attrib( default=None, validator=optional(list_of(str)) ) @attrs class Task(Base): - id = attrib( + id: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - state = attrib( + state: Optional[str] = attrib( default=None, validator=optional(in_( ["UNKNOWN", "QUEUED", "INITIALIZING", "RUNNING", "COMPLETE", "CANCELED", "EXECUTOR_ERROR", "SYSTEM_ERROR"] )) ) - name = attrib( + name: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - description = attrib( + description: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - inputs = attrib( + inputs: Optional[List[Input]] = attrib( default=None, validator=optional(list_of(Input)) ) - outputs = attrib( + outputs: Optional[List[Output]] = attrib( default=None, validator=optional(list_of(Output)) ) - resources = attrib( + resources: Optional[Resources] = attrib( default=None, validator=optional(instance_of(Resources)) ) - executors = attrib( + executors: Optional[List[Executor]] = attrib( default=None, validator=optional(list_of(Executor)) ) - volumes = attrib( + volumes: Optional[List[str]] = attrib( default=None, validator=optional(list_of(str)) ) - tags = attrib( + tags: Optional[Dict] = attrib( default=None, validator=optional(instance_of(dict)) ) - logs = attrib( + logs: Optional[List[TaskLog]] = attrib( default=None, validator=optional(list_of(TaskLog)) ) - creation_time = attrib( + creation_time: datetime = attrib( default=None, converter=timestampconv, validator=optional(instance_of(datetime)) ) - def is_valid(self): + def is_valid(self) -> Tuple[bool, Union[None, TypeError]]: errs = [] if self.executors is None or len(self.executors) == 0: errs.append("Must provide one or more Executors") @@ -313,7 +305,7 @@ def is_valid(self): if not os.path.isabs(e.stderr): errs.append("Executor stderr must be an absolute path") if e.env is not None: - for k, v in self.executors.env: + for k, v in e.env: if not isinstance(k, str) and not isinstance(k, str): errs.append( "Executor env keys and values must be StrType" @@ -336,7 +328,7 @@ def is_valid(self): errs.append("Output url must be provided") if o.path is None: errs.append("Output path must be provided") - elif not os.path.isabs(i.path): + elif not os.path.isabs(o.path): errs.append("Output path must be absolute") if self.volumes is not None: @@ -360,17 +352,17 @@ def is_valid(self): @attrs class GetTaskRequest(Base): - id = attrib( + id: str = attrib( converter=strconv, validator=instance_of(str) ) - view = attrib( + view: Optional[str] = attrib( default=None, validator=optional(in_(["MINIMAL", "BASIC", "FULL"])) ) @attrs class CreateTaskResponse(Base): - id = attrib( + id: str = attrib( converter=strconv, validator=instance_of(str) ) @@ -382,20 +374,20 @@ class ServiceInfoRequest(Base): @attrs class ServiceInfo(Base): - name = attrib( + name: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - doc = attrib( + doc: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - storage = attrib( + storage: Optional[List[str]] = attrib( default=None, converter=strconv, validator=optional(list_of(str)) ) @attrs class CancelTaskRequest(Base): - id = attrib( + id: str = attrib( converter=strconv, validator=instance_of(str) ) @@ -407,28 +399,28 @@ class CancelTaskResponse(Base): @attrs class ListTasksRequest(Base): - project = attrib( + project: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - name_prefix = attrib( + name_prefix: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - page_size = attrib( + page_size: Optional[int] = attrib( default=None, validator=optional(instance_of(int)) ) - page_token = attrib( + page_token: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) - view = attrib( + view: Optional[str] = attrib( default=None, validator=optional(in_(["MINIMAL", "BASIC", "FULL"])) ) @attrs class ListTasksResponse(Base): - tasks = attrib( + tasks: Optional[List[Task]] = attrib( default=None, validator=optional(list_of(Task)) ) - next_page_token = attrib( + next_page_token: Optional[str] = attrib( default=None, converter=strconv, validator=optional(instance_of(str)) ) diff --git a/tes/utils.py b/tes/utils.py index dfec1ac..199543b 100644 --- a/tes/utils.py +++ b/tes/utils.py @@ -1,8 +1,8 @@ -from __future__ import absolute_import, print_function, unicode_literals - import json import re +from typing import Any, Dict, Type + from tes.models import (Task, Input, Output, Resources, Executor, TaskLog, ExecutorLog, OutputFileLog) @@ -11,7 +11,7 @@ all_cap_re = re.compile('([a-z0-9])([A-Z])') -def camel_to_snake(name): +def camel_to_snake(name: str) -> str: s1 = first_cap_re.sub(r'\1_\2', name) return all_cap_re.sub(r'\1_\2', s1).lower() @@ -26,15 +26,17 @@ def __init__(self, *args, **kwargs): Exception.__init__(self, *args, **kwargs) -def unmarshal(j, o, convert_camel_case=True): +def unmarshal(j: Any, o: Type, convert_camel_case=True) -> Any: if isinstance(j, str): m = json.loads(j) elif isinstance(j, dict): m = j + elif j is None: + return None else: - raise TypeError("j must be a str or dict") + raise TypeError("j must be a str, a dict or None") - d = {} + d: Dict[str, Any] = {} if convert_camel_case: for k, v in m.items(): d[camel_to_snake(k)] = v @@ -61,7 +63,7 @@ def unmarshal(j, o, convert_camel_case=True): } } - def _unmarshal(v, obj): + def _unmarshal(v: Any, obj: Type) -> Any: if isinstance(v, list): field = [] for item in v: diff --git a/tests/requirements.txt b/tests/requirements.txt index b361eb6..45208ca 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,5 +1,5 @@ -coverage==4.5.4 -coveralls>=1.1 -flake8>=3.3.0 -nose>=1.3.7 -requests_mock>=1.3.0 +coverage>=6.5.0 +coveralls>=3.3.1 +flake8>=5.0.4 +pytest>=7.2.1 +requests_mock>=1.10.0 From c30424c89057a698a0e8687c5c96c67cdcc53b11 Mon Sep 17 00:00:00 2001 From: Alex Kanitz Date: Thu, 9 Feb 2023 22:03:17 +0100 Subject: [PATCH 2/3] feat: pass through json serialization options --- tes/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tes/models.py b/tes/models.py index 30a38f5..8550291 100644 --- a/tes/models.py +++ b/tes/models.py @@ -86,10 +86,11 @@ def as_dict(self, drop_empty: bool = True) -> Dict[str, Any]: return _drop_none(obj) return obj - def as_json(self, drop_empty: bool = True) -> str: + def as_json(self, drop_empty: bool = True, **kwargs) -> str: return json.dumps( self.as_dict(drop_empty), - default=datetime_json_handler + default=datetime_json_handler, + **kwargs ) From 1abde51e63c2254714fbfa1588953071d8266af6 Mon Sep 17 00:00:00 2001 From: Alex Kanitz Date: Thu, 9 Feb 2023 23:33:43 +0100 Subject: [PATCH 3/3] docs: add advanced usage examples --- README.md | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 62110a7..356cb5c 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,10 @@ pip install py-tes ### Example -``` +```python import tes +# define task task = tes.Task( executors=[ tes.Executor( @@ -30,8 +31,123 @@ task = tes.Task( ] ) -cli = tes.HTTPClient("http://funnel.example.com", timeout=5) +# create client +cli = tes.HTTPClient("https://funnel.example.com", timeout=5) + +# access endpoints +service_info = cli.get_service_info() task_id = cli.create_task(task) -res = cli.get_task(task_id) +task_info = cli.get_task(task_id, view="BASIC") cli.cancel_task(task_id) +tasks_list = cli.list_tasks(view="MINIMAL") # default view +``` + +### How to... + +> Makes use of the objects above... + +#### ...export a model to a dictionary + +```python +task_dict = task.as_dict(drop_empty=False) +``` + +`task_dict` contents: + +```console +{'id': None, 'state': None, 'name': None, 'description': None, 'inputs': None, 'outputs': None, 'resources': None, 'executors': [{'image': 'alpine', 'command': ['echo', 'hello'], 'workdir': None, 'stdin': None, 'stdout': None, 'stderr': None, 'env': None}], 'volumes': None, 'tags': None, 'logs': None, 'creation_time': None} +``` + +#### ...export a model to JSON + +```python +task_json = task.as_json() # also accepts `drop_empty` arg +``` + +`task_json` contents: + +```console +{"executors": [{"image": "alpine", "command": ["echo", "hello"]}]} +``` + +#### ...pretty print a model + +```python +print(task.as_json(indent=3)) # keyword args are passed to `json.dumps()` +``` + +Output: + +```json +{ + "executors": [ + { + "image": "alpine", + "command": [ + "echo", + "hello" + ] + } + ] +} +``` + +#### ...access a specific task from the task list + +```python +specific_task = tasks_list.tasks[5] +``` + +`specific_task` contents: + +```console +Task(id='393K43', state='COMPLETE', name=None, description=None, inputs=None, outputs=None, resources=None, executors=None, volumes=None, tags=None, logs=None, creation_time=None) +``` + +#### ...iterate over task list items + +```python +for t in tasks_list[:3]: + print(t.as_json(indent=3)) +``` + +Output: + +```console +{ + "id": "task_A2GFS4", + "state": "RUNNING" +} +{ + "id": "task_O8G1PZ", + "state": "CANCELED" +} +{ + "id": "task_W246I6", + "state": "COMPLETE" +} +``` + +#### ...instantiate a model from a JSON representation + +```python +task_from_json = tes.client.unmarshal(task_json, tes.Task) +``` + +`task_from_json` contents: + +```console +Task(id=None, state=None, name=None, description=None, inputs=None, outputs=None, resources=None, executors=[Executor(image='alpine', command=['echo', 'hello'], workdir=None, stdin=None, stdout=None, stderr=None, env=None)], volumes=None, tags=None, logs=None, creation_time=None) +``` + +Which is equivalent to `task`: + +```python +print(task_from_json == task) +``` + +Output: + +```console +True ```