├── .github └── FUNDING.yml ├── README.md ├── pythonista_module_version_async.py ├── pythonista_module_versions.py ├── pythonista_undocumented.py └── requirements.txt /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [cclauss] 2 | patreon: cclauss 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pythonista-module-versions 2 | ========================== 3 | 4 | ![alt text](https://img.shields.io/badge/Python-2.7.12-blue.svg "Python 2.7.12") 5 | ![alt text](https://img.shields.io/badge/Python-3.6.1-blue.svg "Python 3.6.1") 6 | 7 | Compare the version numbers of extra modules in Pythonista with PyPI. 8 | 9 | Results: 10 | * https://pyup.io/account/repos/github/cclauss/pythonista-module-versions/ 11 | * https://requires.io/github/cclauss/pythonista-module-versions/requirements/ 12 | 13 | ``` 14 | Pythonista version 3.1.1 (311016) running Python 3.6.1 on iOS 10.3.3 on an iPad3,4. 15 | ========================================================= 16 | | module | local | PyPI | 17 | | name | version | version | 18 | | ------------- | ----------- | ----------- | 19 | | arrow | 0.10.0 | 0.10.0 | 20 | | bottle | 0.12.5 | 0.12.13 | Upgrade? 21 | | bs4 | 4.4.1 | 4.6.0 | Upgrade? 22 | | certifi | 2016.02.28 | 2017.11.5 | Upgrade? 23 | | Crypto | 2.6 | 2.6.1 | Upgrade? 24 | | cycler | 0.9.0 | 0.10.0 | Upgrade? 25 | | dateutil | 2.2 | 2.2 | 26 | | dropbox | 6.4.0 | 8.4.1 | Upgrade? 27 | | ecdsa | UNKNOWN | 0.13 | Upgrade? 28 | | et_xmlfile | 1.0.1 | 1.0.1 | 29 | | evernote | ????? | 1.25.2 | ????? 30 | | faker | ????? | 0.8.6 | ????? 31 | | feedparser | 5.2.1 | 5.2.1 | 32 | | flask | 0.10.1 | 0.12.2 | Upgrade? 33 | | google | ????? | 1.9.3 | ????? 34 | | html2text | 2014.4.5 | 2017.10.4 | Upgrade? 35 | | html5lib | 0.999 | 0.999999999 | Upgrade? 36 | | httplib2 | 0.9.2 | 0.10.3 | Upgrade? 37 | | images2gif | ????? | 1.0.1 | ????? 38 | | itsdangerous | ????? | 0.24 | ????? 39 | | jdcal | 1.3 | 1.3 | 40 | | jedi | 0.9.0 | 0.11.0 | Upgrade? 41 | | jinja2 | 2.7 | 2.10 | Upgrade? 42 | | libmodernize | 0.5 | Found | Upgrade? 43 | ### hasattr(markdown, 'version') 44 | | markdown | 2.6.2 | 2.6.9 | Upgrade? 45 | | markdown2 | 2.2.1 | 2.3.5 | Upgrade? 46 | | markupsafe | ????? | 1.0 | ????? 47 | | matplotlib | 1.4.0 | 2.1.0 | Upgrade? 48 | | mccabe | 0.4.0 | 0.6.1 | Upgrade? 49 | | midiutil | ????? | 1.1.3 | ????? 50 | | mpl_toolkits | ????? | Found | ????? 51 | | mpmath | 0.18 | 1.0.0 | Upgrade? 52 | | numpy | 1.8.0 | 1.13.3 | Upgrade? 53 | | oauth2 | 1.9.0.post1 | 1.9.0.post1 | 54 | | paramiko | 1.16.0 | 2.3.1 | Upgrade? 55 | | parsedatetime | 1.5 | 2.4 | Upgrade? 56 | ### hasattr(PIL, 'PILLOW_VERSION') 57 | | PIL | 2.9.0 | 4.3.0 | Upgrade? 58 | | pycparser | 2.10 | 2.18 | Upgrade? 59 | | pyflakes | 1.5.0 | 1.6.0 | Upgrade? 60 | | pygments | 2.1 | 2.2.0 | Upgrade? 61 | | pylab | ????? | Found | ????? 62 | | pyparsing | 2.0.1 | 2.2.0 | Upgrade? 63 | | PyPDF2 | 1.22 | 1.26.0 | Upgrade? 64 | | pytest | 3.0.5 | 3.2.3 | Upgrade? 65 | | pytz | 2015.7 | 2017.3 | Upgrade? 66 | | qrcode | ????? | 5.3 | ????? 67 | ### hasattr(reportlab, 'Version') 68 | | reportlab | 3.1.8 | 3.4.0 | Upgrade? 69 | | requests | 2.9.1 | 2.18.4 | Upgrade? 70 | | sgmllib | ????? | Found | ????? 71 | | simpy | 3.0.8 | 3.0.10 | Upgrade? 72 | | six | 1.6.1 | 1.11.0 | Upgrade? 73 | | sqlalchemy | 0.9.7 | 1.2.0b3 | Upgrade? 74 | ### hasattr(sqlite3, 'version') 75 | | sqlite3 | 2.6.0 | 2.8.3 | Upgrade? 76 | | sympy | 0.7.4.1 | 1.1.1 | Upgrade? 77 | | thrift | ????? | 0.10.0 | ????? 78 | | turtle | ????? | 0.0.2 | ????? 79 | | twitter | ????? | 1.18.0 | ????? 80 | | wavebender | 0.3 | Found | Upgrade? 81 | | werkzeug | 0.9.4 | 0.12.2 | Upgrade? 82 | | wsgiref | ????? | 0.1.2 | ????? 83 | | xmltodict | 0.8.7 | 0.11.0 | Upgrade? 84 | | yaml | 3.11 | 3.12 | Upgrade? 85 | | yapf | 0.16.1 | 0.19.0 | Upgrade? 86 | | ------------- | ----------- | ----------- | 87 | 88 | 89 | Pythonista version 3.1.1 (311016) running Python 2.7.12 on iOS 10.3.3 on an iPad3,4. 90 | ========================================================= 91 | | module | local | PyPI | 92 | | name | version | version | 93 | | ------------- | ----------- | ----------- | 94 | | arrow | 0.10.0 | 0.10.0 | 95 | | bottle | 0.12.5 | 0.12.13 | Upgrade? 96 | | bs4 | 4.3.2 | 4.6.0 | Upgrade? 97 | | Crypto | 2.6 | 2.6.1 | Upgrade? 98 | | dateutil | 2.2 | 2.2 | 99 | | dropbox | 6.4.0 | 8.4.1 | Upgrade? 100 | | ecdsa | 0.11 | 0.13 | Upgrade? 101 | | et_xmlfile | 1.0.1 | 1.0.1 | 102 | | evernote | ????? | 1.25.2 | ????? 103 | | faker | ????? | 0.8.6 | ????? 104 | | feedparser | 5.1.3 | 5.2.1 | Upgrade? 105 | | flask | 0.10.1 | 0.12.2 | Upgrade? 106 | | google | ????? | 1.9.3 | ????? 107 | | html2text | 2014.4.5 | 2017.10.4 | Upgrade? 108 | | html5lib | 0.999 | 0.999999999 | Upgrade? 109 | | httplib2 | 0.8 | 0.10.3 | Upgrade? 110 | | images2gif | ????? | 1.0.1 | ????? 111 | | itsdangerous | ????? | 0.24 | ????? 112 | | jdcal | 1.3 | 1.3 | 113 | | jedi | 0.9.0 | 0.11.0 | Upgrade? 114 | | jinja2 | 2.7 | 2.10 | Upgrade? 115 | ### hasattr(markdown, 'version') 116 | | markdown | 2.2.0 | 2.6.9 | Upgrade? 117 | | markdown2 | 2.2.1 | 2.3.5 | Upgrade? 118 | | markupsafe | ????? | 1.0 | ????? 119 | | matplotlib | 1.4.0 | 2.1.0 | Upgrade? 120 | | midiutil | ????? | 1.1.3 | ????? 121 | | mpl_toolkits | ????? | Found | ????? 122 | | mpmath | 0.18 | 1.0.0 | Upgrade? 123 | | numpy | 1.8.0 | 1.13.3 | Upgrade? 124 | | oauth2 | 1.5.211 | 1.9.0.post1 | Upgrade? 125 | | paramiko | 1.16.0 | 2.3.1 | Upgrade? 126 | | parsedatetime | 1.3 | 2.4 | Upgrade? 127 | ### hasattr(PIL, 'PILLOW_VERSION') 128 | | PIL | 2.9.0 | 4.3.0 | Upgrade? 129 | | pycparser | 2.10 | 2.18 | Upgrade? 130 | | pyflakes | 1.5.0 | 1.6.0 | Upgrade? 131 | | pygments | 1.6 | 2.2.0 | Upgrade? 132 | | pylab | ????? | Found | ????? 133 | | pyparsing | 2.0.1 | 2.2.0 | Upgrade? 134 | | PyPDF2 | 1.22 | 1.26.0 | Upgrade? 135 | | pytest | 3.0.5 | 3.2.3 | Upgrade? 136 | | pytz | 2013b | 2017.3 | Upgrade? 137 | | qrcode | ????? | 5.3 | ????? 138 | ### hasattr(reportlab, 'Version') 139 | | reportlab | 3.1.8 | 3.4.0 | Upgrade? 140 | | requests | 2.5.1 | 2.18.4 | Upgrade? 141 | | sgmllib | ????? | Found | ????? 142 | | simpy | 3.0.2 | 3.0.10 | Upgrade? 143 | | six | 1.6.1 | 1.11.0 | Upgrade? 144 | | sqlalchemy | 0.9.7 | 1.2.0b3 | Upgrade? 145 | ### hasattr(sqlite3, 'version') 146 | | sqlite3 | 2.6.0 | 2.8.3 | Upgrade? 147 | | sympy | 0.7.4.1 | 1.1.1 | Upgrade? 148 | | thrift | ????? | 0.10.0 | ????? 149 | | turtle | ????? | 0.0.2 | ????? 150 | | twitter | ????? | 1.18.0 | ????? 151 | | wavebender | 0.3 | Found | Upgrade? 152 | | werkzeug | 0.9.4 | 0.12.2 | Upgrade? 153 | | wsgiref | ????? | 0.1.2 | ????? 154 | | xmltodict | 0.8.7 | 0.11.0 | Upgrade? 155 | | yaml | 3.09 | 3.12 | Upgrade? 156 | | yapf | 0.16.1 | 0.19.0 | Upgrade? 157 | | ------------- | ----------- | ----------- | 158 | 159 | 160 | Starting GitHub Action for pyup Safety:safety command 161 | Warning: unpinned requirement 'ecdsa' found in requirements.txt, unable to check. 162 | Warning: unpinned requirement 'evernote' found in requirements.txt, unable to check. 163 | Warning: unpinned requirement 'faker' found in requirements.txt, unable to check. 164 | Warning: unpinned requirement 'google' found in requirements.txt, unable to check. 165 | Warning: unpinned requirement 'images2gif' found in requirements.txt, unable to check. 166 | Warning: unpinned requirement 'itsdangerous' found in requirements.txt, unable to check. 167 | Warning: unpinned requirement 'markupsafe' found in requirements.txt, unable to check. 168 | Warning: unpinned requirement 'midiutil' found in requirements.txt, unable to check. 169 | Warning: unpinned requirement 'mpl_toolkits' found in requirements.txt, unable to check. 170 | Warning: unpinned requirement 'pylab' found in requirements.txt, unable to check. 171 | Warning: unpinned requirement 'qrcode' found in requirements.txt, unable to check. 172 | Warning: unpinned requirement 'sgmllib' found in requirements.txt, unable to check. 173 | Warning: unpinned requirement 'thrift' found in requirements.txt, unable to check. 174 | Warning: unpinned requirement 'turtle' found in requirements.txt, unable to check. 175 | Warning: unpinned requirement 'twitter' found in requirements.txt, unable to check. 176 | Warning: unpinned requirement 'wsgiref' found in requirements.txt, unable to check. 177 | ╒══════════════════════════════════════════════════════════════════════════════╕ 178 | │ │ 179 | │ /$$$$$$ /$$ │ 180 | │ /$$__ $$ | $$ │ 181 | │ /$$$$$$$ /$$$$$$ | $$ \__//$$$$$$ /$$$$$$ /$$ /$$ │ 182 | │ /$$_____/ |____ $$| $$$$ /$$__ $$|_ $$_/ | $$ | $$ │ 183 | │ | $$$$$$ /$$$$$$$| $$_/ | $$$$$$$$ | $$ | $$ | $$ │ 184 | │ \____ $$ /$$__ $$| $$ | $$_____/ | $$ /$$| $$ | $$ │ 185 | │ /$$$$$$$/| $$$$$$$| $$ | $$$$$$$ | $$$$/| $$$$$$$ │ 186 | │ |_______/ \_______/|__/ \_______/ \___/ \____ $$ │ 187 | │ /$$ | $$ │ 188 | │ | $$$$$$/ │ 189 | │ by pyup.io \______/ │ 190 | │ │ 191 | ╞══════════════════════════════════════════════════════════════════════════════╡ 192 | │ REPORT │ 193 | │ checked 46 packages, using default DB │ 194 | ╞════════════════════════════╤═══════════╤══════════════════════════╤══════════╡ 195 | │ package │ installed │ affected │ ID │ 196 | ╞════════════════════════════╧═══════════╧══════════════════════════╧══════════╡ 197 | │ bottle │ 0.12.5 │ <0.12.10 │ 25642 │ 198 | ╞══════════════════════════════════════════════════════════════════════════════╡ 199 | │ redirect() in bottle.py in bottle 0.12.10 doesn't filter a "\r\n" sequence, │ 200 | │ which leads to a CRLF attack, as demonstrated by a redirect("233\r\nSet- │ 201 | │ Cookie: name=salt") call. │ 202 | ╞══════════════════════════════════════════════════════════════════════════════╡ 203 | │ bottle │ 0.12.5 │ >=0.12,<0.12.6 │ 35548 │ 204 | ╞══════════════════════════════════════════════════════════════════════════════╡ 205 | │ Bottle 0.10.x before 0.10.12, 0.11.x before 0.11.7, and 0.12.x before 0.12.6 │ 206 | │ does not properly limit content types, which allows remote attackers to │ 207 | │ bypass intended access restrictions via an accepted Content-Type followed by │ 208 | │ a ; (semi-colon) and a Content-Type that would not be accepted, as │ 209 | │ demonstrated in YouCompleteMe to execute arbitrary code. │ 210 | ╞══════════════════════════════════════════════════════════════════════════════╡ 211 | │ flask │ 0.10.1 │ <0.12.3 │ 36388 │ 212 | ╞══════════════════════════════════════════════════════════════════════════════╡ 213 | │ flask version Before 0.12.3 contains a CWE-20: Improper Input Validation │ 214 | │ vulnerability in flask that can result in Large amount of memory usage │ 215 | │ possibly leading to denial of service. This attack appear to be exploitable │ 216 | │ via Attacker provides JSON data in incorrect encoding. This vulnerability │ 217 | │ appears to have been fixed in 0.12.3. │ 218 | ╞══════════════════════════════════════════════════════════════════════════════╡ 219 | │ html5lib │ 0.999 │ <0.99999999 │ 35693 │ 220 | ╞══════════════════════════════════════════════════════════════════════════════╡ 221 | │ The serializer in html5lib before 0.99999999 might allow remote attackers to │ 222 | │ conduct cross-site scripting (XSS) attacks by leveraging mishandling of the │ 223 | │ < (less than) character in attribute values. │ 224 | ╞══════════════════════════════════════════════════════════════════════════════╡ 225 | │ html5lib │ 0.999 │ <0.99999999 │ 35694 │ 226 | ╞══════════════════════════════════════════════════════════════════════════════╡ 227 | │ The serializer in html5lib before 0.99999999 might allow remote attackers to │ 228 | │ conduct cross-site scripting (XSS) attacks by leveraging mishandling of │ 229 | │ special characters in attribute values, a different vulnerability than │ 230 | │ CVE-2016-9909. │ 231 | ╞══════════════════════════════════════════════════════════════════════════════╡ 232 | │ html5lib │ 0.999 │ <0.99999999 │ 25846 │ 233 | ╞══════════════════════════════════════════════════════════════════════════════╡ 234 | │ html5lib before 0.99999999 is vulnerable to a XSS attack. Upgrading avoids │ 235 | │ the XSS bug potentially caused by serializer allowing attribute values to be │ 236 | │ escaped out of in old browser versions, changing the quote_attr_values │ 237 | │ option on serializer to take one of three values, "always" (the old True │ 238 | │ value), "legacy" (the new option, and the new default), and "spec" (the old │ 239 | │ False value, and the old default). │ 240 | ╞══════════════════════════════════════════════════════════════════════════════╡ 241 | │ httplib2 │ 0.9.2 │ <=0.9.2 │ 25848 │ 242 | ╞══════════════════════════════════════════════════════════════════════════════╡ 243 | │ httplib2 before and including 0.9.2 on "SSL certificate hostname mismatch" │ 244 | │ it is checked only once: https://github.com/httplib2/httplib2/issues/5 │ 245 | ╞══════════════════════════════════════════════════════════════════════════════╡ 246 | │ jinja2 │ 2.7 │ <2.7.2 │ 25865 │ 247 | ╞══════════════════════════════════════════════════════════════════════════════╡ 248 | │ jinja2 2.7.2 fixes a security issue: Changed the default folder for the │ 249 | │ filesystem cache to be user specific and read and write protected on UNIX │ 250 | │ systems. See http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=734747 for │ 251 | │ more information. │ 252 | ╞══════════════════════════════════════════════════════════════════════════════╡ 253 | │ jinja2 │ 2.7 │ <2.7.3 │ 25866 │ 254 | ╞══════════════════════════════════════════════════════════════════════════════╡ 255 | │ The default configuration for bccache.FileSystemBytecodeCache in Jinja2 │ 256 | │ before 2.7.2 does not properly create temporary files, which allows local │ 257 | │ users to gain privileges via a crafted .cache file with a name starting with │ 258 | │ __jinja2_ in /tmp. │ 259 | ╞══════════════════════════════════════════════════════════════════════════════╡ 260 | │ markdown2 │ 2.2.1 │ <2.3.5 │ 35760 │ 261 | ╞══════════════════════════════════════════════════════════════════════════════╡ 262 | │ An issue was discovered in markdown2 (aka python-markdown2) through 2.3.5. │ 263 | │ The safe_mode feature, which is supposed to sanitize user input against XSS, │ 264 | │ is flawed and does not escape the input properly. With a crafted payload, │ 265 | │ XSS can be triggered, as demonstrated by omitting the final '>' character │ 266 | │ from an IMG tag. │ 267 | ╞══════════════════════════════════════════════════════════════════════════════╡ 268 | │ pillow │ 2.9.0 │ <3.1.1 │ 33134 │ 269 | ╞══════════════════════════════════════════════════════════════════════════════╡ 270 | │ Buffer overflow in the ImagingLibTiffDecode function in │ 271 | │ libImaging/TiffDecode.c in Pillow before 3.1.1 allows remote attackers to │ 272 | │ overwrite memory via a crafted TIFF file. │ 273 | ╞══════════════════════════════════════════════════════════════════════════════╡ 274 | │ pillow │ 2.9.0 │ <3.1.1 │ 33135 │ 275 | ╞══════════════════════════════════════════════════════════════════════════════╡ 276 | │ Buffer overflow in the ImagingFliDecode function in libImaging/FliDecode.c │ 277 | │ in Pillow before 3.1.1 allows remote attackers to cause a denial of service │ 278 | │ (crash) via a crafted FLI file. │ 279 | ╞══════════════════════════════════════════════════════════════════════════════╡ 280 | │ pillow │ 2.9.0 │ <3.1.1 │ 33136 │ 281 | ╞══════════════════════════════════════════════════════════════════════════════╡ 282 | │ Buffer overflow in the ImagingPcdDecode function in PcdDecode.c in Pillow │ 283 | │ before 3.1.1 and Python Imaging Library (PIL) 1.1.7 and earlier allows │ 284 | │ remote attackers to cause a denial of service (crash) via a crafted PhotoCD │ 285 | │ file. │ 286 | ╞══════════════════════════════════════════════════════════════════════════════╡ 287 | │ pillow │ 2.9.0 │ <3.1.1 │ 33137 │ 288 | ╞══════════════════════════════════════════════════════════════════════════════╡ 289 | │ Integer overflow in the ImagingResampleHorizontal function in │ 290 | │ libImaging/Resample.c in Pillow before 3.1.1 allows remote attackers to have │ 291 | │ unspecified impact via negative values of the new size, which triggers a │ 292 | │ heap-based buffer overflow. │ 293 | ╞══════════════════════════════════════════════════════════════════════════════╡ 294 | │ pillow │ 2.9.0 │ <3.1.2 │ 25943 │ 295 | ╞══════════════════════════════════════════════════════════════════════════════╡ 296 | │ pillow before 3.1.2 is vulnerable to an integer overflow in Jpeg2KEncode.c │ 297 | │ causing a buffer overflow. CVE-2016-3076. │ 298 | ╞══════════════════════════════════════════════════════════════════════════════╡ 299 | │ pillow │ 2.9.0 │ <3.3.2 │ 33138 │ 300 | ╞══════════════════════════════════════════════════════════════════════════════╡ 301 | │ Pillow before 3.3.2 allows context-dependent attackers to execute arbitrary │ 302 | │ code by using the "crafted image file" approach, related to an "Insecure │ 303 | │ Sign Extension" issue affecting the ImagingNew in Storage.c component. │ 304 | ╞══════════════════════════════════════════════════════════════════════════════╡ 305 | │ pillow │ 2.9.0 │ <3.3.2 │ 33139 │ 306 | ╞══════════════════════════════════════════════════════════════════════════════╡ 307 | │ Pillow before 3.3.2 allows context-dependent attackers to obtain sensitive │ 308 | │ information by using the "crafted image file" approach, related to an │ 309 | │ "Integer Overflow" issue affecting the Image.core.map_buffer in map.c │ 310 | │ component. │ 311 | ╞══════════════════════════════════════════════════════════════════════════════╡ 312 | │ requests │ 2.9.1 │ <=2.19.1 │ 36546 │ 313 | ╞══════════════════════════════════════════════════════════════════════════════╡ 314 | │ The Requests package before 2.19.1 sends an HTTP Authorization header to an │ 315 | │ http URI upon receiving a same-hostname https-to-http redirect, which makes │ 316 | │ it easier for remote attackers to discover credentials by sniffing the │ 317 | │ network. │ 318 | ╞══════════════════════════════════════════════════════════════════════════════╡ 319 | │ werkzeug │ 0.9.4 │ <0.11.11 │ 35661 │ 320 | ╞══════════════════════════════════════════════════════════════════════════════╡ 321 | │ Cross-site scripting (XSS) vulnerability in the render_full function in │ 322 | │ debug/tbtools.py in the debugger in Pallets Werkzeug before 0.11.11 (as used │ 323 | │ in Pallets Flask and other products) allows remote attackers to inject │ 324 | │ arbitrary web script or HTML via a field that contains an exception message. │ 325 | ╘══════════════════════════════════════════════════════════════════════════════╛ 326 | ``` 327 | -------------------------------------------------------------------------------- /pythonista_module_version_async.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 # MUST be Python 3.5 or later 2 | # coding: utf-8 3 | 4 | import asyncio, bs4, importlib, os, platform, plistlib, requests, sys, time #, pkgutil 5 | 6 | # Translate from Python module --> PyPI module name 7 | pypi_dict = { 'bs4' : 'beautifulsoup4', 8 | 'dateutil' : 'py-dateutil', 9 | 'faker' : 'Faker', 10 | 'sqlite3' : 'pysqlite', 11 | 'yaml' : 'PyYAML', 12 | 'xhtml2pdf' : 'pisa', 13 | 'Crypto' : 'pycrypto', 14 | 'PIL' : 'Pillow' } 15 | 16 | modules = '''bottle bs4 Crypto dateutil dropbox ecdsa evernote faker feedparser 17 | flask html2text html5lib httplib2 itsdangerous jedi jinja2 18 | markdown markdown2 matplotlib mpmath numpy oauth2 paramiko 19 | parsedatetime PIL pycparser pyflakes pygments pyparsing PyPDF2 20 | pytz qrcode reportlab requests simpy six sqlalchemy sqlite3 21 | thrift werkzeug wsgiref xmltodict yaml'''.split() 22 | # modules = ['requests'] 23 | 24 | ## Removed: mechanize midiutil screenplain xhtml2pdf sympy 25 | 26 | def get_module_version(in_module_name='requests'): 27 | mod = importlib.import_module(in_module_name) 28 | fmt = "### hasattr({}, '{}')".format(in_module_name, '{}') 29 | for attr_name in '__version__ version __VERSION__ PILLOW_VERSION VERSION'.split(): 30 | if in_module_name == 'markdown' and attr_name == '__version__': 31 | continue 32 | if in_module_name == 'reportlab': 33 | attr_name = 'Version' 34 | if hasattr(mod, attr_name): 35 | if attr_name != '__version__': 36 | print(fmt.format(attr_name)) 37 | the_attr = getattr(mod, attr_name) 38 | #if isinstance(the_attr, tuple): # mechanize workaround 39 | # the_attr = '.'.join([str(i) for i in the_attr[:3]]) 40 | return str(the_attr() if callable(the_attr) else the_attr) 41 | return '?' * 5 42 | 43 | def get_module_version_from_pypi(module_name='bs4'): 44 | module_name = pypi_dict.get(module_name, module_name) 45 | url = 'https://pypi.python.org/pypi/{}'.format(module_name) 46 | soup = bs4.BeautifulSoup(requests.get(url).content, 'html5lib') 47 | vers_str = soup.title.string.partition(':')[0].split()[-1] 48 | if vers_str == 'Packages': 49 | return soup.find('div', class_='section').a.string.split()[-1] 50 | return vers_str 51 | 52 | async def fetch_one(url): 53 | return requests.get(url).content 54 | 55 | async def get_module_version_from_pypi_async(module_name='bs4'): 56 | # print(0) 57 | module_name = pypi_dict.get(module_name, module_name) 58 | # print(1) 59 | url = 'https://pypi.python.org/pypi/{}'.format(module_name) 60 | # print(2) 61 | content = await fetch_one(url) 62 | soup = bs4.BeautifulSoup(content, 'html5lib') 63 | # print(3) 64 | vers_str = soup.title.string.partition(':')[0].split()[-1] 65 | if vers_str == 'Packages': 66 | return soup.find('div', class_='section').a.string.split()[-1] 67 | return vers_str 68 | 69 | ''' 70 | for _, pkg_name, _ in pkgutil.walk_packages(): 71 | #print(pkg) 72 | #pkg_name = pkg[1] 73 | if 'Gist Commit' in pkg_name: 74 | sys.exit(pkg_name) 75 | if '.' in pkg_name: 76 | continue 77 | '#'' 78 | if ('ctypes.test.test' in pkg_name 79 | or 'unittest.__main__' in pkg_name 80 | or 'numpy.ma.version' in pkg_name 81 | or 'numpy.testing.print_coercion_tables' in pkg_name 82 | or 'sympy.mpmath.libmp.exec_py3' in pkg_name 83 | or 'pycparser._build_tables' in pkg_name 84 | or 'FileBrowser' in pkg_name): 85 | continue 86 | '#'' 87 | #if pkg_name not in ['test_blasdot', 'nose']: 88 | with open('versions.txt', 'w') as out_file: 89 | out_file.write(pkg_name) 90 | #print(pkg_name) 91 | try: 92 | mod_vers = str(get_module_version(pkg_name)).strip('?') 93 | if mod_vers: 94 | print('{:<10} {}'.format(pkg_name, mod_vers)) 95 | except (ImportError, ValueError) as e: 96 | print('{:<10} {}'.format(pkg_name, e)) 97 | print('=' * 16) 98 | ''' 99 | 100 | def pythonista_version(): 101 | plist = plistlib.readPlist(os.path.abspath(os.path.join(sys.executable, '..', 'Info.plist'))) 102 | return '{CFBundleShortVersionString} ({CFBundleVersion})'.format(**plist) 103 | 104 | print('```') # start the output with a markdown literal 105 | fmt = 'Pythonista version {0} running Python {1} on iOS {2} on an {4}.' 106 | print(fmt.format(pythonista_version(), platform.python_version(), *platform.mac_ver())) 107 | print('=' * 57) 108 | 109 | def get_modules_versions_sync(modules): 110 | return {module: get_module_version_from_pypi(module) for module in modules} 111 | 112 | async def get_modules_versions_async(modules): 113 | tasks = [get_module_version_from_pypi_async(module) for module in modules] 114 | print('{} tasks are ready to run in {} seconds'.format(len(tasks), time.time() - start)) 115 | loop = asyncio.get_event_loop() 116 | # d = loop.run_until_complete(get_modules_versions_async(modules)) 117 | return {module: version for module, version in zip(modules, await asyncio.gather(*tasks))} 118 | 119 | start = time.time() 120 | loop = asyncio.get_event_loop() 121 | d = loop.run_until_complete(get_modules_versions_async(modules)) 122 | print('async:', time.time() - start, 'seconds for', len(d), 'modules.') 123 | 124 | start = time.time() 125 | assert get_modules_versions_sync(modules) == d 126 | print(' sync:', time.time() - start, 'seconds for', len(d), 'modules.') 127 | 128 | 129 | ''' 130 | fmt = '| {:<13} | {:<11} | {:<11} | {}' 131 | div = fmt.format('-' * 13, '-' * 11, '-' * 11, '') 132 | print(fmt.format('module', 'local', 'PyPI', '')) 133 | print(fmt.format('name', 'version', 'version', '')) 134 | 135 | print(div) 136 | for module_name in modules: 137 | local_version = get_module_version(module_name) 138 | pypi_version = get_module_version_from_pypi(module_name) 139 | if '?' in local_version or '$' in local_version: 140 | advise = local_version 141 | else: 142 | advise = '' if local_version == pypi_version else 'Upgrade?' 143 | print(fmt.format(module_name, local_version, pypi_version, advise)) 144 | print(div) 145 | print('```') # end of markdown literal 146 | print('=' * 16) 147 | ''' -------------------------------------------------------------------------------- /pythonista_module_versions.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import bs4, importlib, os, platform, plistlib, requests, sys #, pkgutil 3 | 4 | # Translate from Python module --> PyPI module name 5 | pypi_dict = { 'bs4' : 'beautifulsoup4', 6 | 'dateutil' : 'py-dateutil', 7 | 'faker' : 'Faker', 8 | 'sqlite3' : 'pysqlite', 9 | 'yaml' : 'PyYAML', 10 | 'xhtml2pdf' : 'pisa', 11 | 'Crypto' : 'pycrypto', 12 | 'PIL' : 'Pillow' } 13 | 14 | modules = '''arrow bottle bs4 certifi Crypto cycler dateutil dropbox ecdsa 15 | et_xmlfile evernote faker feedparser flask google html2text 16 | html5lib httplib2 images2gif itsdangerous jdcal jedi jinja2 17 | libmodernize markdown markdown2 markupsafe matplotlib mccabe 18 | midiutil mpl_toolkits mpmath numpy oauth2 paramiko parsedatetime 19 | PIL pycparser pyflakes pygments pylab pyparsing PyPDF2 pytest pytz 20 | qrcode reportlab requests sgmllib simpy six sqlalchemy sqlite3 21 | sympy thrift turtle twitter wavebender werkzeug wsgiref xmltodict 22 | yaml yapf'''.split() 23 | 24 | ## Removed: mechanize midiutil screenplain xhtml2pdf 25 | 26 | def get_module_version(in_module_name='requests'): 27 | mod = importlib.import_module(in_module_name) 28 | fmt = "### hasattr({}, '{}')".format(in_module_name, '{}') 29 | for attr_name in '__version__ version __VERSION__ PILLOW_VERSION VERSION'.split(): 30 | if in_module_name == 'markdown' and attr_name == '__version__': 31 | continue 32 | if in_module_name == 'reportlab': 33 | attr_name = 'Version' 34 | if hasattr(mod, attr_name): 35 | if attr_name != '__version__': 36 | print(fmt.format(attr_name)) 37 | the_attr = getattr(mod, attr_name) 38 | #if isinstance(the_attr, tuple): # mechanize workaround 39 | # the_attr = '.'.join([str(i) for i in the_attr[:3]]) 40 | return str(the_attr() if callable(the_attr) else the_attr) 41 | return '?' * 5 42 | 43 | def get_module_version_from_pypi(module_name='bs4'): 44 | module_name = pypi_dict.get(module_name, module_name) 45 | url = 'https://pypi.python.org/pypi/{}'.format(module_name) 46 | soup = bs4.BeautifulSoup(requests.get(url).content, 'html5lib') 47 | vers_str = soup.title.string.partition(':')[0].split()[-1] 48 | if vers_str == 'Packages': 49 | return soup.find('div', class_='section').a.string.split()[-1] 50 | return vers_str 51 | 52 | ''' 53 | for _, pkg_name, _ in pkgutil.walk_packages(): 54 | #print(pkg) 55 | #pkg_name = pkg[1] 56 | if 'Gist Commit' in pkg_name: 57 | sys.exit(pkg_name) 58 | if '.' in pkg_name: 59 | continue 60 | '#'' 61 | if ('ctypes.test.test' in pkg_name 62 | or 'unittest.__main__' in pkg_name 63 | or 'numpy.ma.version' in pkg_name 64 | or 'numpy.testing.print_coercion_tables' in pkg_name 65 | or 'sympy.mpmath.libmp.exec_py3' in pkg_name 66 | or 'pycparser._build_tables' in pkg_name 67 | or 'FileBrowser' in pkg_name): 68 | continue 69 | '#'' 70 | #if pkg_name not in ['test_blasdot', 'nose']: 71 | with open('versions.txt', 'w') as out_file: 72 | out_file.write(pkg_name) 73 | #print(pkg_name) 74 | try: 75 | mod_vers = str(get_module_version(pkg_name)).strip('?') 76 | if mod_vers: 77 | print('{:<10} {}'.format(pkg_name, mod_vers)) 78 | except (ImportError, ValueError) as e: 79 | print('{:<10} {}'.format(pkg_name, e)) 80 | print('=' * 16) 81 | ''' 82 | 83 | def pythonista_version(): 84 | plist = plistlib.readPlist(os.path.abspath(os.path.join(sys.executable, '..', 'Info.plist'))) 85 | return '{CFBundleShortVersionString} ({CFBundleVersion})'.format(**plist) 86 | 87 | print('```') # start the output with a markdown literal 88 | fmt = 'Pythonista version {0} running Python {1} on iOS {2} on an {4}.' 89 | print(fmt.format(pythonista_version(), platform.python_version(), *platform.mac_ver())) 90 | print('=' * 57) 91 | 92 | fmt = '| {:<13} | {:<11} | {:<11} | {}' 93 | div = fmt.format('-' * 13, '-' * 11, '-' * 11, '') 94 | print(fmt.format('module', 'local', 'PyPI', '')) 95 | print(fmt.format('name', 'version', 'version', '')) 96 | 97 | print(div) 98 | for module_name in modules: 99 | local_version = get_module_version(module_name) 100 | pypi_version = get_module_version_from_pypi(module_name) 101 | if '?' in local_version or '$' in local_version: 102 | advise = local_version 103 | else: 104 | advise = '' if local_version == pypi_version else 'Upgrade?' 105 | print(fmt.format(module_name, local_version, pypi_version, advise)) 106 | print(div) 107 | print('```') # end of markdown literal 108 | print('=' * 16) 109 | -------------------------------------------------------------------------------- /pythonista_undocumented.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import bs4, importlib, inspect, requests, itertools 3 | 4 | modules = '''canvas clipboard console contacts editor keychain linguistictagger 5 | location motion notification photos scene sound speech ui'''.split() 6 | modules += '''appex cb ctypes dialogs sk'''.split() # modules added in Pythonista v1.6 Beta 7 | 8 | def inspect_functions(module_name): 9 | module = importlib.import_module(module_name) 10 | return [member[0] for member in inspect.getmembers(module) 11 | if callable(member[1]) and not member[0].startswith('__')] 12 | 13 | def inspect_attributes(module_name): 14 | module = importlib.import_module(module_name) 15 | return [member[0] for member in inspect.getmembers(module) 16 | if not callable(member[1]) and not member[0].startswith('__')] 17 | 18 | def get_html(module_name): 19 | fmt = 'http://omz-software.com/pythonista/docs/ios/{}.html' 20 | return requests.get(fmt.format(module_name)).text 21 | #import os 22 | # with open(os.path.join(os.path.split(os.__file__)[0],'../Documentation/ios/{}.html'.format(module_name))) as f: 23 | # return f.read() 24 | 25 | def website_functions(module_name): 26 | headerlinks = bs4.BeautifulSoup(get_html(module_name)).find_all('a', 'headerlink') 27 | return sorted(list(set([hl['href'].partition('.')[2] 28 | for hl in headerlinks if '.' in hl['href']]))) 29 | 30 | def find_module_class_members(module_name): 31 | '''find all class members in the module''' 32 | module = importlib.import_module(module_name) 33 | class_members= [find_class_members(cls) 34 | for cls in [m for m in inspect.getmembers(module) 35 | if isinstance(m[1],type)] if not isbuiltin(cls[1])] 36 | return set(itertools.chain(*class_members)) # unique and flatten() 37 | 38 | def find_class_members(cls): 39 | '''find all members of class tuple (classname,class). ''' 40 | return [m for m in [find_oldest_ancestor(member[0],cls[1]) for member in inspect.getmembers(cls[1]) if not member[0].startswith('__')] if m] 41 | 42 | def find_oldest_ancestor(member_name,thiscls): 43 | '''given a member name, return string of form ancestor.member_name where an estor is the oldest ancestor containing that attribute, or return None for ancestor == a builtin''' 44 | for cls in [c for c in thiscls.mro()[-2::-1]]: 45 | if hasattr(cls, member_name): 46 | return '.'.join((cls.__name__,member_name)) if not isbuiltin(cls) else None 47 | def isbuiltin(cls): 48 | return cls.__module__=='__builtin__' 49 | def undocumented_functions(module_name): 50 | print('looking for undocumented in {} module...'.format(module_name)) 51 | inspect_funcs = inspect_functions(module_name) 52 | inspect_attr = inspect_attributes(module_name) 53 | class_members = find_module_class_members(module_name) 54 | website_funcs = website_functions(module_name) 55 | 56 | return sorted(itertools.chain(['{}.{}()'.format(module_name, f) 57 | for f in inspect_funcs if f not in website_funcs], 58 | ['{}.{}'.format(module_name, f) 59 | for f in inspect_attr if f not in website_funcs], 60 | ['{}.{}'.format(module_name,f) for f in class_members if f not in website_funcs])) 61 | 62 | undocumented = [x for x in [undocumented_functions(module_name) 63 | for module_name in sorted(modules)]] 64 | print('=' * 40) 65 | print('\n'.join(itertools.chain(*undocumented))) # flatten() -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | arrow==0.10.0 2 | beautifulsoup4==4.4.1 3 | bottle==0.12.5 4 | certifi==2016.02.28 5 | Crypto==2.6 6 | cycler==0.9.0 7 | dropbox==6.4.0 8 | ecdsa 9 | et_xmlfile==1.0.1 10 | evernote 11 | faker 12 | feedparser==5.2.1 13 | flask==0.10.1 14 | google 15 | html2text==2014.4.5 16 | html5lib==0.999 17 | httplib2==0.9.2 18 | images2gif 19 | itsdangerous 20 | jdcal==1.3 21 | jedi==0.9.0 22 | jinja2==2.7 23 | libmodernize==0.5 24 | markdown==2.6.2 25 | markdown2==2.2.1 26 | markupsafe 27 | matplotlib==1.4.0 28 | mccabe==0.4.0 29 | midiutil 30 | mpl_toolkits 31 | mpmath==0.18 32 | numpy==1.8.0 33 | oauth2==1.9.0.post1 34 | paramiko==1.16.0 35 | parsedatetime==1.5 36 | Pillow==2.9.0 37 | pycparser==2.10 38 | py-dateutil==2.2 39 | pyflakes==1.5.0 40 | pygments==2.1 41 | pylab 42 | pyparsing==2.0.1 43 | PyPDF2==1.22 44 | pytest==3.0.5 45 | pytz==2015.7 46 | qrcode 47 | reportlab==3.1.8 48 | requests==2.9.1 49 | sgmllib 50 | simpy==3.0.8 51 | six==1.6.1 52 | sqlalchemy==0.9.7 53 | sqlite3==2.6.0 54 | sympy==0.7.4.1 55 | thrift 56 | turtle 57 | twitter==1.19.6 58 | wavebender==0.3 59 | werkzeug==0.9.4 60 | wsgiref 61 | xmltodict==0.8.7 62 | yapf==0.16.1 63 | --------------------------------------------------------------------------------