├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── merge_pyi.py ├── test_merge_pyi.py └── testdata ├── decoration.comment.py ├── decoration.pep484.py ├── decoration.py ├── decoration.pyi ├── defaults.comment.py ├── defaults.pep484.py ├── defaults.py ├── defaults.pyi ├── heuristics.comment.py ├── heuristics.pep484.py ├── heuristics.py ├── heuristics.pyi ├── imports.comment.py ├── imports.pep484.py ├── imports.py ├── imports.pyi ├── imports_notouch.comment.py ├── imports_notouch.pep484.py ├── imports_notouch.py ├── imports_notouch.pyi ├── member_var.comment.py ├── member_var.pep484.py ├── member_var.py ├── member_var.pyi ├── mismatch.comment.py ├── mismatch.pep484.py ├── mismatch.py ├── mismatch.pyi ├── nonearg.comment.py ├── nonearg.pep484.py ├── nonearg.py ├── nonearg.pyi ├── oneliner.comment.py ├── oneliner.pep484.py ├── oneliner.py ├── oneliner.pyi ├── packed_tuple.comment.py ├── packed_tuple.pep484.py ├── packed_tuple.py ├── packed_tuple.pyi ├── parse_error.py ├── parse_error.pyi ├── partial.comment.py ├── partial.pep484.py ├── partial.py ├── partial.pyi ├── pyi_variations.comment.py ├── pyi_variations.pep484.py ├── pyi_variations.py ├── pyi_variations.pyi ├── redefine.comment.py ├── redefine.pep484.py ├── redefine.py ├── redefine.pyi ├── retval_heuristics.comment.py ├── retval_heuristics.pep484.py ├── retval_heuristics.py ├── retval_heuristics.pyi ├── scope.comment.py ├── scope.pep484.py ├── scope.py ├── scope.pyi ├── simple.comment.py ├── simple.pep484.py ├── simple.py ├── simple.pyi ├── stars.comment.py ├── stars.pep484.py ├── stars.py ├── stars.pyi ├── trailing_comma.comment.py ├── trailing_comma.pep484.py ├── trailing_comma.py ├── trailing_comma.pyi ├── typevar.comment.py ├── typevar.pep484.py ├── typevar.py └── typevar.pyi /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at 2 | the end). 3 | 4 | ### Before you contribute 5 | Before we can use your code, you must sign the 6 | [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual?csw=1) 7 | (CLA), which you can do online. The CLA is necessary mainly because you own the 8 | copyright to your changes, even after your contribution becomes part of our 9 | codebase, so we need your permission to use and distribute your code. We also 10 | need to be sure of various other things -- for instance that you'll tell us if you 11 | know that your code infringes on other people's patents. You don't have to sign 12 | the CLA until after you've submitted your code for review and a member has 13 | approved it, but you must do it before we can put your code into our codebase. 14 | Before you start working on a larger contribution, you should get in touch with 15 | us first through the issue tracker with your idea so that we can help out and 16 | possibly guide you. Coordinating up front makes it much easier to avoid 17 | frustration later on. 18 | 19 | ### Code reviews 20 | All submissions, including submissions by project members, require review. We 21 | use Github pull requests for this purpose. 22 | 23 | ### The small print 24 | Contributions made by corporations are covered by a different agreement than 25 | the one mentioned above; they're covered by the the Software Grant and 26 | Corporate Contributor License Agreement. 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Google, Inc. 2016. 2 | 3 | merge_pyi is based on parts of Mypy, which is licensed under the MIT license, 4 | reproduced below. 5 | 6 | = = = = = 7 | 8 | Apache License 9 | Version 2.0, January 2004 10 | http://www.apache.org/licenses/ 11 | 12 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 13 | 14 | 1. Definitions. 15 | 16 | "License" shall mean the terms and conditions for use, reproduction, 17 | and distribution as defined by Sections 1 through 9 of this document. 18 | 19 | "Licensor" shall mean the copyright owner or entity authorized by 20 | the copyright owner that is granting the License. 21 | 22 | "Legal Entity" shall mean the union of the acting entity and all 23 | other entities that control, are controlled by, or are under common 24 | control with that entity. For the purposes of this definition, 25 | "control" means (i) the power, direct or indirect, to cause the 26 | direction or management of such entity, whether by contract or 27 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 28 | outstanding shares, or (iii) beneficial ownership of such entity. 29 | 30 | "You" (or "Your") shall mean an individual or Legal Entity 31 | exercising permissions granted by this License. 32 | 33 | "Source" form shall mean the preferred form for making modifications, 34 | including but not limited to software source code, documentation 35 | source, and configuration files. 36 | 37 | "Object" form shall mean any form resulting from mechanical 38 | transformation or translation of a Source form, including but 39 | not limited to compiled object code, generated documentation, 40 | and conversions to other media types. 41 | 42 | "Work" shall mean the work of authorship, whether in Source or 43 | Object form, made available under the License, as indicated by a 44 | copyright notice that is included in or attached to the work 45 | (an example is provided in the Appendix below). 46 | 47 | "Derivative Works" shall mean any work, whether in Source or Object 48 | form, that is based on (or derived from) the Work and for which the 49 | editorial revisions, annotations, elaborations, or other modifications 50 | represent, as a whole, an original work of authorship. For the purposes 51 | of this License, Derivative Works shall not include works that remain 52 | separable from, or merely link (or bind by name) to the interfaces of, 53 | the Work and Derivative Works thereof. 54 | 55 | "Contribution" shall mean any work of authorship, including 56 | the original version of the Work and any modifications or additions 57 | to that Work or Derivative Works thereof, that is intentionally 58 | submitted to Licensor for inclusion in the Work by the copyright owner 59 | or by an individual or Legal Entity authorized to submit on behalf of 60 | the copyright owner. For the purposes of this definition, "submitted" 61 | means any form of electronic, verbal, or written communication sent 62 | to the Licensor or its representatives, including but not limited to 63 | communication on electronic mailing lists, source code control systems, 64 | and issue tracking systems that are managed by, or on behalf of, the 65 | Licensor for the purpose of discussing and improving the Work, but 66 | excluding communication that is conspicuously marked or otherwise 67 | designated in writing by the copyright owner as "Not a Contribution." 68 | 69 | "Contributor" shall mean Licensor and any individual or Legal Entity 70 | on behalf of whom a Contribution has been received by Licensor and 71 | subsequently incorporated within the Work. 72 | 73 | 2. Grant of Copyright License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | copyright license to reproduce, prepare Derivative Works of, 77 | publicly display, publicly perform, sublicense, and distribute the 78 | Work and such Derivative Works in Source or Object form. 79 | 80 | 3. Grant of Patent License. Subject to the terms and conditions of 81 | this License, each Contributor hereby grants to You a perpetual, 82 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 83 | (except as stated in this section) patent license to make, have made, 84 | use, offer to sell, sell, import, and otherwise transfer the Work, 85 | where such license applies only to those patent claims licensable 86 | by such Contributor that are necessarily infringed by their 87 | Contribution(s) alone or by combination of their Contribution(s) 88 | with the Work to which such Contribution(s) was submitted. If You 89 | institute patent litigation against any entity (including a 90 | cross-claim or counterclaim in a lawsuit) alleging that the Work 91 | or a Contribution incorporated within the Work constitutes direct 92 | or contributory patent infringement, then any patent licenses 93 | granted to You under this License for that Work shall terminate 94 | as of the date such litigation is filed. 95 | 96 | 4. Redistribution. You may reproduce and distribute copies of the 97 | Work or Derivative Works thereof in any medium, with or without 98 | modifications, and in Source or Object form, provided that You 99 | meet the following conditions: 100 | 101 | (a) You must give any other recipients of the Work or 102 | Derivative Works a copy of this License; and 103 | 104 | (b) You must cause any modified files to carry prominent notices 105 | stating that You changed the files; and 106 | 107 | (c) You must retain, in the Source form of any Derivative Works 108 | that You distribute, all copyright, patent, trademark, and 109 | attribution notices from the Source form of the Work, 110 | excluding those notices that do not pertain to any part of 111 | the Derivative Works; and 112 | 113 | (d) If the Work includes a "NOTICE" text file as part of its 114 | distribution, then any Derivative Works that You distribute must 115 | include a readable copy of the attribution notices contained 116 | within such NOTICE file, excluding those notices that do not 117 | pertain to any part of the Derivative Works, in at least one 118 | of the following places: within a NOTICE text file distributed 119 | as part of the Derivative Works; within the Source form or 120 | documentation, if provided along with the Derivative Works; or, 121 | within a display generated by the Derivative Works, if and 122 | wherever such third-party notices normally appear. The contents 123 | of the NOTICE file are for informational purposes only and 124 | do not modify the License. You may add Your own attribution 125 | notices within Derivative Works that You distribute, alongside 126 | or as an addendum to the NOTICE text from the Work, provided 127 | that such additional attribution notices cannot be construed 128 | as modifying the License. 129 | 130 | You may add Your own copyright statement to Your modifications and 131 | may provide additional or different license terms and conditions 132 | for use, reproduction, or distribution of Your modifications, or 133 | for any such Derivative Works as a whole, provided Your use, 134 | reproduction, and distribution of the Work otherwise complies with 135 | the conditions stated in this License. 136 | 137 | 5. Submission of Contributions. Unless You explicitly state otherwise, 138 | any Contribution intentionally submitted for inclusion in the Work 139 | by You to the Licensor shall be under the terms and conditions of 140 | this License, without any additional terms or conditions. 141 | Notwithstanding the above, nothing herein shall supersede or modify 142 | the terms of any separate license agreement you may have executed 143 | with Licensor regarding such Contributions. 144 | 145 | 6. Trademarks. This License does not grant permission to use the trade 146 | names, trademarks, service marks, or product names of the Licensor, 147 | except as required for reasonable and customary use in describing the 148 | origin of the Work and reproducing the content of the NOTICE file. 149 | 150 | 7. Disclaimer of Warranty. Unless required by applicable law or 151 | agreed to in writing, Licensor provides the Work (and each 152 | Contributor provides its Contributions) on an "AS IS" BASIS, 153 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 154 | implied, including, without limitation, any warranties or conditions 155 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 156 | PARTICULAR PURPOSE. You are solely responsible for determining the 157 | appropriateness of using or redistributing the Work and assume any 158 | risks associated with Your exercise of permissions under this License. 159 | 160 | 8. Limitation of Liability. In no event and under no legal theory, 161 | whether in tort (including negligence), contract, or otherwise, 162 | unless required by applicable law (such as deliberate and grossly 163 | negligent acts) or agreed to in writing, shall any Contributor be 164 | liable to You for damages, including any direct, indirect, special, 165 | incidental, or consequential damages of any character arising as a 166 | result of this License or out of the use or inability to use the 167 | Work (including but not limited to damages for loss of goodwill, 168 | work stoppage, computer failure or malfunction, or any and all 169 | other commercial damages or losses), even if such Contributor 170 | has been advised of the possibility of such damages. 171 | 172 | 9. Accepting Warranty or Additional Liability. While redistributing 173 | the Work or Derivative Works thereof, You may choose to offer, 174 | and charge a fee for, acceptance of support, warranty, indemnity, 175 | or other liability obligations and/or rights consistent with this 176 | License. However, in accepting such obligations, You may act only 177 | on Your own behalf and on Your sole responsibility, not on behalf 178 | of any other Contributor, and only if You agree to indemnify, 179 | defend, and hold each Contributor harmless for any liability 180 | incurred by, or claims asserted against, such Contributor by reason 181 | of your accepting any such warranty or additional liability. 182 | 183 | END OF TERMS AND CONDITIONS 184 | 185 | APPENDIX: How to apply the Apache License to your work. 186 | 187 | To apply the Apache License to your work, attach the following 188 | boilerplate notice, with the fields enclosed by brackets "[]" 189 | replaced with your own identifying information. (Don't include 190 | the brackets!) The text should be enclosed in the appropriate 191 | comment syntax for the file format. We also recommend that a 192 | file or class name and description of purpose be included on the 193 | same "printed page" as the copyright notice for easier 194 | identification within third-party archives. 195 | 196 | Copyright [yyyy] [name of copyright owner] 197 | 198 | Licensed under the Apache License, Version 2.0 (the "License"); 199 | you may not use this file except in compliance with the License. 200 | You may obtain a copy of the License at 201 | 202 | http://www.apache.org/licenses/LICENSE-2.0 203 | 204 | Unless required by applicable law or agreed to in writing, software 205 | distributed under the License is distributed on an "AS IS" BASIS, 206 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 207 | See the License for the specific language governing permissions and 208 | limitations under the License. 209 | 210 | 211 | = = = = = 212 | 213 | The MIT License 214 | 215 | Copyright (c) 2015-2016 Jukka Lehtosalo and contributors 216 | 217 | Permission is hereby granted, free of charge, to any person obtaining a 218 | copy of this software and associated documentation files (the "Software"), 219 | to deal in the Software without restriction, including without limitation 220 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 221 | and/or sell copies of the Software, and to permit persons to whom the 222 | Software is furnished to do so, subject to the following conditions: 223 | 224 | The above copyright notice and this permission notice shall be included in 225 | all copies or substantial portions of the Software. 226 | 227 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 228 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 229 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 230 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 231 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 232 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 233 | DEALINGS IN THE SOFTWARE. 234 | 235 | = = = = = 236 | 237 | Portions of mypy are licensed under different licenses. The files 238 | under stdlib-samples and lib-typing are licensed under the PSF 2 239 | License, reproduced below. 240 | 241 | = = = = = 242 | 243 | PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 244 | -------------------------------------------- 245 | 246 | 1. This LICENSE AGREEMENT is between the Python Software Foundation 247 | ("PSF"), and the Individual or Organization ("Licensee") accessing and 248 | otherwise using this software ("Python") in source or binary form and 249 | its associated documentation. 250 | 251 | 2. Subject to the terms and conditions of this License Agreement, PSF hereby 252 | grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, 253 | analyze, test, perform and/or display publicly, prepare derivative works, 254 | distribute, and otherwise use Python alone or in any derivative version, 255 | provided, however, that PSF's License Agreement and PSF's notice of copyright, 256 | i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 257 | 2011, 2012 Python Software Foundation; All Rights Reserved" are retained in Python 258 | alone or in any derivative version prepared by Licensee. 259 | 260 | 3. In the event Licensee prepares a derivative work that is based on 261 | or incorporates Python or any part thereof, and wants to make 262 | the derivative work available to others as provided herein, then 263 | Licensee hereby agrees to include in any such work a brief summary of 264 | the changes made to Python. 265 | 266 | 4. PSF is making Python available to Licensee on an "AS IS" 267 | basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR 268 | IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND 269 | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS 270 | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT 271 | INFRINGE ANY THIRD PARTY RIGHTS. 272 | 273 | 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 274 | FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS 275 | A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, 276 | OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 277 | 278 | 6. This License Agreement will automatically terminate upon a material 279 | breach of its terms and conditions. 280 | 281 | 7. Nothing in this License Agreement shall be deemed to create any 282 | relationship of agency, partnership, or joint venture between PSF and 283 | Licensee. This License Agreement does not grant permission to use PSF 284 | trademarks or trade name in a trademark sense to endorse or promote 285 | products or services of Licensee, or any third party. 286 | 287 | 8. By copying, installing or otherwise using Python, Licensee 288 | agrees to be bound by the terms and conditions of this License 289 | Agreement. 290 | 291 | 292 | BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 293 | ------------------------------------------- 294 | 295 | BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 296 | 297 | 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an 298 | office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the 299 | Individual or Organization ("Licensee") accessing and otherwise using 300 | this software in source or binary form and its associated 301 | documentation ("the Software"). 302 | 303 | 2. Subject to the terms and conditions of this BeOpen Python License 304 | Agreement, BeOpen hereby grants Licensee a non-exclusive, 305 | royalty-free, world-wide license to reproduce, analyze, test, perform 306 | and/or display publicly, prepare derivative works, distribute, and 307 | otherwise use the Software alone or in any derivative version, 308 | provided, however, that the BeOpen Python License is retained in the 309 | Software, alone or in any derivative version prepared by Licensee. 310 | 311 | 3. BeOpen is making the Software available to Licensee on an "AS IS" 312 | basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR 313 | IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND 314 | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS 315 | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT 316 | INFRINGE ANY THIRD PARTY RIGHTS. 317 | 318 | 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE 319 | SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS 320 | AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY 321 | DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 322 | 323 | 5. This License Agreement will automatically terminate upon a material 324 | breach of its terms and conditions. 325 | 326 | 6. This License Agreement shall be governed by and interpreted in all 327 | respects by the law of the State of California, excluding conflict of 328 | law provisions. Nothing in this License Agreement shall be deemed to 329 | create any relationship of agency, partnership, or joint venture 330 | between BeOpen and Licensee. This License Agreement does not grant 331 | permission to use BeOpen trademarks or trade names in a trademark 332 | sense to endorse or promote products or services of Licensee, or any 333 | third party. As an exception, the "BeOpen Python" logos available at 334 | http://www.pythonlabs.com/logos.html may be used according to the 335 | permissions granted on that web page. 336 | 337 | 7. By copying, installing or otherwise using the software, Licensee 338 | agrees to be bound by the terms and conditions of this License 339 | Agreement. 340 | 341 | 342 | CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 343 | --------------------------------------- 344 | 345 | 1. This LICENSE AGREEMENT is between the Corporation for National 346 | Research Initiatives, having an office at 1895 Preston White Drive, 347 | Reston, VA 20191 ("CNRI"), and the Individual or Organization 348 | ("Licensee") accessing and otherwise using Python 1.6.1 software in 349 | source or binary form and its associated documentation. 350 | 351 | 2. Subject to the terms and conditions of this License Agreement, CNRI 352 | hereby grants Licensee a nonexclusive, royalty-free, world-wide 353 | license to reproduce, analyze, test, perform and/or display publicly, 354 | prepare derivative works, distribute, and otherwise use Python 1.6.1 355 | alone or in any derivative version, provided, however, that CNRI's 356 | License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) 357 | 1995-2001 Corporation for National Research Initiatives; All Rights 358 | Reserved" are retained in Python 1.6.1 alone or in any derivative 359 | version prepared by Licensee. Alternately, in lieu of CNRI's License 360 | Agreement, Licensee may substitute the following text (omitting the 361 | quotes): "Python 1.6.1 is made available subject to the terms and 362 | conditions in CNRI's License Agreement. This Agreement together with 363 | Python 1.6.1 may be located on the Internet using the following 364 | unique, persistent identifier (known as a handle): 1895.22/1013. This 365 | Agreement may also be obtained from a proxy server on the Internet 366 | using the following URL: http://hdl.handle.net/1895.22/1013". 367 | 368 | 3. In the event Licensee prepares a derivative work that is based on 369 | or incorporates Python 1.6.1 or any part thereof, and wants to make 370 | the derivative work available to others as provided herein, then 371 | Licensee hereby agrees to include in any such work a brief summary of 372 | the changes made to Python 1.6.1. 373 | 374 | 4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" 375 | basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR 376 | IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND 377 | DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS 378 | FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT 379 | INFRINGE ANY THIRD PARTY RIGHTS. 380 | 381 | 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 382 | 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS 383 | A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, 384 | OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 385 | 386 | 6. This License Agreement will automatically terminate upon a material 387 | breach of its terms and conditions. 388 | 389 | 7. This License Agreement shall be governed by the federal 390 | intellectual property law of the United States, including without 391 | limitation the federal copyright law, and, to the extent such 392 | U.S. federal law does not apply, by the law of the Commonwealth of 393 | Virginia, excluding Virginia's conflict of law provisions. 394 | Notwithstanding the foregoing, with regard to derivative works based 395 | on Python 1.6.1 that incorporate non-separable material that was 396 | previously distributed under the GNU General Public License (GPL), the 397 | law of the Commonwealth of Virginia shall govern this License 398 | Agreement only as to issues arising under or with respect to 399 | Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this 400 | License Agreement shall be deemed to create any relationship of 401 | agency, partnership, or joint venture between CNRI and Licensee. This 402 | License Agreement does not grant permission to use CNRI trademarks or 403 | trade name in a trademark sense to endorse or promote products or 404 | services of Licensee, or any third party. 405 | 406 | 8. By clicking on the "ACCEPT" button where indicated, or by copying, 407 | installing or otherwise using Python 1.6.1, Licensee agrees to be 408 | bound by the terms and conditions of this License Agreement. 409 | 410 | ACCEPT 411 | 412 | 413 | CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 414 | -------------------------------------------------- 415 | 416 | Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, 417 | The Netherlands. All rights reserved. 418 | 419 | Permission to use, copy, modify, and distribute this software and its 420 | documentation for any purpose and without fee is hereby granted, 421 | provided that the above copyright notice appear in all copies and that 422 | both that copyright notice and this permission notice appear in 423 | supporting documentation, and that the name of Stichting Mathematisch 424 | Centrum or CWI not be used in advertising or publicity pertaining to 425 | distribution of the software without specific, written prior 426 | permission. 427 | 428 | STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO 429 | THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 430 | FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE 431 | FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 432 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 433 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 434 | OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 435 | 436 | = = = = = -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is not an official Google product. 2 | 3 | Copy type annotations from a PEP484 stub file into python source. 4 | 5 | Sample use: 6 | 7 | python merge_pyi.py --pep484 --diff testdata/simple.py testdata/simple.pyi 8 | 9 | [PyCon 2016 slides](https://docs.google.com/a/google.com/presentation/d/1S3Pa-6ogG-yNcQpbU-JrhiFEHNtw_8laF1svOekuYOI/pub?start=false&loop=false&delayms=3000) 10 | 11 | #### Regression tests 12 | 13 | ``` 14 | testdata/foo.py : input we want to annotate 15 | testdata/foo.pyi : type hints we want to add to foo.py (may be intentionally bad) 16 | 17 | testdata/foo.comment.py : expected output, inserting types as comments 18 | testdata/foo.pep484.py : expected output, inserting types in PEP484 style 19 | ``` 20 | -------------------------------------------------------------------------------- /merge_pyi.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # 16 | # Based on 17 | # https://github.com/python/mypy/blob/master/misc/fix_annotate.py 18 | # 19 | # The MIT License 20 | # 21 | # Copyright (c) 2015-2016 Jukka Lehtosalo and contributors 22 | # 23 | # Permission is hereby granted, free of charge, to any person obtaining a 24 | # copy of this software and associated documentation files (the "Software"), 25 | # to deal in the Software without restriction, including without limitation 26 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 27 | # and/or sell copies of the Software, and to permit persons to whom the 28 | # Software is furnished to do so, subject to the following conditions: 29 | # 30 | # The above copyright notice and this permission notice shall be included in 31 | # all copies or substantial portions of the Software. 32 | # 33 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 34 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 35 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 36 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 37 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 38 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 39 | # DEALINGS IN THE SOFTWARE. 40 | 41 | 42 | """Fixer that inserts type annotations from pyi files into methods. 43 | 44 | Annotations are inserted either as comments or using the PEP484 syntax (requires 45 | python3.5). 46 | 47 | For example, if provided with a pyi containing 48 | 49 | def foo(self, bar: Any, baz: int) -> Any: ... 50 | 51 | this transforms 52 | 53 | def foo(self, bar, baz=12): 54 | return bar + baz 55 | 56 | into (comment annotation) 57 | 58 | def foo(self, bar, baz=12): 59 | # type: (Any, int) -> Any 60 | return bar + baz 61 | 62 | or (PEP484 annotation) 63 | 64 | def foo(self, bar: Any, baz: int=12) -> Any: 65 | return bar + baz 66 | 67 | The pyi is assumed to be generated by another tool. 68 | 69 | When inserting annotations as comments, and the pyi has only partial information, 70 | it uses some basic heuristics to decide when to ignore the first argument of a 71 | class method: 72 | 73 | - always if it's named 'self' 74 | - if there's a @classmethod decorator 75 | 76 | Finally, it knows that __init__() is supposed to return None. 77 | """ 78 | 79 | from __future__ import print_function 80 | 81 | from collections import namedtuple 82 | import itertools 83 | import logging 84 | import sys 85 | 86 | from lib2to3 import pygram, pytree, refactor 87 | from lib2to3.fixer_base import BaseFix 88 | from lib2to3.fixer_util import token, syms, touch_import, find_indentation, find_root 89 | from lib2to3.patcomp import compile_pattern 90 | from lib2to3.pgen2 import driver 91 | from lib2to3.pytree import Leaf, Node 92 | 93 | __all__ = ['KnownError', 94 | 'FixMergePyi', 95 | 'annotate_string', 96 | 'main'] 97 | 98 | 99 | class KnownError(Exception): 100 | """Exceptions we already know about""" 101 | pass 102 | 103 | 104 | class Util(object): 105 | 106 | return_expr = compile_pattern("""return_stmt< 'return' any >""") 107 | 108 | @classmethod 109 | def has_return_exprs(cls, node): 110 | """Traverse the tree below node looking for 'return expr'. 111 | 112 | Return True if at least 'return expr' is found, False if not. 113 | (If both 'return' and 'return expr' are found, return True.) 114 | """ 115 | results = {} 116 | if cls.return_expr.match(node, results): 117 | return True 118 | for child in node.children: 119 | if child.type not in (syms.funcdef, syms.classdef): 120 | if cls.has_return_exprs(child): 121 | return True 122 | return False 123 | 124 | driver = driver.Driver(pygram.python_grammar, 125 | convert=pytree.convert) 126 | 127 | @classmethod 128 | def parse_string(cls, text): 129 | """Use lib2to3 to parse text into a Node.""" 130 | 131 | text = text.strip() 132 | if not text: 133 | # self.driver.parse_string just returns the ENDMARKER Leaf, wrap in a Node 134 | # for consistency 135 | return Node(syms.file_input, [Leaf(token.ENDMARKER, '')]) 136 | 137 | # workaround: parsing text without trailing '\n' throws exception 138 | text += '\n' 139 | return cls.driver.parse_string(text) 140 | 141 | 142 | class ArgSignature(object): 143 | """Partially parsed representation of a function argument""" 144 | 145 | def __init__(self, arg_nodes): 146 | sig = ArgSignature._split_arg(arg_nodes) 147 | self._is_tuple, self._stars, self._arg_type, self._name_nodes, self._default = sig 148 | self._wasModified = False 149 | 150 | @property 151 | def is_tuple(self): 152 | """Do we use the unusual packed-tuple syntax (see PEP 3113)""" 153 | return self._is_tuple 154 | 155 | @property 156 | def stars(self): 157 | """String: (''|'*'|'**')""" 158 | return self._stars 159 | 160 | @property 161 | def arg_type(self): 162 | """Existing annotation: (Node|Leaf|None)""" 163 | return self._arg_type 164 | 165 | @property 166 | def default(self): 167 | """Node holding default value or None""" 168 | return self._default 169 | 170 | @property 171 | def name(self): 172 | """Our name as a string. Throws if is_tuple (no reasonable name).""" 173 | 174 | assert not self.is_tuple 175 | n = self._name_nodes[-1] 176 | 177 | assert n.type == token.NAME, repr(n) 178 | return n.value 179 | 180 | @staticmethod 181 | def _split_arg(arg): 182 | """Takes list of nodes corresponding to a function argument, returns 183 | a tuple holding its constituent pieces: 184 | 185 | is_tuple: bool, are we a packed-tuple arg 186 | stars: (''|'*'|'**') 187 | arg_type: (Node|Leaf|None) -- existing annotation 188 | name_nodes: NonEmptyList(Node|Leaf) -- argument name 189 | default: (Node|Leaf) -- default value 190 | """ 191 | # in cpython, see ast_for_arguments in ast.c 192 | 193 | assert arg, "Need non-empty list" 194 | arg = list(arg) 195 | 196 | is_tuple, stars, arg_type, default = False, '', None, None 197 | 198 | def is_leaf(n): return isinstance(n, Leaf) 199 | 200 | def get_unique_idx(nodes, test_set): 201 | """If it exists, get index of unique Leaf node n where n.value in 202 | test_set. Return None if no such element""" 203 | matches = [i for i, n in enumerate(nodes) 204 | if is_leaf(n) and n.value in test_set] 205 | assert len(matches) in (0, 1) 206 | return matches[0] if matches else None 207 | 208 | # [('*'|'**')] (NAME | packed_tuple) [':' test] ['=' test] 209 | 210 | # Strip stars 211 | idx = get_unique_idx(arg, ['*', '**']) 212 | if idx is not None: 213 | assert idx == 0 214 | stars = arg.pop(idx).value 215 | 216 | # Strip default 217 | idx = get_unique_idx(arg, '=') 218 | if idx is not None: 219 | assert idx == (len(arg) - 2) 220 | arg, default = arg[:idx], arg[idx+1] 221 | 222 | def split_colon(nodes): 223 | idx = get_unique_idx(nodes, ':') 224 | if idx is None: 225 | return nodes, None 226 | assert idx == (len(nodes) - 2) 227 | return nodes[:idx], nodes[idx + 1] 228 | 229 | # Strip one flavor of arg_type (the other flavor, where we have a tname 230 | # Node, is handled below) 231 | arg, arg_type = split_colon(arg) 232 | 233 | if 3 == len(arg): 234 | assert arg[0].type == token.LPAR 235 | assert arg[2].type == token.RPAR 236 | assert arg[1].type in (syms.tfpdef, syms.tfplist) 237 | 238 | is_tuple = True 239 | 240 | assert stars == '' 241 | assert arg_type is None # type declaration goes inside tuple 242 | 243 | return is_tuple, stars, arg_type, arg, default 244 | 245 | if 1 != len(arg): 246 | raise KnownError() # expected/parse_error.py 247 | 248 | node = arg[0] 249 | if is_leaf(node): 250 | return is_tuple, stars, arg_type, arg, default 251 | 252 | assert node.type in (syms.tname, syms.tfpdef) 253 | 254 | is_tuple = (node.type == syms.tfpdef) 255 | 256 | if node.type == syms.tname: 257 | arg, inner_arg_type = split_colon(node.children) 258 | if inner_arg_type is not None: 259 | assert arg_type is None 260 | arg_type = inner_arg_type 261 | 262 | return is_tuple, stars, arg_type, arg, default 263 | 264 | def insert_annotation(self, arg_type): 265 | """Modifies tree to set string arg_type as our type annotation""" 266 | # maybe the right way to do this is to insert as next child 267 | # in our parent instead? Or could replace self._arg[-1] 268 | # with modified version of itself 269 | assert self.arg_type is None, 'already annotated' 270 | assert not self._wasModified, 'can only set annotation once' 271 | self._wasModified = True 272 | 273 | name = self._name_nodes[-1] 274 | assert name.type == token.NAME 275 | 276 | typed_name = Node(syms.tname, 277 | [Leaf(token.NAME, self.name), 278 | Leaf(token.COLON, ':'), 279 | clean_clone(arg_type, False)]) 280 | 281 | typed_name.prefix = name.prefix 282 | 283 | name.replace(typed_name) 284 | 285 | 286 | class FuncSignature(object): 287 | """A function or method""" 288 | 289 | # The pattern to match. 290 | PATTERN = """ 291 | funcdef< 292 | 'def' name=NAME 293 | parameters< '(' [args=any+] ')' > 294 | ['->' ret_annotation=any] 295 | colon=':' suite=any+ > 296 | """ 297 | 298 | def __init__(self, node, match_results): 299 | """node must match PATTERN, return FuncSignature, else return None.""" 300 | 301 | name = match_results.get('name') 302 | assert isinstance(name, Leaf), repr(name) 303 | assert name.type == token.NAME, repr(name) 304 | 305 | self._ret_type = match_results.get('ret_annotation') 306 | self._full_name = self._make_function_key(name) 307 | 308 | args = self._split_args(match_results.get('args')) 309 | self._arg_sigs = tuple(map(ArgSignature, args)) 310 | 311 | self._node = node 312 | self._match_results = match_results 313 | self._inserted_ret_annotation = False 314 | 315 | def __str__(self): 316 | return self.full_name 317 | 318 | @property 319 | def full_name(self): 320 | """Fully-qualified name. string""" 321 | return self._full_name 322 | 323 | @property 324 | def short_name(self): 325 | return self._match_results.get('name').value 326 | 327 | @property 328 | def ret_type(self): 329 | """Return type, Node? or None""" 330 | return self._ret_type 331 | 332 | @property 333 | def arg_sigs(self): 334 | """List[ArgSignature]""" 335 | return self._arg_sigs 336 | 337 | # The parse tree has a different shape when there is a single 338 | # decorator vs. when there are multiple decorators. 339 | decorated_pattern = compile_pattern(""" 340 | decorated< (d=decorator | decorators< dd=decorator+ >) funcdef > 341 | """) 342 | 343 | @property 344 | def decorators(self): 345 | """A list of our decorators. 346 | 347 | This is a list of strings; only simple decorators 348 | (e.g. @staticmethod) are returned. 349 | 350 | If the function is undecorated or only non-simple decorators 351 | are found, return []. 352 | """ 353 | # TODO: memoize 354 | node = self._node 355 | if node.parent is None: 356 | return [] 357 | results = {} 358 | if not self.decorated_pattern.match(node.parent, results): 359 | return [] 360 | decorators = results.get('dd') or [results['d']] 361 | decs = [] 362 | for d in decorators: 363 | for child in d.children: 364 | if child.type == token.NAME: 365 | decs.append(child.value) 366 | return decs 367 | 368 | @property 369 | def is_method(self): 370 | """Whether we are (directly) inside a class.""" 371 | # TODO: memoize 372 | node = self._node.parent 373 | while node is not None: 374 | if node.type == syms.classdef: 375 | return True 376 | if node.type == syms.funcdef: 377 | return False 378 | node = node.parent 379 | return False 380 | 381 | @property 382 | def has_return_exprs(self): 383 | """True if function has "return expr" anywhere""" 384 | return Util.has_return_exprs(self._node) 385 | 386 | @property 387 | def has_pep484_annotations(self): 388 | """Do we have any pep484 annotations""" 389 | return self.ret_type or any(arg.arg_type for arg in self.arg_sigs) 390 | 391 | @property 392 | def has_comment_annotations(self): 393 | """Do we have any comment annotations""" 394 | children = self._match_results['suite'][0].children 395 | for ch in children: 396 | if ch.prefix.lstrip().startswith('# type:'): 397 | return True 398 | 399 | return False 400 | 401 | def insert_ret_annotation(self, ret_type): 402 | """In-place annotation. Can only call once""" 403 | assert not self._inserted_ret_annotation 404 | self._inserted_ret_annotation = True 405 | 406 | colon = self._match_results.get('colon') 407 | # TODO: insert as a Node, not as a prefix 408 | colon.prefix = ' -> ' + str(ret_type).strip() + colon.prefix 409 | 410 | def try_insert_comment_annotation(self, annotation): 411 | """Try to insert '# type: {annotation}' comment. """ 412 | # For reference, see lib2to3/fixes/fix_tuple_params.py in stdlib. 413 | # "Compact" functions (e.g. 'def foo(x, y): return max(x, y)') 414 | # are not annotated. 415 | 416 | children = self._match_results['suite'][0].children 417 | if not (len(children) >= 2 and children[1].type == token.INDENT): 418 | return False # can't annotate 419 | 420 | node = children[1] 421 | node.prefix = '%s# type: %s\n%s' % (node.value, annotation, node.prefix) 422 | node.changed() 423 | return True 424 | 425 | scope_pattern = compile_pattern("""( 426 | funcdef < 'def' name=TOKEN any*> | 427 | classdef< 'class' name=TOKEN any*> 428 | )""") 429 | 430 | @classmethod 431 | def _make_function_key(cls, node): 432 | """Return fully-qualified name of function that node is under. 433 | 434 | If source is 435 | 436 | class C(object): 437 | def foo(self): 438 | x = 1 439 | 440 | We'll return 'C.foo' for any nodes related to 'x', '1', 'foo', 'self', 441 | and either 'C' or '' otherwise.""" 442 | 443 | result = [] 444 | while node is not None: 445 | match_result = {} 446 | if cls.scope_pattern.match(node, match_result): 447 | result.append(match_result.get('name').value) 448 | 449 | node = node.parent 450 | 451 | return '.'.join(reversed(result)) 452 | 453 | @staticmethod 454 | def _split_args(args): 455 | """Takes match of PATTERN.args, returns list of non-empty lists of nodes, where each list 456 | corresponds to a function argument.""" 457 | if args is None: 458 | return [] 459 | 460 | assert isinstance(args, list) and 1 == len(args), repr(args) 461 | 462 | args = args[0] 463 | if isinstance(args, Leaf) or args.type == syms.tname: 464 | args = [args] 465 | else: 466 | args = args.children 467 | 468 | return split_comma(args) 469 | 470 | 471 | class FixMergePyi(BaseFix): 472 | 473 | # This fixer is compatible with the bottom matcher. 474 | BM_compatible = True 475 | 476 | # This fixer shouldn't run by default. 477 | explicit = True 478 | 479 | PATTERN = FuncSignature.PATTERN 480 | 481 | def __init__(self, options, log): 482 | super(FixMergePyi, self).__init__(options, log) 483 | 484 | # ParsedPyi obtained from .pyi file 485 | self.parsed_pyi = None 486 | 487 | # Did we add globals required by pyi to the top of the py file 488 | self.added_pyi_globals = False 489 | 490 | self.logger = logging.getLogger(self.__class__.__name__) 491 | 492 | # Options below 493 | 494 | # insert type annotations in PEP484 style. Otherwise insert as comments 495 | self._annotate_pep484 = False 496 | 497 | @property 498 | def annotate_pep484(self): 499 | return self._annotate_pep484 500 | 501 | @annotate_pep484.setter 502 | def annotate_pep484(self, value): 503 | self._annotate_pep484 = bool(value) 504 | 505 | def transform(self, node, results): 506 | assert self.parsed_pyi, 'must provide pyi_string' 507 | 508 | src_sig = FuncSignature(node, results) 509 | if not self.can_annotate(src_sig): 510 | return 511 | pyi_sig = self.parsed_pyi.funcs[src_sig.full_name] 512 | 513 | if self.annotate_pep484: 514 | self.insert_annotation(src_sig, pyi_sig) 515 | else: 516 | annot = self.get_comment_annotation(src_sig, pyi_sig) 517 | if src_sig.try_insert_comment_annotation(annot) and 'Any' in annot: 518 | touch_import('typing', 'Any', node) 519 | 520 | self.add_globals(node) 521 | 522 | def insert_annotation(self, src_sig, pyi_sig): 523 | """Insert annotation in PEP484 format.""" 524 | for arg_sig, pyi_arg_sig in zip(src_sig.arg_sigs, pyi_sig.arg_sigs): 525 | if not pyi_arg_sig.arg_type: 526 | continue 527 | new_type = clean_clone(pyi_arg_sig.arg_type, False) 528 | arg_sig.insert_annotation(new_type) 529 | 530 | if pyi_sig.ret_type: 531 | src_sig.insert_ret_annotation(pyi_sig.ret_type) 532 | 533 | def get_comment_annotation(self, src_sig, pyi_sig): 534 | """Return function annotation as a comment string, doesn't modify tree.""" 535 | arg_types = [] 536 | for i, (arg_sig, pyi_arg_sig) in enumerate(zip(src_sig.arg_sigs, pyi_sig.arg_sigs)): 537 | is_first = (i == 0) 538 | new_type = clean_clone(pyi_arg_sig.arg_type, True) 539 | 540 | if new_type: 541 | new_type_str = str(new_type).strip() 542 | elif self.infer_should_annotate(src_sig, arg_sig, is_first): 543 | new_type_str = 'Any' 544 | else: 545 | continue 546 | 547 | arg_types.append(arg_sig.stars + new_type_str) 548 | 549 | ret_type = pyi_sig.ret_type 550 | if not ret_type: 551 | ret_type = self.infer_ret_type(src_sig) 552 | 553 | return '(' + ', '.join(arg_types) + ') -> ' + str(ret_type).strip() 554 | 555 | def can_annotate(self, src_sig): 556 | if src_sig.has_pep484_annotations or src_sig.has_comment_annotations: 557 | self.logger.warning('already annotated, skipping %s', src_sig) 558 | return False 559 | 560 | if src_sig.full_name not in self.parsed_pyi.funcs: 561 | self.logger.warning('no signature for %s, skipping', src_sig) 562 | return False 563 | 564 | pyi_sig = self.parsed_pyi.funcs[src_sig.full_name] 565 | 566 | if not pyi_sig.has_pep484_annotations: 567 | self.logger.warning('ignoring pyi definition with no annotations: %s', pyi_sig) 568 | return False 569 | 570 | if not self.func_sig_compatible(src_sig, pyi_sig): 571 | self.logger.warning('incompatible annotation, skipping %s', src_sig) 572 | return False 573 | 574 | return True 575 | 576 | def add_globals(self, node): 577 | """Add required globals to the root of node. Idempotent.""" 578 | if self.added_pyi_globals: 579 | return 580 | # TODO: get rid of this -- added to prevent adding .parsed_pyi.top_lines every time 581 | # we annotate a different function in the same file, but can break when we run the tool 582 | # twice on the same file. Have to do something like what touch_import does. 583 | self.added_pyi_globals = True 584 | 585 | imports, top_lines = self.parsed_pyi.imports, self.parsed_pyi.top_lines 586 | 587 | # Copy imports if not already present 588 | for pkg, names in imports: 589 | if names is None: 590 | # TODO: do ourselves, touch_import puts stuff above license headers 591 | touch_import(None, pkg, node) # == 'import pkg' 592 | else: 593 | for name in names: 594 | touch_import(pkg, name, node) 595 | 596 | root = find_root(node) 597 | 598 | import_idx = [idx for idx, node in enumerate(root.children) 599 | if self.import_pattern.match(node)] 600 | if import_idx: 601 | insert_pos = import_idx[-1] + 1 602 | else: 603 | insert_pos = 0 604 | 605 | # first string (normally docstring) 606 | for idx, node in enumerate(root.children): 607 | if (node.type == syms.simple_stmt and node.children and 608 | node.children[0].type == token.STRING): 609 | insert_pos = idx + 1 610 | break 611 | 612 | top_lines = '\n'.join(top_lines) 613 | top_lines = Util.parse_string(top_lines) # strips some newlines 614 | for offset, node in enumerate(top_lines.children[:-1]): 615 | root.insert_child(insert_pos + offset, node) 616 | 617 | @staticmethod 618 | def func_sig_compatible(src_sig, pyi_sig): 619 | """Can src_sig be annotated with the info in pyi_sig: number of arguments must match, 620 | they must have the same star signature and they can't be tuple arguments. 621 | """ 622 | 623 | if len(pyi_sig.arg_sigs) != len(src_sig.arg_sigs): 624 | return False 625 | 626 | for pyi, cur in zip(pyi_sig.arg_sigs, src_sig.arg_sigs): 627 | # Entirely skip functions that use tuple args 628 | if cur.is_tuple or pyi.is_tuple: 629 | return False 630 | 631 | # Stars are expected to match 632 | if cur.stars != pyi.stars: 633 | return False 634 | 635 | return True 636 | 637 | @staticmethod 638 | def infer_ret_type(src_sig): 639 | """Heuristic for return type of a function.""" 640 | if src_sig.short_name == '__init__' or not src_sig.has_return_exprs: 641 | return 'None' 642 | return 'Any' 643 | 644 | @staticmethod 645 | def infer_should_annotate(func, arg, at_start): 646 | """Heuristic for whether arg, in func, should be annotated.""" 647 | 648 | if func.is_method and at_start and 'staticmethod' not in func.decorators: 649 | # Don't annotate the first argument if it's named 'self'. 650 | # Don't annotate the first argument of a class method. 651 | if 'self' == arg.name or 'classmethod' in func.decorators: 652 | return False 653 | 654 | return True 655 | 656 | def set_pyi_string(self, pyi_string): 657 | """Set the annotations the fixer will use""" 658 | self.parsed_pyi = self.parse_pyi_string(pyi_string) 659 | self.added_pyi_globals = False 660 | 661 | def parse_pyi_string(self, text): 662 | """Parse .pyi string, return as ParsedPyi""" 663 | tree = Util.parse_string(text) 664 | 665 | funcs = {} 666 | for node, match_results in generate_matches(tree, self.pattern): 667 | sig = FuncSignature(node, match_results) 668 | 669 | if sig.full_name in funcs: 670 | self.logger.warning('Ignoring redefinition: %s', sig) 671 | else: 672 | funcs[sig.full_name] = sig 673 | 674 | imports = [] 675 | for node, match_results in generate_top_matches(tree, self.import_pattern): 676 | imp = self.parse_top_import(node, match_results) 677 | if imp: 678 | imports.append(imp) 679 | 680 | top_lines = [] 681 | for node, match_results in generate_top_matches(tree, self.assign_pattern): 682 | text = str(node).strip() 683 | 684 | # hack to avoid shadowing real variables -- proper solution is more complicated, 685 | # use util.find_binding 686 | if 'TypeVar' in text or (text and '_' == text[0]): 687 | top_lines.append(text) 688 | else: 689 | self.logger.warning("ignoring %s", repr(text)) 690 | 691 | return ParsedPyi(tuple(imports), top_lines, funcs) 692 | 693 | assign_pattern = compile_pattern(""" 694 | simple_stmt< expr_stmt any* > 695 | """) 696 | 697 | import_pattern = compile_pattern(""" 698 | simple_stmt< 699 | ( import_from< 'from' pkg=any+ 'import' ['('] names=any [')'] > | 700 | import_name< 'import' pkg=any+ > ) 701 | any* 702 | > 703 | """) 704 | import_as_pattern = compile_pattern("""import_as_name""") 705 | 706 | def parse_top_import(self, node, results): 707 | """Takes result of import_pattern, returns component strings: 708 | 709 | Examples: 710 | 711 | 'from pkg import a,b,c' gives 712 | ('pkg', ('a', 'b', 'c')) 713 | 714 | 'import pkg' gives 715 | ('pkg', None) 716 | 717 | 'from pkg import a as b' or 'import pkg as pkg2' are not supported. 718 | """ 719 | 720 | # TODO: this might have to be generalized to "get top-level statements that aren't 721 | # class or function definitions": 722 | # _T = typing.TypeVar('_T') is used in pyis. 723 | # Still not clear what is and isn't valid in a pyi... Could we have a loop? 724 | 725 | pkg, names = results['pkg'], results.get('names', None) 726 | pkg = ''.join(map(str, pkg)).strip() 727 | 728 | if names: 729 | is_import_as = any(True for _ in generate_matches(names, self.import_as_pattern)) 730 | 731 | if is_import_as: 732 | # fixer_util.touch_import doesn't handle this 733 | # If necessary, will have to stick import at top of .py file 734 | self.logger.warning('Ignoring unhandled import-as: %s', repr(str(node).strip())) 735 | return None 736 | 737 | names = split_comma(names.leaves()) 738 | for name in names: 739 | assert 1 == len(name) 740 | assert name[0].type in (token.NAME, token.STAR) 741 | names = [name[0].value for name in names] 742 | 743 | return pkg, names 744 | 745 | 746 | class StandaloneRefactoringTool(refactor.RefactoringTool): 747 | """Slightly modified RefactoringTool that makes the fixer accessible, for running outside of 748 | the standard 2to3 installation.""" 749 | 750 | def __init__(self, options): 751 | self._fixer = None 752 | super(StandaloneRefactoringTool, self).__init__([], options=options) 753 | 754 | def get_fixers(self): 755 | if self.fixer.order == 'pre': 756 | return [self.fixer], [] 757 | else: 758 | return [], [self.fixer] 759 | 760 | @property 761 | def fixer(self): 762 | if not self._fixer: 763 | self._fixer = FixMergePyi(self.options, self.fixer_log) 764 | return self._fixer 765 | 766 | ParsedPyi = namedtuple('ParsedPyi', 'imports top_lines funcs') 767 | 768 | 769 | def is_top_level(node): 770 | """Is node at top indentation level (module globals)""" 771 | return 0 == len(find_indentation(node)) 772 | 773 | 774 | def generate_matches(tree, pattern): 775 | """Generator yielding nodes in tree that match pattern.""" 776 | for node in tree.pre_order(): 777 | results = {} 778 | if pattern.match(node, results): 779 | yield node, results 780 | 781 | 782 | def generate_top_matches(node, pattern): 783 | """Generator yielding direct children of node that match pattern.""" 784 | for node in node.children: 785 | results = {} 786 | if pattern.match(node, results): 787 | yield node, results 788 | 789 | 790 | def clean_clone(node, strip_formatting): 791 | """Clone node so it can be inserted in a tree. Optionally strip formatting.""" 792 | if not node: 793 | return None 794 | 795 | if strip_formatting: 796 | # strip formatting and comments, represent as prettyfied string 797 | # For comment-style annotations, important to have a single line 798 | # TODO: this seems to work if node is a type annotation but will break for a general node 799 | # (example: 'import foo' -> 'importfoo' 800 | s = ''.join(', ' if token.COMMA == n.type else n.value for n in node.leaves()) 801 | assert s 802 | 803 | # parse back into a Node 804 | node = Util.parse_string(s) 805 | assert 2 == len(node.children) 806 | node = node.children[0] 807 | else: 808 | node = node.clone() 809 | 810 | node.parent = None 811 | 812 | # TODO: strip line numbers? Not clear if they matter 813 | return node 814 | 815 | 816 | def split_comma(nodes): 817 | """Take iterable of nodes, return list of lists of nodes""" 818 | def is_comma(n): return token.COMMA == n.type 819 | 820 | groups = itertools.groupby(nodes, is_comma) 821 | return [list(group) for comma, group in groups if not comma] 822 | 823 | 824 | def annotate_string(args, py_src, pyi_src): 825 | tool = StandaloneRefactoringTool(options={}) 826 | fixer = tool.fixer 827 | 828 | fixer.annotate_pep484 = not args.as_comments 829 | fixer.set_pyi_string(pyi_src) 830 | 831 | tree = tool.refactor_string(py_src + '\n', None) 832 | 833 | # tool.refactor_file knows how to handle encodings, use that 834 | 835 | annotated_src = str(tree)[:-1] 836 | 837 | return annotated_src 838 | 839 | 840 | def get_diff(a, b): 841 | import difflib 842 | a, b = a.split('\n'), b.split('\n') 843 | 844 | diff = difflib.Differ().compare(a, b) 845 | return '\n'.join(diff) 846 | 847 | 848 | def parse_args(argv): 849 | import argparse 850 | 851 | parser = argparse.ArgumentParser(prog=argv[0], 852 | description='Copy type annotations from file.pyi to file.py.', 853 | epilog='Outputs merged file to stdout.') 854 | 855 | group = parser.add_mutually_exclusive_group() 856 | 857 | group.add_argument('-w', action='store_true', 858 | help='overwrite file.py') 859 | 860 | parser.add_argument('--as-comments', action='store_true', 861 | help='insert type annotations as comments') 862 | 863 | group.add_argument('--diff', action='store_true', 864 | help='print out a diff') 865 | 866 | parser.add_argument('py', type=argparse.FileType('r'), metavar='file.py', 867 | help='python file to annotate') 868 | 869 | parser.add_argument('pyi', type=argparse.FileType('r'), metavar='file.pyi', 870 | help='PEP484 stub file with annotations for file.py') 871 | 872 | return parser.parse_args(argv[1:]) 873 | 874 | 875 | def main(argv=None): 876 | """Apply FixMergePyi to a source file without using the 2to3 main program. 877 | 878 | Needed so we can have our own options. 879 | """ 880 | logging.basicConfig(level=logging.DEBUG) 881 | 882 | if argv is None: 883 | argv = sys.argv 884 | args = parse_args(argv) 885 | 886 | py_src = args.py.read() 887 | pyi_src = args.pyi.read() 888 | 889 | annotated_src = annotate_string(args, py_src, pyi_src) 890 | src_changed = annotated_src != py_src 891 | 892 | if args.diff: 893 | if src_changed: 894 | diff = get_diff(py_src, annotated_src) 895 | print(diff) 896 | elif args.w: 897 | if src_changed: 898 | with open(args.py.name, 'w') as f: 899 | f.write(annotated_src) 900 | else: 901 | sys.stdout.write(annotated_src) 902 | 903 | if __name__ == '__main__': 904 | main() 905 | -------------------------------------------------------------------------------- /test_merge_pyi.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | import collections 3 | import difflib 4 | import logging 5 | import os 6 | import re 7 | import unittest 8 | 9 | import merge_pyi 10 | 11 | 12 | __all__ = ('TestBuilder', 13 | 'load_tests') 14 | 15 | 16 | PY, PYI = 'py', 'pyi' 17 | OVERWRITE_EXPECTED = 0 # flip to regenerate expected files 18 | 19 | 20 | def load_tests(unused_loader, standard_tests, unused_pattern): 21 | standard_tests.addTests(TestBuilder().build('testdata')) 22 | return standard_tests 23 | 24 | 25 | class TestBuilder(object): 26 | 27 | def build(self, data_dir): 28 | """Return a unittest.TestSuite with regression tests for the files in data_dir.""" 29 | files_by_base = self._get_files_by_base(data_dir) 30 | 31 | args_list = [ 32 | Args(as_comments=0), 33 | Args(as_comments=1), 34 | ] 35 | 36 | suite = unittest.TestSuite() 37 | for args in args_list: 38 | arg_suite = unittest.TestSuite() 39 | suite.addTest(arg_suite) 40 | 41 | for base, files_by_ext in sorted(files_by_base.items()): 42 | if not (PY in files_by_ext and PYI in files_by_ext): 43 | continue 44 | 45 | if not OVERWRITE_EXPECTED and args.expected_ext not in files_by_ext: 46 | continue 47 | 48 | py, pyi = [files_by_ext[x] for x in (PY, PYI)] 49 | outfile = os.path.join(data_dir, base + '.' + args.expected_ext) 50 | 51 | test = RegressionTest(args, py, pyi, outfile) 52 | arg_suite.addTest(test) 53 | 54 | return suite 55 | 56 | def _get_files_by_base(self, data_dir): 57 | files = os.listdir(data_dir) 58 | 59 | file_pat = re.compile(r'(?P(?P.+?)\.(?P.*))$') 60 | matches = [m for m in map(file_pat.match, files) if m] 61 | ret = collections.defaultdict(dict) 62 | for m in matches: 63 | base, ext, filename = m.group('base'), m.group('ext'), m.group('filename') 64 | ret[base][ext] = os.path.join(data_dir, filename) 65 | 66 | return ret 67 | 68 | 69 | class Args(object): 70 | 71 | def __init__(self, as_comments=False): 72 | self.as_comments = as_comments 73 | 74 | @property 75 | def expected_ext(self): 76 | """Extension of expected filename.""" 77 | exts = { 78 | 0: 'pep484', 79 | 1: 'comment', 80 | } 81 | return exts[int(self.as_comments)] + '.py' 82 | 83 | 84 | class RegressionTest(unittest.TestCase): 85 | 86 | def __init__(self, args, py, pyi, outfile): 87 | super(RegressionTest, self).__init__('run_test') 88 | self.args = args # merge_pyi args 89 | self.py = py 90 | self.pyi = pyi 91 | self.outfile = outfile 92 | 93 | def __str__(self): 94 | return os.path.basename(self.outfile) 95 | 96 | def run_test(self): 97 | py_input, pyi_src = [_read_file(f) for f in (self.py, self.pyi)] 98 | 99 | output = merge_pyi.annotate_string(self.args, py_input, pyi_src) 100 | 101 | if OVERWRITE_EXPECTED: 102 | with open(self.outfile, 'w') as f: 103 | f.write(output) 104 | else: 105 | expected = _read_file(self.outfile) 106 | self.failUnlessEqual(expected, output, 107 | _get_diff(expected, output)) 108 | 109 | 110 | def _read_file(filename): 111 | with open(filename) as f: 112 | return f.read() 113 | 114 | 115 | def _get_diff(a, b): 116 | a, b = a.split('\n'), b.split('\n') 117 | 118 | diff = difflib.Differ().compare(a, b) 119 | return '\n'.join(diff) 120 | 121 | 122 | if __name__ == '__main__': 123 | logging.basicConfig(level=logging.CRITICAL) 124 | unittest.main() 125 | -------------------------------------------------------------------------------- /testdata/decoration.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def decoration(func): 3 | return func 4 | 5 | @decoration 6 | def f1(a): 7 | # type: (t) -> r 8 | pass 9 | -------------------------------------------------------------------------------- /testdata/decoration.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def decoration(func): 3 | return func 4 | 5 | @decoration 6 | def f1(a: t) -> r: 7 | pass 8 | -------------------------------------------------------------------------------- /testdata/decoration.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def decoration(func): 3 | return func 4 | 5 | @decoration 6 | def f1(a): 7 | pass 8 | -------------------------------------------------------------------------------- /testdata/decoration.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x : t) -> r: ... 3 | -------------------------------------------------------------------------------- /testdata/defaults.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x="foo"): 3 | # type: (e1) -> r1 4 | pass 5 | 6 | def f2(x= 7 | # 8 | "foo"): 9 | # type: (e2) -> r2 10 | pass 11 | 12 | def f3(# 13 | x#= 14 | = # 15 | # 16 | 123# 17 | # 18 | ): 19 | # type: (e3) -> r3 20 | pass 21 | 22 | 23 | def f4(x=(1,2)): 24 | # type: (e4) -> r4 25 | pass 26 | 27 | def f5(x=(1,)): 28 | # type: (e5) -> r5 29 | pass 30 | 31 | def f6(x=int): 32 | # type: (e6) -> r6 33 | pass 34 | 35 | # static analysis would give error here 36 | def f7(x : int=int): 37 | pass 38 | 39 | def f8(x={1:2}): 40 | # type: (e8) -> r8 41 | pass 42 | 43 | def f9(x=[1,2][:1]): 44 | # type: (e9) -> r9 45 | pass 46 | -------------------------------------------------------------------------------- /testdata/defaults.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x:e1="foo") -> r1: 3 | pass 4 | 5 | def f2(x:e2= 6 | # 7 | "foo") -> r2: 8 | pass 9 | 10 | def f3(# 11 | x:e3#= 12 | = # 13 | # 14 | 123# 15 | # 16 | ) -> r3: 17 | pass 18 | 19 | 20 | def f4(x:e4=(1,2)) -> r4: 21 | pass 22 | 23 | def f5(x:e5=(1,)) -> r5: 24 | pass 25 | 26 | def f6(x:e6=int) -> r6: 27 | pass 28 | 29 | # static analysis would give error here 30 | def f7(x : int=int): 31 | pass 32 | 33 | def f8(x:e8={1:2}) -> r8: 34 | pass 35 | 36 | def f9(x:e9=[1,2][:1]) -> r9: 37 | pass 38 | -------------------------------------------------------------------------------- /testdata/defaults.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x="foo"): 3 | pass 4 | 5 | def f2(x= 6 | # 7 | "foo"): 8 | pass 9 | 10 | def f3(# 11 | x#= 12 | = # 13 | # 14 | 123# 15 | # 16 | ): 17 | pass 18 | 19 | 20 | def f4(x=(1,2)): 21 | pass 22 | 23 | def f5(x=(1,)): 24 | pass 25 | 26 | def f6(x=int): 27 | pass 28 | 29 | # static analysis would give error here 30 | def f7(x : int=int): 31 | pass 32 | 33 | def f8(x={1:2}): 34 | pass 35 | 36 | def f9(x=[1,2][:1]): 37 | pass 38 | -------------------------------------------------------------------------------- /testdata/defaults.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x:e1) -> r1: ... 3 | def f2(x:e2) -> r2: ... 4 | def f3(x:e3) -> r3: ... 5 | def f4(x:e4) -> r4: ... 6 | def f5(x:e5) -> r5: ... 7 | def f6(x:e6) -> r6: ... 8 | def f7(x:e7) -> r7: ... 9 | def f8(x:e8) -> r8: ... 10 | def f9(x:e9) -> r9: ... 11 | -------------------------------------------------------------------------------- /testdata/heuristics.comment.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 3 | # If not annotate_pep484, info in pyi files is augmented with heuristics to decide if un-annotated 4 | # arguments are "Any" or "" (like "self") 5 | 6 | class B(object): 7 | def __init__(self): 8 | pass 9 | 10 | def f(self, x): 11 | # type: (e1) -> None 12 | pass 13 | 14 | class C(object): 15 | def __init__(self, x): 16 | # type: (e2) -> None 17 | pass 18 | 19 | @staticmethod 20 | def f2(): 21 | pass 22 | 23 | @staticmethod 24 | def f3(x, y): 25 | # type: (Any, e3) -> None 26 | pass 27 | 28 | @classmethod 29 | def f4(cls): 30 | pass 31 | -------------------------------------------------------------------------------- /testdata/heuristics.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # If not annotate_pep484, info in pyi files is augmented with heuristics to decide if un-annotated 3 | # arguments are "Any" or "" (like "self") 4 | 5 | class B(object): 6 | def __init__(self): 7 | pass 8 | 9 | def f(self, x: e1): 10 | pass 11 | 12 | class C(object): 13 | def __init__(self, x: e2): 14 | pass 15 | 16 | @staticmethod 17 | def f2(): 18 | pass 19 | 20 | @staticmethod 21 | def f3(x, y: e3): 22 | pass 23 | 24 | @classmethod 25 | def f4(cls): 26 | pass 27 | -------------------------------------------------------------------------------- /testdata/heuristics.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # If not annotate_pep484, info in pyi files is augmented with heuristics to decide if un-annotated 3 | # arguments are "Any" or "" (like "self") 4 | 5 | class B(object): 6 | def __init__(self): 7 | pass 8 | 9 | def f(self, x): 10 | pass 11 | 12 | class C(object): 13 | def __init__(self, x): 14 | pass 15 | 16 | @staticmethod 17 | def f2(): 18 | pass 19 | 20 | @staticmethod 21 | def f3(x, y): 22 | pass 23 | 24 | @classmethod 25 | def f4(cls): 26 | pass 27 | -------------------------------------------------------------------------------- /testdata/heuristics.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # Most of these are handled by our no-annotations logic 3 | 4 | class B(object): 5 | def __init__(self): ... 6 | def f(self, x: e1): ... 7 | 8 | class C(object): 9 | def __init__(self, x: e2): ... 10 | 11 | @staticmethod 12 | def f2(): ... 13 | 14 | @staticmethod 15 | def f3(x, y: e3): ... 16 | 17 | @classmethod 18 | def f4(cls): ... 19 | -------------------------------------------------------------------------------- /testdata/imports.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | import existing_import 3 | from m1 import A 4 | from m2 import B 5 | from m2 import C 6 | import m3 7 | from m4 import D 8 | from m4 import E 9 | from m5.m6 import F 10 | from m7 import G 11 | from m10 import * 12 | from m12 import a 13 | from m12 import b 14 | from m12 import c 15 | from m13 import a 16 | from m13 import b 17 | import q3 18 | from ......m16 import g 19 | import o1 as o2 20 | 21 | def f(x): 22 | # type: (a) -> None 23 | pass 24 | 25 | -------------------------------------------------------------------------------- /testdata/imports.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | import existing_import 3 | from m1 import A 4 | from m2 import B 5 | from m2 import C 6 | import m3 7 | from m4 import D 8 | from m4 import E 9 | from m5.m6 import F 10 | from m7 import G 11 | from m10 import * 12 | from m12 import a 13 | from m12 import b 14 | from m12 import c 15 | from m13 import a 16 | from m13 import b 17 | import q3 18 | from ......m16 import g 19 | import o1 as o2 20 | 21 | def f(x: a): 22 | pass 23 | 24 | -------------------------------------------------------------------------------- /testdata/imports.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | import existing_import 3 | 4 | def f(x): 5 | pass 6 | 7 | -------------------------------------------------------------------------------- /testdata/imports.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # Following imports need to be copied into .py 3 | 4 | from m1 import A 5 | from m2 import B, C 6 | import m3 7 | 8 | from m4 import (D, 9 | E) 10 | 11 | from m5.m6 import F 12 | 13 | from m7 import G # foo 14 | 15 | if False: 16 | import m8 17 | 18 | def g(): 19 | import m9 20 | 21 | 22 | from m10 import * 23 | 24 | from m11 import m as n 25 | 26 | from m12 import (a, b, c) 27 | 28 | from m13 import (a, b, ) 29 | 30 | from m14 import a, b as c 31 | 32 | from m15 import a as a1, b as b1 33 | 34 | 35 | #asdasd 36 | if False: 37 | import q1 38 | 39 | 40 | # can't just look at first leaf's column to see if import is "top-level" 41 | if False: 42 | \ 43 | import q2 44 | 45 | 46 | # top-level, but first column is not 0 47 | \ 48 | import q3 49 | 50 | 51 | 52 | from ......m16 import g 53 | 54 | import existing_import 55 | 56 | import o1 as o2 57 | 58 | 59 | def f(x: a): ... 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /testdata/imports_notouch.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def func_without_annotation(x): 3 | pass -------------------------------------------------------------------------------- /testdata/imports_notouch.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def func_without_annotation(x): 3 | pass -------------------------------------------------------------------------------- /testdata/imports_notouch.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def func_without_annotation(x): 3 | pass -------------------------------------------------------------------------------- /testdata/imports_notouch.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # import should not be copied because we don't have any useful annotations 3 | import do_not_want 4 | 5 | def func(x:int): 6 | pass 7 | -------------------------------------------------------------------------------- /testdata/member_var.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # pytype generates member variable annotations as comments, check that fix_annotate ignores them 3 | # properly 4 | 5 | class C(object): 6 | def __init__(self, x): 7 | # type: (Union[complex, float, int, long]) -> None 8 | self.y = 1 + x 9 | -------------------------------------------------------------------------------- /testdata/member_var.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # pytype generates member variable annotations as comments, check that fix_annotate ignores them 3 | # properly 4 | 5 | class C(object): 6 | def __init__(self, x: Union[complex, float, int, long]) -> None: 7 | self.y = 1 + x 8 | -------------------------------------------------------------------------------- /testdata/member_var.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # pytype generates member variable annotations as comments, check that fix_annotate ignores them 3 | # properly 4 | 5 | class C(object): 6 | def __init__(self, x): 7 | self.y = 1 + x 8 | -------------------------------------------------------------------------------- /testdata/member_var.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | class C(object): 3 | y = ... # type: Union[complex, float, int, long] 4 | 5 | def __init__(self, x: Union[complex, float, int, long]) -> None: ... 6 | -------------------------------------------------------------------------------- /testdata/mismatch.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(*a): 3 | pass 4 | 5 | def f2(a): 6 | pass 7 | -------------------------------------------------------------------------------- /testdata/mismatch.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(*a): 3 | pass 4 | 5 | def f2(a): 6 | pass 7 | -------------------------------------------------------------------------------- /testdata/mismatch.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(*a): 3 | pass 4 | 5 | def f2(a): 6 | pass 7 | -------------------------------------------------------------------------------- /testdata/mismatch.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(a : t1) -> r1: ... 3 | def f2(*a : t2) -> r2: ... 4 | 5 | -------------------------------------------------------------------------------- /testdata/nonearg.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f(x): 3 | # type: (None) -> None 4 | pass 5 | -------------------------------------------------------------------------------- /testdata/nonearg.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f(x: None): 3 | pass 4 | -------------------------------------------------------------------------------- /testdata/nonearg.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f(x): 3 | pass 4 | -------------------------------------------------------------------------------- /testdata/nonearg.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f(x: None): ... 3 | -------------------------------------------------------------------------------- /testdata/oneliner.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x): pass 3 | def f2(x): pass 4 | def f3(x): pass 5 | -------------------------------------------------------------------------------- /testdata/oneliner.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x: e1): pass 3 | def f2(x) -> r2: pass 4 | def f3(x: e3) -> r3: pass 5 | -------------------------------------------------------------------------------- /testdata/oneliner.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x): pass 3 | def f2(x): pass 4 | def f3(x): pass 5 | -------------------------------------------------------------------------------- /testdata/oneliner.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x: e1): ... 3 | def f2(x) -> r2: ... 4 | def f3(x: e3) -> r3: ... 5 | -------------------------------------------------------------------------------- /testdata/packed_tuple.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # tuple unpacking 3 | def f1((a, b)): 4 | pass 5 | 6 | def f1b((a, b)): 7 | pass 8 | 9 | def f2((a, b, (c,d))=(1,2, (3,4))): 10 | pass 11 | 12 | def f3((a, b : int)): 13 | pass 14 | 15 | def f4((a, b : SomeType(a=(3,(4,3))))): 16 | pass 17 | 18 | def f5((((((a,)))))): 19 | pass 20 | 21 | def f6((((((a,)),),))): 22 | pass 23 | 24 | def f7( 25 | ( 26 | ( 27 | (((a,)),),))): 28 | pass 29 | -------------------------------------------------------------------------------- /testdata/packed_tuple.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # tuple unpacking 3 | def f1((a, b)): 4 | pass 5 | 6 | def f1b((a, b)): 7 | pass 8 | 9 | def f2((a, b, (c,d))=(1,2, (3,4))): 10 | pass 11 | 12 | def f3((a, b : int)): 13 | pass 14 | 15 | def f4((a, b : SomeType(a=(3,(4,3))))): 16 | pass 17 | 18 | def f5((((((a,)))))): 19 | pass 20 | 21 | def f6((((((a,)),),))): 22 | pass 23 | 24 | def f7( 25 | ( 26 | ( 27 | (((a,)),),))): 28 | pass 29 | -------------------------------------------------------------------------------- /testdata/packed_tuple.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # tuple unpacking 3 | def f1((a, b)): 4 | pass 5 | 6 | def f1b((a, b)): 7 | pass 8 | 9 | def f2((a, b, (c,d))=(1,2, (3,4))): 10 | pass 11 | 12 | def f3((a, b : int)): 13 | pass 14 | 15 | def f4((a, b : SomeType(a=(3,(4,3))))): 16 | pass 17 | 18 | def f5((((((a,)))))): 19 | pass 20 | 21 | def f6((((((a,)),),))): 22 | pass 23 | 24 | def f7( 25 | ( 26 | ( 27 | (((a,)),),))): 28 | pass 29 | -------------------------------------------------------------------------------- /testdata/packed_tuple.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | 3 | # should produce signature mismatch 4 | def f1(x : e1) -> r1: ... 5 | 6 | # properly annotated, but currently ignored 7 | def f1b((x : e1, y : e2)) -> r1: ... 8 | 9 | # should produce signature mismatch 10 | def f2(x : e2) -> r2: ... 11 | -------------------------------------------------------------------------------- /testdata/parse_error.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # syntax error doesn't get detected by 2to3 3 | 4 | def f(*): pass 5 | 6 | 7 | # Gets parsed as: 8 | # Node(funcdef, 9 | # [Leaf(1, 'def'), Leaf(1, 'f'), 10 | # Node(parameters, [Leaf(7, '('), Leaf(16, '*'), Leaf(8, ')')]), 11 | # Leaf(11, ':'), 12 | # Node(simple_stmt, [Leaf(1, 'pass')])]) 13 | -------------------------------------------------------------------------------- /testdata/parse_error.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | -------------------------------------------------------------------------------- /testdata/partial.comment.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 3 | def f1(a, b : int): 4 | pass 5 | 6 | def f2(a, b) -> int: 7 | pass 8 | 9 | def f3(a): 10 | # type: (Any) -> r3 11 | pass 12 | 13 | def f4(a, b): 14 | # type: (e4, Any) -> None 15 | pass 16 | 17 | def f5(a): 18 | # type: (Any) -> Any 19 | pass 20 | -------------------------------------------------------------------------------- /testdata/partial.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(a, b : int): 3 | pass 4 | 5 | def f2(a, b) -> int: 6 | pass 7 | 8 | def f3(a) -> r3: 9 | pass 10 | 11 | def f4(a: e4, b): 12 | pass 13 | 14 | def f5(a): 15 | # type: (Any) -> Any 16 | pass 17 | -------------------------------------------------------------------------------- /testdata/partial.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(a, b : int): 3 | pass 4 | 5 | def f2(a, b) -> int: 6 | pass 7 | 8 | def f3(a): 9 | pass 10 | 11 | def f4(a, b): 12 | pass 13 | 14 | def f5(a): 15 | # type: (Any) -> Any 16 | pass 17 | -------------------------------------------------------------------------------- /testdata/partial.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(a : e1, b : e2) -> r1: ... 3 | 4 | def f2(a : e1, b : e2) -> r2: ... 5 | 6 | def f3(a) -> r3: ... 7 | 8 | def f4(a : e4, b): ... 9 | 10 | def f5(a: e5) -> r5: ... 11 | 12 | -------------------------------------------------------------------------------- /testdata/pyi_variations.comment.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 3 | def f1(x): 4 | # type: (e1) -> r1 5 | pass 6 | 7 | def f2(x): 8 | # type: (Any) -> r2 9 | pass 10 | 11 | def f3(x): 12 | # type: (e3) -> None 13 | pass 14 | 15 | def f4(x): 16 | # type: (e4) -> r4 17 | pass 18 | 19 | def f5(x): 20 | # type: (e5) -> r5 21 | pass 22 | 23 | def f6(x): 24 | # type: (e6) -> r6 25 | pass 26 | 27 | def f7(x): 28 | # type: (e7) -> r7 29 | pass 30 | 31 | def f8(x): 32 | # type: (e8) -> \ 33 | r8 34 | pass 35 | 36 | def f9(x): 37 | # type: (Any) -> """ 38 | this is 39 | valid""" 40 | pass 41 | 42 | def f10(x): 43 | # type: (""" 44 | this is 45 | valid""") -> None 46 | pass 47 | -------------------------------------------------------------------------------- /testdata/pyi_variations.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x: e1) -> r1: 3 | pass 4 | 5 | def f2(x) -> r2: 6 | pass 7 | 8 | def f3(x: e3): 9 | pass 10 | 11 | def f4(x: e4) -> r4: 12 | pass 13 | 14 | def f5(x: # stripme 15 | e5) -> r5: 16 | pass 17 | 18 | def f6(x: e6) -> r6: 19 | pass 20 | 21 | def f7(x: e7) -> r7: 22 | pass 23 | 24 | def f8(x: \ 25 | e8) -> \ 26 | r8: 27 | pass 28 | 29 | def f9(x) -> """ 30 | this is 31 | valid""": 32 | pass 33 | 34 | def f10(x: """ 35 | this is 36 | valid"""): 37 | pass 38 | -------------------------------------------------------------------------------- /testdata/pyi_variations.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x): 3 | pass 4 | 5 | def f2(x): 6 | pass 7 | 8 | def f3(x): 9 | pass 10 | 11 | def f4(x): 12 | pass 13 | 14 | def f5(x): 15 | pass 16 | 17 | def f6(x): 18 | pass 19 | 20 | def f7(x): 21 | pass 22 | 23 | def f8(x): 24 | pass 25 | 26 | def f9(x): 27 | pass 28 | 29 | def f10(x): 30 | pass 31 | -------------------------------------------------------------------------------- /testdata/pyi_variations.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x : e1) -> r1: ... 3 | 4 | def f2(x) -> r2: ... 5 | 6 | def f3(x : e3): ... 7 | 8 | def f4(x : e4 = d4) -> r4: ... 9 | 10 | def f5(x : # stripme 11 | e5) -> r5: ... 12 | 13 | #trailing comma 14 | def f6(x : e6,) -> r6: ... 15 | 16 | def f7(x : e7 # stripme 17 | ) -> r7: ... 18 | 19 | def f8( \ 20 | x \ 21 | : \ 22 | e8 \ 23 | ) \ 24 | -> \ 25 | r8: \ 26 | ... 27 | 28 | def f9(a) -> """ 29 | this is 30 | valid""" : ... 31 | 32 | def f10(a: """ 33 | this is 34 | valid""") : ... 35 | -------------------------------------------------------------------------------- /testdata/redefine.comment.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 3 | def f1(x): 4 | # type: (Any) -> Union[int, str] 5 | return 1 6 | def f1(x): 7 | # type: (Any) -> Union[int, str] 8 | return 'foo' 9 | 10 | def f2(x): 11 | # type: (Any) -> None 12 | pass 13 | def f2(x,y): 14 | pass 15 | 16 | def f3(x): 17 | # type: (int) -> int 18 | return 1+x 19 | def f3(x): 20 | # type: (int) -> int 21 | return 'asd'+x 22 | -------------------------------------------------------------------------------- /testdata/redefine.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x) -> Union[int, str]: 3 | return 1 4 | def f1(x) -> Union[int, str]: 5 | return 'foo' 6 | 7 | def f2(x) -> None: 8 | pass 9 | def f2(x,y): 10 | pass 11 | 12 | def f3(x: int) -> int: 13 | return 1+x 14 | def f3(x: int) -> int: 15 | return 'asd'+x 16 | -------------------------------------------------------------------------------- /testdata/redefine.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x): 3 | return 1 4 | def f1(x): 5 | return 'foo' 6 | 7 | def f2(x): 8 | pass 9 | def f2(x,y): 10 | pass 11 | 12 | def f3(x): 13 | return 1+x 14 | def f3(x): 15 | return 'asd'+x 16 | -------------------------------------------------------------------------------- /testdata/redefine.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | #pytype output: merged f1 but not f3? 3 | def f1(x) -> Union[int, str]: ... 4 | def f2(x) -> None: ... 5 | def f2(x, y) -> None: ... 6 | def f3(x: int) -> int: ... 7 | def f3(x: Union[bytearray, str, unicode]) -> Union[str, unicode]: ... 8 | -------------------------------------------------------------------------------- /testdata/retval_heuristics.comment.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 3 | # With heuristics, have to pick between returning None or Any. If generating comment annotations, 4 | # heuristics matter even if we have a pyi 5 | 6 | 7 | def f1(x): 8 | # type: (e1) -> Any 9 | return 1 10 | 11 | def f2(x): 12 | # type: (e2) -> None 13 | pass 14 | 15 | def f3(x): 16 | # type: (e3) -> None 17 | return 18 | 19 | def f4(x): 20 | # type: (e4) -> None 21 | def f(y): 22 | return 1 23 | 24 | def f5(x): 25 | # type: (e5) -> Any 26 | return \ 27 | 1 28 | 29 | def f6(x): 30 | # type: (e6) -> None 31 | return # foo 32 | -------------------------------------------------------------------------------- /testdata/retval_heuristics.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # With heuristics, have to pick between returning None or Any. If generating comment annotations, 3 | # heuristics matter even if we have a pyi 4 | 5 | 6 | def f1(x: e1): 7 | return 1 8 | 9 | def f2(x: e2): 10 | pass 11 | 12 | def f3(x: e3): 13 | return 14 | 15 | def f4(x: e4): 16 | def f(y): 17 | return 1 18 | 19 | def f5(x: e5): 20 | return \ 21 | 1 22 | 23 | def f6(x: e6): 24 | return # foo 25 | -------------------------------------------------------------------------------- /testdata/retval_heuristics.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # With heuristics, have to pick between returning None or Any. If generating comment annotations, 3 | # heuristics matter even if we have a pyi 4 | 5 | 6 | def f1(x): 7 | return 1 8 | 9 | def f2(x): 10 | pass 11 | 12 | def f3(x): 13 | return 14 | 15 | def f4(x): 16 | def f(y): 17 | return 1 18 | 19 | def f5(x): 20 | return \ 21 | 1 22 | 23 | def f6(x): 24 | return # foo 25 | -------------------------------------------------------------------------------- /testdata/retval_heuristics.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x: e1): ... 3 | def f2(x: e2): ... 4 | def f3(x: e3): ... 5 | def f4(x: e4): ... 6 | def f5(x: e5): ... 7 | def f6(x: e6): ... 8 | -------------------------------------------------------------------------------- /testdata/scope.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | class C(object): 3 | def f(self, x): 4 | # type: (e1) -> r1 5 | pass 6 | 7 | def g(self): 8 | # type: () -> function 9 | def f(x): #gets ignored by pytype but fixer sees it, generates warning (FIXME?) 10 | return 1 11 | return f 12 | -------------------------------------------------------------------------------- /testdata/scope.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | class C(object): 3 | def f(self, x: e1) -> r1: 4 | pass 5 | 6 | def g(self) -> function: 7 | def f(x): #gets ignored by pytype but fixer sees it, generates warning (FIXME?) 8 | return 1 9 | return f 10 | -------------------------------------------------------------------------------- /testdata/scope.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | class C(object): 3 | def f(self, x): 4 | pass 5 | 6 | def g(self): 7 | def f(x): #gets ignored by pytype but fixer sees it, generates warning (FIXME?) 8 | return 1 9 | return f 10 | -------------------------------------------------------------------------------- /testdata/scope.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | class C(object): 3 | def f(self, x : e1) -> r1: ... 4 | def g(self) -> function: ... 5 | -------------------------------------------------------------------------------- /testdata/simple.comment.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 3 | def f1(a, b): 4 | # type: (Any, Any) -> r1 5 | """Doc""" 6 | return a+b 7 | 8 | def f2(a): 9 | # type: (Any) -> r2 10 | return 1 11 | 12 | def f3(a): 13 | return 1 14 | -------------------------------------------------------------------------------- /testdata/simple.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(a, b) -> r1: 3 | """Doc""" 4 | return a+b 5 | 6 | def f2(a) -> r2: 7 | return 1 8 | 9 | def f3(a): 10 | return 1 11 | -------------------------------------------------------------------------------- /testdata/simple.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(a, b): 3 | """Doc""" 4 | return a+b 5 | 6 | def f2(a): 7 | return 1 8 | 9 | def f3(a): 10 | return 1 11 | -------------------------------------------------------------------------------- /testdata/simple.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(a, b) -> r1: ... 3 | 4 | def f2(a) -> r2: ... 5 | 6 | # non-annotation 7 | def f3(a): ... 8 | -------------------------------------------------------------------------------- /testdata/stars.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(*a): 3 | pass 4 | 5 | def f2(**a): 6 | pass 7 | 8 | def f3(a, *b): 9 | pass 10 | 11 | def f4(a, **b): 12 | pass 13 | 14 | ## arg with default after *args is valid python3, not python2 15 | def f5(*a, b=1): 16 | pass 17 | 18 | def f6(*a, b=1, **c): 19 | pass 20 | 21 | def f7(x=1, *a, b=1, **c): 22 | pass 23 | 24 | def f8(#asd 25 | *a): 26 | pass 27 | -------------------------------------------------------------------------------- /testdata/stars.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(*a): 3 | pass 4 | 5 | def f2(**a): 6 | pass 7 | 8 | def f3(a, *b): 9 | pass 10 | 11 | def f4(a, **b): 12 | pass 13 | 14 | ## arg with default after *args is valid python3, not python2 15 | def f5(*a, b=1): 16 | pass 17 | 18 | def f6(*a, b=1, **c): 19 | pass 20 | 21 | def f7(x=1, *a, b=1, **c): 22 | pass 23 | 24 | def f8(#asd 25 | *a): 26 | pass 27 | -------------------------------------------------------------------------------- /testdata/stars.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(*a): 3 | pass 4 | 5 | def f2(**a): 6 | pass 7 | 8 | def f3(a, *b): 9 | pass 10 | 11 | def f4(a, **b): 12 | pass 13 | 14 | ## arg with default after *args is valid python3, not python2 15 | def f5(*a, b=1): 16 | pass 17 | 18 | def f6(*a, b=1, **c): 19 | pass 20 | 21 | def f7(x=1, *a, b=1, **c): 22 | pass 23 | 24 | def f8(#asd 25 | *a): 26 | pass 27 | -------------------------------------------------------------------------------- /testdata/stars.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def f1(x:e1) -> r1: ... 3 | -------------------------------------------------------------------------------- /testdata/trailing_comma.comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # Test trailing comma after last arg 3 | 4 | def h1(a,): 5 | # type: (a1) -> r1 6 | pass 7 | 8 | def h2(a,b,): 9 | # type: (a2, b2) -> r2 10 | pass 11 | 12 | def h3(a): 13 | # type: (a3) -> r3 14 | pass 15 | 16 | def h4(a): 17 | # type: (a4) -> r4 18 | pass 19 | -------------------------------------------------------------------------------- /testdata/trailing_comma.pep484.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # Test trailing comma after last arg 3 | 4 | def h1(a:a1,) -> r1: 5 | pass 6 | 7 | def h2(a:a2,b:b2,) -> r2: 8 | pass 9 | 10 | def h3(a:a3) -> r3: 11 | pass 12 | 13 | def h4(a:a4) -> r4: 14 | pass 15 | -------------------------------------------------------------------------------- /testdata/trailing_comma.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | # Test trailing comma after last arg 3 | 4 | def h1(a,): 5 | pass 6 | 7 | def h2(a,b,): 8 | pass 9 | 10 | def h3(a): 11 | pass 12 | 13 | def h4(a): 14 | pass 15 | -------------------------------------------------------------------------------- /testdata/trailing_comma.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | def h1(a:a1) -> r1 : ... 3 | 4 | def h2(a:a2, b:b2) -> r2: ... 5 | 6 | def h3(a:a3,) -> r3: ... 7 | 8 | def h4(a:a4 # 9 | , ) -> r4: ... 10 | 11 | -------------------------------------------------------------------------------- /testdata/typevar.comment.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from typing import TypeVar 3 | _KT = TypeVar('_KT') 4 | _VT = TypeVar('_VT') 5 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 6 | class UserDict(object): 7 | def __init__(self, initialdata = None): 8 | # type: (Dict[_KT, _VT]) -> None 9 | pass 10 | -------------------------------------------------------------------------------- /testdata/typevar.pep484.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from typing import TypeVar 3 | _KT = TypeVar('_KT') 4 | _VT = TypeVar('_VT') 5 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 6 | class UserDict(object): 7 | def __init__(self, initialdata: Dict[_KT, _VT] = None): 8 | pass 9 | -------------------------------------------------------------------------------- /testdata/typevar.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | class UserDict(object): 3 | def __init__(self, initialdata = None): 4 | pass 5 | -------------------------------------------------------------------------------- /testdata/typevar.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 Google Inc. (under http://www.apache.org/licenses/LICENSE-2.0) 2 | from typing import Dict, TypeVar 3 | 4 | _KT = TypeVar('_KT') 5 | _VT = TypeVar('_VT') 6 | 7 | class UserDict(Dict[_KT, _VT]): 8 | def __init__(self, initialdata: Dict[_KT, _VT] = ...): ... 9 | 10 | 11 | a,b = c, d 12 | 13 | a = 3 # comment 14 | 15 | # Below is unwanted 16 | 17 | f(a=2) 18 | 19 | f( 20 | a=2 21 | ) 22 | 23 | def f(): 24 | dontwant = 3 25 | --------------------------------------------------------------------------------