├── .gitignore ├── LICENSE.txt ├── MANIFEST.in ├── README.txt ├── ez_setup.py ├── libevent.py ├── samples ├── .gitignore ├── hello.py └── http.py ├── setup.cfg ├── setup.py ├── src ├── _libevent.c ├── pybase.c ├── pybase.h ├── pybuffer.c ├── pybuffer.h ├── pybufferevent.c ├── pybufferevent.h ├── pyevent.c ├── pyevent.h ├── pyhttp.c ├── pyhttp.h ├── pylistener.c └── pylistener.h ├── tests ├── .gitignore ├── __init__.py ├── test_buffer.py ├── test_bufferevent.py ├── test_event.py └── test_http.py └── version.py /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | *.egg-info 4 | *.egg 5 | *.pyc 6 | *.so 7 | *.pyd 8 | RELEASE-VERSION 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | 460 | How to Apply These Terms to Your New Libraries 461 | 462 | If you develop a new library, and you want it to be of the greatest 463 | possible use to the public, we recommend making it free software that 464 | everyone can redistribute and change. You can do so by permitting 465 | redistribution under these terms (or, alternatively, under the terms of the 466 | ordinary General Public License). 467 | 468 | To apply these terms, attach the following notices to the library. It is 469 | safest to attach them to the start of each source file to most effectively 470 | convey the exclusion of warranty; and each file should have at least the 471 | "copyright" line and a pointer to where the full notice is found. 472 | 473 | 474 | Copyright (C) 475 | 476 | This library is free software; you can redistribute it and/or 477 | modify it under the terms of the GNU Lesser General Public 478 | License as published by the Free Software Foundation; either 479 | version 2.1 of the License, or (at your option) any later version. 480 | 481 | This library is distributed in the hope that it will be useful, 482 | but WITHOUT ANY WARRANTY; without even the implied warranty of 483 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 484 | Lesser General Public License for more details. 485 | 486 | You should have received a copy of the GNU Lesser General Public 487 | License along with this library; if not, write to the Free Software 488 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 489 | 490 | Also add information on how to contact you by electronic and paper mail. 491 | 492 | You should also get your employer (if you work as a programmer) or your 493 | school, if any, to sign a "copyright disclaimer" for the library, if 494 | necessary. Here is a sample; alter the names: 495 | 496 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 497 | library `Frob' (a library for tweaking knobs) written by James Random Hacker. 498 | 499 | , 1 April 1990 500 | Ty Coon, President of Vice 501 | 502 | That's all there is to it! 503 | 504 | 505 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include ez_setup.py version.py *.txt 2 | include RELEASE-VERSION 3 | include tests/*.py 4 | include src/*.h 5 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | python-libevent - Python bindings for libevent 2 | ======================================================================== 3 | 4 | See the "samples" directory and the tests for now. 5 | -------------------------------------------------------------------------------- /ez_setup.py: -------------------------------------------------------------------------------- 1 | #!python 2 | """Bootstrap setuptools installation 3 | 4 | If you want to use setuptools in your package's setup.py, just include this 5 | file in the same directory with it, and add this to the top of your setup.py:: 6 | 7 | from ez_setup import use_setuptools 8 | use_setuptools() 9 | 10 | If you want to require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, you can do so by supplying 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import sys 17 | DEFAULT_VERSION = "0.6c11" 18 | DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] 19 | 20 | md5_data = { 21 | 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', 22 | 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', 23 | 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', 24 | 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', 25 | 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', 26 | 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', 27 | 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', 28 | 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', 29 | 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', 30 | 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', 31 | 'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090', 32 | 'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4', 33 | 'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7', 34 | 'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5', 35 | 'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de', 36 | 'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b', 37 | 'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2', 38 | 'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086', 39 | 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', 40 | 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', 41 | 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', 42 | 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', 43 | 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', 44 | 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', 45 | 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', 46 | 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', 47 | 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', 48 | 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', 49 | 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', 50 | 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', 51 | 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', 52 | 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', 53 | 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', 54 | 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', 55 | 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', 56 | 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', 57 | 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', 58 | 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', 59 | 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03', 60 | 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a', 61 | 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6', 62 | 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a', 63 | } 64 | 65 | import sys, os 66 | try: from hashlib import md5 67 | except ImportError: from md5 import md5 68 | 69 | def _validate_md5(egg_name, data): 70 | if egg_name in md5_data: 71 | digest = md5(data).hexdigest() 72 | if digest != md5_data[egg_name]: 73 | print >>sys.stderr, ( 74 | "md5 validation of %s failed! (Possible download problem?)" 75 | % egg_name 76 | ) 77 | sys.exit(2) 78 | return data 79 | 80 | def use_setuptools( 81 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, 82 | download_delay=15 83 | ): 84 | """Automatically find/download setuptools and make it available on sys.path 85 | 86 | `version` should be a valid setuptools version number that is available 87 | as an egg for download under the `download_base` URL (which should end with 88 | a '/'). `to_dir` is the directory where setuptools will be downloaded, if 89 | it is not already available. If `download_delay` is specified, it should 90 | be the number of seconds that will be paused before initiating a download, 91 | should one be required. If an older version of setuptools is installed, 92 | this routine will print a message to ``sys.stderr`` and raise SystemExit in 93 | an attempt to abort the calling script. 94 | """ 95 | was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules 96 | def do_download(): 97 | egg = download_setuptools(version, download_base, to_dir, download_delay) 98 | sys.path.insert(0, egg) 99 | import setuptools; setuptools.bootstrap_install_from = egg 100 | try: 101 | import pkg_resources 102 | except ImportError: 103 | return do_download() 104 | try: 105 | pkg_resources.require("setuptools>="+version); return 106 | except pkg_resources.VersionConflict, e: 107 | if was_imported: 108 | print >>sys.stderr, ( 109 | "The required version of setuptools (>=%s) is not available, and\n" 110 | "can't be installed while this script is running. Please install\n" 111 | " a more recent version first, using 'easy_install -U setuptools'." 112 | "\n\n(Currently using %r)" 113 | ) % (version, e.args[0]) 114 | sys.exit(2) 115 | else: 116 | del pkg_resources, sys.modules['pkg_resources'] # reload ok 117 | return do_download() 118 | except pkg_resources.DistributionNotFound: 119 | return do_download() 120 | 121 | def download_setuptools( 122 | version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, 123 | delay = 15 124 | ): 125 | """Download setuptools from a specified location and return its filename 126 | 127 | `version` should be a valid setuptools version number that is available 128 | as an egg for download under the `download_base` URL (which should end 129 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 130 | `delay` is the number of seconds to pause before an actual download attempt. 131 | """ 132 | import urllib2, shutil 133 | egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) 134 | url = download_base + egg_name 135 | saveto = os.path.join(to_dir, egg_name) 136 | src = dst = None 137 | if not os.path.exists(saveto): # Avoid repeated downloads 138 | try: 139 | from distutils import log 140 | if delay: 141 | log.warn(""" 142 | --------------------------------------------------------------------------- 143 | This script requires setuptools version %s to run (even to display 144 | help). I will attempt to download it for you (from 145 | %s), but 146 | you may need to enable firewall access for this script first. 147 | I will start the download in %d seconds. 148 | 149 | (Note: if this machine does not have network access, please obtain the file 150 | 151 | %s 152 | 153 | and place it in this directory before rerunning this script.) 154 | ---------------------------------------------------------------------------""", 155 | version, download_base, delay, url 156 | ); from time import sleep; sleep(delay) 157 | log.warn("Downloading %s", url) 158 | src = urllib2.urlopen(url) 159 | # Read/write all in one block, so we don't create a corrupt file 160 | # if the download is interrupted. 161 | data = _validate_md5(egg_name, src.read()) 162 | dst = open(saveto,"wb"); dst.write(data) 163 | finally: 164 | if src: src.close() 165 | if dst: dst.close() 166 | return os.path.realpath(saveto) 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | def main(argv, version=DEFAULT_VERSION): 204 | """Install or upgrade setuptools and EasyInstall""" 205 | try: 206 | import setuptools 207 | except ImportError: 208 | egg = None 209 | try: 210 | egg = download_setuptools(version, delay=0) 211 | sys.path.insert(0,egg) 212 | from setuptools.command.easy_install import main 213 | return main(list(argv)+[egg]) # we're done here 214 | finally: 215 | if egg and os.path.exists(egg): 216 | os.unlink(egg) 217 | else: 218 | if setuptools.__version__ == '0.0.1': 219 | print >>sys.stderr, ( 220 | "You have an obsolete version of setuptools installed. Please\n" 221 | "remove it from your system entirely before rerunning this script." 222 | ) 223 | sys.exit(2) 224 | 225 | req = "setuptools>="+version 226 | import pkg_resources 227 | try: 228 | pkg_resources.require(req) 229 | except pkg_resources.VersionConflict: 230 | try: 231 | from setuptools.command.easy_install import main 232 | except ImportError: 233 | from easy_install import main 234 | main(list(argv)+[download_setuptools(delay=0)]) 235 | sys.exit(0) # try to force an exit 236 | else: 237 | if argv: 238 | from setuptools.command.easy_install import main 239 | main(argv) 240 | else: 241 | print "Setuptools version",version,"or greater has been installed." 242 | print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' 243 | 244 | def update_md5(filenames): 245 | """Update our built-in md5 registry""" 246 | 247 | import re 248 | 249 | for name in filenames: 250 | base = os.path.basename(name) 251 | f = open(name,'rb') 252 | md5_data[base] = md5(f.read()).hexdigest() 253 | f.close() 254 | 255 | data = [" %r: %r,\n" % it for it in md5_data.items()] 256 | data.sort() 257 | repl = "".join(data) 258 | 259 | import inspect 260 | srcfile = inspect.getsourcefile(sys.modules[__name__]) 261 | f = open(srcfile, 'rb'); src = f.read(); f.close() 262 | 263 | match = re.search("\nmd5_data = {\n([^}]+)}", src) 264 | if not match: 265 | print >>sys.stderr, "Internal error!" 266 | sys.exit(2) 267 | 268 | src = src[:match.start(1)] + repl + src[match.end(1):] 269 | f = open(srcfile,'w') 270 | f.write(src) 271 | f.close() 272 | 273 | 274 | if __name__=='__main__': 275 | if len(sys.argv)>2 and sys.argv[1]=='--md5update': 276 | update_md5(sys.argv[2:]) 277 | else: 278 | main(sys.argv[1:]) 279 | 280 | 281 | 282 | 283 | 284 | 285 | -------------------------------------------------------------------------------- /libevent.py: -------------------------------------------------------------------------------- 1 | from _libevent import * 2 | import weakref 3 | 4 | class Timer(Event): 5 | """Simplified class for timers.""" 6 | 7 | __slots__ = ('_callback') 8 | 9 | def __init__(self, base, callback, userdata=None): 10 | if not callable(callback): 11 | raise TypeError("the callback must be callable") 12 | 13 | def _fire(evt, fd, what, userdata, selfref=weakref.ref(self)): 14 | """Special internal class to prevent circular references.""" 15 | self = selfref() 16 | if self is not None: 17 | self._callback(self, userdata) 18 | 19 | super(Timer, self).__init__(base, -1, 0, _fire, userdata) 20 | self._callback = callback 21 | 22 | class Signal(Event): 23 | """Simplified class for signals.""" 24 | 25 | __slots__ = ('_callback') 26 | 27 | def __init__(self, base, signum, callback, userdata=None): 28 | if not callable(callback): 29 | raise TypeError("the callback must be callable") 30 | 31 | def _fire(evt, fd, what, userdata, selfref=weakref.ref(self)): 32 | """Special internal class to prevent circular references.""" 33 | self = selfref() 34 | if self is not None: 35 | self._callback(self, fd, userdata) 36 | 37 | super(Signal, self).__init__(base, signum, EV_SIGNAL|EV_PERSIST, _fire, userdata) 38 | self._callback = callback 39 | -------------------------------------------------------------------------------- /samples/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | libevent.py 3 | *.so 4 | *.pyd 5 | -------------------------------------------------------------------------------- /samples/hello.py: -------------------------------------------------------------------------------- 1 | import errno 2 | import libevent 3 | import socket 4 | 5 | def event_ready(evt, fd, what, conn): 6 | if what & libevent.EV_TIMEOUT: 7 | # the client didn't send something for too long 8 | print 'Timeout' 9 | # close the socket... 10 | conn.close() 11 | # ...and stop the event loop 12 | evt.base.loopbreak() 13 | return 14 | 15 | if what & libevent.EV_READ: 16 | data = '' 17 | # get all available data 18 | while True: 19 | try: 20 | add = conn.recv(1024) 21 | if not add: 22 | break 23 | except socket.error, e: 24 | # we have non-blocking sockets, so EAGAIN is normal 25 | if e.args[0] != errno.EAGAIN: 26 | raise 27 | break 28 | 29 | data += add 30 | 31 | data = data.strip() 32 | print 'Received:', data 33 | if data == 'bye': 34 | # the client wants to close the connection 35 | print 'Closing' 36 | # close the socket... 37 | conn.close() 38 | # ...and stop the event loop 39 | evt.base.loopbreak() 40 | return 41 | 42 | def main(): 43 | # the classic hello world example... 44 | 45 | # we need an event base that will hold the events 46 | base = libevent.Base() 47 | print 'Initialized base with %r backend' % (base.method) 48 | 49 | # create listening socket 50 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 51 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 52 | s.bind(('localhost', 1234)) 53 | s.listen(1) 54 | print 'Running on %s:%d' % s.getsockname() 55 | 56 | # wait for client connection 57 | conn, addr = s.accept() 58 | conn.setblocking(False) 59 | print 'Connected from %s:%d' % addr 60 | 61 | # add event that waits for available data with a timeout of 5 seconds 62 | evt = libevent.Event(base, conn.fileno(), libevent.EV_READ|libevent.EV_PERSIST, event_ready, conn) 63 | evt.add(5) 64 | 65 | # run event loop until termination 66 | base.loop() 67 | 68 | if __name__ == '__main__': 69 | main() 70 | -------------------------------------------------------------------------------- /samples/http.py: -------------------------------------------------------------------------------- 1 | import errno 2 | import libevent 3 | import socket 4 | 5 | def readable(bev, userdata): 6 | bev.disable(libevent.EV_READ) 7 | print 'Readable:', (bev, userdata) 8 | data = bev.read(8192) 9 | print data 10 | bev.enable(libevent.EV_WRITE) 11 | 12 | def writable(bev, userdata): 13 | bev.disable(libevent.EV_WRITE) 14 | print 'Writable:', (bev, userdata) 15 | bev.write('lala') 16 | bev.enable(libevent.EV_READ) 17 | 18 | def event_received(bev, what, userdata): 19 | print 'Event:', (bev, what, userdata) 20 | 21 | def main(): 22 | # the classic hello world example... 23 | 24 | # we need an event base that will hold the events 25 | base = libevent.Base() 26 | print 'Initialized base with %r backend' % (base.method) 27 | 28 | # create listening socket 29 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 30 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 31 | s.bind(('localhost', 1234)) 32 | s.listen(1) 33 | print 'Running on %s:%d' % s.getsockname() 34 | 35 | # wait for client connection 36 | conn, addr = s.accept() 37 | conn.setblocking(False) 38 | print 'Connected from %s:%d' % addr 39 | 40 | bev = libevent.BufferEvent(base, conn.fileno()) 41 | bev.set_callbacks(readable, writable, event_received) 42 | bev.enable(libevent.EV_READ) 43 | bev.set_timeouts(5, 5) 44 | 45 | # run event loop until termination 46 | base.loop() 47 | 48 | if __name__ == '__main__': 49 | main() 50 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [egg_info] 2 | tag_build = dev 3 | tag_svn_revision = 1 4 | 5 | [aliases] 6 | release = egg_info -RDb '' 7 | source = register sdist 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python -u 2 | # 3 | # Python Bindings for libevent 4 | # 5 | # Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 6 | # 7 | # This library is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU Lesser General Public 9 | # License as published by the Free Software Foundation; either 10 | # version 2.1 of the License, or (at your option) any later version. 11 | # 12 | # This library is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | # Lesser General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU Lesser General Public 18 | # License along with this library; if not, write to the Free Software 19 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | # 21 | import sys, os 22 | 23 | try: 24 | from setuptools import setup, Extension 25 | except ImportError: 26 | from ez_setup import use_setuptools 27 | use_setuptools() 28 | 29 | from setuptools import setup, Extension 30 | 31 | import version 32 | 33 | LIBEVENT_ROOT = os.environ.get('LIBEVENT_ROOT') 34 | if LIBEVENT_ROOT is None: 35 | raise TypeError('Please set the environment variable LIBEVENT_ROOT ' \ 36 | 'to the path of your libevent root directory and make sure ' \ 37 | 'to pass "--with-pic" to configure when building it') 38 | 39 | descr = "Python bindings for libevent" 40 | modules = [ 41 | 'libevent', 42 | ] 43 | c_files = [ 44 | 'src/_libevent.c', 45 | 'src/pybase.c', 46 | 'src/pybuffer.c', 47 | 'src/pybufferevent.c', 48 | 'src/pyevent.c', 49 | 'src/pyhttp.c', 50 | 'src/pylistener.c', 51 | ] 52 | include_dirs = [ 53 | os.path.join(LIBEVENT_ROOT, 'include'), 54 | ] 55 | library_dirs = [ 56 | ] 57 | libraries = [ 58 | ] 59 | extra_link_args = [ 60 | ] 61 | if os.name == 'posix': 62 | # enable thread support 63 | extra_link_args.extend([ 64 | os.path.join(LIBEVENT_ROOT, '.libs', 'libevent.a'), 65 | os.path.join(LIBEVENT_ROOT, '.libs', 'libevent_pthreads.a'), 66 | '-lrt', 67 | '-lpthread', 68 | ]) 69 | libraries.append('rt') 70 | libraries.append('pthread') 71 | elif os.name == 'nt': 72 | # enable thread support 73 | extra_link_args.extend([ 74 | os.path.join(LIBEVENT_ROOT, 'libevent.lib'), 75 | ]) 76 | libraries.append('ws2_32') 77 | libraries.append('Advapi32') 78 | 79 | extens = [ 80 | Extension('_libevent', c_files, libraries=libraries, 81 | include_dirs=include_dirs, library_dirs=library_dirs, 82 | extra_link_args=extra_link_args), 83 | ] 84 | 85 | setup( 86 | name = "python-libevent", 87 | version = version.get_git_version(), 88 | description = descr, 89 | author = "Joachim Bauch", 90 | author_email = "mail@joachim-bauch.de", 91 | url = "http://www.joachim-bauch.de/projects/python-libevent/", 92 | download_url = "http://pypi.python.org/pypi/python-libevent/", 93 | license = 'LGPL', 94 | keywords = "libevent network", 95 | classifiers = [ 96 | 'Development Status :: 5 - Production/Stable', 97 | 'Programming Language :: Python', 98 | 'Topic :: Software Development :: Libraries :: Python Modules', 99 | 'Intended Audience :: Developers', 100 | 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 101 | 'Operating System :: OS Independent', 102 | ], 103 | py_modules = modules, 104 | ext_modules = extens, 105 | test_suite = 'tests.suite', 106 | ) 107 | -------------------------------------------------------------------------------- /src/_libevent.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #if defined(WITH_THREAD) 29 | #include 30 | #endif 31 | 32 | #include "pybase.h" 33 | #include "pyevent.h" 34 | #include "pybuffer.h" 35 | #include "pybufferevent.h" 36 | #include "pyhttp.h" 37 | #include "pylistener.h" 38 | 39 | #if !defined(PyModule_AddIntMacro) 40 | #define PyModule_AddIntMacro(module, name) PyModule_AddIntConstant(module, #name, name); 41 | #endif 42 | #if !defined(PyModule_AddStringMacro) 43 | #define PyModule_AddStringMacro(module, name) PyModule_AddStringConstant(module, #name, name); 44 | #endif 45 | 46 | static PyObject *pylog_callback; 47 | static PyObject *pyfatal_callback; 48 | 49 | static PyObject * 50 | enable_debug_mode(PyObject *self, PyObject *args) 51 | { 52 | event_enable_debug_mode(); 53 | Py_RETURN_NONE; 54 | } 55 | 56 | static PyObject * 57 | socket_get_error(PyObject *self, PyObject *args) 58 | { 59 | evutil_socket_t sock; 60 | 61 | if (!PyArg_ParseTuple(args, "i", &sock)) 62 | return NULL; 63 | 64 | return PyInt_FromLong(evutil_socket_geterror(sock)); 65 | } 66 | 67 | static PyObject * 68 | socket_error_to_string(PyObject *self, PyObject *args) 69 | { 70 | int errorcode; 71 | 72 | if (!PyArg_ParseTuple(args, "i", &errorcode)) 73 | return NULL; 74 | 75 | return PyString_FromString(evutil_socket_error_to_string(errorcode)); 76 | } 77 | 78 | static void 79 | _pylog_callback(int severity, const char *msg) 80 | { 81 | START_BLOCK_THREADS 82 | if (pylog_callback != NULL) { 83 | PyObject *result = PyObject_CallFunction(pylog_callback, "is", severity, msg); 84 | if (result == NULL) { 85 | PyErr_Print(); 86 | } else { 87 | Py_DECREF(result); 88 | } 89 | } 90 | END_BLOCK_THREADS 91 | } 92 | 93 | static PyObject * 94 | set_log_callback(PyObject *self, PyObject *args) 95 | { 96 | PyObject *cb; 97 | PyObject *tmp; 98 | 99 | if (!PyArg_ParseTuple(args, "O", &cb)) 100 | return NULL; 101 | 102 | if (cb != Py_None && !PyCallable_Check(cb)) { 103 | PyErr_Format(PyExc_TypeError, "expected a callable or None, not %s", cb->ob_type->tp_name); 104 | return NULL; 105 | } 106 | 107 | if (cb == pylog_callback) { 108 | // No change 109 | Py_RETURN_NONE; 110 | } 111 | 112 | tmp = pylog_callback; 113 | pylog_callback = NULL; 114 | Py_XDECREF(tmp); 115 | if (cb == Py_None) { 116 | event_set_log_callback(NULL); 117 | } else { 118 | pylog_callback = cb; 119 | Py_INCREF(cb); 120 | event_set_log_callback(_pylog_callback); 121 | } 122 | 123 | Py_RETURN_NONE; 124 | } 125 | 126 | static void 127 | _pyfatal_callback(int err) 128 | { 129 | START_BLOCK_THREADS 130 | if (pyfatal_callback != NULL) { 131 | PyObject *result = PyObject_CallFunction(pyfatal_callback, "i", err); 132 | if (result == NULL) { 133 | PyErr_Print(); 134 | } else { 135 | Py_DECREF(result); 136 | } 137 | } 138 | END_BLOCK_THREADS 139 | } 140 | 141 | static PyObject * 142 | set_fatal_callback(PyObject *self, PyObject *args) 143 | { 144 | PyObject *cb; 145 | PyObject *tmp; 146 | 147 | if (!PyArg_ParseTuple(args, "O", &cb)) 148 | return NULL; 149 | 150 | if (cb != Py_None && !PyCallable_Check(cb)) { 151 | PyErr_Format(PyExc_TypeError, "expected a callable or None, not %s", cb->ob_type->tp_name); 152 | return NULL; 153 | } 154 | 155 | if (cb == pyfatal_callback) { 156 | // No change 157 | Py_RETURN_NONE; 158 | } 159 | 160 | tmp = pyfatal_callback; 161 | pyfatal_callback = NULL; 162 | Py_XDECREF(tmp); 163 | if (cb == Py_None) { 164 | event_set_fatal_callback(NULL); 165 | } else { 166 | pyfatal_callback = cb; 167 | Py_INCREF(cb); 168 | event_set_fatal_callback(_pyfatal_callback); 169 | } 170 | 171 | Py_RETURN_NONE; 172 | } 173 | 174 | PyMethodDef 175 | methods[] = { 176 | // exported functions 177 | {"enable_debug_mode", (PyCFunction)enable_debug_mode, METH_NOARGS, NULL}, 178 | {"socket_get_error", (PyCFunction)socket_get_error, METH_VARARGS, NULL}, 179 | {"socket_error_to_string", (PyCFunction)socket_error_to_string, METH_VARARGS, NULL}, 180 | {"set_log_callback", (PyCFunction)set_log_callback, METH_VARARGS, NULL}, 181 | {"set_fatal_callback", (PyCFunction)set_fatal_callback, METH_VARARGS, NULL}, 182 | {NULL, NULL}, 183 | }; 184 | 185 | PyMODINIT_FUNC 186 | init_libevent(void) 187 | { 188 | PyObject *m; 189 | PyObject *base_methods; 190 | const char **evmethods; 191 | 192 | pylog_callback = NULL; 193 | pyfatal_callback = NULL; 194 | 195 | #if defined(WITH_THREAD) 196 | // enable thread support in Python 197 | PyEval_InitThreads(); 198 | 199 | // enable thread support in libevent 200 | #if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED) 201 | evthread_use_windows_threads(); 202 | #endif 203 | #if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED) 204 | evthread_use_pthreads(); 205 | #endif 206 | #endif 207 | 208 | PyEventBase_Type.tp_new = PyType_GenericNew; 209 | if (PyType_Ready(&PyEventBase_Type) < 0) 210 | return; 211 | 212 | PyConfig_Type.tp_new = PyType_GenericNew; 213 | if (PyType_Ready(&PyConfig_Type) < 0) 214 | return; 215 | 216 | PyEvent_Type.tp_new = PyType_GenericNew; 217 | if (PyType_Ready(&PyEvent_Type) < 0) 218 | return; 219 | 220 | PyEventBuffer_Type.tp_new = PyType_GenericNew; 221 | if (PyType_Ready(&PyEventBuffer_Type) < 0) 222 | return; 223 | 224 | PyBufferEvent_Type.tp_new = PyType_GenericNew; 225 | if (PyType_Ready(&PyBufferEvent_Type) < 0) 226 | return; 227 | 228 | PyBucketConfig_Type.tp_new = PyType_GenericNew; 229 | if (PyType_Ready(&PyBucketConfig_Type) < 0) 230 | return; 231 | 232 | PyHttpServer_Type.tp_new = PyType_GenericNew; 233 | if (PyType_Ready(&PyHttpServer_Type) < 0) 234 | return; 235 | 236 | PyBoundSocket_Type.tp_new = PyType_GenericNew; 237 | if (PyType_Ready(&PyBoundSocket_Type) < 0) 238 | return; 239 | 240 | PyHttpCallback_Type.tp_new = PyType_GenericNew; 241 | if (PyType_Ready(&PyHttpCallback_Type) < 0) 242 | return; 243 | 244 | PyHttpRequest_Type.tp_new = PyType_GenericNew; 245 | if (PyType_Ready(&PyHttpRequest_Type) < 0) 246 | return; 247 | 248 | PyListener_Type.tp_new = PyType_GenericNew; 249 | if (PyType_Ready(&PyListener_Type) < 0) 250 | return; 251 | 252 | base_methods = PyTuple_New(0); 253 | if (base_methods == NULL) { 254 | return; 255 | } 256 | evmethods = event_get_supported_methods(); 257 | while (*evmethods != NULL) { 258 | _PyTuple_Resize(&base_methods, PyTuple_GET_SIZE(base_methods)+1); 259 | if (base_methods == NULL) { 260 | return; 261 | } 262 | 263 | PyTuple_SET_ITEM(base_methods, PyTuple_GET_SIZE(base_methods)-1, PyString_FromString(*evmethods)); 264 | evmethods++; 265 | } 266 | 267 | m = Py_InitModule("_libevent", methods); 268 | 269 | PyModule_AddObject(m, "METHODS", base_methods); 270 | 271 | Py_INCREF(&PyEventBase_Type); 272 | PyModule_AddObject(m, "Base", (PyObject *)&PyEventBase_Type); 273 | Py_INCREF(&PyConfig_Type); 274 | PyModule_AddObject(m, "Config", (PyObject *)&PyConfig_Type); 275 | Py_INCREF(&PyEvent_Type); 276 | PyModule_AddObject(m, "Event", (PyObject *)&PyEvent_Type); 277 | Py_INCREF(&PyEventBuffer_Type); 278 | PyModule_AddObject(m, "Buffer", (PyObject *)&PyEventBuffer_Type); 279 | Py_INCREF(&PyBufferEvent_Type); 280 | PyModule_AddObject(m, "BufferEvent", (PyObject *)&PyBufferEvent_Type); 281 | Py_INCREF(&PyBucketConfig_Type); 282 | PyModule_AddObject(m, "BucketConfig", (PyObject *)&PyBucketConfig_Type); 283 | Py_INCREF(&PyHttpServer_Type); 284 | PyModule_AddObject(m, "HttpServer", (PyObject *)&PyHttpServer_Type); 285 | Py_INCREF(&PyBoundSocket_Type); 286 | PyModule_AddObject(m, "BoundSocket", (PyObject *)&PyBoundSocket_Type); 287 | Py_INCREF(&PyHttpRequest_Type); 288 | PyModule_AddObject(m, "HttpRequest", (PyObject *)&PyHttpRequest_Type); 289 | Py_INCREF(&PyListener_Type); 290 | PyModule_AddObject(m, "Listener", (PyObject *)&PyListener_Type); 291 | 292 | // event.h flags 293 | PyModule_AddIntMacro(m, EV_FEATURE_ET); 294 | PyModule_AddIntMacro(m, EV_FEATURE_O1); 295 | PyModule_AddIntMacro(m, EV_FEATURE_FDS); 296 | 297 | PyModule_AddIntMacro(m, EVENT_BASE_FLAG_NOLOCK); 298 | PyModule_AddIntMacro(m, EVENT_BASE_FLAG_IGNORE_ENV); 299 | PyModule_AddIntMacro(m, EVENT_BASE_FLAG_STARTUP_IOCP); 300 | PyModule_AddIntMacro(m, EVENT_BASE_FLAG_NO_CACHE_TIME); 301 | PyModule_AddIntMacro(m, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST); 302 | 303 | PyModule_AddIntMacro(m, EVLOOP_ONCE); 304 | PyModule_AddIntMacro(m, EVLOOP_NONBLOCK); 305 | 306 | PyModule_AddIntMacro(m, EV_TIMEOUT); 307 | PyModule_AddIntMacro(m, EV_READ); 308 | PyModule_AddIntMacro(m, EV_WRITE); 309 | PyModule_AddIntMacro(m, EV_SIGNAL); 310 | PyModule_AddIntMacro(m, EV_PERSIST); 311 | PyModule_AddIntMacro(m, EV_ET); 312 | 313 | PyModule_AddStringMacro(m, LIBEVENT_VERSION); 314 | PyModule_AddIntMacro(m, LIBEVENT_VERSION_NUMBER); 315 | PyModule_AddIntMacro(m, EVENT_MAX_PRIORITIES); 316 | 317 | PyModule_AddIntConstant(m, "EVENT_LOG_DEBUG", _EVENT_LOG_DEBUG); 318 | PyModule_AddIntConstant(m, "EVENT_LOG_MSG", _EVENT_LOG_MSG); 319 | PyModule_AddIntConstant(m, "EVENT_LOG_WARN", _EVENT_LOG_WARN); 320 | PyModule_AddIntConstant(m, "EVENT_LOG_ERR", _EVENT_LOG_ERR); 321 | 322 | // buffer.h flags 323 | PyModule_AddIntMacro(m, EVBUFFER_EOL_ANY); 324 | PyModule_AddIntMacro(m, EVBUFFER_EOL_CRLF); 325 | PyModule_AddIntMacro(m, EVBUFFER_EOL_CRLF_STRICT); 326 | PyModule_AddIntMacro(m, EVBUFFER_EOL_LF); 327 | 328 | PyModule_AddIntMacro(m, EVBUFFER_PTR_SET); 329 | PyModule_AddIntMacro(m, EVBUFFER_PTR_ADD); 330 | 331 | // bufferevent.h flags 332 | PyModule_AddIntMacro(m, BEV_EVENT_READING); 333 | PyModule_AddIntMacro(m, BEV_EVENT_WRITING); 334 | PyModule_AddIntMacro(m, BEV_EVENT_EOF); 335 | PyModule_AddIntMacro(m, BEV_EVENT_ERROR); 336 | PyModule_AddIntMacro(m, BEV_EVENT_TIMEOUT); 337 | PyModule_AddIntMacro(m, BEV_EVENT_CONNECTED); 338 | 339 | PyModule_AddIntMacro(m, BEV_OPT_CLOSE_ON_FREE); 340 | PyModule_AddIntMacro(m, BEV_OPT_THREADSAFE); 341 | PyModule_AddIntMacro(m, BEV_OPT_DEFER_CALLBACKS); 342 | PyModule_AddIntMacro(m, BEV_OPT_UNLOCK_CALLBACKS); 343 | 344 | PyModule_AddIntMacro(m, EV_RATE_LIMIT_MAX); 345 | 346 | // http.h flags 347 | PyModule_AddIntMacro(m, HTTP_OK); 348 | PyModule_AddIntMacro(m, HTTP_NOCONTENT); 349 | PyModule_AddIntMacro(m, HTTP_MOVEPERM); 350 | PyModule_AddIntMacro(m, HTTP_MOVETEMP); 351 | PyModule_AddIntMacro(m, HTTP_NOTMODIFIED); 352 | PyModule_AddIntMacro(m, HTTP_BADREQUEST); 353 | PyModule_AddIntMacro(m, HTTP_NOTFOUND); 354 | PyModule_AddIntMacro(m, HTTP_BADMETHOD); 355 | PyModule_AddIntMacro(m, HTTP_INTERNAL); 356 | PyModule_AddIntMacro(m, HTTP_BADMETHOD); 357 | PyModule_AddIntMacro(m, HTTP_NOTIMPLEMENTED); 358 | PyModule_AddIntMacro(m, HTTP_SERVUNAVAIL); 359 | 360 | PyModule_AddIntMacro(m, EVHTTP_REQ_GET); 361 | PyModule_AddIntMacro(m, EVHTTP_REQ_POST); 362 | PyModule_AddIntMacro(m, EVHTTP_REQ_HEAD); 363 | PyModule_AddIntMacro(m, EVHTTP_REQ_PUT); 364 | PyModule_AddIntMacro(m, EVHTTP_REQ_DELETE); 365 | PyModule_AddIntMacro(m, EVHTTP_REQ_OPTIONS); 366 | PyModule_AddIntMacro(m, EVHTTP_REQ_TRACE); 367 | PyModule_AddIntMacro(m, EVHTTP_REQ_CONNECT); 368 | PyModule_AddIntMacro(m, EVHTTP_REQ_PATCH); 369 | 370 | // listener.h flags 371 | PyModule_AddIntMacro(m, LEV_OPT_LEAVE_SOCKETS_BLOCKING); 372 | PyModule_AddIntMacro(m, LEV_OPT_CLOSE_ON_FREE); 373 | PyModule_AddIntMacro(m, LEV_OPT_CLOSE_ON_EXEC); 374 | PyModule_AddIntMacro(m, LEV_OPT_REUSEABLE); 375 | PyModule_AddIntMacro(m, LEV_OPT_THREADSAFE); 376 | } 377 | -------------------------------------------------------------------------------- /src/pybase.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include "pybase.h" 28 | 29 | #ifdef WIN32 30 | #define suseconds_t long 31 | #endif 32 | 33 | typedef struct _PyConfigObject { 34 | PyObject_HEAD 35 | struct event_config *config; 36 | } PyConfigObject; 37 | 38 | void 39 | timeval_init(struct timeval *tv, double time) 40 | { 41 | tv->tv_sec = (time_t) time; 42 | tv->tv_usec = (suseconds_t) ((time - tv->tv_sec) * 1000000); 43 | } 44 | 45 | void 46 | pybase_store_error(PyEventBaseObject *self) 47 | { 48 | if (self->error_type == NULL) { 49 | // Store exception for later reuse and signal loop to stop 50 | PyErr_Fetch(&self->error_type, &self->error_value, &self->error_traceback); 51 | Py_BEGIN_ALLOW_THREADS 52 | event_base_loopbreak(self->base); 53 | Py_END_ALLOW_THREADS 54 | } 55 | } 56 | 57 | static PyObject * 58 | pybase_evalute_error_response(PyEventBaseObject *self) 59 | { 60 | if (self->error_type != NULL) { 61 | // The loop was interrupted due to an error, re-raise 62 | PyErr_Restore(self->error_type, self->error_value, self->error_traceback); 63 | self->error_type = NULL; 64 | self->error_value = NULL; 65 | self->error_traceback = NULL; 66 | return NULL; 67 | } 68 | 69 | Py_RETURN_NONE; 70 | } 71 | 72 | static PyObject * 73 | pybase_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 74 | { 75 | PyEventBaseObject *s; 76 | s = (PyEventBaseObject *)type->tp_alloc(type, 0); 77 | if (s != NULL) { 78 | s->base = NULL; 79 | s->method = NULL; 80 | s->features = 0; 81 | s->error_type = NULL; 82 | s->error_value = NULL; 83 | s->error_traceback = NULL; 84 | } 85 | return (PyObject *)s; 86 | } 87 | 88 | static int 89 | pybase_init(PyEventBaseObject *self, PyObject *args, PyObject *kwds) 90 | { 91 | PyConfigObject *cfg=NULL; 92 | if (!PyArg_ParseTuple(args, "|O!", &PyConfig_Type, &cfg)) 93 | return -1; 94 | 95 | if (cfg == NULL) { 96 | self->base = event_base_new(); 97 | } else { 98 | self->base = event_base_new_with_config(cfg->config); 99 | } 100 | if (self->base == NULL) { 101 | PyErr_NoMemory(); 102 | return -1; 103 | } 104 | self->method = PyString_FromString(event_base_get_method(self->base)); 105 | self->features = event_base_get_features(self->base); 106 | return 0; 107 | } 108 | 109 | static void 110 | pybase_dealloc(PyEventBaseObject *self) 111 | { 112 | Py_XDECREF(self->error_type); 113 | Py_XDECREF(self->error_value); 114 | Py_XDECREF(self->error_traceback); 115 | Py_DECREF(self->method); 116 | if (self->base != NULL) { 117 | Py_BEGIN_ALLOW_THREADS 118 | event_base_free(self->base); 119 | Py_END_ALLOW_THREADS 120 | } 121 | Py_TYPE(self)->tp_free(self); 122 | } 123 | 124 | PyDoc_STRVAR(pybase_reinit_doc, "Reinitialized the event base after a fork."); 125 | 126 | static PyObject * 127 | pybase_reinit(PyEventBaseObject *self, PyObject *args) 128 | { 129 | Py_BEGIN_ALLOW_THREADS 130 | event_reinit(self->base); 131 | Py_END_ALLOW_THREADS 132 | Py_RETURN_NONE; 133 | } 134 | 135 | PyDoc_STRVAR(pybase_dispatch_doc, "Threadsafe event dispatching loop."); 136 | 137 | static PyObject * 138 | pybase_dispatch(PyEventBaseObject *self, PyObject *args) 139 | { 140 | Py_BEGIN_ALLOW_THREADS 141 | event_base_dispatch(self->base); 142 | Py_END_ALLOW_THREADS 143 | return pybase_evalute_error_response(self); 144 | } 145 | 146 | PyDoc_STRVAR(pybase_loop_doc, "Handle events (threadsafe version)."); 147 | 148 | static PyObject * 149 | pybase_loop(PyEventBaseObject *self, PyObject *args) 150 | { 151 | int flags=0; 152 | if (!PyArg_ParseTuple(args, "|i", &flags)) 153 | return NULL; 154 | 155 | Py_BEGIN_ALLOW_THREADS 156 | event_base_loop(self->base, flags); 157 | Py_END_ALLOW_THREADS 158 | return pybase_evalute_error_response(self); 159 | } 160 | 161 | PyDoc_STRVAR(pybase_loopexit_doc, "Exit the event loop after the specified time (threadsafe variant)."); 162 | 163 | static PyObject * 164 | pybase_loopexit(PyEventBaseObject *self, PyObject *args) 165 | { 166 | double duration; 167 | struct timeval tv; 168 | if (!PyArg_ParseTuple(args, "d", &duration)) 169 | return NULL; 170 | 171 | timeval_init(&tv, duration); 172 | Py_BEGIN_ALLOW_THREADS 173 | event_base_loopexit(self->base, &tv); 174 | Py_END_ALLOW_THREADS 175 | Py_RETURN_NONE; 176 | } 177 | 178 | PyDoc_STRVAR(pybase_loopbreak_doc, "Abort the active loop() immediately."); 179 | 180 | static PyObject * 181 | pybase_loopbreak(PyEventBaseObject *self, PyObject *args) 182 | { 183 | Py_BEGIN_ALLOW_THREADS 184 | event_base_loopbreak(self->base); 185 | Py_END_ALLOW_THREADS 186 | Py_RETURN_NONE; 187 | } 188 | 189 | PyDoc_STRVAR(pybase_got_exit_doc, "Checks if the event loop was told to exit by loopexit()."); 190 | 191 | static PyObject * 192 | pybase_got_exit(PyEventBaseObject *self, PyObject *args) 193 | { 194 | int result; 195 | Py_BEGIN_ALLOW_THREADS 196 | result = event_base_got_exit(self->base); 197 | Py_END_ALLOW_THREADS; 198 | return PyBool_FromLong(result); 199 | } 200 | 201 | PyDoc_STRVAR(pybase_got_break_doc, "Checks if the event loop was told to abort immediately by loopbreak()."); 202 | 203 | static PyObject * 204 | pybase_got_break(PyEventBaseObject *self, PyObject *args) 205 | { 206 | int result; 207 | Py_BEGIN_ALLOW_THREADS 208 | result = event_base_got_break(self->base); 209 | Py_END_ALLOW_THREADS; 210 | return PyBool_FromLong(result); 211 | } 212 | 213 | PyDoc_STRVAR(pybase_priority_init_doc, "Set the number of different event priorities (threadsafe variant)."); 214 | 215 | static PyObject * 216 | pybase_priority_init(PyEventBaseObject *self, PyObject *args) 217 | { 218 | int priorities; 219 | if (!PyArg_ParseTuple(args, "i", &priorities)) 220 | return NULL; 221 | 222 | Py_BEGIN_ALLOW_THREADS 223 | event_base_priority_init(self->base, priorities); 224 | Py_END_ALLOW_THREADS 225 | Py_RETURN_NONE; 226 | } 227 | 228 | static PyMethodDef 229 | pybase_methods[] = { 230 | {"reinit", (PyCFunction)pybase_reinit, METH_NOARGS, pybase_reinit_doc}, 231 | {"dispatch", (PyCFunction)pybase_dispatch, METH_NOARGS, pybase_dispatch_doc}, 232 | {"loop", (PyCFunction)pybase_loop, METH_VARARGS, pybase_loop_doc}, 233 | {"loopexit", (PyCFunction)pybase_loopexit, METH_VARARGS, pybase_loopexit_doc}, 234 | {"loopbreak", (PyCFunction)pybase_loopbreak, METH_NOARGS, pybase_loopbreak_doc}, 235 | {"got_exit", (PyCFunction)pybase_got_exit, METH_NOARGS, pybase_got_exit_doc}, 236 | {"got_break", (PyCFunction)pybase_got_break, METH_NOARGS, pybase_got_break_doc}, 237 | {"priority_init", (PyCFunction)pybase_priority_init, METH_VARARGS, pybase_priority_init_doc}, 238 | {NULL, NULL}, 239 | }; 240 | 241 | static PyMemberDef 242 | pybase_members[] = { 243 | {"method", T_OBJECT, offsetof(PyEventBaseObject, method), READONLY, "kernel event notification mechanism"}, 244 | {"features", T_INT, offsetof(PyEventBaseObject, features), READONLY, "bitmask of the features implemented"}, 245 | {NULL} 246 | }; 247 | 248 | PyDoc_STRVAR(pybase_doc, "Event base"); 249 | 250 | PyTypeObject 251 | PyEventBase_Type = { 252 | PyObject_HEAD_INIT(NULL) 253 | 0, /* tp_internal */ 254 | "event.Base", /* tp_name */ 255 | sizeof(PyEventBaseObject), /* tp_basicsize */ 256 | 0, /* tp_itemsize */ 257 | (destructor)pybase_dealloc, /* tp_dealloc */ 258 | 0, /* tp_print */ 259 | 0, /* tp_getattr */ 260 | 0, /* tp_setattr */ 261 | 0, /* tp_compare */ 262 | 0, /* tp_repr */ 263 | 0, /* tp_as_number */ 264 | 0, /* tp_as_sequence */ 265 | 0, /* tp_as_mapping */ 266 | 0, /* tp_hash */ 267 | 0, /* tp_call */ 268 | 0, /* tp_str */ 269 | 0, /* tp_getattro */ 270 | 0, /* tp_setattro */ 271 | 0, /* tp_as_buffer */ 272 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 273 | pybase_doc, /* tp_doc */ 274 | 0, /* tp_traverse */ 275 | 0, /* tp_clear */ 276 | 0, /* tp_richcompare */ 277 | 0, /* tp_weaklistoffset */ 278 | 0, /* tp_iter */ 279 | 0, /* tp_iternext */ 280 | pybase_methods, /* tp_methods */ 281 | pybase_members, /* tp_members */ 282 | 0, /* tp_getset */ 283 | 0, /* tp_base */ 284 | 0, /* tp_dict */ 285 | 0, /* tp_descr_get */ 286 | 0, /* tp_descr_set */ 287 | 0, /* tp_dictoffset */ 288 | (initproc)pybase_init, /* tp_init */ 289 | 0, /* tp_alloc */ 290 | pybase_new, /* tp_new */ 291 | 0, /* tp_free */ 292 | }; 293 | 294 | static PyObject * 295 | pyconfig_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 296 | { 297 | PyConfigObject *s = (PyConfigObject *)type->tp_alloc(type, 0); 298 | if (s != NULL) { 299 | s->config = NULL; 300 | } 301 | return (PyObject *)s; 302 | } 303 | 304 | static int 305 | pyconfig_init(PyConfigObject *self, PyObject *args, PyObject *kwds) 306 | { 307 | if (!PyArg_ParseTuple(args, "", args)) 308 | return -1; 309 | 310 | self->config = event_config_new(); 311 | if (self->config == NULL) { 312 | PyErr_NoMemory(); 313 | return -1; 314 | } 315 | return 0; 316 | } 317 | 318 | static void 319 | pyconfig_dealloc(PyConfigObject *self) 320 | { 321 | if (self->config != NULL) { 322 | event_config_free(self->config); 323 | } 324 | Py_TYPE(self)->tp_free(self); 325 | } 326 | 327 | PyDoc_STRVAR(pyconfig_avoid_method_doc, "Enters an event method that should be avoided into the configuration."); 328 | 329 | static PyObject * 330 | pyconfig_avoid_method(PyConfigObject *self, PyObject *args) 331 | { 332 | char *method; 333 | 334 | if (!PyArg_ParseTuple(args, "s", &method)) 335 | return NULL; 336 | 337 | event_config_avoid_method(self->config, method); 338 | Py_RETURN_NONE; 339 | } 340 | 341 | PyDoc_STRVAR(pyconfig_require_features_doc, "Enters a required event method feature that the application demands."); 342 | 343 | static PyObject * 344 | pyconfig_require_features(PyConfigObject *self, PyObject *args) 345 | { 346 | int features; 347 | 348 | if (!PyArg_ParseTuple(args, "i", &features)) 349 | return NULL; 350 | 351 | event_config_require_features(self->config, features); 352 | Py_RETURN_NONE; 353 | } 354 | 355 | PyDoc_STRVAR(pyconfig_set_flag_doc, "Sets one or more flags to configure what parts of the eventual event_base will be initialized, and how they'll work."); 356 | 357 | static PyObject * 358 | pyconfig_set_flag(PyConfigObject *self, PyObject *args) 359 | { 360 | int flag; 361 | 362 | if (!PyArg_ParseTuple(args, "i", &flag)) 363 | return NULL; 364 | 365 | event_config_set_flag(self->config, flag); 366 | Py_RETURN_NONE; 367 | } 368 | 369 | PyDoc_STRVAR(pyconfig_set_num_cpus_hint_doc, "Records a hint for the number of CPUs in the system."); 370 | 371 | static PyObject * 372 | pyconfig_set_num_cpus_hint(PyConfigObject *self, PyObject *args) 373 | { 374 | int cpus; 375 | 376 | if (!PyArg_ParseTuple(args, "i", &cpus)) 377 | return NULL; 378 | 379 | event_config_set_num_cpus_hint(self->config, cpus); 380 | Py_RETURN_NONE; 381 | } 382 | 383 | static PyMethodDef 384 | pyconfig_methods[] = { 385 | {"avoid_method", (PyCFunction)pyconfig_avoid_method, METH_VARARGS, pyconfig_avoid_method_doc}, 386 | {"require_features", (PyCFunction)pyconfig_require_features, METH_VARARGS, pyconfig_require_features_doc}, 387 | {"set_flag", (PyCFunction)pyconfig_set_flag, METH_VARARGS, pyconfig_set_flag_doc}, 388 | {"set_num_cpus_hint", (PyCFunction)pyconfig_set_num_cpus_hint, METH_VARARGS, pyconfig_set_num_cpus_hint_doc}, 389 | {NULL, NULL}, 390 | }; 391 | 392 | PyDoc_STRVAR(pyconfig_doc, "Configuration object"); 393 | 394 | PyTypeObject 395 | PyConfig_Type = { 396 | PyObject_HEAD_INIT(NULL) 397 | 0, /* tp_internal */ 398 | "event.Config", /* tp_name */ 399 | sizeof(PyConfigObject), /* tp_basicsize */ 400 | 0, /* tp_itemsize */ 401 | (destructor)pyconfig_dealloc, /* tp_dealloc */ 402 | 0, /* tp_print */ 403 | 0, /* tp_getattr */ 404 | 0, /* tp_setattr */ 405 | 0, /* tp_compare */ 406 | 0, /* tp_repr */ 407 | 0, /* tp_as_number */ 408 | 0, /* tp_as_sequence */ 409 | 0, /* tp_as_mapping */ 410 | 0, /* tp_hash */ 411 | 0, /* tp_call */ 412 | 0, /* tp_str */ 413 | 0, /* tp_getattro */ 414 | 0, /* tp_setattro */ 415 | 0, /* tp_as_buffer */ 416 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 417 | pyconfig_doc, /* tp_doc */ 418 | 0, /* tp_traverse */ 419 | 0, /* tp_clear */ 420 | 0, /* tp_richcompare */ 421 | 0, /* tp_weaklistoffset */ 422 | 0, /* tp_iter */ 423 | 0, /* tp_iternext */ 424 | pyconfig_methods, /* tp_methods */ 425 | 0, /* tp_members */ 426 | 0, /* tp_getset */ 427 | 0, /* tp_base */ 428 | 0, /* tp_dict */ 429 | 0, /* tp_descr_get */ 430 | 0, /* tp_descr_set */ 431 | 0, /* tp_dictoffset */ 432 | (initproc)pyconfig_init, /* tp_init */ 433 | 0, /* tp_alloc */ 434 | pyconfig_new, /* tp_new */ 435 | 0, /* tp_free */ 436 | }; 437 | -------------------------------------------------------------------------------- /src/pybase.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #ifndef ___EVENT_PYEVENTBASE__H___ 22 | #define ___EVENT_PYEVENTBASE__H___ 23 | 24 | #include 25 | #include 26 | 27 | #if defined(WITH_THREAD) 28 | #define START_BLOCK_THREADS \ 29 | PyGILState_STATE __savestate = PyGILState_Ensure(); 30 | #define END_BLOCK_THREADS \ 31 | PyGILState_Release(__savestate); 32 | #else 33 | #define START_BLOCK_THREADS 34 | #define END_BLOCK_THREADS 35 | #endif 36 | 37 | #if !defined(Py_TYPE) 38 | #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type) 39 | #endif 40 | 41 | #if !defined(PyLong_FromSsize_t) 42 | #define PyLong_FromSsize_t(v) PyLong_FromLong(v) 43 | #endif 44 | 45 | typedef struct _PyEventBaseObject { 46 | PyObject_HEAD 47 | struct event_base *base; 48 | PyObject *method; 49 | int features; 50 | PyObject *error_type; 51 | PyObject *error_value; 52 | PyObject *error_traceback; 53 | } PyEventBaseObject; 54 | 55 | extern PyTypeObject PyEventBase_Type; 56 | extern PyTypeObject PyConfig_Type; 57 | 58 | extern void timeval_init(struct timeval *tv, double time); 59 | extern void pybase_store_error(PyEventBaseObject *self); 60 | 61 | #define PyEventBase_Check(ob) ((ob)->ob_type == &PyEventBase_Type) 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/pybuffer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include "pybase.h" 27 | #include "pybuffer.h" 28 | 29 | static PyObject * 30 | pybuffer_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 31 | { 32 | PyBufferObject *s; 33 | s = (PyBufferObject *)type->tp_alloc(type, 0); 34 | if (s != NULL) { 35 | s->buffer = NULL; 36 | s->base = NULL; 37 | s->owned = 0; 38 | } 39 | return (PyObject *)s; 40 | } 41 | 42 | PyBufferObject * 43 | _pybuffer_create(struct evbuffer *buffer) 44 | { 45 | PyBufferObject *result = (PyBufferObject *)PyEventBuffer_Type.tp_alloc(&PyEventBuffer_Type, 0); 46 | if (result == NULL) { 47 | return NULL; 48 | } 49 | 50 | result->buffer = buffer; 51 | result->base = NULL; 52 | result->owned = 0; 53 | return result; 54 | } 55 | 56 | static int 57 | pybuffer_init(PyBufferObject *self, PyObject *args, PyObject *kwds) 58 | { 59 | self->buffer = evbuffer_new(); 60 | if (self->buffer == NULL) { 61 | PyErr_NoMemory(); 62 | return -1; 63 | } 64 | 65 | self->owned = 1; 66 | return 0; 67 | } 68 | 69 | static void 70 | pybuffer_dealloc(PyBufferObject *self) 71 | { 72 | Py_BEGIN_ALLOW_THREADS 73 | if (self->owned && self->buffer != NULL) { 74 | evbuffer_free(self->buffer); 75 | } 76 | Py_END_ALLOW_THREADS 77 | Py_XDECREF(self->base); 78 | Py_TYPE(self)->tp_free(self); 79 | } 80 | 81 | PyDoc_STRVAR(buffer_enable_locking_doc, "Enable locking on an evbuffer."); 82 | 83 | static PyObject * 84 | pybuffer_enable_locking(PyBufferObject *self, PyObject *args) 85 | { 86 | #if defined(WITH_THREAD) 87 | Py_BEGIN_ALLOW_THREADS 88 | evbuffer_enable_locking(self->buffer, NULL); 89 | Py_END_ALLOW_THREADS 90 | #endif 91 | Py_RETURN_NONE; 92 | } 93 | 94 | PyDoc_STRVAR(buffer_lock_doc, "Acquire the lock on an evbuffer."); 95 | 96 | static PyObject * 97 | pybuffer_lock(PyBufferObject *self, PyObject *args) 98 | { 99 | Py_BEGIN_ALLOW_THREADS 100 | evbuffer_lock(self->buffer); 101 | Py_END_ALLOW_THREADS 102 | Py_RETURN_NONE; 103 | } 104 | 105 | PyDoc_STRVAR(buffer_unlock_doc, "Release the lock on an evbuffer."); 106 | 107 | static PyObject * 108 | pybuffer_unlock(PyBufferObject *self, PyObject *args) 109 | { 110 | Py_BEGIN_ALLOW_THREADS 111 | evbuffer_unlock(self->buffer); 112 | Py_END_ALLOW_THREADS 113 | Py_RETURN_NONE; 114 | } 115 | 116 | PyDoc_STRVAR(buffer_get_contiguous_space_doc, "Returns the number of contiguous available bytes in the first buffer chain."); 117 | 118 | static PyObject * 119 | pybuffer_get_contiguous_space(PyBufferObject *self, PyObject *args) 120 | { 121 | size_t result; 122 | Py_BEGIN_ALLOW_THREADS 123 | result = evbuffer_get_contiguous_space(self->buffer); 124 | Py_END_ALLOW_THREADS 125 | return PyLong_FromUnsignedLong(result); 126 | } 127 | 128 | PyDoc_STRVAR(buffer_expand_doc, "Expands the available space in an event buffer."); 129 | 130 | static PyObject * 131 | pybuffer_expand(PyBufferObject *self, PyObject *args) 132 | { 133 | Py_ssize_t size; 134 | 135 | if (!PyArg_ParseTuple(args, "n", &size)) 136 | return NULL; 137 | 138 | Py_BEGIN_ALLOW_THREADS 139 | evbuffer_expand(self->buffer, size); 140 | Py_END_ALLOW_THREADS 141 | Py_RETURN_NONE; 142 | } 143 | 144 | PyDoc_STRVAR(buffer_add_doc, "Append data to the end of an evbuffer."); 145 | 146 | static void 147 | _pybuffer_decref(const void *data, size_t datalen, void *extra) 148 | { 149 | PyObject *obj = (PyObject *) extra; 150 | START_BLOCK_THREADS 151 | Py_DECREF(obj); 152 | END_BLOCK_THREADS 153 | } 154 | 155 | static PyObject * 156 | pybuffer_add(PyBufferObject *self, PyObject *args) 157 | { 158 | PyObject *pydata; 159 | char *data; 160 | Py_ssize_t length; 161 | int result; 162 | 163 | if (!PyArg_ParseTuple(args, "O", &pydata)) 164 | return NULL; 165 | 166 | if (PyEventBuffer_Check(pydata)) { 167 | Py_BEGIN_ALLOW_THREADS 168 | result = evbuffer_add_buffer(self->buffer, ((PyBufferObject *) pydata)->buffer); 169 | Py_END_ALLOW_THREADS 170 | } else { 171 | if (PyObject_AsReadBuffer(pydata, (const void **) &data, &length) != 0) { 172 | return NULL; 173 | } 174 | 175 | Py_BEGIN_ALLOW_THREADS 176 | evbuffer_lock(self->buffer); 177 | result = evbuffer_add_reference(self->buffer, data, length, _pybuffer_decref, pydata); 178 | Py_END_ALLOW_THREADS 179 | if (result >= 0) { 180 | Py_INCREF(pydata); 181 | } 182 | Py_BEGIN_ALLOW_THREADS 183 | evbuffer_unlock(self->buffer); 184 | Py_END_ALLOW_THREADS 185 | } 186 | if (result < 0) { 187 | PyErr_SetString(PyExc_TypeError, "could not add data to buffer"); 188 | return NULL; 189 | } 190 | 191 | Py_RETURN_NONE; 192 | } 193 | 194 | PyDoc_STRVAR(buffer_remove_doc, "Read data from an event buffer and drain the bytes read."); 195 | 196 | static PyObject * 197 | pybuffer_remove(PyBufferObject *self, PyObject *args) 198 | { 199 | char *data; 200 | Py_ssize_t size; 201 | Py_ssize_t length=-1; 202 | PyObject *result; 203 | int unlock=0; 204 | 205 | if (!PyArg_ParseTuple(args, "|n", &length)) 206 | return NULL; 207 | 208 | if (length == -1) { 209 | Py_BEGIN_ALLOW_THREADS 210 | evbuffer_lock(self->buffer); 211 | length = evbuffer_get_length(self->buffer); 212 | if (length == 0) { 213 | evbuffer_unlock(self->buffer); 214 | } else { 215 | unlock = 1; 216 | } 217 | Py_END_ALLOW_THREADS 218 | } else if (length < 0) { 219 | PyErr_Format(PyExc_TypeError, "can't read %d bytes from the buffer", (int) length); 220 | return NULL; 221 | } 222 | 223 | if (length == 0) { 224 | return PyString_FromString(""); 225 | } 226 | 227 | result = PyString_FromStringAndSize(NULL, length); 228 | if (result == NULL) { 229 | if (unlock) { 230 | Py_BEGIN_ALLOW_THREADS 231 | evbuffer_unlock(self->buffer); 232 | Py_END_ALLOW_THREADS 233 | } 234 | return PyErr_NoMemory(); 235 | } 236 | 237 | data = PyString_AS_STRING(result); 238 | Py_BEGIN_ALLOW_THREADS 239 | size = evbuffer_remove(self->buffer, data, length); 240 | if (unlock) { 241 | evbuffer_unlock(self->buffer); 242 | } 243 | Py_END_ALLOW_THREADS 244 | if (size < 0) { 245 | Py_DECREF(result); 246 | PyErr_SetString(PyExc_TypeError, "could not remove data from buffer"); 247 | result = NULL; 248 | } else if (size == 0) { 249 | Py_DECREF(result); 250 | result = PyString_FromString(""); 251 | } else if (size != length) { 252 | _PyString_Resize(&result, size); 253 | } 254 | return result; 255 | } 256 | 257 | PyDoc_STRVAR(buffer_copyout_doc, "Read data from an event buffer, and leave the buffer unchanged."); 258 | 259 | static PyObject * 260 | pybuffer_copyout(PyBufferObject *self, PyObject *args) 261 | { 262 | char *data; 263 | ev_ssize_t size; 264 | Py_ssize_t length=-1; 265 | PyObject *result; 266 | int unlock=0; 267 | 268 | if (!PyArg_ParseTuple(args, "|n", &length)) 269 | return NULL; 270 | 271 | if (length == -1) { 272 | Py_BEGIN_ALLOW_THREADS 273 | evbuffer_lock(self->buffer); 274 | length = evbuffer_get_length(self->buffer); 275 | if (length == 0) { 276 | evbuffer_unlock(self->buffer); 277 | } else { 278 | unlock = 1; 279 | } 280 | Py_END_ALLOW_THREADS 281 | } else if (length < 0) { 282 | PyErr_Format(PyExc_TypeError, "can't read %d bytes from the buffer", (int) length); 283 | return NULL; 284 | } 285 | 286 | if (length == 0) { 287 | return PyString_FromString(""); 288 | } 289 | 290 | result = PyString_FromStringAndSize(NULL, length); 291 | if (result == NULL) { 292 | if (unlock) { 293 | Py_BEGIN_ALLOW_THREADS 294 | evbuffer_unlock(self->buffer); 295 | Py_END_ALLOW_THREADS 296 | } 297 | return PyErr_NoMemory(); 298 | } 299 | 300 | data = PyString_AS_STRING(result); 301 | Py_BEGIN_ALLOW_THREADS 302 | size = evbuffer_copyout(self->buffer, data, length); 303 | if (unlock) { 304 | evbuffer_unlock(self->buffer); 305 | } 306 | Py_END_ALLOW_THREADS 307 | if (size < 0) { 308 | Py_DECREF(result); 309 | PyErr_SetString(PyExc_TypeError, "could not copy data from buffer"); 310 | result = NULL; 311 | } else if (size == 0) { 312 | Py_DECREF(result); 313 | result = PyString_FromString(""); 314 | } else if (size != length) { 315 | _PyString_Resize(&result, size); 316 | } 317 | return result; 318 | } 319 | 320 | PyDoc_STRVAR(buffer_remove_buffer_doc, "Read data from an event buffer into another event buffer draining the bytes from the src buffer read."); 321 | 322 | static PyObject * 323 | pybuffer_remove_buffer(PyBufferObject *self, PyObject *args) 324 | { 325 | PyBufferObject *dst; 326 | ev_ssize_t size; 327 | Py_ssize_t length; 328 | 329 | if (!PyArg_ParseTuple(args, "O!n", &PyBuffer_Type, &dst, &length)) 330 | return NULL; 331 | 332 | Py_BEGIN_ALLOW_THREADS 333 | size = evbuffer_remove_buffer(self->buffer, dst->buffer, length); 334 | Py_END_ALLOW_THREADS 335 | if (size < 0) { 336 | PyErr_SetString(PyExc_TypeError, "could not remove data from buffer"); 337 | return NULL; 338 | } 339 | Py_RETURN_NONE; 340 | } 341 | 342 | PyDoc_STRVAR(buffer_readln_doc, "Read a single line from an event buffer."); 343 | 344 | static PyObject * 345 | pybuffer_readln(PyBufferObject *self, PyObject *args) 346 | { 347 | int flags=EVBUFFER_EOL_ANY; 348 | char *data; 349 | size_t size; 350 | PyObject *result; 351 | 352 | if (!PyArg_ParseTuple(args, "|i", &flags)) 353 | return NULL; 354 | 355 | Py_BEGIN_ALLOW_THREADS 356 | data = evbuffer_readln(self->buffer, &size, flags); 357 | Py_END_ALLOW_THREADS 358 | if (data == NULL) { 359 | result = PyString_FromString(""); 360 | } else { 361 | result = PyString_FromStringAndSize(data, size); 362 | free(data); 363 | } 364 | return result; 365 | } 366 | 367 | PyDoc_STRVAR(buffer_add_file_doc, "Move data from a file into the evbuffer for writing to a socket."); 368 | 369 | static PyObject * 370 | pybuffer_add_file(PyBufferObject *self, PyObject *args) 371 | { 372 | int fd; 373 | Py_ssize_t offset; 374 | Py_ssize_t length; 375 | int result; 376 | 377 | if (!PyArg_ParseTuple(args, "inn", &fd, &offset, &length)) 378 | return NULL; 379 | 380 | Py_BEGIN_ALLOW_THREADS 381 | result = evbuffer_add_file(self->buffer, fd, offset, length); 382 | Py_END_ALLOW_THREADS 383 | if (result < 0) { 384 | PyErr_SetString(PyExc_TypeError, "could not add data from file to the buffer"); 385 | return NULL; 386 | } 387 | Py_RETURN_NONE; 388 | } 389 | 390 | PyDoc_STRVAR(buffer_drain_doc, "Remove a specified number of bytes data from the beginning of a buffer."); 391 | 392 | static PyObject * 393 | pybuffer_drain(PyBufferObject *self, PyObject *args) 394 | { 395 | Py_ssize_t length; 396 | int result; 397 | 398 | if (!PyArg_ParseTuple(args, "n", &length)) 399 | return NULL; 400 | 401 | Py_BEGIN_ALLOW_THREADS 402 | result = evbuffer_drain(self->buffer, length); 403 | Py_END_ALLOW_THREADS 404 | if (result < 0) { 405 | PyErr_SetString(PyExc_TypeError, "could not drain data from the buffer"); 406 | return NULL; 407 | } 408 | Py_RETURN_NONE; 409 | } 410 | 411 | PyDoc_STRVAR(buffer_write_doc, "Write the contents of an evbuffer to a file descriptor."); 412 | 413 | static PyObject * 414 | pybuffer_write(PyBufferObject *self, PyObject *args) 415 | { 416 | int fd; 417 | int result; 418 | int length=-1; 419 | 420 | if (!PyArg_ParseTuple(args, "i|i", &fd, &length)) 421 | return NULL; 422 | 423 | Py_BEGIN_ALLOW_THREADS 424 | if (length < 0) { 425 | result = evbuffer_write(self->buffer, fd); 426 | } else { 427 | result = evbuffer_write_atmost(self->buffer, fd, length); 428 | } 429 | Py_END_ALLOW_THREADS 430 | if (result < 0) { 431 | PyErr_SetString(PyExc_TypeError, "could not write buffer to file descriptor"); 432 | return NULL; 433 | } 434 | return PyLong_FromLong(result); 435 | } 436 | 437 | PyDoc_STRVAR(buffer_read_doc, "Read from a file descriptor and store the result in an evbuffer."); 438 | 439 | static PyObject * 440 | pybuffer_read(PyBufferObject *self, PyObject *args) 441 | { 442 | int fd; 443 | int result; 444 | int length; 445 | 446 | if (!PyArg_ParseTuple(args, "ii", &fd, &length)) 447 | return NULL; 448 | 449 | Py_BEGIN_ALLOW_THREADS 450 | result = evbuffer_read(self->buffer, fd, length); 451 | Py_END_ALLOW_THREADS 452 | if (result < 0) { 453 | PyErr_SetString(PyExc_TypeError, "could not read buffer from file descriptor"); 454 | return NULL; 455 | } 456 | return PyLong_FromLong(result); 457 | } 458 | 459 | PyDoc_STRVAR(buffer_pullup_doc, "Makes the data at the begining of an evbuffer contiguous."); 460 | 461 | static PyObject * 462 | pybuffer_pullup(PyBufferObject *self, PyObject *args) 463 | { 464 | Py_ssize_t length=-1; 465 | 466 | if (!PyArg_ParseTuple(args, "|n", &length)) 467 | return NULL; 468 | 469 | Py_BEGIN_ALLOW_THREADS 470 | evbuffer_pullup(self->buffer, length); 471 | Py_END_ALLOW_THREADS 472 | Py_RETURN_NONE; 473 | } 474 | 475 | PyDoc_STRVAR(buffer_prepend_doc, "Prepends data to the beginning of the evbuffer."); 476 | 477 | static PyObject * 478 | pybuffer_prepend(PyBufferObject *self, PyObject *args) 479 | { 480 | PyObject *pydata; 481 | char *data; 482 | Py_ssize_t length; 483 | int result; 484 | 485 | if (!PyArg_ParseTuple(args, "O", &pydata)) 486 | return NULL; 487 | 488 | if (PyEventBuffer_Check(pydata)) { 489 | Py_BEGIN_ALLOW_THREADS 490 | result = evbuffer_prepend_buffer(self->buffer, ((PyBufferObject *) pydata)->buffer); 491 | Py_END_ALLOW_THREADS 492 | } else { 493 | if (PyObject_AsReadBuffer(pydata, (const void **) &data, &length) != 0) { 494 | return NULL; 495 | } 496 | 497 | Py_BEGIN_ALLOW_THREADS 498 | result = evbuffer_prepend(self->buffer, data, length); 499 | Py_END_ALLOW_THREADS 500 | } 501 | if (result < 0) { 502 | PyErr_SetString(PyExc_TypeError, "could not prepend data to buffer"); 503 | return NULL; 504 | } 505 | Py_RETURN_NONE; 506 | } 507 | 508 | PyDoc_STRVAR(buffer_freeze_doc, "Prevent calls that modify an evbuffer from succeeding."); 509 | 510 | static PyObject * 511 | pybuffer_freeze(PyBufferObject *self, PyObject *args) 512 | { 513 | int at_front; 514 | 515 | if (!PyArg_ParseTuple(args, "i", &at_front)) 516 | return NULL; 517 | 518 | Py_BEGIN_ALLOW_THREADS 519 | evbuffer_freeze(self->buffer, at_front); 520 | Py_END_ALLOW_THREADS 521 | Py_RETURN_NONE; 522 | } 523 | 524 | PyDoc_STRVAR(buffer_unfreeze_doc, "Re-enable calls that modify an evbuffer."); 525 | 526 | static PyObject * 527 | pybuffer_unfreeze(PyBufferObject *self, PyObject *args) 528 | { 529 | int at_front; 530 | 531 | if (!PyArg_ParseTuple(args, "i", &at_front)) 532 | return NULL; 533 | 534 | Py_BEGIN_ALLOW_THREADS 535 | evbuffer_unfreeze(self->buffer, at_front); 536 | Py_END_ALLOW_THREADS 537 | Py_RETURN_NONE; 538 | } 539 | 540 | PyDoc_STRVAR(buffer_defer_callbacks_doc, "Serialize callbacks to given base."); 541 | 542 | static PyObject * 543 | pybuffer_defer_callbacks(PyBufferObject *self, PyObject *args) 544 | { 545 | PyObject *base; 546 | 547 | if (!PyArg_ParseTuple(args, "O", &base)) 548 | return NULL; 549 | 550 | if (base != Py_None && !PyEventBase_Check(base)) { 551 | PyErr_Format(PyExc_TypeError, "expected None or a base but got a %s", base->ob_type->tp_name); 552 | return NULL; 553 | } 554 | 555 | Py_BEGIN_ALLOW_THREADS 556 | if (base == Py_None) { 557 | evbuffer_defer_callbacks(self->buffer, NULL); 558 | } else { 559 | evbuffer_defer_callbacks(self->buffer, ((PyEventBaseObject *) base)->base); 560 | } 561 | Py_END_ALLOW_THREADS 562 | Py_XDECREF(self->base); 563 | if (base == Py_None) { 564 | self->base = NULL; 565 | } else { 566 | self->base = (PyEventBaseObject *) base; 567 | Py_INCREF(self->base); 568 | } 569 | Py_RETURN_NONE; 570 | } 571 | 572 | PyDoc_STRVAR(buffer_search_doc, "Search for a string within an evbuffer."); 573 | 574 | static PyObject * 575 | pybuffer_search(PyBufferObject *self, PyObject *args) 576 | { 577 | char *data; 578 | int length; 579 | Py_ssize_t start=0; 580 | struct evbuffer_ptr start_pos; 581 | struct evbuffer_ptr pos; 582 | 583 | if (!PyArg_ParseTuple(args, "s#|n", &data, &length, &start)) 584 | return NULL; 585 | 586 | Py_BEGIN_ALLOW_THREADS 587 | if (start > 0) { 588 | evbuffer_ptr_set(self->buffer, &start_pos, start, EVBUFFER_PTR_SET); 589 | pos = evbuffer_search(self->buffer, data, length, &start_pos); 590 | } else { 591 | pos = evbuffer_search(self->buffer, data, length, NULL); 592 | } 593 | Py_END_ALLOW_THREADS 594 | 595 | return PyLong_FromSsize_t(pos.pos); 596 | } 597 | 598 | static PyMethodDef 599 | pybuffer_methods[] = { 600 | {"enable_locking", (PyCFunction)pybuffer_enable_locking, METH_NOARGS, buffer_enable_locking_doc}, 601 | {"lock", (PyCFunction)pybuffer_lock, METH_NOARGS, buffer_lock_doc}, 602 | {"unlock", (PyCFunction)pybuffer_unlock, METH_NOARGS, buffer_unlock_doc}, 603 | {"__enter__", (PyCFunction)pybuffer_lock, METH_VARARGS, buffer_lock_doc}, 604 | {"__exit__", (PyCFunction)pybuffer_unlock, METH_VARARGS, buffer_unlock_doc}, 605 | {"get_contiguous_space", (PyCFunction)pybuffer_get_contiguous_space, METH_NOARGS, buffer_get_contiguous_space_doc}, 606 | {"expand", (PyCFunction)pybuffer_expand, METH_VARARGS, buffer_expand_doc}, 607 | {"add", (PyCFunction)pybuffer_add, METH_VARARGS, buffer_add_doc}, 608 | {"remove", (PyCFunction)pybuffer_remove, METH_VARARGS, buffer_remove_doc}, 609 | {"copyout", (PyCFunction)pybuffer_copyout, METH_VARARGS, buffer_copyout_doc}, 610 | {"remove_buffer", (PyCFunction)pybuffer_remove_buffer, METH_VARARGS, buffer_remove_buffer_doc}, 611 | {"readln", (PyCFunction)pybuffer_readln, METH_VARARGS, buffer_readln_doc}, 612 | {"add_file", (PyCFunction)pybuffer_add_file, METH_VARARGS, buffer_add_file_doc}, 613 | {"drain", (PyCFunction)pybuffer_drain, METH_VARARGS, buffer_drain_doc}, 614 | {"write", (PyCFunction)pybuffer_write, METH_VARARGS, buffer_write_doc}, 615 | {"read", (PyCFunction)pybuffer_read, METH_VARARGS, buffer_read_doc}, 616 | {"pullup", (PyCFunction)pybuffer_pullup, METH_VARARGS, buffer_pullup_doc}, 617 | {"prepend", (PyCFunction)pybuffer_prepend, METH_VARARGS, buffer_prepend_doc}, 618 | {"freeze", (PyCFunction)pybuffer_freeze, METH_VARARGS, buffer_freeze_doc}, 619 | {"unfreeze", (PyCFunction)pybuffer_unfreeze, METH_VARARGS, buffer_unfreeze_doc}, 620 | {"defer_callbacks", (PyCFunction)pybuffer_defer_callbacks, METH_VARARGS, buffer_defer_callbacks_doc}, 621 | {"search", (PyCFunction)pybuffer_search, METH_VARARGS, buffer_search_doc}, 622 | {NULL, NULL}, 623 | }; 624 | 625 | static Py_ssize_t 626 | pybuffer_length(PyBufferObject *self) 627 | { 628 | Py_ssize_t result; 629 | Py_BEGIN_ALLOW_THREADS 630 | result = evbuffer_get_length(self->buffer); 631 | Py_END_ALLOW_THREADS 632 | return result; 633 | } 634 | 635 | static int 636 | pybuffer_contains(PyBufferObject *self, PyObject *value) 637 | { 638 | struct evbuffer_ptr pos; 639 | if (!PyString_Check(value)) { 640 | PyErr_SetString(PyExc_TypeError, "can only check for strings"); 641 | return -1; 642 | } 643 | 644 | Py_BEGIN_ALLOW_THREADS 645 | pos = evbuffer_search(self->buffer, PyString_AS_STRING(value), PyString_GET_SIZE(value), NULL); 646 | Py_END_ALLOW_THREADS 647 | return (pos.pos == -1 ? 0 : 1); 648 | } 649 | 650 | static PySequenceMethods 651 | pybuffer_as_seq = { 652 | (lenfunc)pybuffer_length, /*sq_length*/ 653 | NULL, /*sq_concat*/ 654 | NULL, /*sq_repeat*/ 655 | NULL, /*sq_item*/ 656 | NULL, /*sq_slice*/ 657 | NULL, /*sq_ass_item*/ 658 | NULL, /*sq_ass_slice*/ 659 | (objobjproc)pybuffer_contains, /*sq_contains*/ 660 | NULL, /*sq_inplace_concat*/ 661 | NULL /*sq_inplace_repeat*/ 662 | }; 663 | 664 | static PyMappingMethods 665 | pybuffer_as_mapping = { 666 | (lenfunc)pybuffer_length, /*mp_length*/ 667 | NULL, /*mp_subscript*/ 668 | NULL, /*mp_ass_subscript*/ 669 | }; 670 | PyDoc_STRVAR(buffer_doc, "Buffer"); 671 | 672 | PyTypeObject 673 | PyEventBuffer_Type = { 674 | PyObject_HEAD_INIT(NULL) 675 | 0, /* tp_internal */ 676 | "event.Buffer", /* tp_name */ 677 | sizeof(PyBufferObject), /* tp_basicsize */ 678 | 0, /* tp_itemsize */ 679 | (destructor)pybuffer_dealloc, /* tp_dealloc */ 680 | 0, /* tp_print */ 681 | 0, /* tp_getattr */ 682 | 0, /* tp_setattr */ 683 | 0, /* tp_compare */ 684 | 0, /* tp_repr */ 685 | 0, /* tp_as_number */ 686 | &pybuffer_as_seq, /* tp_as_sequence */ 687 | &pybuffer_as_mapping, /* tp_as_mapping */ 688 | 0, /* tp_hash */ 689 | 0, /* tp_call */ 690 | 0, /* tp_str */ 691 | 0, /* tp_getattro */ 692 | 0, /* tp_setattro */ 693 | 0, /* tp_as_buffer */ 694 | Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ 695 | buffer_doc, /* tp_doc */ 696 | 0, /* tp_traverse */ 697 | 0, /* tp_clear */ 698 | 0, /* tp_richcompare */ 699 | 0, /* tp_weaklistoffset */ 700 | 0, /* tp_iter */ 701 | 0, /* tp_iternext */ 702 | pybuffer_methods, /* tp_methods */ 703 | 0, /* tp_members */ 704 | 0, /* tp_getset */ 705 | 0, /* tp_base */ 706 | 0, /* tp_dict */ 707 | 0, /* tp_descr_get */ 708 | 0, /* tp_descr_set */ 709 | 0, /* tp_dictoffset */ 710 | (initproc)pybuffer_init, /* tp_init */ 711 | 0, /* tp_alloc */ 712 | pybuffer_new, /* tp_new */ 713 | 0, /* tp_free */ 714 | }; 715 | -------------------------------------------------------------------------------- /src/pybuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #ifndef ___EVENT_PYBUFFER__H___ 22 | #define ___EVENT_PYBUFFER__H___ 23 | 24 | typedef struct _PyBufferObject { 25 | PyObject_HEAD 26 | struct evbuffer *buffer; 27 | PyEventBaseObject *base; 28 | int owned; 29 | } PyBufferObject; 30 | 31 | extern PyTypeObject PyEventBuffer_Type; 32 | extern PyBufferObject *_pybuffer_create(struct evbuffer *buffer); 33 | 34 | #define PyEventBuffer_Check(ob) ((ob)->ob_type == &PyEventBuffer_Type) 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/pybufferevent.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include "pybase.h" 28 | #include "pybuffer.h" 29 | #include "pybufferevent.h" 30 | 31 | typedef struct _PyBucketConfigObject { 32 | PyObject_HEAD 33 | struct ev_token_bucket_cfg *cfg; 34 | PyObject *read_rate; 35 | PyObject *read_burst; 36 | PyObject *write_rate; 37 | PyObject *write_burst; 38 | double tick_len; 39 | } PyBucketConfigObject; 40 | 41 | typedef struct _PyBufferEventObject { 42 | PyObject_HEAD 43 | struct bufferevent *buffer; 44 | PyEventBaseObject *base; 45 | PyBufferObject *input; 46 | PyBufferObject *output; 47 | PyBucketConfigObject *bucket; 48 | PyObject *readcb; 49 | PyObject *writecb; 50 | PyObject *eventcb; 51 | PyObject *cbdata; 52 | PyObject *weakrefs; 53 | } PyBufferEventObject; 54 | 55 | static void 56 | _pybufferevent_readcb(struct bufferevent *bev, void *ctx) 57 | { 58 | PyBufferEventObject *self = (PyBufferEventObject *) ctx; 59 | if (self->readcb != NULL) { 60 | START_BLOCK_THREADS 61 | PyObject *result = PyObject_CallFunction(self->readcb, "OO", self, self->cbdata); 62 | if (result == NULL) { 63 | pybase_store_error(self->base); 64 | } else { 65 | Py_DECREF(result); 66 | } 67 | END_BLOCK_THREADS 68 | } 69 | } 70 | 71 | static void 72 | _pybufferevent_writecb(struct bufferevent *bev, void *ctx) 73 | { 74 | PyBufferEventObject *self = (PyBufferEventObject *) ctx; 75 | if (self->writecb != NULL) { 76 | START_BLOCK_THREADS 77 | PyObject *result = PyObject_CallFunction(self->writecb, "OO", self, self->cbdata); 78 | if (result == NULL) { 79 | pybase_store_error(self->base); 80 | } else { 81 | Py_DECREF(result); 82 | } 83 | END_BLOCK_THREADS 84 | } 85 | } 86 | 87 | static void 88 | _pybufferevent_eventcb(struct bufferevent *bev, short what, void *ctx) 89 | { 90 | PyBufferEventObject *self = (PyBufferEventObject *) ctx; 91 | if (self->eventcb != NULL) { 92 | START_BLOCK_THREADS 93 | PyObject *result = PyObject_CallFunction(self->eventcb, "OiO", self, what, self->cbdata); 94 | if (result == NULL) { 95 | pybase_store_error(self->base); 96 | } else { 97 | Py_DECREF(result); 98 | } 99 | END_BLOCK_THREADS 100 | } 101 | } 102 | 103 | static PyObject * 104 | pybufferevent_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 105 | { 106 | PyBufferEventObject *s; 107 | s = (PyBufferEventObject *)type->tp_alloc(type, 0); 108 | if (s != NULL) { 109 | s->buffer = NULL; 110 | s->base = NULL; 111 | s->input = NULL; 112 | s->output = NULL; 113 | s->bucket = NULL; 114 | s->readcb = NULL; 115 | s->writecb = NULL; 116 | s->eventcb = NULL; 117 | s->cbdata = NULL; 118 | s->weakrefs = NULL; 119 | } 120 | return (PyObject *)s; 121 | } 122 | 123 | static int 124 | pybufferevent_init(PyBufferEventObject *self, PyObject *args, PyObject *kwds) 125 | { 126 | PyEventBaseObject *base; 127 | int fd=-1; 128 | int options=0; 129 | 130 | if (!PyArg_ParseTuple(args, "O!|ii", &PyEventBase_Type, &base, &fd, &options)) 131 | return -1; 132 | 133 | self->buffer = bufferevent_socket_new(base->base, fd, options); 134 | if (self->buffer == NULL) { 135 | PyErr_NoMemory(); 136 | return -1; 137 | } 138 | 139 | self->base = base; 140 | Py_INCREF(base); 141 | self->input = _pybuffer_create(bufferevent_get_input(self->buffer)); 142 | self->output = _pybuffer_create(bufferevent_get_output(self->buffer)); 143 | self->cbdata = Py_None; 144 | Py_INCREF(Py_None); 145 | return 0; 146 | } 147 | 148 | static int 149 | pybufferevent_traverse(PyBufferEventObject *self, visitproc visit, void *arg) 150 | { 151 | Py_VISIT(self->input); 152 | Py_VISIT(self->output); 153 | Py_VISIT(self->bucket); 154 | Py_VISIT(self->readcb); 155 | Py_VISIT(self->writecb); 156 | Py_VISIT(self->eventcb); 157 | Py_VISIT(self->cbdata); 158 | Py_VISIT(self->base); 159 | return 0; 160 | } 161 | 162 | static int 163 | pybufferevent_clear(PyBufferEventObject *self) 164 | { 165 | if (self->input != NULL) { 166 | self->input->buffer = NULL; 167 | Py_CLEAR(self->input); 168 | } 169 | if (self->output != NULL) { 170 | self->output->buffer = NULL; 171 | Py_CLEAR(self->output); 172 | } 173 | Py_BEGIN_ALLOW_THREADS 174 | if (self->buffer != NULL) { 175 | bufferevent_free(self->buffer); 176 | self->buffer = NULL; 177 | } 178 | Py_END_ALLOW_THREADS 179 | Py_CLEAR(self->bucket); 180 | Py_CLEAR(self->readcb); 181 | Py_CLEAR(self->writecb); 182 | Py_CLEAR(self->eventcb); 183 | Py_CLEAR(self->cbdata); 184 | Py_CLEAR(self->base); 185 | return 0; 186 | } 187 | 188 | static void 189 | pybufferevent_dealloc(PyBufferEventObject *self) 190 | { 191 | if (self->weakrefs != NULL) { 192 | PyObject_ClearWeakRefs((PyObject *) self); 193 | } 194 | pybufferevent_clear(self); 195 | Py_TYPE(self)->tp_free(self); 196 | } 197 | 198 | PyDoc_STRVAR(pybufferevent_lock_doc, "Acquire the lock on a bufferevent."); 199 | 200 | static PyObject * 201 | pybufferevent_lock(PyBufferEventObject *self, PyObject *args) 202 | { 203 | Py_BEGIN_ALLOW_THREADS 204 | bufferevent_lock(self->buffer); 205 | Py_END_ALLOW_THREADS 206 | Py_RETURN_NONE; 207 | } 208 | 209 | PyDoc_STRVAR(pybufferevent_unlock_doc, "Release the lock on a bufferevent."); 210 | 211 | static PyObject * 212 | pybufferevent_unlock(PyBufferEventObject *self, PyObject *args) 213 | { 214 | Py_BEGIN_ALLOW_THREADS 215 | bufferevent_unlock(self->buffer); 216 | Py_END_ALLOW_THREADS 217 | Py_RETURN_NONE; 218 | } 219 | 220 | PyDoc_STRVAR(pybufferevent_setcb_doc, "Change the callbacks for a bufferevent."); 221 | 222 | static PyObject * 223 | pybufferevent_setcb(PyBufferEventObject *self, PyObject *args) 224 | { 225 | PyObject *readcb; 226 | PyObject *writecb; 227 | PyObject *eventcb; 228 | PyObject *cbdata=Py_None; 229 | 230 | if (!PyArg_ParseTuple(args, "OOO|O", &readcb, &writecb, &eventcb, &cbdata)) 231 | return NULL; 232 | 233 | // make changes atomic 234 | Py_BEGIN_ALLOW_THREADS 235 | bufferevent_lock(self->buffer); 236 | Py_END_ALLOW_THREADS 237 | 238 | if (self->readcb != readcb) { 239 | PyObject *old = self->readcb; 240 | self->readcb = (readcb == Py_None ? NULL : readcb); 241 | Py_XINCREF(self->readcb); 242 | Py_XDECREF(old); 243 | } 244 | if (self->writecb != writecb) { 245 | PyObject *old = self->writecb; 246 | self->writecb = (writecb == Py_None ? NULL : writecb); 247 | Py_XINCREF(self->writecb); 248 | Py_XDECREF(old); 249 | } 250 | if (self->eventcb != eventcb) { 251 | PyObject *old = self->eventcb; 252 | self->eventcb = (eventcb == Py_None ? NULL : eventcb); 253 | Py_XINCREF(self->eventcb); 254 | Py_XDECREF(old); 255 | } 256 | if (self->cbdata != cbdata) { 257 | PyObject *old = self->cbdata; 258 | self->cbdata = cbdata; 259 | Py_INCREF(cbdata); 260 | Py_XDECREF(old); 261 | } 262 | 263 | Py_BEGIN_ALLOW_THREADS 264 | bufferevent_setcb(self->buffer, 265 | readcb == Py_None ? NULL : _pybufferevent_readcb, 266 | writecb == Py_None ? NULL : _pybufferevent_writecb, 267 | eventcb == Py_None ? NULL : _pybufferevent_eventcb, 268 | self); 269 | bufferevent_unlock(self->buffer); 270 | Py_END_ALLOW_THREADS 271 | Py_RETURN_NONE; 272 | } 273 | 274 | PyDoc_STRVAR(pybufferevent_write_doc, "Write data to a bufferevent buffer."); 275 | 276 | static void 277 | _pybufferevent_decref(const void *data, size_t datalen, void *extra) 278 | { 279 | PyObject *obj = (PyObject *) extra; 280 | START_BLOCK_THREADS 281 | Py_DECREF(obj); 282 | END_BLOCK_THREADS 283 | } 284 | 285 | static PyObject * 286 | pybufferevent_write(PyBufferEventObject *self, PyObject *args) 287 | { 288 | PyObject *pydata; 289 | char *data; 290 | Py_ssize_t length; 291 | int result; 292 | 293 | if (!PyArg_ParseTuple(args, "O", &pydata)) 294 | return NULL; 295 | 296 | if (PyEventBuffer_Check(pydata)) { 297 | Py_BEGIN_ALLOW_THREADS 298 | result = bufferevent_write_buffer(self->buffer, ((PyBufferObject *) pydata)->buffer); 299 | Py_END_ALLOW_THREADS 300 | } else { 301 | if (PyObject_AsReadBuffer(pydata, (const void **) &data, &length) != 0) { 302 | return NULL; 303 | } 304 | 305 | Py_BEGIN_ALLOW_THREADS 306 | evbuffer_lock(self->output->buffer); 307 | result = evbuffer_add_reference(self->output->buffer, data, length, _pybufferevent_decref, pydata); 308 | Py_END_ALLOW_THREADS 309 | if (result >= 0) { 310 | Py_INCREF(pydata); 311 | } 312 | Py_BEGIN_ALLOW_THREADS 313 | evbuffer_unlock(self->output->buffer); 314 | Py_END_ALLOW_THREADS 315 | } 316 | if (result < 0) { 317 | PyErr_SetString(PyExc_TypeError, "could not write data to buffer"); 318 | return NULL; 319 | } 320 | 321 | Py_RETURN_NONE; 322 | } 323 | 324 | PyDoc_STRVAR(pybufferevent_read_doc, "Read data from a bufferevent buffer."); 325 | 326 | static PyObject * 327 | pybufferevent_read(PyBufferEventObject *self, PyObject *args) 328 | { 329 | PyObject *pydest=Py_None; 330 | PyObject *pysize; 331 | char *data; 332 | Py_ssize_t size; 333 | Py_ssize_t length; 334 | PyObject *result; 335 | int unlock=0; 336 | 337 | if (!PyArg_ParseTuple(args, "|O", &pydest)) 338 | return NULL; 339 | 340 | if (PyEventBuffer_Check(pydest)) { 341 | Py_BEGIN_ALLOW_THREADS 342 | size = bufferevent_read_buffer(self->buffer, ((PyBufferObject *) pydest)->buffer); 343 | Py_END_ALLOW_THREADS 344 | if (size != 0) { 345 | PyErr_SetString(PyExc_TypeError, "could not read data from buffer"); 346 | return NULL; 347 | } 348 | Py_INCREF(Py_None); 349 | return Py_None; 350 | } else if (pydest == Py_None) { 351 | // read complete buffer 352 | Py_BEGIN_ALLOW_THREADS 353 | bufferevent_lock(self->buffer); 354 | length = evbuffer_get_length(bufferevent_get_input(self->buffer)); 355 | if (length == 0) { 356 | bufferevent_unlock(self->buffer); 357 | } 358 | unlock = 1; 359 | Py_END_ALLOW_THREADS 360 | if (length == 0) { 361 | return PyString_FromString(""); 362 | } 363 | } else { 364 | // read given amount 365 | pysize = PyNumber_Int(pydest); 366 | if (pysize == NULL) { 367 | PyErr_Format(PyExc_TypeError, "expected a Buffer or a number, not '%s'", pydest->ob_type->tp_name); 368 | return NULL; 369 | } 370 | 371 | length = PyInt_AsLong(pysize); 372 | Py_DECREF(pysize); 373 | } 374 | result = PyString_FromStringAndSize(NULL, length); 375 | if (result == NULL) { 376 | if (unlock) { 377 | Py_BEGIN_ALLOW_THREADS 378 | bufferevent_unlock(self->buffer); 379 | Py_END_ALLOW_THREADS 380 | } 381 | return PyErr_NoMemory(); 382 | } 383 | 384 | data = PyString_AS_STRING(result); 385 | Py_BEGIN_ALLOW_THREADS 386 | size = bufferevent_read(self->buffer, data, length); 387 | if (unlock) { 388 | bufferevent_unlock(self->buffer); 389 | } 390 | Py_END_ALLOW_THREADS 391 | if (size < 0) { 392 | Py_DECREF(result); 393 | PyErr_SetString(PyExc_TypeError, "could not read data from buffer"); 394 | result = NULL; 395 | } else if (size == 0) { 396 | Py_DECREF(result); 397 | result = PyString_FromString(""); 398 | } else if (size != length) { 399 | _PyString_Resize(&result, size); 400 | } 401 | return result; 402 | } 403 | 404 | PyDoc_STRVAR(pybufferevent_enable_doc, "Enable a bufferevent."); 405 | 406 | static PyObject * 407 | pybufferevent_enable(PyBufferEventObject *self, PyObject *args) 408 | { 409 | int what; 410 | 411 | if (!PyArg_ParseTuple(args, "i", &what)) 412 | return NULL; 413 | 414 | Py_BEGIN_ALLOW_THREADS 415 | bufferevent_enable(self->buffer, what); 416 | Py_END_ALLOW_THREADS 417 | Py_RETURN_NONE; 418 | } 419 | 420 | PyDoc_STRVAR(pybufferevent_disable_doc, "Disable a bufferevent."); 421 | 422 | static PyObject * 423 | pybufferevent_disable(PyBufferEventObject *self, PyObject *args) 424 | { 425 | int what; 426 | 427 | if (!PyArg_ParseTuple(args, "i", &what)) 428 | return NULL; 429 | 430 | Py_BEGIN_ALLOW_THREADS 431 | bufferevent_disable(self->buffer, what); 432 | Py_END_ALLOW_THREADS 433 | Py_RETURN_NONE; 434 | } 435 | 436 | PyDoc_STRVAR(pybufferevent_set_timeouts_doc, "Set the read and write timeout for a buffered event."); 437 | 438 | static PyObject * 439 | pybufferevent_set_timeouts(PyBufferEventObject *self, PyObject *args) 440 | { 441 | double read; 442 | double write; 443 | struct timeval read_tv; 444 | struct timeval write_tv; 445 | 446 | if (!PyArg_ParseTuple(args, "dd", &read, &write)) 447 | return NULL; 448 | 449 | timeval_init(&read_tv, read); 450 | timeval_init(&write_tv, write); 451 | 452 | Py_BEGIN_ALLOW_THREADS 453 | bufferevent_set_timeouts(self->buffer, 454 | read <= 0 ? NULL : &read_tv, 455 | write <= 0 ? NULL : &write_tv); 456 | Py_END_ALLOW_THREADS 457 | Py_RETURN_NONE; 458 | } 459 | 460 | PyDoc_STRVAR(pybufferevent_set_watermark_doc, "Sets the watermarks for read and write events."); 461 | 462 | static PyObject * 463 | pybufferevent_set_watermark(PyBufferEventObject *self, PyObject *args) 464 | { 465 | int what; 466 | Py_ssize_t lowmark; 467 | Py_ssize_t highmark; 468 | 469 | if (!PyArg_ParseTuple(args, "inn", &what, &lowmark, &highmark)) 470 | return NULL; 471 | 472 | Py_BEGIN_ALLOW_THREADS 473 | bufferevent_setwatermark(self->buffer, what, lowmark, highmark); 474 | Py_END_ALLOW_THREADS 475 | Py_RETURN_NONE; 476 | } 477 | 478 | PyDoc_STRVAR(pybufferevent_set_ratelimit_doc, "Set the rate-limit of the bufferevent."); 479 | 480 | static PyObject * 481 | pybufferevent_set_ratelimit(PyBufferEventObject *self, PyObject *args) 482 | { 483 | PyObject *limit; 484 | 485 | if (!PyArg_ParseTuple(args, "O", &limit)) 486 | return NULL; 487 | 488 | if (limit != Py_None && !PyBucketConfig_Check(limit)) { 489 | PyErr_Format(PyExc_TypeError, "expected a BucketConfig object or None, but got %s", limit->ob_type->tp_name); 490 | return NULL; 491 | } 492 | 493 | if (limit == (PyObject *) self->bucket || (limit == Py_None && self->bucket == NULL)) { 494 | // No change 495 | Py_RETURN_NONE; 496 | } 497 | 498 | Py_BEGIN_ALLOW_THREADS 499 | if (limit == Py_None) { 500 | bufferevent_set_rate_limit(self->buffer, NULL); 501 | } else { 502 | bufferevent_set_rate_limit(self->buffer, ((PyBucketConfigObject *) limit)->cfg); 503 | } 504 | Py_END_ALLOW_THREADS 505 | Py_XDECREF(self->bucket); 506 | if (limit == Py_None) { 507 | self->bucket = NULL; 508 | } else { 509 | self->bucket = (PyBucketConfigObject *) limit; 510 | Py_INCREF(limit); 511 | } 512 | Py_RETURN_NONE; 513 | } 514 | 515 | static PyMethodDef 516 | pybufferevent_methods[] = { 517 | {"lock", (PyCFunction)pybufferevent_lock, METH_NOARGS, pybufferevent_lock_doc}, 518 | {"unlock", (PyCFunction)pybufferevent_unlock, METH_NOARGS, pybufferevent_unlock_doc}, 519 | {"__enter__", (PyCFunction)pybufferevent_lock, METH_VARARGS, pybufferevent_lock_doc}, 520 | {"__exit__", (PyCFunction)pybufferevent_unlock, METH_VARARGS, pybufferevent_unlock_doc}, 521 | {"set_callbacks", (PyCFunction)pybufferevent_setcb, METH_VARARGS, pybufferevent_setcb_doc}, 522 | {"write", (PyCFunction)pybufferevent_write, METH_VARARGS, pybufferevent_write_doc}, 523 | {"read", (PyCFunction)pybufferevent_read, METH_VARARGS, pybufferevent_read_doc}, 524 | {"enable", (PyCFunction)pybufferevent_enable, METH_VARARGS, pybufferevent_enable_doc}, 525 | {"disable", (PyCFunction)pybufferevent_disable, METH_VARARGS, pybufferevent_disable_doc}, 526 | {"set_timeouts", (PyCFunction)pybufferevent_set_timeouts, METH_VARARGS, pybufferevent_set_timeouts_doc}, 527 | {"set_watermark", (PyCFunction)pybufferevent_set_watermark, METH_VARARGS, pybufferevent_set_watermark_doc}, 528 | {"set_ratelimit", (PyCFunction)pybufferevent_set_ratelimit, METH_VARARGS, pybufferevent_set_ratelimit_doc}, 529 | {NULL, NULL}, 530 | }; 531 | 532 | static PyMemberDef 533 | pybufferevent_members[] = { 534 | {"base", T_OBJECT, offsetof(PyBufferEventObject, base), READONLY, "the base this event is assigned to"}, 535 | {"input", T_OBJECT, offsetof(PyBufferEventObject, input), READONLY, "the input buffer"}, 536 | {"output", T_OBJECT, offsetof(PyBufferEventObject, output), READONLY, "the output buffer"}, 537 | {"bucket", T_OBJECT, offsetof(PyBufferEventObject, bucket), READONLY, "the rate limit"}, 538 | {NULL} 539 | }; 540 | 541 | PyDoc_STRVAR(pybufferevent_doc, "Bufferevent"); 542 | 543 | PyTypeObject 544 | PyBufferEvent_Type = { 545 | PyObject_HEAD_INIT(NULL) 546 | 0, /* tp_internal */ 547 | "event.BufferEvent", /* tp_name */ 548 | sizeof(PyBufferEventObject), /* tp_basicsize */ 549 | 0, /* tp_itemsize */ 550 | (destructor)pybufferevent_dealloc, /* tp_dealloc */ 551 | 0, /* tp_print */ 552 | 0, /* tp_getattr */ 553 | 0, /* tp_setattr */ 554 | 0, /* tp_compare */ 555 | 0, /* tp_repr */ 556 | 0, /* tp_as_number */ 557 | 0, /* tp_as_sequence */ 558 | 0, /* tp_as_mapping */ 559 | 0, /* tp_hash */ 560 | 0, /* tp_call */ 561 | 0, /* tp_str */ 562 | 0, /* tp_getattro */ 563 | 0, /* tp_setattro */ 564 | 0, /* tp_as_buffer */ 565 | Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ 566 | pybufferevent_doc, /* tp_doc */ 567 | (traverseproc)pybufferevent_traverse, /* tp_traverse */ 568 | (inquiry)pybufferevent_clear, /* tp_clear */ 569 | 0, /* tp_richcompare */ 570 | offsetof(PyBufferEventObject, weakrefs), /* tp_weaklistoffset */ 571 | 0, /* tp_iter */ 572 | 0, /* tp_iternext */ 573 | pybufferevent_methods, /* tp_methods */ 574 | pybufferevent_members, /* tp_members */ 575 | 0, /* tp_getset */ 576 | 0, /* tp_base */ 577 | 0, /* tp_dict */ 578 | 0, /* tp_descr_get */ 579 | 0, /* tp_descr_set */ 580 | 0, /* tp_dictoffset */ 581 | (initproc)pybufferevent_init, /* tp_init */ 582 | 0, /* tp_alloc */ 583 | pybufferevent_new, /* tp_new */ 584 | 0, /* tp_free */ 585 | }; 586 | 587 | static PyObject * 588 | pybucketconfig_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 589 | { 590 | PyBucketConfigObject *s = (PyBucketConfigObject *)type->tp_alloc(type, 0); 591 | if (s != NULL) { 592 | s->cfg = NULL; 593 | s->read_rate = NULL; 594 | s->read_burst = NULL; 595 | s->write_rate = NULL; 596 | s->write_burst = NULL; 597 | } 598 | return (PyObject *)s; 599 | } 600 | 601 | static int 602 | pybucketconfig_init(PyBucketConfigObject *self, PyObject *args, PyObject *kwds) 603 | { 604 | Py_ssize_t read_rate; 605 | Py_ssize_t read_burst; 606 | Py_ssize_t write_rate; 607 | Py_ssize_t write_burst; 608 | double tick_len=1; 609 | struct timeval tv; 610 | 611 | if (!PyArg_ParseTuple(args, "nnnn|d", &read_rate, &read_burst, &write_rate, &write_burst, &tick_len)) 612 | return -1; 613 | 614 | timeval_init(&tv, tick_len); 615 | self->cfg = ev_token_bucket_cfg_new(read_rate, read_burst, write_rate, write_burst, &tv); 616 | if (self->cfg == NULL) { 617 | PyErr_NoMemory(); 618 | return -1; 619 | } 620 | 621 | self->read_rate = PyLong_FromSsize_t(read_rate); 622 | self->read_burst = PyLong_FromSsize_t(read_burst); 623 | self->write_rate = PyLong_FromSsize_t(write_rate); 624 | self->write_burst = PyLong_FromSsize_t(write_burst); 625 | self->tick_len = tick_len; 626 | return 0; 627 | } 628 | 629 | static void 630 | pybucketconfig_dealloc(PyBucketConfigObject *self) 631 | { 632 | Py_BEGIN_ALLOW_THREADS 633 | if (self->cfg != NULL) { 634 | ev_token_bucket_cfg_free(self->cfg); 635 | } 636 | Py_END_ALLOW_THREADS 637 | Py_XDECREF(self->read_rate); 638 | Py_XDECREF(self->read_burst); 639 | Py_XDECREF(self->write_rate); 640 | Py_XDECREF(self->write_burst); 641 | Py_TYPE(self)->tp_free(self); 642 | } 643 | 644 | static PyMemberDef 645 | pybucketconfig_members[] = { 646 | {"read_rate", T_OBJECT, offsetof(PyBucketConfigObject, read_rate), READONLY, "The maximum number of bytes to read per tick on average."}, 647 | {"read_burst", T_OBJECT, offsetof(PyBucketConfigObject, read_burst), READONLY, "The maximum number of bytes to read in any single tick."}, 648 | {"write_rate", T_OBJECT, offsetof(PyBucketConfigObject, write_rate), READONLY, "The maximum number of bytes to write per tick on average."}, 649 | {"write_burst", T_OBJECT, offsetof(PyBucketConfigObject, write_burst), READONLY, "The maximum number of bytes to write in any single tick."}, 650 | {"tick_len", T_DOUBLE, offsetof(PyBucketConfigObject, tick_len), READONLY, "The length of a single tick."}, 651 | {NULL} 652 | }; 653 | 654 | PyDoc_STRVAR(pybucketconfig_doc, "BucketConfig"); 655 | 656 | PyTypeObject 657 | PyBucketConfig_Type = { 658 | PyObject_HEAD_INIT(NULL) 659 | 0, /* tp_internal */ 660 | "event.BucketConfig", /* tp_name */ 661 | sizeof(PyBucketConfigObject), /* tp_basicsize */ 662 | 0, /* tp_itemsize */ 663 | (destructor)pybucketconfig_dealloc, /* tp_dealloc */ 664 | 0, /* tp_print */ 665 | 0, /* tp_getattr */ 666 | 0, /* tp_setattr */ 667 | 0, /* tp_compare */ 668 | 0, /* tp_repr */ 669 | 0, /* tp_as_number */ 670 | 0, /* tp_as_sequence */ 671 | 0, /* tp_as_mapping */ 672 | 0, /* tp_hash */ 673 | 0, /* tp_call */ 674 | 0, /* tp_str */ 675 | 0, /* tp_getattro */ 676 | 0, /* tp_setattro */ 677 | 0, /* tp_as_buffer */ 678 | Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ 679 | pybucketconfig_doc, /* tp_doc */ 680 | 0, /* tp_traverse */ 681 | 0, /* tp_clear */ 682 | 0, /* tp_richcompare */ 683 | 0, /* tp_weaklistoffset */ 684 | 0, /* tp_iter */ 685 | 0, /* tp_iternext */ 686 | 0, /* tp_methods */ 687 | pybucketconfig_members, /* tp_members */ 688 | 0, /* tp_getset */ 689 | 0, /* tp_base */ 690 | 0, /* tp_dict */ 691 | 0, /* tp_descr_get */ 692 | 0, /* tp_descr_set */ 693 | 0, /* tp_dictoffset */ 694 | (initproc)pybucketconfig_init, /* tp_init */ 695 | 0, /* tp_alloc */ 696 | pybucketconfig_new, /* tp_new */ 697 | 0, /* tp_free */ 698 | }; 699 | -------------------------------------------------------------------------------- /src/pybufferevent.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #ifndef ___EVENT_PYBUFFEREVENT__H___ 22 | #define ___EVENT_PYBUFFEREVENT__H___ 23 | 24 | extern PyTypeObject PyBufferEvent_Type; 25 | extern PyTypeObject PyBucketConfig_Type; 26 | 27 | #define PyBufferEvent_Check(ob) ((ob)->ob_type == &PyBufferEvent_Type) 28 | #define PyBucketConfig_Check(ob) ((ob)->ob_type == &PyBucketConfig_Type) 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /src/pyevent.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include "pybase.h" 27 | #include "pyevent.h" 28 | 29 | typedef struct _PyEventObject { 30 | PyObject_HEAD 31 | PyEventBaseObject *base; 32 | struct event *event; 33 | PyObject *callback; 34 | PyObject *userdata; 35 | PyObject *weakrefs; 36 | int fd; 37 | } PyEventObject; 38 | 39 | static void 40 | pyevent_callback(evutil_socket_t fd, short what, void *userdata) 41 | { 42 | PyEventObject *self = (PyEventObject *) userdata; 43 | START_BLOCK_THREADS 44 | PyObject *result = PyObject_CallFunction(self->callback, "OiiO", self, fd, what, self->userdata); 45 | if (result == NULL) { 46 | pybase_store_error(self->base); 47 | } else { 48 | Py_DECREF(result); 49 | } 50 | END_BLOCK_THREADS 51 | } 52 | 53 | static PyObject * 54 | pyevent_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 55 | { 56 | PyEventObject *s = (PyEventObject *)type->tp_alloc(type, 0); 57 | if (s != NULL) { 58 | s->base = NULL; 59 | s->event = NULL; 60 | s->callback = NULL; 61 | s->userdata = NULL; 62 | s->weakrefs = NULL; 63 | } 64 | return (PyObject *)s; 65 | } 66 | 67 | static int 68 | pyevent_init(PyEventObject *self, PyObject *args, PyObject *kwds) 69 | { 70 | PyEventBaseObject *base; 71 | int fd; 72 | int event; 73 | PyObject *callback; 74 | PyObject *userdata = Py_None; 75 | if (!PyArg_ParseTuple(args, "O!iiO|O", &PyEventBase_Type, &base, &fd, &event, &callback, &userdata)) 76 | return -1; 77 | 78 | if (!PyCallable_Check(callback)) { 79 | PyErr_SetString(PyExc_TypeError, "the callback must be callable"); 80 | return -1; 81 | } 82 | 83 | self->event = event_new(base->base, fd, event, pyevent_callback, self); 84 | if (self->event == NULL) { 85 | PyErr_NoMemory(); 86 | return -1; 87 | } 88 | 89 | self->base = base; 90 | Py_INCREF(base); 91 | self->callback = callback; 92 | Py_INCREF(callback); 93 | self->userdata = userdata; 94 | Py_INCREF(userdata); 95 | self->fd = fd; 96 | return 0; 97 | } 98 | 99 | static int 100 | pyevent_traverse(PyEventObject *self, visitproc visit, void *arg) 101 | { 102 | Py_VISIT(self->callback); 103 | Py_VISIT(self->userdata); 104 | Py_VISIT(self->base); 105 | return 0; 106 | } 107 | 108 | static int 109 | pyevent_clear(PyEventObject *self) 110 | { 111 | Py_BEGIN_ALLOW_THREADS 112 | if (self->event != NULL) { 113 | event_free(self->event); 114 | self->event = NULL; 115 | } 116 | Py_END_ALLOW_THREADS 117 | Py_CLEAR(self->callback); 118 | Py_CLEAR(self->userdata); 119 | Py_CLEAR(self->base); 120 | return 0; 121 | } 122 | 123 | static void 124 | pyevent_dealloc(PyEventObject *self) 125 | { 126 | if (self->weakrefs != NULL) { 127 | PyObject_ClearWeakRefs((PyObject *) self); 128 | } 129 | pyevent_clear(self); 130 | Py_TYPE(self)->tp_free(self); 131 | } 132 | 133 | PyDoc_STRVAR(event_add_doc, "Add event."); 134 | 135 | static PyObject * 136 | pyevent_add(PyEventObject *self, PyObject *args) 137 | { 138 | double time=-1; 139 | struct timeval tv; 140 | if (!PyArg_ParseTuple(args, "|d", &time)) 141 | return NULL; 142 | 143 | timeval_init(&tv, time); 144 | Py_BEGIN_ALLOW_THREADS 145 | if (time <= 0) { 146 | event_add(self->event, NULL); 147 | } else { 148 | event_add(self->event, &tv); 149 | } 150 | Py_END_ALLOW_THREADS 151 | Py_RETURN_NONE; 152 | } 153 | 154 | PyDoc_STRVAR(event_delete_doc, "Remove timer event."); 155 | 156 | static PyObject * 157 | pyevent_delete(PyEventObject *self, PyObject *args) 158 | { 159 | Py_BEGIN_ALLOW_THREADS 160 | event_del(self->event); 161 | Py_END_ALLOW_THREADS 162 | Py_RETURN_NONE; 163 | } 164 | 165 | PyDoc_STRVAR(event_set_priority_doc, "Assign a priority to an event."); 166 | 167 | static PyObject * 168 | pyevent_set_priority(PyEventObject *self, PyObject *args) 169 | { 170 | int priority; 171 | if (!PyArg_ParseTuple(args, "i", &priority)) 172 | return NULL; 173 | 174 | Py_BEGIN_ALLOW_THREADS 175 | event_priority_set(self->event, priority); 176 | Py_END_ALLOW_THREADS 177 | Py_RETURN_NONE; 178 | } 179 | 180 | static PyMethodDef 181 | pyevent_methods[] = { 182 | {"add", (PyCFunction)pyevent_add, METH_VARARGS, event_add_doc}, 183 | {"delete", (PyCFunction)pyevent_delete, METH_NOARGS, event_delete_doc}, 184 | {"set_priority", (PyCFunction)pyevent_set_priority, METH_VARARGS, event_set_priority_doc}, 185 | {NULL, NULL}, 186 | }; 187 | 188 | static PyMemberDef 189 | pyevent_members[] = { 190 | {"base", T_OBJECT, offsetof(PyEventObject, base), READONLY, "the base this event is assigned to"}, 191 | {"callback", T_OBJECT, offsetof(PyEventObject, callback), READONLY, "the callback to execute"}, 192 | {"userdata", T_OBJECT, offsetof(PyEventObject, userdata), READONLY, "the userdata to pass to callback"}, 193 | {"fd", T_INT, offsetof(PyEventObject, fd), READONLY, "the file descriptor this event is assigned to"}, 194 | {NULL} 195 | }; 196 | 197 | PyDoc_STRVAR(event_doc, "Event"); 198 | 199 | PyTypeObject 200 | PyEvent_Type = { 201 | PyObject_HEAD_INIT(NULL) 202 | 0, /* tp_internal */ 203 | "event.Event", /* tp_name */ 204 | sizeof(PyEventObject), /* tp_basicsize */ 205 | 0, /* tp_itemsize */ 206 | (destructor)pyevent_dealloc, /* tp_dealloc */ 207 | 0, /* tp_print */ 208 | 0, /* tp_getattr */ 209 | 0, /* tp_setattr */ 210 | 0, /* tp_compare */ 211 | 0, /* tp_repr */ 212 | 0, /* tp_as_number */ 213 | 0, /* tp_as_sequence */ 214 | 0, /* tp_as_mapping */ 215 | 0, /* tp_hash */ 216 | 0, /* tp_call */ 217 | 0, /* tp_str */ 218 | 0, /* tp_getattro */ 219 | 0, /* tp_setattro */ 220 | 0, /* tp_as_buffer */ 221 | Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ 222 | event_doc, /* tp_doc */ 223 | (traverseproc)pyevent_traverse, /* tp_traverse */ 224 | (inquiry)pyevent_clear, /* tp_clear */ 225 | 0, /* tp_richcompare */ 226 | offsetof(PyEventObject, weakrefs), /* tp_weaklistoffset */ 227 | 0, /* tp_iter */ 228 | 0, /* tp_iternext */ 229 | pyevent_methods, /* tp_methods */ 230 | pyevent_members, /* tp_members */ 231 | 0, /* tp_getset */ 232 | 0, /* tp_base */ 233 | 0, /* tp_dict */ 234 | 0, /* tp_descr_get */ 235 | 0, /* tp_descr_set */ 236 | 0, /* tp_dictoffset */ 237 | (initproc)pyevent_init, /* tp_init */ 238 | 0, /* tp_alloc */ 239 | pyevent_new, /* tp_new */ 240 | 0, /* tp_free */ 241 | }; 242 | -------------------------------------------------------------------------------- /src/pyevent.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #ifndef ___EVENT_PYEVENT__H___ 22 | #define ___EVENT_PYEVENT__H___ 23 | 24 | extern PyTypeObject PyEvent_Type; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/pyhttp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | #include "pyhttp.h" 28 | #include "pybase.h" 29 | #include "pybuffer.h" 30 | 31 | typedef struct _PyHttpServerObject { 32 | PyObject_HEAD 33 | struct evhttp *http; 34 | PyEventBaseObject *base; 35 | PyObject *callbacks; 36 | } PyHttpServerObject; 37 | 38 | typedef struct _PyBoundSocketObject { 39 | PyObject_HEAD 40 | struct evhttp_bound_socket *socket; 41 | PyHttpServerObject *http; 42 | } PyBoundSocketObject; 43 | 44 | typedef struct _PyHttpCallbackObject { 45 | PyObject_HEAD 46 | PyHttpServerObject *http; 47 | PyObject *path; 48 | PyObject *callback; 49 | PyObject *userdata; 50 | } PyHttpCallbackObject; 51 | 52 | typedef struct _PyHttpRequestObject { 53 | PyObject_HEAD 54 | struct evhttp_request *request; 55 | PyHttpServerObject *http; 56 | } PyHttpRequestObject; 57 | 58 | static PyBoundSocketObject * 59 | _pyhttp_new_bound_socket(PyHttpServerObject *self, struct evhttp_bound_socket *socket) 60 | { 61 | PyBoundSocketObject *result = (PyBoundSocketObject *) PyBoundSocket_Type.tp_alloc(&PyBoundSocket_Type, 0); 62 | if (result == NULL) { 63 | Py_BEGIN_ALLOW_THREADS 64 | evhttp_del_accept_socket(self->http, socket); 65 | Py_END_ALLOW_THREADS 66 | return NULL; 67 | } 68 | 69 | result->socket = socket; 70 | result->http = self; 71 | Py_INCREF(self); 72 | return result; 73 | } 74 | 75 | static PyHttpCallbackObject * 76 | _pyhttp_new_callback(PyHttpServerObject *self, char *path, PyObject *callback, PyObject *userdata) 77 | { 78 | PyHttpCallbackObject *result = (PyHttpCallbackObject *) PyHttpCallback_Type.tp_alloc(&PyHttpCallback_Type, 0); 79 | if (result == NULL) { 80 | return NULL; 81 | } 82 | 83 | result->http = self; 84 | Py_INCREF(self); 85 | if (path != NULL) { 86 | result->path = PyString_FromString(path); 87 | } else { 88 | result->path = Py_None; 89 | Py_INCREF(Py_None); 90 | } 91 | result->callback = callback; 92 | Py_INCREF(callback); 93 | result->userdata = userdata; 94 | Py_INCREF(userdata); 95 | return result; 96 | } 97 | 98 | static PyHttpRequestObject * 99 | _pyhttp_new_request(PyHttpServerObject *self, struct evhttp_request *request) 100 | { 101 | PyHttpRequestObject *result = (PyHttpRequestObject *) PyHttpRequest_Type.tp_alloc(&PyHttpRequest_Type, 0); 102 | if (result == NULL) { 103 | return NULL; 104 | } 105 | 106 | result->http = self; 107 | Py_INCREF(self); 108 | result->request = request; 109 | return result; 110 | } 111 | 112 | static PyObject * 113 | pyhttp_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 114 | { 115 | PyHttpServerObject *s; 116 | s = (PyHttpServerObject *)type->tp_alloc(type, 0); 117 | if (s != NULL) { 118 | s->http = NULL; 119 | s->base = NULL; 120 | s->callbacks = NULL; 121 | } 122 | return (PyObject *)s; 123 | } 124 | 125 | static int 126 | pyhttp_init(PyHttpServerObject *self, PyObject *args, PyObject *kwds) 127 | { 128 | PyEventBaseObject *base; 129 | if (!PyArg_ParseTuple(args, "O!", &PyEventBase_Type, &base)) 130 | return -1; 131 | 132 | self->http = evhttp_new(base->base); 133 | if (self->http == NULL) { 134 | PyErr_NoMemory(); 135 | return -1; 136 | } 137 | 138 | self->base = base; 139 | Py_INCREF(base); 140 | self->callbacks = PyDict_New(); 141 | return 0; 142 | } 143 | 144 | static void 145 | pyhttp_dealloc(PyHttpServerObject *self) 146 | { 147 | Py_BEGIN_ALLOW_THREADS 148 | if (self->http != NULL) { 149 | evhttp_free(self->http); 150 | } 151 | Py_END_ALLOW_THREADS 152 | Py_XDECREF(self->base); 153 | Py_DECREF(self->callbacks); 154 | Py_TYPE(self)->tp_free(self); 155 | } 156 | 157 | PyDoc_STRVAR(pyhttp_bind_doc, "Binds an HTTP server on the specified address and port."); 158 | 159 | static PyObject * 160 | pyhttp_bind(PyHttpServerObject *self, PyObject *args) 161 | { 162 | char *hostname; 163 | int port; 164 | struct evhttp_bound_socket *sock; 165 | 166 | if (!PyArg_ParseTuple(args, "si", &hostname, &port)) 167 | return NULL; 168 | 169 | Py_BEGIN_ALLOW_THREADS 170 | sock = evhttp_bind_socket_with_handle(self->http, hostname, port); 171 | Py_END_ALLOW_THREADS 172 | if (sock == NULL) { 173 | PyErr_SetString(PyExc_TypeError, "could not bind socket"); 174 | return NULL; 175 | } 176 | 177 | return (PyObject *) _pyhttp_new_bound_socket(self, sock); 178 | } 179 | 180 | PyDoc_STRVAR(pyhttp_accept_doc, "Makes an HTTP server accept connections on the specified socket."); 181 | 182 | static PyObject * 183 | pyhttp_accept(PyHttpServerObject *self, PyObject *args) 184 | { 185 | PyObject *pysocket; 186 | struct evhttp_bound_socket *sock; 187 | int fd; 188 | 189 | if (!PyArg_ParseTuple(args, "O", &pysocket)) 190 | return NULL; 191 | 192 | if (PyBoundSocket_Check(pysocket)) { 193 | Py_BEGIN_ALLOW_THREADS 194 | sock = evhttp_accept_socket_with_handle(self->http, evhttp_bound_socket_get_fd(((PyBoundSocketObject *) pysocket)->socket)); 195 | Py_END_ALLOW_THREADS 196 | } else { 197 | PyObject *pyfd = PyNumber_Int(pysocket); 198 | if (pyfd == NULL) { 199 | PyErr_Format(PyExc_TypeError, "expected a BoundSocket or a number, not '%s'", pysocket->ob_type->tp_name); 200 | return NULL; 201 | } 202 | fd = PyInt_AsLong(pyfd); 203 | Py_DECREF(pyfd); 204 | Py_BEGIN_ALLOW_THREADS 205 | sock = evhttp_accept_socket_with_handle(self->http, fd); 206 | Py_END_ALLOW_THREADS 207 | } 208 | if (sock == NULL) { 209 | PyErr_SetString(PyExc_TypeError, "could not accept on socket"); 210 | return NULL; 211 | } 212 | 213 | return (PyObject *) _pyhttp_new_bound_socket(self, sock); 214 | } 215 | 216 | PyDoc_STRVAR(pyhttp_set_max_headers_size_doc, "Set the maximum allowed size for request headers."); 217 | 218 | static PyObject * 219 | pyhttp_set_max_headers_size(PyHttpServerObject *self, PyObject *args) 220 | { 221 | Py_ssize_t size; 222 | 223 | if (!PyArg_ParseTuple(args, "n", &size)) 224 | return NULL; 225 | 226 | evhttp_set_max_headers_size(self->http, size); 227 | Py_RETURN_NONE; 228 | } 229 | 230 | PyDoc_STRVAR(pyhttp_set_max_body_size_doc, "Set the maximum allowed size for request body."); 231 | 232 | static PyObject * 233 | pyhttp_set_max_body_size(PyHttpServerObject *self, PyObject *args) 234 | { 235 | Py_ssize_t size; 236 | 237 | if (!PyArg_ParseTuple(args, "n", &size)) 238 | return NULL; 239 | 240 | evhttp_set_max_body_size(self->http, size); 241 | Py_RETURN_NONE; 242 | } 243 | 244 | PyDoc_STRVAR(pyhttp_set_allowed_methods_doc, "Sets the what HTTP methods are supported in requests."); 245 | 246 | static PyObject * 247 | pyhttp_set_allowed_methods(PyHttpServerObject *self, PyObject *args) 248 | { 249 | int methods; 250 | 251 | if (!PyArg_ParseTuple(args, "i", &methods)) 252 | return NULL; 253 | 254 | evhttp_set_allowed_methods(self->http, methods); 255 | Py_RETURN_NONE; 256 | } 257 | 258 | static void 259 | _pyhttp_invoke_callback(struct evhttp_request *req, void *userdata) 260 | { 261 | PyHttpCallbackObject *cb = (PyHttpCallbackObject *) userdata; 262 | START_BLOCK_THREADS 263 | PyHttpRequestObject *request = _pyhttp_new_request(cb->http, req); 264 | if (request == NULL) { 265 | pybase_store_error(cb->http->base); 266 | } else { 267 | PyObject *result = PyObject_CallFunction(cb->callback, "OOO", cb->http, request, cb->userdata); 268 | if (result == NULL) { 269 | pybase_store_error(cb->http->base); 270 | } else { 271 | Py_DECREF(result); 272 | } 273 | Py_DECREF((PyObject *) request); 274 | } 275 | END_BLOCK_THREADS 276 | } 277 | 278 | PyDoc_STRVAR(pyhttp_set_callback_doc, "Set a callback for a specified URI."); 279 | 280 | static PyObject * 281 | pyhttp_set_callback(PyHttpServerObject *self, PyObject *args) 282 | { 283 | char *path; 284 | PyObject *callback; 285 | PyObject *userdata=Py_None; 286 | PyHttpCallbackObject *cb; 287 | int result; 288 | 289 | if (!PyArg_ParseTuple(args, "sO|O", &path, &callback, &userdata)) 290 | return NULL; 291 | 292 | cb = _pyhttp_new_callback(self, path, callback, userdata); 293 | if (cb == NULL) { 294 | return NULL; 295 | } 296 | 297 | Py_BEGIN_ALLOW_THREADS 298 | result = evhttp_set_cb(self->http, path, _pyhttp_invoke_callback, cb); 299 | if (result == -1) { 300 | evhttp_del_cb(self->http, path); 301 | result = evhttp_set_cb(self->http, path, _pyhttp_invoke_callback, cb); 302 | } 303 | Py_END_ALLOW_THREADS 304 | if (result != 0) { 305 | PyErr_SetString(PyExc_TypeError, "could not set callback"); 306 | return NULL; 307 | } 308 | 309 | PyDict_SetItemString(self->callbacks, path, (PyObject *) cb); 310 | Py_RETURN_NONE; 311 | } 312 | 313 | PyDoc_STRVAR(pyhttp_del_callback_doc, "Remove callback for a specified URI."); 314 | 315 | static PyObject * 316 | pyhttp_del_callback(PyHttpServerObject *self, PyObject *args) 317 | { 318 | char *path; 319 | 320 | if (!PyArg_ParseTuple(args, "s", &path)) 321 | return NULL; 322 | 323 | Py_BEGIN_ALLOW_THREADS 324 | evhttp_del_cb(self->http, path); 325 | Py_END_ALLOW_THREADS 326 | 327 | if (PyDict_DelItemString(self->callbacks, path) != 0) { 328 | PyErr_Clear(); 329 | } 330 | Py_RETURN_NONE; 331 | } 332 | 333 | PyDoc_STRVAR(pyhttp_set_generic_callback_doc, "Set a callback for all requests that are not caught by specific callbacks."); 334 | 335 | static PyObject * 336 | pyhttp_set_generic_callback(PyHttpServerObject *self, PyObject *args) 337 | { 338 | PyObject *callback; 339 | PyObject *userdata=Py_None; 340 | PyHttpCallbackObject *cb; 341 | 342 | if (!PyArg_ParseTuple(args, "O|O", &callback, &userdata)) 343 | return NULL; 344 | 345 | if (callback == Py_None) { 346 | Py_BEGIN_ALLOW_THREADS 347 | evhttp_set_gencb(self->http, NULL, NULL); 348 | Py_END_ALLOW_THREADS 349 | 350 | if (PyDict_DelItem(self->callbacks, Py_None) != 0) { 351 | PyErr_Clear(); 352 | } 353 | Py_RETURN_NONE; 354 | } 355 | 356 | cb = _pyhttp_new_callback(self, NULL, callback, userdata); 357 | if (cb == NULL) { 358 | return NULL; 359 | } 360 | 361 | Py_BEGIN_ALLOW_THREADS 362 | evhttp_set_gencb(self->http, _pyhttp_invoke_callback, cb); 363 | Py_END_ALLOW_THREADS 364 | 365 | Py_INCREF(Py_None); 366 | PyDict_SetItem(self->callbacks, Py_None, (PyObject *) cb); 367 | Py_RETURN_NONE; 368 | } 369 | 370 | PyDoc_STRVAR(pyhttp_set_timeout_doc, "Set the timeout for an HTTP request."); 371 | 372 | static PyObject * 373 | pyhttp_set_timeout(PyHttpServerObject *self, PyObject *args) 374 | { 375 | int timeout; 376 | 377 | if (!PyArg_ParseTuple(args, "i", &timeout)) 378 | return NULL; 379 | 380 | evhttp_set_timeout(self->http, timeout); 381 | Py_RETURN_NONE; 382 | } 383 | 384 | static PyMethodDef 385 | pyhttp_methods[] = { 386 | {"bind", (PyCFunction)pyhttp_bind, METH_VARARGS, pyhttp_bind_doc}, 387 | {"accept", (PyCFunction)pyhttp_accept, METH_VARARGS, pyhttp_accept_doc}, 388 | {"set_max_headers_size", (PyCFunction)pyhttp_set_max_headers_size, METH_VARARGS, pyhttp_set_max_headers_size_doc}, 389 | {"set_max_body_size", (PyCFunction)pyhttp_set_max_body_size, METH_VARARGS, pyhttp_set_max_body_size_doc}, 390 | {"set_allowed_methods", (PyCFunction)pyhttp_set_allowed_methods, METH_VARARGS, pyhttp_set_allowed_methods_doc}, 391 | {"set_callback", (PyCFunction)pyhttp_set_callback, METH_VARARGS, pyhttp_set_callback_doc}, 392 | {"del_callback", (PyCFunction)pyhttp_del_callback, METH_VARARGS, pyhttp_del_callback_doc}, 393 | {"set_generic_callback", (PyCFunction)pyhttp_set_generic_callback, METH_VARARGS, pyhttp_set_generic_callback_doc}, 394 | {"set_timeout", (PyCFunction)pyhttp_set_timeout, METH_VARARGS, pyhttp_set_timeout_doc}, 395 | {NULL, NULL}, 396 | }; 397 | 398 | PyDoc_STRVAR(pyhttp_doc, "A basic HTTP server"); 399 | 400 | PyTypeObject 401 | PyHttpServer_Type = { 402 | PyObject_HEAD_INIT(NULL) 403 | 0, /* tp_internal */ 404 | "event.HttpServer", /* tp_name */ 405 | sizeof(PyHttpServerObject), /* tp_basicsize */ 406 | 0, /* tp_itemsize */ 407 | (destructor)pyhttp_dealloc, /* tp_dealloc */ 408 | 0, /* tp_print */ 409 | 0, /* tp_getattr */ 410 | 0, /* tp_setattr */ 411 | 0, /* tp_compare */ 412 | 0, /* tp_repr */ 413 | 0, /* tp_as_number */ 414 | 0, /* tp_as_sequence */ 415 | 0, /* tp_as_mapping */ 416 | 0, /* tp_hash */ 417 | 0, /* tp_call */ 418 | 0, /* tp_str */ 419 | 0, /* tp_getattro */ 420 | 0, /* tp_setattro */ 421 | 0, /* tp_as_buffer */ 422 | Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /* tp_flags */ 423 | pyhttp_doc, /* tp_doc */ 424 | 0, /* tp_traverse */ 425 | 0, /* tp_clear */ 426 | 0, /* tp_richcompare */ 427 | 0, /* tp_weaklistoffset */ 428 | 0, /* tp_iter */ 429 | 0, /* tp_iternext */ 430 | pyhttp_methods, /* tp_methods */ 431 | 0, /* tp_members */ 432 | 0, /* tp_getset */ 433 | 0, /* tp_base */ 434 | 0, /* tp_dict */ 435 | 0, /* tp_descr_get */ 436 | 0, /* tp_descr_set */ 437 | 0, /* tp_dictoffset */ 438 | (initproc)pyhttp_init, /* tp_init */ 439 | 0, /* tp_alloc */ 440 | pyhttp_new, /* tp_new */ 441 | 0, /* tp_free */ 442 | }; 443 | 444 | static PyObject * 445 | pybound_socket_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 446 | { 447 | PyBoundSocketObject *s; 448 | s = (PyBoundSocketObject *)type->tp_alloc(type, 0); 449 | if (s != NULL) { 450 | s->socket = NULL; 451 | s->http = NULL; 452 | } 453 | return (PyObject *)s; 454 | } 455 | 456 | static void 457 | pybound_socket_dealloc(PyBoundSocketObject *self) 458 | { 459 | if (self->http != NULL) { 460 | Py_BEGIN_ALLOW_THREADS 461 | evhttp_del_accept_socket(self->http->http, self->socket); 462 | Py_END_ALLOW_THREADS 463 | } 464 | Py_XDECREF(self->http); 465 | Py_TYPE(self)->tp_free(self); 466 | } 467 | 468 | PyDoc_STRVAR(pybound_socket_doc, "A bound HTTP socket"); 469 | 470 | PyTypeObject 471 | PyBoundSocket_Type = { 472 | PyObject_HEAD_INIT(NULL) 473 | 0, /* tp_internal */ 474 | "event.BoundSocket", /* tp_name */ 475 | sizeof(PyBoundSocketObject), /* tp_basicsize */ 476 | 0, /* tp_itemsize */ 477 | (destructor)pybound_socket_dealloc, /* tp_dealloc */ 478 | 0, /* tp_print */ 479 | 0, /* tp_getattr */ 480 | 0, /* tp_setattr */ 481 | 0, /* tp_compare */ 482 | 0, /* tp_repr */ 483 | 0, /* tp_as_number */ 484 | 0, /* tp_as_sequence */ 485 | 0, /* tp_as_mapping */ 486 | 0, /* tp_hash */ 487 | 0, /* tp_call */ 488 | 0, /* tp_str */ 489 | 0, /* tp_getattro */ 490 | 0, /* tp_setattro */ 491 | 0, /* tp_as_buffer */ 492 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 493 | pybound_socket_doc, /* tp_doc */ 494 | 0, /* tp_traverse */ 495 | 0, /* tp_clear */ 496 | 0, /* tp_richcompare */ 497 | 0, /* tp_weaklistoffset */ 498 | 0, /* tp_iter */ 499 | 0, /* tp_iternext */ 500 | 0, /* tp_methods */ 501 | 0, /* tp_members */ 502 | 0, /* tp_getset */ 503 | 0, /* tp_base */ 504 | 0, /* tp_dict */ 505 | 0, /* tp_descr_get */ 506 | 0, /* tp_descr_set */ 507 | 0, /* tp_dictoffset */ 508 | 0, /* tp_init */ 509 | 0, /* tp_alloc */ 510 | pybound_socket_new, /* tp_new */ 511 | 0, /* tp_free */ 512 | }; 513 | 514 | static PyObject * 515 | pyhttp_callback_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 516 | { 517 | PyHttpCallbackObject *s; 518 | s = (PyHttpCallbackObject *)type->tp_alloc(type, 0); 519 | if (s != NULL) { 520 | s->http = NULL; 521 | s->path = NULL; 522 | s->callback = NULL; 523 | s->userdata = NULL; 524 | } 525 | return (PyObject *)s; 526 | } 527 | 528 | static void 529 | pyhttp_callback_dealloc(PyHttpCallbackObject *self) 530 | { 531 | if (self->path != NULL) { 532 | Py_BEGIN_ALLOW_THREADS 533 | evhttp_del_cb(self->http->http, PyString_AS_STRING(self->path)); 534 | Py_END_ALLOW_THREADS 535 | } 536 | Py_XDECREF(self->http); 537 | Py_XDECREF(self->path); 538 | Py_XDECREF(self->callback); 539 | Py_XDECREF(self->userdata); 540 | Py_TYPE(self)->tp_free(self); 541 | } 542 | 543 | PyTypeObject 544 | PyHttpCallback_Type = { 545 | PyObject_HEAD_INIT(NULL) 546 | 0, /* tp_internal */ 547 | "event.HttpCallback", /* tp_name */ 548 | sizeof(PyHttpCallbackObject), /* tp_basicsize */ 549 | 0, /* tp_itemsize */ 550 | (destructor)pyhttp_callback_dealloc, /* tp_dealloc */ 551 | 0, /* tp_print */ 552 | 0, /* tp_getattr */ 553 | 0, /* tp_setattr */ 554 | 0, /* tp_compare */ 555 | 0, /* tp_repr */ 556 | 0, /* tp_as_number */ 557 | 0, /* tp_as_sequence */ 558 | 0, /* tp_as_mappping */ 559 | 0, /* tp_hash */ 560 | 0, /* tp_call */ 561 | 0, /* tp_str */ 562 | 0, /* tp_getattro */ 563 | 0, /* tp_setattro */ 564 | 0, /* tp_as_buffer */ 565 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 566 | 0, /* tp_doc */ 567 | 0, /* tp_traverse */ 568 | 0, /* tp_clear */ 569 | 0, /* tp_richcompare */ 570 | 0, /* tp_weaklistoffset */ 571 | 0, /* tp_iter */ 572 | 0, /* tp_iternext */ 573 | 0, /* tp_methods */ 574 | 0, /* tp_members */ 575 | 0, /* tp_getset */ 576 | 0, /* tp_base */ 577 | 0, /* tp_dict */ 578 | 0, /* tp_descr_get */ 579 | 0, /* tp_descr_set */ 580 | 0, /* tp_dictoffset */ 581 | 0, /* tp_init */ 582 | 0, /* tp_alloc */ 583 | pyhttp_callback_new, /* tp_new */ 584 | 0, /* tp_free */ 585 | }; 586 | 587 | static PyObject * 588 | pyhttp_request_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 589 | { 590 | PyHttpRequestObject *s; 591 | s = (PyHttpRequestObject *)type->tp_alloc(type, 0); 592 | if (s != NULL) { 593 | s->http = NULL; 594 | s->request = NULL; 595 | } 596 | return (PyObject *)s; 597 | } 598 | 599 | static void 600 | pyhttp_request_dealloc(PyHttpRequestObject *self) 601 | { 602 | if (self->request != NULL) { 603 | Py_BEGIN_ALLOW_THREADS 604 | evhttp_request_free(self->request); 605 | Py_END_ALLOW_THREADS 606 | } 607 | Py_XDECREF(self->http); 608 | Py_TYPE(self)->tp_free(self); 609 | } 610 | 611 | PyDoc_STRVAR(pyhttp_request_send_error_doc, "Send an HTML error message to the client."); 612 | 613 | static PyObject * 614 | pyhttp_request_send_error(PyHttpRequestObject *self, PyObject *args) 615 | { 616 | int code; 617 | char *reason=NULL; 618 | 619 | if (!PyArg_ParseTuple(args, "i|s", &code, &reason)) 620 | return NULL; 621 | 622 | if (self->request == NULL) { 623 | PyErr_SetString(PyExc_TypeError, "request already completed"); 624 | return NULL; 625 | } 626 | 627 | Py_BEGIN_ALLOW_THREADS 628 | evhttp_send_error(self->request, code, reason); 629 | Py_END_ALLOW_THREADS 630 | self->request = NULL; 631 | Py_RETURN_NONE; 632 | } 633 | 634 | PyDoc_STRVAR(pyhttp_request_send_reply_doc, "Send an HTML reply to the client."); 635 | 636 | static PyObject * 637 | pyhttp_request_send_reply(PyHttpRequestObject *self, PyObject *args) 638 | { 639 | int code; 640 | char *reason; 641 | PyObject *pydata; 642 | char *data; 643 | Py_ssize_t length; 644 | 645 | if (!PyArg_ParseTuple(args, "isO", &code, &reason, &pydata)) 646 | return NULL; 647 | 648 | if (self->request == NULL) { 649 | PyErr_SetString(PyExc_TypeError, "request already completed"); 650 | return NULL; 651 | } 652 | 653 | if (PyEventBuffer_Check(pydata)) { 654 | Py_BEGIN_ALLOW_THREADS 655 | evhttp_send_reply(self->request, code, reason, ((PyBufferObject *) data)->buffer); 656 | Py_END_ALLOW_THREADS 657 | } else { 658 | struct evbuffer *buffer = NULL; 659 | if (PyObject_AsReadBuffer(pydata, (const void **) &data, &length) != 0) { 660 | return NULL; 661 | } 662 | 663 | buffer = evbuffer_new(); 664 | if (buffer == NULL) { 665 | return PyErr_NoMemory(); 666 | } 667 | evbuffer_add(buffer, data, length); 668 | Py_BEGIN_ALLOW_THREADS 669 | evhttp_send_reply(self->request, code, reason, buffer); 670 | Py_END_ALLOW_THREADS 671 | evbuffer_free(buffer); 672 | } 673 | 674 | self->request = NULL; 675 | Py_RETURN_NONE; 676 | } 677 | 678 | PyDoc_STRVAR(pyhttp_request_send_reply_start_doc, "Initiate a reply that uses Transfer-Encoding chunked."); 679 | 680 | static PyObject * 681 | pyhttp_request_send_reply_start(PyHttpRequestObject *self, PyObject *args) 682 | { 683 | int code; 684 | char *reason=NULL; 685 | 686 | if (!PyArg_ParseTuple(args, "i|s", &code, &reason)) 687 | return NULL; 688 | 689 | if (self->request == NULL) { 690 | PyErr_SetString(PyExc_TypeError, "request already completed"); 691 | return NULL; 692 | } 693 | 694 | Py_BEGIN_ALLOW_THREADS 695 | evhttp_send_reply_start(self->request, code, reason); 696 | Py_END_ALLOW_THREADS 697 | Py_RETURN_NONE; 698 | } 699 | 700 | PyDoc_STRVAR(pyhttp_request_send_reply_chunk_doc, "Send another data chunk as part of an ongoing chunked reply."); 701 | 702 | static PyObject * 703 | pyhttp_request_send_reply_chunk(PyHttpRequestObject *self, PyObject *args) 704 | { 705 | PyObject *pydata; 706 | char *data; 707 | Py_ssize_t length; 708 | 709 | if (!PyArg_ParseTuple(args, "O", &pydata)) 710 | return NULL; 711 | 712 | if (self->request == NULL) { 713 | PyErr_SetString(PyExc_TypeError, "request already completed"); 714 | return NULL; 715 | } 716 | 717 | if (PyEventBuffer_Check(pydata)) { 718 | Py_BEGIN_ALLOW_THREADS 719 | evhttp_send_reply_chunk(self->request, ((PyBufferObject *) data)->buffer); 720 | Py_END_ALLOW_THREADS 721 | } else { 722 | struct evbuffer *buffer; 723 | if (PyObject_AsReadBuffer(pydata, (const void **) &data, &length) != 0) { 724 | return NULL; 725 | } 726 | 727 | buffer = evbuffer_new(); 728 | if (buffer == NULL) { 729 | return PyErr_NoMemory(); 730 | } 731 | evbuffer_add(buffer, data, length); 732 | Py_BEGIN_ALLOW_THREADS 733 | evhttp_send_reply_chunk(self->request, buffer); 734 | Py_END_ALLOW_THREADS 735 | evbuffer_free(buffer); 736 | } 737 | Py_RETURN_NONE; 738 | } 739 | 740 | PyDoc_STRVAR(pyhttp_request_send_reply_end_doc, "Complete a chunked reply."); 741 | 742 | static PyObject * 743 | pyhttp_request_send_reply_end(PyHttpRequestObject *self, PyObject *args) 744 | { 745 | if (self->request == NULL) { 746 | PyErr_SetString(PyExc_TypeError, "request already completed"); 747 | return NULL; 748 | } 749 | 750 | Py_BEGIN_ALLOW_THREADS 751 | evhttp_send_reply_end(self->request); 752 | Py_END_ALLOW_THREADS 753 | self->request = NULL; 754 | Py_RETURN_NONE; 755 | } 756 | 757 | static PyMethodDef 758 | pyhttp_request_methods[] = { 759 | {"send_error", (PyCFunction)pyhttp_request_send_error, METH_VARARGS, pyhttp_request_send_error_doc}, 760 | {"send_reply", (PyCFunction)pyhttp_request_send_reply, METH_VARARGS, pyhttp_request_send_reply_doc}, 761 | {"send_reply_start", (PyCFunction)pyhttp_request_send_reply_start, METH_VARARGS, pyhttp_request_send_reply_start_doc}, 762 | {"send_reply_chunk", (PyCFunction)pyhttp_request_send_reply_chunk, METH_VARARGS, pyhttp_request_send_reply_chunk_doc}, 763 | {"send_reply_end", (PyCFunction)pyhttp_request_send_reply_end, METH_NOARGS, pyhttp_request_send_reply_end_doc}, 764 | {NULL, NULL}, 765 | }; 766 | 767 | PyDoc_STRVAR(pyhttp_request_doc, "A HTTP request"); 768 | 769 | PyTypeObject 770 | PyHttpRequest_Type = { 771 | PyObject_HEAD_INIT(NULL) 772 | 0, /* tp_internal */ 773 | "event.HttpRequest", /* tp_name */ 774 | sizeof(PyHttpRequestObject), /* tp_basicsize */ 775 | 0, /* tp_itemsize */ 776 | (destructor)pyhttp_request_dealloc, /* tp_dealloc */ 777 | 0, /* tp_print */ 778 | 0, /* tp_getattr */ 779 | 0, /* tp_setattr */ 780 | 0, /* tp_compare */ 781 | 0, /* tp_repr */ 782 | 0, /* tp_as_number */ 783 | 0, /* tp_as_sequence */ 784 | 0, /* tp_as_mappping */ 785 | 0, /* tp_hash */ 786 | 0, /* tp_call */ 787 | 0, /* tp_str */ 788 | 0, /* tp_getattro */ 789 | 0, /* tp_setattro */ 790 | 0, /* tp_as_buffer */ 791 | Py_TPFLAGS_DEFAULT, /* tp_flags */ 792 | pyhttp_request_doc, /* tp_doc */ 793 | 0, /* tp_traverse */ 794 | 0, /* tp_clear */ 795 | 0, /* tp_richcompare */ 796 | 0, /* tp_weaklistoffset */ 797 | 0, /* tp_iter */ 798 | 0, /* tp_iternext */ 799 | pyhttp_request_methods, /* tp_methods */ 800 | 0, /* tp_members */ 801 | 0, /* tp_getset */ 802 | 0, /* tp_base */ 803 | 0, /* tp_dict */ 804 | 0, /* tp_descr_get */ 805 | 0, /* tp_descr_set */ 806 | 0, /* tp_dictoffset */ 807 | 0, /* tp_init */ 808 | 0, /* tp_alloc */ 809 | pyhttp_request_new, /* tp_new */ 810 | 0, /* tp_free */ 811 | }; 812 | -------------------------------------------------------------------------------- /src/pyhttp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #ifndef ___EVENT_PYHTTP__H___ 22 | #define ___EVENT_PYHTTP__H___ 23 | 24 | extern PyTypeObject PyHttpServer_Type; 25 | extern PyTypeObject PyBoundSocket_Type; 26 | extern PyTypeObject PyHttpCallback_Type; 27 | extern PyTypeObject PyHttpRequest_Type; 28 | 29 | #define PyHttpServer_Check(ob) ((ob)->ob_type == &PyHttpServer_Type) 30 | #define PyBoundSocket_Check(ob) ((ob)->ob_type == &PyBoundSocket_Type) 31 | #define PyHttpCallback_Check(ob) ((ob)->ob_type == &PyHttpCallback_Type) 32 | #define PyHttpRequest_Check(ob) ((ob)->ob_type == &PyHttpRequest_Type) 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/pylistener.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | 26 | #include "pybase.h" 27 | #include "pylistener.h" 28 | 29 | typedef struct _PyListenerObject { 30 | PyObject_HEAD 31 | PyEventBaseObject *base; 32 | struct evconnlistener *listener; 33 | PyObject *callback; 34 | PyObject *userdata; 35 | PyObject *weakrefs; 36 | int fd; 37 | } PyListenerObject; 38 | 39 | static void 40 | pylistener_callback(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *addr, int socklen, void *userdata) 41 | { 42 | PyListenerObject *self = (PyListenerObject *) userdata; 43 | START_BLOCK_THREADS 44 | PyObject *result = PyObject_CallFunction(self->callback, "OiO", self, fd, self->userdata); 45 | if (result == NULL) { 46 | pybase_store_error(self->base); 47 | } else { 48 | Py_DECREF(result); 49 | } 50 | END_BLOCK_THREADS 51 | } 52 | 53 | static PyObject * 54 | pylistener_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 55 | { 56 | PyListenerObject *s = (PyListenerObject *)type->tp_alloc(type, 0); 57 | if (s != NULL) { 58 | s->base = NULL; 59 | s->listener = NULL; 60 | s->callback = NULL; 61 | s->userdata = NULL; 62 | s->weakrefs = NULL; 63 | } 64 | return (PyObject *)s; 65 | } 66 | 67 | static int 68 | pylistener_init(PyListenerObject *self, PyObject *args, PyObject *kwds) 69 | { 70 | PyEventBaseObject *base; 71 | int flags; 72 | int backlog; 73 | int fd; 74 | PyObject *callback; 75 | PyObject *userdata = Py_None; 76 | if (!PyArg_ParseTuple(args, "O!Oiii|O", &PyEventBase_Type, &base, &callback, &flags, &backlog, &fd, &userdata)) 77 | return -1; 78 | 79 | if (!PyCallable_Check(callback)) { 80 | PyErr_SetString(PyExc_TypeError, "the callback must be callable"); 81 | return -1; 82 | } 83 | 84 | self->listener = evconnlistener_new(base->base, pylistener_callback, self, flags, backlog, fd); 85 | if (self->listener == NULL) { 86 | PyErr_NoMemory(); 87 | return -1; 88 | } 89 | 90 | self->base = base; 91 | Py_INCREF(base); 92 | self->callback = callback; 93 | Py_INCREF(callback); 94 | self->userdata = userdata; 95 | Py_INCREF(userdata); 96 | self->fd = fd; 97 | return 0; 98 | } 99 | 100 | static int 101 | pylistener_traverse(PyListenerObject *self, visitproc visit, void *arg) 102 | { 103 | Py_VISIT(self->callback); 104 | Py_VISIT(self->userdata); 105 | Py_VISIT(self->base); 106 | return 0; 107 | } 108 | 109 | static int 110 | pylistener_clear(PyListenerObject *self) 111 | { 112 | Py_BEGIN_ALLOW_THREADS 113 | if (self->listener != NULL) { 114 | evconnlistener_free(self->listener); 115 | self->listener = NULL; 116 | } 117 | Py_END_ALLOW_THREADS 118 | Py_CLEAR(self->callback); 119 | Py_CLEAR(self->userdata); 120 | Py_CLEAR(self->base); 121 | return 0; 122 | } 123 | 124 | static void 125 | pylistener_dealloc(PyListenerObject *self) 126 | { 127 | if (self->weakrefs != NULL) { 128 | PyObject_ClearWeakRefs((PyObject *) self); 129 | } 130 | pylistener_clear(self); 131 | Py_TYPE(self)->tp_free(self); 132 | } 133 | 134 | PyDoc_STRVAR(pylistener_enable_doc, "Re-enable an evconnlistener that has been disabled."); 135 | 136 | static PyObject * 137 | pylistener_enable(PyListenerObject *self, PyObject *args) 138 | { 139 | Py_BEGIN_ALLOW_THREADS 140 | evconnlistener_enable(self->listener); 141 | Py_END_ALLOW_THREADS 142 | Py_RETURN_NONE; 143 | } 144 | 145 | PyDoc_STRVAR(pylistener_disable_doc, "Stop listening for connections on an evconnlistener."); 146 | 147 | static PyObject * 148 | pylistener_disable(PyListenerObject *self, PyObject *args) 149 | { 150 | Py_BEGIN_ALLOW_THREADS 151 | evconnlistener_disable(self->listener); 152 | Py_END_ALLOW_THREADS 153 | Py_RETURN_NONE; 154 | } 155 | 156 | static PyMethodDef 157 | pylistener_methods[] = { 158 | {"enable", (PyCFunction)pylistener_enable, METH_NOARGS, pylistener_enable_doc}, 159 | {"disable", (PyCFunction)pylistener_disable, METH_NOARGS, pylistener_disable_doc}, 160 | {NULL, NULL}, 161 | }; 162 | 163 | static PyMemberDef 164 | pylistener_members[] = { 165 | {"base", T_OBJECT, offsetof(PyListenerObject, base), READONLY, "the base this listener is assigned to"}, 166 | {"callback", T_OBJECT, offsetof(PyListenerObject, callback), READONLY, "the callback to execute"}, 167 | {"userdata", T_OBJECT, offsetof(PyListenerObject, userdata), READONLY, "the userdata to pass to callback"}, 168 | {"fd", T_INT, offsetof(PyListenerObject, fd), READONLY, "the file descriptor this listener is assigned to"}, 169 | {NULL} 170 | }; 171 | 172 | PyDoc_STRVAR(pylistener_doc, "Listener"); 173 | 174 | PyTypeObject 175 | PyListener_Type = { 176 | PyObject_HEAD_INIT(NULL) 177 | 0, /* tp_internal */ 178 | "event.Listener", /* tp_name */ 179 | sizeof(PyListenerObject), /* tp_basicsize */ 180 | 0, /* tp_itemsize */ 181 | (destructor)pylistener_dealloc, /* tp_dealloc */ 182 | 0, /* tp_print */ 183 | 0, /* tp_getattr */ 184 | 0, /* tp_setattr */ 185 | 0, /* tp_compare */ 186 | 0, /* tp_repr */ 187 | 0, /* tp_as_number */ 188 | 0, /* tp_as_sequence */ 189 | 0, /* tp_as_mapping */ 190 | 0, /* tp_hash */ 191 | 0, /* tp_call */ 192 | 0, /* tp_str */ 193 | 0, /* tp_getattro */ 194 | 0, /* tp_setattro */ 195 | 0, /* tp_as_buffer */ 196 | Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_WEAKREFS, /* tp_flags */ 197 | pylistener_doc, /* tp_doc */ 198 | (traverseproc)pylistener_traverse, /* tp_traverse */ 199 | (inquiry)pylistener_clear, /* tp_clear */ 200 | 0, /* tp_richcompare */ 201 | offsetof(PyListenerObject, weakrefs), /* tp_weaklistoffset */ 202 | 0, /* tp_iter */ 203 | 0, /* tp_iternext */ 204 | pylistener_methods, /* tp_methods */ 205 | pylistener_members, /* tp_members */ 206 | 0, /* tp_getset */ 207 | 0, /* tp_base */ 208 | 0, /* tp_dict */ 209 | 0, /* tp_descr_get */ 210 | 0, /* tp_descr_set */ 211 | 0, /* tp_dictoffset */ 212 | (initproc)pylistener_init, /* tp_init */ 213 | 0, /* tp_alloc */ 214 | pylistener_new, /* tp_new */ 215 | 0, /* tp_free */ 216 | }; 217 | -------------------------------------------------------------------------------- /src/pylistener.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Python Bindings for libevent 3 | * 4 | * Copyright (c) 2010-2011 by Joachim Bauch, mail@joachim-bauch.de 5 | * 6 | * This library is free software; you can redistribute it and/or 7 | * modify it under the terms of the GNU Lesser General Public 8 | * License as published by the Free Software Foundation; either 9 | * version 2.1 of the License, or (at your option) any later version. 10 | * 11 | * This library is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | * Lesser General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU Lesser General Public 17 | * License along with this library; if not, write to the Free Software 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | 21 | #ifndef ___EVENT_PYLISTENER__H___ 22 | #define ___EVENT_PYLISTENER__H___ 23 | 24 | extern PyTypeObject PyListener_Type; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | # some Python 2.3 unittest compatibility fixes 4 | if not hasattr(unittest.TestCase, 'assertTrue'): 5 | unittest.TestCase.assertTrue = unittest.TestCase.failUnless 6 | if not hasattr(unittest.TestCase, 'assertFalse'): 7 | unittest.TestCase.assertFalse = unittest.TestCase.failIf 8 | 9 | mod_base = 'tests' 10 | 11 | 12 | def suite(): 13 | import os.path 14 | from glob import glob 15 | 16 | suite = unittest.TestSuite() 17 | 18 | for testcase in glob(os.path.join(os.path.dirname(__file__), 'test_*.py')): 19 | mod_name = os.path.basename(testcase).split('.')[0] 20 | full_name = '%s.%s' % (mod_base, mod_name) 21 | 22 | mod = __import__(full_name) 23 | 24 | for part in full_name.split('.')[1:]: 25 | mod = getattr(mod, part) 26 | 27 | suite.addTest(mod.suite()) 28 | 29 | return suite 30 | 31 | 32 | def main(): 33 | unittest.main(defaultTest='suite') 34 | 35 | if __name__ == '__main__': 36 | main() 37 | -------------------------------------------------------------------------------- /tests/test_buffer.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | 4 | import libevent 5 | 6 | class TestBuffer(unittest.TestCase): 7 | 8 | def createBuffer(self): 9 | return libevent.Buffer() 10 | 11 | def test_add_remove(self): 12 | buf = self.createBuffer() 13 | self.failUnlessEqual(len(buf), 0) 14 | self.failIf(bool(buf)) 15 | buf.add('12') 16 | self.failUnless(bool(buf)) 17 | self.failUnlessEqual(len(buf), 2) 18 | buf.add('34') 19 | self.failUnlessEqual(len(buf), 4) 20 | self.failUnless('12' in buf) 21 | self.failUnless('23' in buf) 22 | self.failUnless('34' in buf) 23 | self.failUnless('1234' in buf) 24 | self.failUnlessEqual(buf.remove(4), '1234') 25 | self.failUnlessEqual(len(buf), 0) 26 | self.failUnlessRaises(TypeError, buf.remove, -2) 27 | 28 | def test_add_copyout(self): 29 | buf = self.createBuffer() 30 | buf.add('12') 31 | buf.add('34') 32 | self.failUnlessEqual(buf.copyout(2), '12') 33 | self.failUnlessEqual(buf.copyout(4), '1234') 34 | self.failUnlessRaises(TypeError, buf.copyout, -2) 35 | 36 | def test_add_buffer(self): 37 | buf1 = self.createBuffer() 38 | buf1.add('12') 39 | buf1.add('34') 40 | buf2 = self.createBuffer() 41 | buf2.add(buf1) 42 | self.failUnlessEqual(buf1.remove(1), '') 43 | self.failUnlessEqual(buf2.remove(4), '1234') 44 | 45 | def test_prepend_remove(self): 46 | buf = self.createBuffer() 47 | buf.prepend('12') 48 | buf.prepend('34') 49 | self.failUnlessEqual(buf.remove(4), '3412') 50 | 51 | def test_prepend_buffer(self): 52 | buf1 = self.createBuffer() 53 | buf1.add('12') 54 | buf2 = self.createBuffer() 55 | buf2.add('34') 56 | buf3 = self.createBuffer() 57 | buf3.prepend(buf1) 58 | buf3.prepend(buf2) 59 | self.failUnlessEqual(buf3.remove(4), '3412') 60 | 61 | def test_readln_noline(self): 62 | buf = self.createBuffer() 63 | buf.add('foo') 64 | line = buf.readln() 65 | self.failUnlessEqual(line, '') 66 | buf.add('\n') 67 | line = buf.readln() 68 | self.failUnlessEqual(line, 'foo') 69 | self.failIf(bool(buf)) 70 | 71 | def suite(): 72 | suite = unittest.TestSuite() 73 | 74 | test_cases = [ 75 | TestBuffer, 76 | ] 77 | 78 | for tc in test_cases: 79 | suite.addTest(unittest.makeSuite(tc)) 80 | 81 | return suite 82 | 83 | if __name__ == '__main__': 84 | unittest.main(defaultTest='suite') 85 | -------------------------------------------------------------------------------- /tests/test_bufferevent.py: -------------------------------------------------------------------------------- 1 | import gc 2 | import os 3 | import unittest 4 | import weakref 5 | 6 | import libevent 7 | 8 | class TestBufferEvent(unittest.TestCase): 9 | 10 | def createBase(self): 11 | return libevent.Base() 12 | 13 | def createBufferEvent(self, *args): 14 | return libevent.BufferEvent(*args) 15 | 16 | def test_bucket(self): 17 | base = self.createBase() 18 | buf = self.createBufferEvent(base) 19 | self.failUnlessEqual(buf.bucket, None) 20 | 21 | def test_recursive_callback(self): 22 | class A: 23 | def __init__(self, buf): 24 | self.buf = buf 25 | self.buf.set_callbacks(self._readable, None, None) 26 | 27 | def _readable(self): 28 | pass 29 | 30 | base = self.createBase() 31 | buf = self.createBufferEvent(base) 32 | r1 = weakref.ref(buf) 33 | a = A(buf) 34 | r2 = weakref.ref(a) 35 | del buf 36 | del a 37 | # the class and the bev still exist due to circular references 38 | self.failIfEqual(r1(), None) 39 | self.failIfEqual(r2(), None) 40 | # ...but after a gc run, they are gone 41 | gc.collect() 42 | self.failUnlessEqual(r1(), None) 43 | self.failUnlessEqual(r2(), None) 44 | 45 | def suite(): 46 | suite = unittest.TestSuite() 47 | 48 | test_cases = [ 49 | TestBufferEvent, 50 | ] 51 | 52 | for tc in test_cases: 53 | suite.addTest(unittest.makeSuite(tc)) 54 | 55 | return suite 56 | 57 | if __name__ == '__main__': 58 | unittest.main(defaultTest='suite') 59 | -------------------------------------------------------------------------------- /tests/test_event.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import threading 4 | try: 5 | import signal 6 | except ImportError: 7 | import warnings 8 | warnings.warn('No signals available on this platform, signal tests will be skipped') 9 | signal = None 10 | 11 | import libevent 12 | 13 | class TestEventBase(unittest.TestCase): 14 | 15 | def createBase(self, *args): 16 | return libevent.Base(*args) 17 | 18 | def createConfig(self): 19 | return libevent.Config() 20 | 21 | def createTimer(self, *args): 22 | return libevent.Timer(*args) 23 | 24 | def createSignal(self, *args): 25 | return libevent.Signal(*args) 26 | 27 | def fire_timer(self, _, evt): 28 | evt.set() 29 | 30 | def fire_signal(self, _, signum, evt): 31 | evt.set() 32 | 33 | def test_events(self): 34 | methods = libevent.METHODS 35 | self.failUnless('select' in methods, methods) 36 | self.failUnless('poll' in methods, methods) 37 | 38 | def test_timer(self): 39 | evt = threading.Event() 40 | base = self.createBase() 41 | t = self.createTimer(base, self.fire_timer, evt) 42 | base.loopexit(0.1) 43 | base.loop() 44 | self.failIf(evt.isSet(), 'timer should not have been fired') 45 | 46 | def test_timer_noref(self): 47 | # a timer object without a reference doesn't fire 48 | evt = threading.Event() 49 | base = self.createBase() 50 | t = self.createTimer(base, self.fire_timer, evt) 51 | t.add(0.1) 52 | del t 53 | base.loopexit(0.2) 54 | base.loop() 55 | self.failIf(evt.isSet(), 'timer should not have been fired') 56 | 57 | def test_timer_add(self): 58 | evt = threading.Event() 59 | base = self.createBase() 60 | t = self.createTimer(base, self.fire_timer, evt) 61 | t.add(0.1) 62 | base.loopexit(0.2) 63 | base.loop() 64 | self.failUnless(evt.isSet(), 'timer did not fire') 65 | 66 | def test_timer_delete(self): 67 | evt = threading.Event() 68 | base = self.createBase() 69 | t = self.createTimer(base, self.fire_timer, evt) 70 | t.add(0.1) 71 | t.delete() 72 | base.loopexit(0.2) 73 | base.loop() 74 | self.failIf(evt.isSet(), 'timer should not have been fired') 75 | 76 | if signal is not None: 77 | 78 | def test_signal(self): 79 | evt = threading.Event() 80 | base = self.createBase() 81 | t = self.createSignal(base, signal.SIGUSR1, self.fire_signal, evt) 82 | t.add(0.1) 83 | base.loopexit(0.2) 84 | os.kill(os.getpid(), signal.SIGUSR1) 85 | base.loop() 86 | self.failUnless(evt.isSet(), 'signal did not fire') 87 | 88 | def test_cfg_avoid_method(self): 89 | self.failUnless(len(libevent.METHODS) > 1) 90 | for method in libevent.METHODS: 91 | all_methods = set(libevent.METHODS) 92 | all_methods.remove(method) 93 | cfg = self.createConfig() 94 | for m in all_methods: 95 | cfg.avoid_method(m) 96 | base = self.createBase(cfg) 97 | self.failUnlessEqual(method, base.method) 98 | 99 | def suite(): 100 | suite = unittest.TestSuite() 101 | 102 | test_cases = [ 103 | TestEventBase, 104 | ] 105 | 106 | for tc in test_cases: 107 | suite.addTest(unittest.makeSuite(tc)) 108 | 109 | return suite 110 | 111 | if __name__ == '__main__': 112 | unittest.main(defaultTest='suite') 113 | -------------------------------------------------------------------------------- /tests/test_http.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import threading 4 | 5 | import libevent 6 | 7 | class TestHttpServer(unittest.TestCase): 8 | 9 | def createBase(self): 10 | return libevent.Base() 11 | 12 | def createHttpServer(self, *args): 13 | return libevent.HttpServer(*args) 14 | 15 | def test_bound_socket(self): 16 | socket = libevent.BoundSocket() 17 | 18 | def execute_request(self, server, request, userdata): 19 | print 'Request:', (server, request, userdata) 20 | #request.send_error(404) 21 | request.send_reply(200, 'OK', 'lala\n') 22 | #request.send_reply_start(200, 'OK') 23 | #request.send_reply_chunk('lala') 24 | #request.send_reply_chunk('lala123') 25 | #request.send_reply_end() 26 | 27 | def test_server(self): 28 | base = self.createBase() 29 | server = self.createHttpServer(base) 30 | sock1 = server.bind('localhost', 8080) 31 | self.failUnless(isinstance(sock1, libevent.BoundSocket), sock1) 32 | sock2 = server.accept(sock1) 33 | self.failUnless(isinstance(sock2, libevent.BoundSocket), sock2) 34 | server.set_callback('/', self.execute_request) 35 | base.loopexit(0.1) 36 | base.loop() 37 | 38 | def suite(): 39 | suite = unittest.TestSuite() 40 | 41 | test_cases = [ 42 | TestHttpServer, 43 | ] 44 | 45 | for tc in test_cases: 46 | suite.addTest(unittest.makeSuite(tc)) 47 | 48 | return suite 49 | 50 | if __name__ == '__main__': 51 | unittest.main(defaultTest='suite') 52 | -------------------------------------------------------------------------------- /version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Author: Douglas Creager 3 | # This file is placed into the public domain. 4 | 5 | # Calculates the current version number. If possible, this is the 6 | # output of “git describe”, modified to conform to the versioning 7 | # scheme that setuptools uses. If “git describe” returns an error 8 | # (most likely because we're in an unpacked copy of a release tarball, 9 | # rather than in a git working copy), then we fall back on reading the 10 | # contents of the RELEASE-VERSION file. 11 | # 12 | # To use this script, simply import it your setup.py file, and use the 13 | # results of get_git_version() as your package version: 14 | # 15 | # from version import * 16 | # 17 | # setup( 18 | # version=get_git_version(), 19 | # . 20 | # . 21 | # . 22 | # ) 23 | # 24 | # This will automatically update the RELEASE-VERSION file, if 25 | # necessary. Note that the RELEASE-VERSION file should *not* be 26 | # checked into git; please add it to your top-level .gitignore file. 27 | # 28 | # You'll probably want to distribute the RELEASE-VERSION file in your 29 | # sdist tarballs; to do this, just create a MANIFEST.in file that 30 | # contains the following line: 31 | # 32 | # include RELEASE-VERSION 33 | 34 | __all__ = ("get_git_version") 35 | 36 | from subprocess import Popen, PIPE 37 | 38 | 39 | def call_git_describe(abbrev=4): 40 | try: 41 | p = Popen(['git', 'describe', '--abbrev=%d' % abbrev], 42 | stdout=PIPE, stderr=PIPE) 43 | p.stderr.close() 44 | line = p.stdout.readlines()[0] 45 | version = line.strip() 46 | if version[:1] == 'v': 47 | version = version[1:] 48 | return version 49 | 50 | except: 51 | return None 52 | 53 | 54 | def read_release_version(): 55 | try: 56 | f = open("RELEASE-VERSION", "r") 57 | 58 | try: 59 | version = f.readlines()[0] 60 | return version.strip() 61 | 62 | finally: 63 | f.close() 64 | 65 | except: 66 | return None 67 | 68 | 69 | def write_release_version(version): 70 | f = open("RELEASE-VERSION", "w") 71 | f.write("%s\n" % version) 72 | f.close() 73 | 74 | 75 | def get_git_version(abbrev=4): 76 | # Read in the version that's currently in RELEASE-VERSION. 77 | 78 | release_version = read_release_version() 79 | 80 | # First try to get the current version using “git describe”. 81 | 82 | version = call_git_describe(abbrev) 83 | 84 | # If that doesn't work, fall back on the value that's in 85 | # RELEASE-VERSION. 86 | 87 | if version is None: 88 | version = release_version 89 | 90 | # If we still don't have anything, that's an error. 91 | 92 | if version is None: 93 | return '0.0.0' 94 | 95 | # If the current version is different from what's in the 96 | # RELEASE-VERSION file, update the file to be current. 97 | 98 | if version != release_version: 99 | write_release_version(version) 100 | 101 | # Finally, return the current version. 102 | 103 | return version 104 | 105 | 106 | if __name__ == "__main__": 107 | print get_git_version() 108 | --------------------------------------------------------------------------------