From 7b83304692e754788a8588f739c2106278fabb68 Mon Sep 17 00:00:00 2001 From: Tony Mack Date: Fri, 10 Oct 2008 19:19:56 +0000 Subject: [PATCH] starting new development to support slice conf files --- pycurl/COPYING | 504 +++++++++++++++++++++++++++ pycurl/MANIFEST.in | 22 ++ pycurl/Makefile | 60 ++++ pycurl/README | 12 + pycurl/TODO | 27 ++ pycurl/doc/callbacks.html | 140 ++++++++ pycurl/doc/curlmultiobject.html | 136 ++++++++ pycurl/doc/curlobject.html | 102 ++++++ pycurl/doc/pycurl.html | 120 +++++++ pycurl/python/curl/__init__.py | 146 ++++++++ pycurl/setup.py | 199 +++++++++++ pycurl/tests/test.py | 74 ++++ pycurl/tests/test_cb.py | 28 ++ pycurl/tests/test_debug.py | 16 + pycurl/tests/test_getinfo.py | 49 +++ pycurl/tests/test_gtk.py | 93 +++++ pycurl/tests/test_internals.py | 253 ++++++++++++++ pycurl/tests/test_memleak.py | 53 +++ pycurl/tests/test_multi.py | 33 ++ pycurl/tests/test_multi2.py | 72 ++++ pycurl/tests/test_multi3.py | 87 +++++ pycurl/tests/test_multi4.py | 57 +++ pycurl/tests/test_multi5.py | 60 ++++ pycurl/tests/test_multi6.py | 62 ++++ pycurl/tests/test_multi_vs_thread.py | 262 ++++++++++++++ pycurl/tests/test_post.py | 24 ++ pycurl/tests/test_post2.py | 18 + pycurl/tests/test_post3.py | 32 ++ pycurl/tests/test_stringio.py | 25 ++ pycurl/tests/test_xmlrpc.py | 29 ++ pycurl/tests/util.py | 38 ++ 31 files changed, 2833 insertions(+) create mode 100644 pycurl/COPYING create mode 100644 pycurl/MANIFEST.in create mode 100644 pycurl/Makefile create mode 100644 pycurl/README create mode 100644 pycurl/TODO create mode 100644 pycurl/doc/callbacks.html create mode 100644 pycurl/doc/curlmultiobject.html create mode 100644 pycurl/doc/curlobject.html create mode 100644 pycurl/doc/pycurl.html create mode 100644 pycurl/python/curl/__init__.py create mode 100644 pycurl/setup.py create mode 100644 pycurl/tests/test.py create mode 100644 pycurl/tests/test_cb.py create mode 100644 pycurl/tests/test_debug.py create mode 100644 pycurl/tests/test_getinfo.py create mode 100644 pycurl/tests/test_gtk.py create mode 100644 pycurl/tests/test_internals.py create mode 100644 pycurl/tests/test_memleak.py create mode 100644 pycurl/tests/test_multi.py create mode 100644 pycurl/tests/test_multi2.py create mode 100644 pycurl/tests/test_multi3.py create mode 100644 pycurl/tests/test_multi4.py create mode 100644 pycurl/tests/test_multi5.py create mode 100644 pycurl/tests/test_multi6.py create mode 100644 pycurl/tests/test_multi_vs_thread.py create mode 100644 pycurl/tests/test_post.py create mode 100644 pycurl/tests/test_post2.py create mode 100644 pycurl/tests/test_post3.py create mode 100644 pycurl/tests/test_stringio.py create mode 100644 pycurl/tests/test_xmlrpc.py create mode 100644 pycurl/tests/util.py diff --git a/pycurl/COPYING b/pycurl/COPYING new file mode 100644 index 00000000..99dce334 --- /dev/null +++ b/pycurl/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/pycurl/MANIFEST.in b/pycurl/MANIFEST.in new file mode 100644 index 00000000..f4e3837b --- /dev/null +++ b/pycurl/MANIFEST.in @@ -0,0 +1,22 @@ +# +# MANIFEST.in +# Manifest template for creating the source distribution. +# + +include ChangeLog +include COPYING +include INSTALL +include Makefile +include README +include TODO +include MANIFEST.in +include src/Makefile +include src/pycurl.c +include python/curl/*.py +include examples/*.py +include tests/*.py +include doc/*.html +include setup_win32_ssl.py + +# exclude unfinished test scripts +#exclude tests/test_multi_vs_thread.py diff --git a/pycurl/Makefile b/pycurl/Makefile new file mode 100644 index 00000000..9b2369d1 --- /dev/null +++ b/pycurl/Makefile @@ -0,0 +1,60 @@ +# +# to use a specific python version call +# `make PYTHON=python2.2' +# + +SHELL = /bin/sh + +PYTHON = python2.3 +PYTHON = python + +all build: + $(PYTHON) setup.py build + +build-7.10.8: + $(PYTHON) setup.py build --curl-config=/home/hosts/localhost/packages/curl-7.10.8/bin/curl-config + +test: build + $(PYTHON) tests/test_internals.py -q + +# (needs GNU binutils) +strip: build + strip -p --strip-unneeded build/lib*/*.so + chmod -x build/lib*/*.so + +install install_lib: + $(PYTHON) setup.py $@ + +clean: + -rm -rf build dist + -rm -f *.pyc *.pyo */*.pyc */*.pyo */*/*.pyc */*/*.pyo + -rm -f MANIFEST + cd src && $(MAKE) clean + +distclean: clean + +maintainer-clean: distclean + +dist sdist: distclean + $(PYTHON) setup.py sdist + +# target for maintainer +windist: distclean + rm -rf build + python2.2 setup.py bdist_wininst + rm -rf build + python2.3 setup.py bdist_wininst + rm -rf build + python2.4 setup.py bdist_wininst + rm -rf build + python2.2 setup_win32_ssl.py bdist_wininst + rm -rf build + python2.3 setup_win32_ssl.py bdist_wininst + rm -rf build + python2.4 setup_win32_ssl.py bdist_wininst + rm -rf build + + +.PHONY: all build test strip install install_lib clean distclean maintainer-clean dist sdist windist + +.NOEXPORT: diff --git a/pycurl/README b/pycurl/README new file mode 100644 index 00000000..bd04ab67 --- /dev/null +++ b/pycurl/README @@ -0,0 +1,12 @@ +LICENSE +------- + +Copyright (C) 2001-2005 by Kjetil Jacobsen +Copyright (C) 2001-2005 by Markus F.X.J. Oberhumer + +PycURL is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +A full copy of the LGPL license is included in the file COPYING. diff --git a/pycurl/TODO b/pycurl/TODO new file mode 100644 index 00000000..7541535d --- /dev/null +++ b/pycurl/TODO @@ -0,0 +1,27 @@ +# $Id: TODO 5574 2007-10-25 20:33:17Z thierry $ +# vi:ts=4:et + +If you want to hack on pycurl, here's our list of unresolved issues: + + +NEW FEATURES/IMPROVEMENTS: + + * Add docs to the high-level interface. + + * Add more options to the undocumented and currently mostly useless + Curl.unsetopt() method. Have to carefully check the libcurl source + code for each option we want to support. + + * curl_easy_reset() should probably be supported. But we have to be + careful since curl_easy_reset() e.g. modifies callbacks and other + pointers which could leave pycurl and libcurl out of sync. + + +DEFICIENICES: + + * Using certain invalid options, it may be possible to cause a crash. + This is un-Pythonic behaviour, but you somewhere have to draw a line + between efficiency (and feature completeness) and safety. + There _are_ quite a number of internal error checks, but tracking and + catching all possible (deliberate) misuses is not a goal (and probably + impossible anyway, due to the complexity of libcurl). diff --git a/pycurl/doc/callbacks.html b/pycurl/doc/callbacks.html new file mode 100644 index 00000000..c8086468 --- /dev/null +++ b/pycurl/doc/callbacks.html @@ -0,0 +1,140 @@ + + + + + PyCurl: Callbacks + + + + + + +

Callbacks

+ +

For more fine-grained control, libcurl allows a +number of callbacks to be associated with each connection. In +pycurl, callbacks are defined using the setopt() method for +Curl objects with options WRITEFUNCTION, READFUNCTION, HEADERFUNCTION, +PROGRESSFUNCTION, IOCTLFUNCTION, or DEBUGFUNCTION. These options +correspond to the libcurl options with CURLOPT_* prefix removed. A +callback in pycurl must be either a regular Python function, a class +method or an extension type function.

+ +

There are some limitations to some of the options which can be used +concurrently with the pycurl callbacks compared to the libcurl callbacks. +This is to allow different callback functions to be associated with +different Curl objects. More specifically, WRITEDATA cannot +be used with WRITEFUNCTION, READDATA cannot be used with READFUNCTION, +WRITEHEADER cannot be used with HEADERFUNCTION, PROGRESSDATA cannot be +used with PROGRESSFUNCTION, IOCTLDATA cannot be used with IOCTLFUNCTION, +and DEBUGDATA cannot be used with DEBUGFUNCTION. +In practice, these limitations can be overcome by having a callback +function be a class instance method and rather use the class instance +attributes to store per object data such as files used in the callbacks. +

+ +The signature of each callback used in pycurl is as follows:
+
+WRITEFUNCTION(string) -> number of characters written
+
+
+READFUNCTION(number of characters to read)-> +string
+
+HEADERFUNCTION(string) -> number of characters written
+

+PROGRESSFUNCTION(download total, downloaded, upload total, uploaded) -> status
+
+DEBUGFUNCTION(debug message type, debug message string) +-> None
+
+IOCTLFUNCTION(ioctl cmd) +-> status
+
+
+ +

Example: Callbacks for document header and body

+ +

This example prints the header data to stderr and the body data to +stdout. Also note that neither callback returns the number of bytes +written. For WRITEFUNCTION and HEADERFUNCTION callbacks, returning +None implies that all bytes where written.

+ +
+    ## Callback function invoked when body data is ready
+    def body(buf):
+        # Print body data to stdout
+        import sys
+        sys.stdout.write(buf)
+        # Returning None implies that all bytes were written
+
+    ## Callback function invoked when header data is ready
+    def header(buf):
+        # Print header data to stderr
+        import sys
+        sys.stderr.write(buf)
+        # Returning None implies that all bytes were written
+
+    c = pycurl.Curl()
+    c.setopt(pycurl.URL, "http://www.python.org/")
+    c.setopt(pycurl.WRITEFUNCTION, body)
+    c.setopt(pycurl.HEADERFUNCTION, header)
+    c.perform()
+
+ +

Example: Download/upload progress callback

+ +

This example shows how to use the progress callback. When downloading +a document, the arguments related to uploads are zero, and vice versa.

+ +
+    ## Callback function invoked when download/upload has progress
+    def progress(download_t, download_d, upload_t, upload_d):
+        print "Total to download", download_t
+        print "Total downloaded", download_d
+        print "Total to upload", upload_t
+        print "Total uploaded", upload_d
+
+    c.setopt(c.URL, "http://slashdot.org/")
+    c.setopt(c.NOPROGRESS, 0)
+    c.setopt(c.PROGRESSFUNCTION, progress)
+    c.perform()
+
+ +

Example: Debug callbacks

+ +

This example shows how to use the debug callback. The debug message +type is an integer indicating the type of debug message. The +VERBOSE option must be enabled for this callback to be invoked.

+ +
+    def test(debug_type, debug_msg):
+        print "debug(%d): %s" % (debug_type, debug_msg)
+
+    c = pycurl.Curl()
+    c.setopt(pycurl.URL, "http://curl.haxx.se/")
+    c.setopt(pycurl.VERBOSE, 1)
+    c.setopt(pycurl.DEBUGFUNCTION, test)
+    c.perform()
+
+ +

Other examples

+The pycurl distribution also contains a number of test scripts and +examples which show how to use the various callbacks in libcurl. +For instance, the file 'examples/file_upload.py' in the distribution contains +example code for using READFUNCTION, 'tests/test_cb.py' shows +WRITEFUNCTION and HEADERFUNCTION, 'tests/test_debug.py' shows DEBUGFUNCTION, +and 'tests/test_getinfo.py' shows PROGRESSFUNCTION.

+ + +
+

+ Valid XHTML 1.0! + $Id: callbacks.html 5574 2007-10-25 20:33:17Z thierry $ +

+ + + diff --git a/pycurl/doc/curlmultiobject.html b/pycurl/doc/curlmultiobject.html new file mode 100644 index 00000000..7af3d544 --- /dev/null +++ b/pycurl/doc/curlmultiobject.html @@ -0,0 +1,136 @@ + + + + + PycURL: CurlMulti Objects + + + + + + +

CurlMulti Object

+ +

CurlMulti objects have the following methods:

+ +
+
close() -> None
+
+

Corresponds to +curl_multi_cleanup() in libcurl. +This method is automatically called by pycurl when a CurlMulti object no +longer has any references to it, but can also be called +explicitly.

+
+ +
perform() -> tuple of status and the number of active Curl objects
+
+

Corresponds to +curl_multi_perform() in libcurl.

+
+ +
add_handle(Curl object) -> None
+
+

Corresponds to +curl_multi_add_handle() in libcurl. +This method adds an existing and valid Curl object to the CurlMulti +object.

+ +

IMPORTANT NOTE: add_handle does not implicitly add a Python reference +to the Curl object (and thus does not increase the reference count on the Curl +object).

+
+ +
remove_handle(Curl object) -> None
+
+

Corresponds to +curl_multi_remove_handle() in libcurl. +This method removes an existing and valid Curl object from the CurlMulti +object.

+ +

IMPORTANT NOTE: remove_handle does not implicitly remove a Python reference +from the Curl object (and thus does not decrease the reference count on the Curl +object).

+
+ +
fdset() -> +triple of lists with active file descriptors, +readable, writeable, exceptions.
+
+

Corresponds to +curl_multi_fdset() in libcurl. +This method extracts the file descriptor information from a CurlMulti object. +The returned lists can be used with the select module to +poll for events.

+ +

Example usage:

+ +
+import pycurl
+c = pycurl.Curl()
+c.setopt(pycurl.URL, "http://curl.haxx.se")
+m = pycurl.CurlMulti()
+m.add_handle(c)
+while 1:
+    ret, num_handles = m.perform()
+    if ret != pycurl.E_CALL_MULTI_PERFORM: break
+while num_handles:
+    apply(select.select, m.fdset() + (1,))
+    while 1:
+        ret, num_handles = m.perform()
+        if ret != pycurl.E_CALL_MULTI_PERFORM: break
+
+
+ +
select([timeout]) -> +number of ready file descriptors or -1 on timeout
+
+

This is a convenience function which simplifies the combined +use of fdset() and the select module.

+ +

Example usage:

+ +
import pycurl
+c = pycurl.Curl()
+c.setopt(pycurl.URL, "http://curl.haxx.se")
+m = pycurl.CurlMulti()
+m.add_handle(c)
+while 1:
+    ret, num_handles = m.perform()
+    if ret != pycurl.E_CALL_MULTI_PERFORM: break
+while num_handles:
+    ret = m.select()
+    if ret == -1:  continue
+    while 1:
+        ret, num_handles = m.perform()
+        if ret != pycurl.E_CALL_MULTI_PERFORM: break
+
+
+ +
info_read([max]) -> +numberof queued messages, a list of successful objects, a list of +failed objects
+
+

Corresponds to the +curl_multi_info_read() function in libcurl. +This method extracts at most max messages +from the multi stack and returns them in two lists. The first +list contains the handles which completed successfully and the second +list contains a tuple <curl object, curl error number, curl +error message> for each failed curl object. The number +of queued messages after this method has been called is also +returned.

+
+
+ +
+

+ Valid XHTML 1.0! + $Id: curlmultiobject.html 5574 2007-10-25 20:33:17Z thierry $ +

+ + + diff --git a/pycurl/doc/curlobject.html b/pycurl/doc/curlobject.html new file mode 100644 index 00000000..b6ad7740 --- /dev/null +++ b/pycurl/doc/curlobject.html @@ -0,0 +1,102 @@ + + + + + PycURL: Curl Objects + + + + + + +

Curl Object

+ +

Curl objects have the following methods:

+ +
+
close() -> None
+
+

Corresponds to +curl_easy_cleanup in libcurl. +This method is automatically called by pycurl when a Curl object no longer has +any references to it, but can also be called explicitly.

+
+ +
perform() -> None
+
+

Corresponds to +curl_easy_perform in libcurl.

+
+ +
setopt(option, value) -> None
+
+ +

Corresponds to +curl_easy_setopt in libcurl, where +option is specified with the CURLOPT_* constants in libcurl, +except that the CURLOPT_ prefix has been removed. The type for +value depends on the option, and can be either a string, +integer, long integer, file objects, lists, or functions.

+ +

Example usage:

+ +
+import pycurl
+c = pycurl.Curl()
+c.setopt(pycurl.URL, "http://www.python.org/")
+c.setopt(pycurl.HTTPHEADER, ["Accept:"])
+import StringIO
+b = StringIO.StringIO()
+c.setopt(pycurl.WRITEFUNCTION, b.write)
+c.setopt(pycurl.FOLLOWLOCATION, 1)
+c.setopt(pycurl.MAXREDIRS, 5)
+c.perform()
+print b.getvalue()
+...
+
+
+ +
getinfo(option) -> Result
+
+ +

Corresponds to +curl_easy_getinfo in libcurl, where +option is the same as the CURLINFO_* constants in libcurl, +except that the CURLINFO_ prefix has been removed. +Result contains an integer, float or string, depending on +which option is given. The getinfo method should +not be called unless perform has been called and +finished.

+ +

Example usage:

+ +
+import pycurl
+c = pycurl.Curl()
+c.setopt(pycurl.URL, "http://sf.net")
+c.setopt(pycurl.FOLLOWLOCATION, 1)
+c.perform()
+print c.getinfo(pycurl.HTTP_CODE), c.getinfo(pycurl.EFFECTIVE_URL)
+...
+--> 200 "http://sourceforge.net/"
+
+
+ +
errstr() -> String
+
+

Returns the internal libcurl error buffer of this handle as a string.

+
+
+ + +
+

+ Valid XHTML 1.0! + $Id: curlobject.html 5574 2007-10-25 20:33:17Z thierry $ +

+ + + diff --git a/pycurl/doc/pycurl.html b/pycurl/doc/pycurl.html new file mode 100644 index 00000000..7d796d0e --- /dev/null +++ b/pycurl/doc/pycurl.html @@ -0,0 +1,120 @@ + + + + + PycURL Documentation + + + + + + +

pycurl — A Python interface to the cURL library

+ +

The pycurl package is a Python interface to libcurl (http://curl.haxx.se/libcurl/). pycurl +has been successfully built and tested with Python versions from +2.2 to the current 2.4.x releases.

+ +

libcurl is a client-side URL transfer library supporting FTP, FTPS, +HTTP, HTTPS, GOPHER, TELNET, DICT, FILE and LDAP. libcurl +also supports HTTPS certificates, HTTP POST, HTTP PUT, FTP uploads, proxies, +cookies, basic authentication, file transfer resume of FTP sessions, HTTP +proxy tunneling and more.

+ +

All the functionality provided by libcurl can used through the +pycurl interface. The following subsections describe how to use the +pycurl interface, and assume familiarity with how libcurl works. For +information on how libcurl works, please consult the curl library web pages +(http://curl.haxx.se/libcurl/c/).

+ +
+ +

Module Functionality

+ +
+
pycurl.global_init(option) ->None
+ +

option is one of the constants +pycurl.GLOBAL_SSL, pycurl.GLOBAL_WIN32, pycurl.GLOBAL_ALL, +pycurl.GLOBAL_NOTHING, pycurl.GLOBAL_DEFAULT. Corresponds to +curl_global_init() in libcurl.

+
+ +
pycurl.global_cleanup() -> None
+
+

Corresponds to +curl_global_cleanup() in libcurl.

+
+ +
pycurl.version
+ +

This is a string with version information on libcurl, +corresponding to +curl_version() in libcurl.

+ +

Example usage:

+
+>>> import pycurl
+>>> pycurl.version
+'libcurl/7.12.3 OpenSSL/0.9.7e zlib/1.2.2.1 libidn/0.5.12'
+
+
+ +
pycurl.version_info() -> Tuple
+
+

Corresponds to +curl_version_info() in libcurl. +Returns a tuple of information which is similar to the +curl_version_info_data struct returned by +curl_version_info() in libcurl.

+ +

Example usage:

+
+>>> import pycurl
+>>> pycurl.version_info()
+(2, '7.12.3', 461827, 'i586-pc-linux-gnu', 1565, 'OpenSSL/0.9.7e', 9465951,
+'1.2.2.1', ('ftp', 'gopher', 'telnet', 'dict', 'ldap', 'http', 'file',
+'https', 'ftps'), None, 0, '0.5.12')
+
+
+ +
pycurl.Curl() -> Curl object
+
+

This function creates a new +Curl object which corresponds to a +CURL handle in libcurl. Curl objects automatically +set CURLOPT_VERBOSE to 0, CURLOPT_NOPROGRESS to 1, +provide a default CURLOPT_USERAGENT and setup +CURLOPT_ERRORBUFFER to point to a private error buffer.

+
+ +
pycurl.CurlMulti() -> CurlMulti object
+
+

This function creates a new +CurlMulti object which corresponds to +a CURLM handle in libcurl.

+
+
+ +
+ +

Subsections

+ + + +
+

+ Valid XHTML 1.0! + $Id: pycurl.html 5574 2007-10-25 20:33:17Z thierry $ +

+ + + diff --git a/pycurl/python/curl/__init__.py b/pycurl/python/curl/__init__.py new file mode 100644 index 00000000..8fecb4d8 --- /dev/null +++ b/pycurl/python/curl/__init__.py @@ -0,0 +1,146 @@ +# A high-level interface to the pycurl extension +# +# ** mfx NOTE: the CGI class uses "black magic" using COOKIEFILE in +# combination with a non-existant file name. See the libcurl docs +# for more info. +# +# If you want thread-safe operation, you'll have to set the NOSIGNAL option +# yourself. +# +# By Eric S. Raymond, April 2003. + +import os, sys, urllib, exceptions, mimetools, pycurl +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + + +class Curl: + "High-level interface to cURL functions." + def __init__(self, base_url="", fakeheaders=[]): + self.handle = pycurl.Curl() + # These members might be set. + self.set_url(base_url) + self.verbosity = 0 + self.fakeheaders = fakeheaders + # Nothing past here should be modified by the caller. + self.payload = "" + self.header = StringIO() + # Verify that we've got the right site; harmless on a non-SSL connect. + self.set_option(pycurl.SSL_VERIFYHOST, 2) + # Follow redirects in case it wants to take us to a CGI... + self.set_option(pycurl.FOLLOWLOCATION, 1) + self.set_option(pycurl.MAXREDIRS, 5) + # Setting this option with even a nonexistent file makes libcurl + # handle cookie capture and playback automatically. + self.set_option(pycurl.COOKIEFILE, "/dev/null") + # Set timeouts to avoid hanging too long + self.set_timeout(30) + # Use password identification from .netrc automatically + self.set_option(pycurl.NETRC, 1) + # Set up a callback to capture the payload + def payload_callback(x): + self.payload += x + self.set_option(pycurl.WRITEFUNCTION, payload_callback) + def header_callback(x): + self.header.write(x) + self.set_option(pycurl.HEADERFUNCTION, header_callback) + + def set_timeout(self, timeout): + "Set timeout for connect and object retrieval (applies for both)" + self.set_option(pycurl.CONNECTTIMEOUT, timeout) + self.set_option(pycurl.TIMEOUT, timeout) + + def set_url(self, url): + "Set the base URL to be retrieved." + self.base_url = url + self.set_option(pycurl.URL, self.base_url) + + def set_option(self, *args): + "Set an option on the retrieval," + apply(self.handle.setopt, args) + + def set_verbosity(self, level): + "Set verbosity to 1 to see transactions." + self.set_option(pycurl.VERBOSE, level) + + def __request(self, relative_url=None): + "Perform the pending request." + if self.fakeheaders: + self.set_option(pycurl.HTTPHEADER, self.fakeheaders) + if relative_url: + self.set_option(pycurl.URL,os.path.join(self.base_url,relative_url)) + self.header.seek(0,0) + self.payload = "" + self.handle.perform() + return self.payload + + def get(self, url="", params=None): + "Ship a GET request for a specified URL, capture the response." + if params: + url += "?" + urllib.urlencode(params) + self.set_option(pycurl.HTTPGET, 1) + return self.__request(url) + + def post(self, cgi, params): + "Ship a POST request to a specified CGI, capture the response." + self.set_option(pycurl.POST, 1) + self.set_option(pycurl.POSTFIELDS, urllib.urlencode(params)) + return self.__request(cgi) + + def body(self): + "Return the body from the last response." + return self.payload + + def info(self): + "Return an RFC822 object with info on the page." + self.header.seek(0,0) + url = self.handle.getinfo(pycurl.EFFECTIVE_URL) + if url[:5] == 'http:': + self.header.readline() + m = mimetools.Message(self.header) + else: + m = mimetools.Message(StringIO()) + m['effective-url'] = url + m['http-code'] = str(self.handle.getinfo(pycurl.HTTP_CODE)) + m['total-time'] = str(self.handle.getinfo(pycurl.TOTAL_TIME)) + m['namelookup-time'] = str(self.handle.getinfo(pycurl.NAMELOOKUP_TIME)) + m['connect-time'] = str(self.handle.getinfo(pycurl.CONNECT_TIME)) + m['pretransfer-time'] = str(self.handle.getinfo(pycurl.PRETRANSFER_TIME)) + m['redirect-time'] = str(self.handle.getinfo(pycurl.REDIRECT_TIME)) + m['redirect-count'] = str(self.handle.getinfo(pycurl.REDIRECT_COUNT)) + m['size-upload'] = str(self.handle.getinfo(pycurl.SIZE_UPLOAD)) + m['size-download'] = str(self.handle.getinfo(pycurl.SIZE_DOWNLOAD)) + m['speed-upload'] = str(self.handle.getinfo(pycurl.SPEED_UPLOAD)) + m['header-size'] = str(self.handle.getinfo(pycurl.HEADER_SIZE)) + m['request-size'] = str(self.handle.getinfo(pycurl.REQUEST_SIZE)) + m['content-length-download'] = str(self.handle.getinfo(pycurl.CONTENT_LENGTH_DOWNLOAD)) + m['content-length-upload'] = str(self.handle.getinfo(pycurl.CONTENT_LENGTH_UPLOAD)) + m['content-type'] = (self.handle.getinfo(pycurl.CONTENT_TYPE) or '').strip(';') + return m + + def answered(self, check): + "Did a given check string occur in the last payload?" + return self.payload.find(check) >= 0 + + def close(self): + "Close a session, freeing resources." + self.handle.close() + self.header.close() + + def __del__(self): + self.close() + + +if __name__ == "__main__": + if len(sys.argv) < 2: + url = 'http://curl.haxx.se' + else: + url = sys.argv[1] + c = Curl() + c.get(url) + print c.body() + print '='*74 + '\n' + print c.info() + c.close() diff --git a/pycurl/setup.py b/pycurl/setup.py new file mode 100644 index 00000000..8c7b68b8 --- /dev/null +++ b/pycurl/setup.py @@ -0,0 +1,199 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: setup.py 5574 2007-10-25 20:33:17Z thierry $ + +"""Setup script for the PycURL module distribution.""" + +PACKAGE = "pycurl" +PY_PACKAGE = "curl" +VERSION = "7.13.1" + +import glob, os, re, sys, string +import distutils +from distutils.core import setup +from distutils.extension import Extension +from distutils.util import split_quoted +from distutils.version import LooseVersion + +include_dirs = [] +define_macros = [] +library_dirs = [] +libraries = [] +runtime_library_dirs = [] +extra_objects = [] +extra_compile_args = [] +extra_link_args = [] + + +def scan_argv(s, default): + p = default + i = 1 + while i < len(sys.argv): + arg = sys.argv[i] + if string.find(arg, s) == 0: + p = arg[len(s):] + assert p, arg + del sys.argv[i] + else: + i = i + 1 + ##print sys.argv + return p + + +# append contents of an environment variable to library_dirs[] +def add_libdirs(envvar, sep, fatal=0): + v = os.environ.get(envvar) + if not v: + return + for dir in string.split(v, sep): + dir = string.strip(dir) + if not dir: + continue + dir = os.path.normpath(dir) + if os.path.isdir(dir): + if not dir in library_dirs: + library_dirs.append(dir) + elif fatal: + print "FATAL: bad directory %s in environment variable %s" % (dir, envvar) + sys.exit(1) + + +if sys.platform == "win32": + # Windows users have to configure the CURL_DIR path parameter to match + # their cURL source installation. The path set here is just an example + # and thus unlikely to match your installation. + CURL_DIR = r"c:\src\build\pycurl\curl-7.13.1" + CURL_DIR = scan_argv("--curl-dir=", CURL_DIR) + print "Using curl directory:", CURL_DIR + assert os.path.isdir(CURL_DIR), "please check CURL_DIR in setup.py" + include_dirs.append(os.path.join(CURL_DIR, "include")) + extra_objects.append(os.path.join(CURL_DIR, "lib", "libcurl.lib")) + extra_link_args.extend(["gdi32.lib", "winmm.lib", "ws2_32.lib",]) + add_libdirs("LIB", ";") + if string.find(sys.version, "MSC") >= 0: + extra_compile_args.append("-O2") + extra_compile_args.append("-GF") # enable read-only string pooling + extra_compile_args.append("-WX") # treat warnings as errors + extra_link_args.append("/opt:nowin98") # use small section alignment +else: + # Find out the rest the hard way + CURL_CONFIG = "curl-config" + CURL_CONFIG = scan_argv("--curl-config=", CURL_CONFIG) + d = os.popen("'%s' --version" % CURL_CONFIG).read() + if d: + d = string.strip(d) + if not d: + raise Exception, ("`%s' not found -- please install the libcurl development files" % CURL_CONFIG) + print "Using %s (%s)" % (CURL_CONFIG, d) + for e in split_quoted(os.popen("'%s' --cflags" % CURL_CONFIG).read()): + if e[:2] == "-I": + # do not add /usr/include + if not re.search(r"^\/+usr\/+include\/*$", e[2:]): + include_dirs.append(e[2:]) + else: + extra_compile_args.append(e) + for e in split_quoted(os.popen("'%s' --libs" % CURL_CONFIG).read()): + if e[:2] == "-l": + libraries.append(e[2:]) + elif e[:2] == "-L": + library_dirs.append(e[2:]) + else: + extra_link_args.append(e) + if not libraries: + libraries.append("curl") + # Add extra compile flag for MacOS X + if sys.platform[:-1] == "darwin": + extra_link_args.append("-flat_namespace") + + +############################################################################### + +def get_kw(**kw): return kw + +ext = Extension( + name=PACKAGE, + sources=[ + os.path.join("src", "pycurl.c"), + ], + include_dirs=include_dirs, + define_macros=define_macros, + library_dirs=library_dirs, + libraries=libraries, + runtime_library_dirs=runtime_library_dirs, + extra_objects=extra_objects, + extra_compile_args=extra_compile_args, + extra_link_args=extra_link_args, +) +##print ext.__dict__; sys.exit(1) + + +############################################################################### + +# prepare data_files + +def get_data_files(): + # a list of tuples with (path to install to, a list of local files) + data_files = [] + if sys.platform == "win32": + datadir = os.path.join("doc", PACKAGE) + else: + datadir = os.path.join("share", "doc", PACKAGE) + # + files = ["ChangeLog", "COPYING", "INSTALL", "README", "TODO",] + if files: + data_files.append((os.path.join(datadir), files)) + files = glob.glob(os.path.join("doc", "*.html")) + if files: + data_files.append((os.path.join(datadir, "html"), files)) + files = glob.glob(os.path.join("examples", "*.py")) + if files: + data_files.append((os.path.join(datadir, "examples"), files)) + files = glob.glob(os.path.join("tests", "*.py")) + if files: + data_files.append((os.path.join(datadir, "tests"), files)) + # + assert data_files + for install_dir, files in data_files: + assert files + for f in files: + assert os.path.isfile(f), (f, install_dir) + return data_files + +##print get_data_files(); sys.exit(1) + + +############################################################################### + +setup_args = get_kw( + name=PACKAGE, + version=VERSION, + description="PycURL -- cURL library module for Python", + author="Kjetil Jacobsen, Markus F.X.J. Oberhumer", + author_email="kjetilja@cs.uit.no, markus@oberhumer.com", + maintainer="Kjetil Jacobsen, Markus F.X.J. Oberhumer", + maintainer_email="kjetilja@cs.uit.no, markus@oberhumer.com", + url="http://pycurl.sourceforge.net/", + license="GNU Lesser General Public License (LGPL)", + data_files=get_data_files(), + ext_modules=[ext], + long_description=""" +This module provides Python bindings for the cURL library.""", +) + +if sys.version >= "2.2": + setup_args["packages"] = [PY_PACKAGE] + setup_args["package_dir"] = { PY_PACKAGE: os.path.join('python', 'curl') } + + +##print distutils.__version__ +if LooseVersion(distutils.__version__) > LooseVersion("1.0.1"): + setup_args["platforms"] = "All" +if LooseVersion(distutils.__version__) < LooseVersion("1.0.3"): + setup_args["licence"] = setup_args["license"] + +if __name__ == "__main__": + for o in ext.extra_objects: + assert os.path.isfile(o), o + # We can live with the deprecationwarning for a while + apply(setup, (), setup_args) diff --git a/pycurl/tests/test.py b/pycurl/tests/test.py new file mode 100644 index 00000000..de787a66 --- /dev/null +++ b/pycurl/tests/test.py @@ -0,0 +1,74 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test.py 5574 2007-10-25 20:33:17Z thierry $ + +import sys, threading, time +import pycurl + +# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see +# the libcurl tutorial for more info. +try: + import signal + from signal import SIGPIPE, SIG_IGN + signal.signal(signal.SIGPIPE, signal.SIG_IGN) +except ImportError: + pass + + +class Test(threading.Thread): + def __init__(self, url, ofile): + threading.Thread.__init__(self) + self.curl = pycurl.Curl() + self.curl.setopt(pycurl.URL, url) + self.curl.setopt(pycurl.WRITEDATA, ofile) + self.curl.setopt(pycurl.FOLLOWLOCATION, 1) + self.curl.setopt(pycurl.MAXREDIRS, 5) + self.curl.setopt(pycurl.NOSIGNAL, 1) + + def run(self): + self.curl.perform() + self.curl.close() + sys.stdout.write(".") + sys.stdout.flush() + + +# Read list of URIs from file specified on commandline +try: + urls = open(sys.argv[1]).readlines() +except IndexError: + # No file was specified, show usage string + print "Usage: %s " % sys.argv[0] + raise SystemExit + +# Initialize thread array and the file number +threads = [] +fileno = 0 + +# Start one thread per URI in parallel +t1 = time.time() +for url in urls: + f = open(str(fileno), "wb") + t = Test(url, f) + t.start() + threads.append((t, f)) + fileno = fileno + 1 +# Wait for all threads to finish +for thread, file in threads: + thread.join() + file.close() +t2 = time.time() +print "\n** Multithreading, %d seconds elapsed for %d uris" % (int(t2-t1), len(urls)) + +# Start one thread per URI in sequence +fileno = 0 +t1 = time.time() +for url in urls: + f = open(str(fileno), "wb") + t = Test(url, f) + t.start() + fileno = fileno + 1 + t.join() + f.close() +t2 = time.time() +print "\n** Singlethreading, %d seconds elapsed for %d uris" % (int(t2-t1), len(urls)) diff --git a/pycurl/tests/test_cb.py b/pycurl/tests/test_cb.py new file mode 100644 index 00000000..936293df --- /dev/null +++ b/pycurl/tests/test_cb.py @@ -0,0 +1,28 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_cb.py 5574 2007-10-25 20:33:17Z thierry $ + +import sys +import pycurl + +## Callback function invoked when body data is ready +def body(buf): + # Print body data to stdout + sys.stdout.write(buf) + +## Callback function invoked when header data is ready +def header(buf): + # Print header data to stderr + sys.stderr.write(buf) + +c = pycurl.Curl() +c.setopt(pycurl.URL, 'http://www.python.org/') +c.setopt(pycurl.WRITEFUNCTION, body) +c.setopt(pycurl.HEADERFUNCTION, header) +c.setopt(pycurl.FOLLOWLOCATION, 1) +c.setopt(pycurl.MAXREDIRS, 5) +c.perform() +c.setopt(pycurl.URL, 'http://curl.haxx.se/') +c.perform() +c.close() diff --git a/pycurl/tests/test_debug.py b/pycurl/tests/test_debug.py new file mode 100644 index 00000000..07bec020 --- /dev/null +++ b/pycurl/tests/test_debug.py @@ -0,0 +1,16 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_debug.py 5574 2007-10-25 20:33:17Z thierry $ + +import pycurl + +def test(t, b): + print "debug(%d): %s" % (t, b) + +c = pycurl.Curl() +c.setopt(pycurl.URL, 'http://curl.haxx.se/') +c.setopt(pycurl.VERBOSE, 1) +c.setopt(pycurl.DEBUGFUNCTION, test) +c.perform() +c.close() diff --git a/pycurl/tests/test_getinfo.py b/pycurl/tests/test_getinfo.py new file mode 100644 index 00000000..927048b2 --- /dev/null +++ b/pycurl/tests/test_getinfo.py @@ -0,0 +1,49 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_getinfo.py 5574 2007-10-25 20:33:17Z thierry $ + +import time +import pycurl + + +## Callback function invoked when progress information is updated +def progress(download_t, download_d, upload_t, upload_d): + print "Total to download %d bytes, have %d bytes so far" % \ + (download_t, download_d) + +url = "http://www.cnn.com" + +print "Starting downloading", url +print +f = open("body", "wb") +h = open("header", "wb") +c = pycurl.Curl() +c.setopt(c.URL, url) +c.setopt(c.WRITEDATA, f) +c.setopt(c.NOPROGRESS, 0) +c.setopt(c.PROGRESSFUNCTION, progress) +c.setopt(c.FOLLOWLOCATION, 1) +c.setopt(c.MAXREDIRS, 5) +c.setopt(c.WRITEHEADER, h) +c.setopt(c.OPT_FILETIME, 1) +c.perform() + +print +print "HTTP-code:", c.getinfo(c.HTTP_CODE) +print "Total-time:", c.getinfo(c.TOTAL_TIME) +print "Download speed: %.2f bytes/second" % c.getinfo(c.SPEED_DOWNLOAD) +print "Document size: %d bytes" % c.getinfo(c.SIZE_DOWNLOAD) +print "Effective URL:", c.getinfo(c.EFFECTIVE_URL) +print "Content-type:", c.getinfo(c.CONTENT_TYPE) +print "Namelookup-time:", c.getinfo(c.NAMELOOKUP_TIME) +print "Redirect-time:", c.getinfo(c.REDIRECT_TIME) +print "Redirect-count:", c.getinfo(c.REDIRECT_COUNT) +epoch = c.getinfo(c.INFO_FILETIME) +print "Filetime: %d (%s)" % (epoch, time.ctime(epoch)) +print +print "Header is in file 'header', body is in file 'body'" + +c.close() +f.close() +h.close() diff --git a/pycurl/tests/test_gtk.py b/pycurl/tests/test_gtk.py new file mode 100644 index 00000000..1b306f68 --- /dev/null +++ b/pycurl/tests/test_gtk.py @@ -0,0 +1,93 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_gtk.py 5574 2007-10-25 20:33:17Z thierry $ + +import sys, threading +from gtk import * +import pycurl + +# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see +# the libcurl tutorial for more info. +try: + import signal + from signal import SIGPIPE, SIG_IGN + signal.signal(signal.SIGPIPE, signal.SIG_IGN) +except ImportError: + pass + + +class ProgressBar: + def __init__(self, uri): + self.round = 0.0 + win = GtkDialog() + win.set_title("PycURL progress") + win.show() + vbox = GtkVBox(spacing=5) + vbox.set_border_width(10) + win.vbox.pack_start(vbox) + win.set_default_size(200, 20) + vbox.show() + label = GtkLabel("Downloading %s" % uri) + label.set_alignment(0, 0.5) + vbox.pack_start(label, expand=FALSE) + label.show() + pbar = GtkProgressBar() + pbar.show() + self.pbar = pbar + vbox.pack_start(pbar) + win.connect("destroy", self.close_app) + win.connect("delete_event", self.close_app) + + def progress(self, download_t, download_d, upload_t, upload_d): + threads_enter() + if download_t == 0: + self.round = self.round + 0.1 + if self.round >= 1.0: self.round = 0.0 + else: + self.round = float(download_d) / float(download_t) + self.pbar.update(self.round) + threads_leave() + + def mainloop(self): + threads_enter() + mainloop() + threads_leave() + + def close_app(self, *args): + args[0].destroy() + mainquit() + + +class Test(threading.Thread): + def __init__(self, url, target_file, progress): + threading.Thread.__init__(self) + self.target_file = target_file + self.progress = progress + self.curl = pycurl.Curl() + self.curl.setopt(pycurl.URL, url) + self.curl.setopt(pycurl.WRITEDATA, self.target_file) + self.curl.setopt(pycurl.FOLLOWLOCATION, 1) + self.curl.setopt(pycurl.NOPROGRESS, 0) + self.curl.setopt(pycurl.PROGRESSFUNCTION, self.progress) + self.curl.setopt(pycurl.MAXREDIRS, 5) + self.curl.setopt(pycurl.NOSIGNAL, 1) + + def run(self): + self.curl.perform() + self.curl.close() + self.target_file.close() + self.progress(1.0, 1.0, 0, 0) + + +# Check command line args +if len(sys.argv) < 3: + print "Usage: %s " % sys.argv[0] + raise SystemExit + +# Make a progress bar window +p = ProgressBar(sys.argv[1]) +# Start thread for fetching url +Test(sys.argv[1], open(sys.argv[2], 'wb'), p.progress).start() +# Enter the GTK mainloop +p.mainloop() diff --git a/pycurl/tests/test_internals.py b/pycurl/tests/test_internals.py new file mode 100644 index 00000000..6a6c02fa --- /dev/null +++ b/pycurl/tests/test_internals.py @@ -0,0 +1,253 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_internals.py 5574 2007-10-25 20:33:17Z thierry $ + +# +# a simple self-test +# + +try: + # need Python 2.2 or better for garbage collection + from gc import get_objects + import gc + del get_objects + gc.enable() +except ImportError: + gc = None +import copy, os, sys +from StringIO import StringIO +try: + import cPickle +except ImportError: + cPickle = None +try: + import pickle +except ImportError: + pickle = None + +# update sys.path when running in the build directory +from util import get_sys_path +sys.path = get_sys_path() + +import pycurl +from pycurl import Curl, CurlMulti + + +class opts: + verbose = 1 + +if "-q" in sys.argv: + opts.verbose = opts.verbose - 1 + + +print "Python", sys.version +print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM) +print "PycURL version info", pycurl.version_info() +print " %s, compiled %s" % (pycurl.__file__, pycurl.COMPILE_DATE) + + +# /*********************************************************************** +# // test misc +# ************************************************************************/ + +if 1: + c = Curl() + assert c.URL is pycurl.URL + del c + + +# /*********************************************************************** +# // test handles +# ************************************************************************/ + +# remove an invalid handle: this should fail +if 1: + m = CurlMulti() + c = Curl() + try: + m.remove_handle(c) + except pycurl.error: + pass + else: + assert 0, "internal error" + del m, c + + +# remove an invalid but closed handle +if 1: + m = CurlMulti() + c = Curl() + c.close() + m.remove_handle(c) + del m, c + + +# add a closed handle: this should fail +if 1: + m = CurlMulti() + c = Curl() + c.close() + try: + m.add_handle(c) + except pycurl.error: + pass + else: + assert 0, "internal error" + m.close() + del m, c + + +# add a handle twice: this should fail +if 1: + m = CurlMulti() + c = Curl() + m.add_handle(c) + try: + m.add_handle(c) + except pycurl.error: + pass + else: + assert 0, "internal error" + del m, c + + +# add a handle on multiple stacks: this should fail +if 1: + m1 = CurlMulti() + m2 = CurlMulti() + c = Curl() + m1.add_handle(c) + try: + m2.add_handle(c) + except pycurl.error: + pass + else: + assert 0, "internal error" + del m1, m2, c + + +# move a handle +if 1: + m1 = CurlMulti() + m2 = CurlMulti() + c = Curl() + m1.add_handle(c) + m1.remove_handle(c) + m2.add_handle(c) + del m1, m2, c + + +# /*********************************************************************** +# // test copying and pickling - copying and pickling of +# // instances of Curl and CurlMulti is not allowed +# ************************************************************************/ + +if 1 and copy: + c = Curl() + m = CurlMulti() + try: + copy.copy(c) + except copy.Error: + pass + else: + assert 0, "internal error - copying should fail" + try: + copy.copy(m) + except copy.Error: + pass + else: + assert 0, "internal error - copying should fail" + +if 1 and pickle: + c = Curl() + m = CurlMulti() + fp = StringIO() + p = pickle.Pickler(fp, 1) + try: + p.dump(c) + except pickle.PicklingError: + pass + else: + assert 0, "internal error - pickling should fail" + try: + p.dump(m) + except pickle.PicklingError: + pass + else: + assert 0, "internal error - pickling should fail" + del c, m, fp, p + +if 1 and cPickle: + c = Curl() + m = CurlMulti() + fp = StringIO() + p = cPickle.Pickler(fp, 1) + try: + p.dump(c) + except cPickle.PicklingError: + pass + else: + assert 0, "internal error - pickling should fail" + try: + p.dump(m) + except cPickle.PicklingError: + pass + else: + assert 0, "internal error - pickling should fail" + del c, m, fp, p + + +# /*********************************************************************** +# // test refcounts +# ************************************************************************/ + +# basic check of reference counting (use a memory checker like valgrind) +if 1: + c = Curl() + m = CurlMulti() + m.add_handle(c) + del m + m = CurlMulti() + c.close() + del m, c + +# basic check of cyclic garbage collection +if 1 and gc: + gc.collect() + c = Curl() + c.m = CurlMulti() + c.m.add_handle(c) + # create some nasty cyclic references + c.c = c + c.c.c1 = c + c.c.c2 = c + c.c.c3 = c.c + c.c.c4 = c.m + c.m.c = c + c.m.m = c.m + c.m.c = c + # delete + gc.collect() + flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS + if opts.verbose >= 1: + flags = flags | gc.DEBUG_STATS + gc.set_debug(flags) + gc.collect() + ##print gc.get_referrers(c) + ##print gc.get_objects() + if opts.verbose >= 1: + print "Tracked objects:", len(gc.get_objects()) + # The `del' below should delete these 4 objects: + # Curl + internal dict, CurlMulti + internal dict + del c + gc.collect() + if opts.verbose >= 1: + print "Tracked objects:", len(gc.get_objects()) + + +# /*********************************************************************** +# // done +# ************************************************************************/ + +print "All tests passed." diff --git a/pycurl/tests/test_memleak.py b/pycurl/tests/test_memleak.py new file mode 100644 index 00000000..a43ef060 --- /dev/null +++ b/pycurl/tests/test_memleak.py @@ -0,0 +1,53 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_memleak.py 5574 2007-10-25 20:33:17Z thierry $ + +# +# just a simple self-test +# need Python 2.2 or better for garbage collection +# + +import gc, pycurl, sys +gc.enable() + + +print "Python", sys.version +print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM) +##print "PycURL version info", pycurl.version_info() +print " %s, compiled %s" % (pycurl.__file__, pycurl.COMPILE_DATE) + + +gc.collect() +flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS +if 1: + flags = flags | gc.DEBUG_STATS +gc.set_debug(flags) +gc.collect() + +print "Tracked objects:", len(gc.get_objects()) + +multi = pycurl.CurlMulti() +t = [] +for a in range(100): + curl = pycurl.Curl() + multi.add_handle(curl) + t.append(curl) + +print "Tracked objects:", len(gc.get_objects()) + +for curl in t: + curl.close() + multi.remove_handle(curl) + +print "Tracked objects:", len(gc.get_objects()) + +del curl +del t +del multi + +print "Tracked objects:", len(gc.get_objects()) +gc.collect() +print "Tracked objects:", len(gc.get_objects()) + + diff --git a/pycurl/tests/test_multi.py b/pycurl/tests/test_multi.py new file mode 100644 index 00000000..0befd4e8 --- /dev/null +++ b/pycurl/tests/test_multi.py @@ -0,0 +1,33 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_multi.py 5574 2007-10-25 20:33:17Z thierry $ + +import pycurl + +m = pycurl.CurlMulti() +m.handles = [] +c1 = pycurl.Curl() +c2 = pycurl.Curl() +c1.setopt(c1.URL, 'http://curl.haxx.se') +c2.setopt(c2.URL, 'http://cnn.com') +c2.setopt(c2.FOLLOWLOCATION, 1) +m.add_handle(c1) +m.add_handle(c2) +m.handles.append(c1) +m.handles.append(c2) + +num_handles = len(m.handles) +while num_handles: + while 1: + ret, num_handles = m.perform() + if ret != pycurl.E_CALL_MULTI_PERFORM: + break + m.select() + +m.remove_handle(c2) +m.remove_handle(c1) +del m.handles +m.close() +c1.close() +c2.close() diff --git a/pycurl/tests/test_multi2.py b/pycurl/tests/test_multi2.py new file mode 100644 index 00000000..6da93c25 --- /dev/null +++ b/pycurl/tests/test_multi2.py @@ -0,0 +1,72 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_multi2.py 5574 2007-10-25 20:33:17Z thierry $ + +import os, sys +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO +import pycurl + + +urls = ( + "http://curl.haxx.se", + "http://www.python.org", + "http://pycurl.sourceforge.net", + "http://pycurl.sourceforge.net/tests/403_FORBIDDEN", # that actually exists ;-) + "http://pycurl.sourceforge.net/tests/404_NOT_FOUND", +) + +# Read list of URIs from file specified on commandline +try: + urls = open(sys.argv[1], "rb").readlines() +except IndexError: + # No file was specified + pass + +# init +m = pycurl.CurlMulti() +m.handles = [] +for url in urls: + c = pycurl.Curl() + # save info in standard Python attributes + c.url = url + c.body = StringIO() + c.http_code = -1 + m.handles.append(c) + # pycurl API calls + c.setopt(c.URL, c.url) + c.setopt(c.WRITEFUNCTION, c.body.write) + m.add_handle(c) + +# get data +num_handles = len(m.handles) +while num_handles: + while 1: + ret, num_handles = m.perform() + if ret != pycurl.E_CALL_MULTI_PERFORM: + break + # currently no more I/O is pending, could do something in the meantime + # (display a progress bar, etc.) + m.select() + +# close handles +for c in m.handles: + # save info in standard Python attributes + c.http_code = c.getinfo(c.HTTP_CODE) + # pycurl API calls + m.remove_handle(c) + c.close() +m.close() + +# print result +for c in m.handles: + data = c.body.getvalue() + if 0: + print "**********", c.url, "**********" + print data + else: + print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data)) + diff --git a/pycurl/tests/test_multi3.py b/pycurl/tests/test_multi3.py new file mode 100644 index 00000000..8889246b --- /dev/null +++ b/pycurl/tests/test_multi3.py @@ -0,0 +1,87 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_multi3.py 5574 2007-10-25 20:33:17Z thierry $ + +# same as test_multi2.py, but enforce some debugging and strange API-calls + +import os, sys +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO +import pycurl + + +urls = ( + "http://curl.haxx.se", + "http://www.python.org", + "http://pycurl.sourceforge.net", + "http://pycurl.sourceforge.net/THIS_HANDLE_IS_CLOSED", +) + +# init +m = pycurl.CurlMulti() +m.handles = [] +for url in urls: + c = pycurl.Curl() + # save info in standard Python attributes + c.url = url + c.body = StringIO() + c.http_code = -1 + c.debug = 0 + m.handles.append(c) + # pycurl API calls + c.setopt(c.URL, c.url) + c.setopt(c.WRITEFUNCTION, c.body.write) + m.add_handle(c) + +# debug - close a handle +if 1: + c = m.handles[3] + c.debug = 1 + c.close() + +# get data +num_handles = len(m.handles) +while num_handles: + while 1: + ret, num_handles = m.perform() + if ret != pycurl.E_CALL_MULTI_PERFORM: + break + # currently no more I/O is pending, could do something in the meantime + # (display a progress bar, etc.) + m.select() + +# close handles +for c in m.handles: + # save info in standard Python attributes + try: + c.http_code = c.getinfo(c.HTTP_CODE) + except pycurl.error: + # handle already closed - see debug above + assert c.debug + c.http_code = -1 + # pycurl API calls + if 0: + m.remove_handle(c) + c.close() + elif 0: + # in the C API this is the wrong calling order, but pycurl + # handles this automatically + c.close() + m.remove_handle(c) + else: + # actually, remove_handle is called automatically on close + c.close() +m.close() + +# print result +for c in m.handles: + data = c.body.getvalue() + if 0: + print "**********", c.url, "**********" + print data + else: + print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data)) + diff --git a/pycurl/tests/test_multi4.py b/pycurl/tests/test_multi4.py new file mode 100644 index 00000000..5ab5be86 --- /dev/null +++ b/pycurl/tests/test_multi4.py @@ -0,0 +1,57 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_multi4.py 5574 2007-10-25 20:33:17Z thierry $ + +import sys, select, time +import pycurl + +c1 = pycurl.Curl() +c2 = pycurl.Curl() +c3 = pycurl.Curl() +c1.setopt(c1.URL, "http://www.python.org") +c2.setopt(c2.URL, "http://curl.haxx.se") +c3.setopt(c3.URL, "http://slashdot.org") +c1.body = open("doc1", "wb") +c2.body = open("doc2", "wb") +c3.body = open("doc3", "wb") +c1.setopt(c1.WRITEFUNCTION, c1.body.write) +c2.setopt(c2.WRITEFUNCTION, c2.body.write) +c3.setopt(c3.WRITEFUNCTION, c3.body.write) + +m = pycurl.CurlMulti() +m.add_handle(c1) +m.add_handle(c2) +m.add_handle(c3) + +# Number of seconds to wait for a timeout to happen +SELECT_TIMEOUT = 10 + +# Stir the state machine into action +while 1: + ret, num_handles = m.perform() + if ret != pycurl.E_CALL_MULTI_PERFORM: + break + +# Keep going until all the connections have terminated +while num_handles: + apply(select.select, m.fdset() + (SELECT_TIMEOUT,)) + while 1: + ret, num_handles = m.perform() + if ret != pycurl.E_CALL_MULTI_PERFORM: + break + +# Cleanup +m.remove_handle(c3) +m.remove_handle(c2) +m.remove_handle(c1) +m.close() +c1.body.close() +c2.body.close() +c3.body.close() +c1.close() +c2.close() +c3.close() +print "http://www.python.org is in file doc1" +print "http://curl.haxx.se is in file doc2" +print "http://slashdot.org is in file doc3" diff --git a/pycurl/tests/test_multi5.py b/pycurl/tests/test_multi5.py new file mode 100644 index 00000000..0ddb5188 --- /dev/null +++ b/pycurl/tests/test_multi5.py @@ -0,0 +1,60 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_multi5.py 5574 2007-10-25 20:33:17Z thierry $ + +import sys, select, time +import pycurl + +c1 = pycurl.Curl() +c2 = pycurl.Curl() +c3 = pycurl.Curl() +c1.setopt(c1.URL, "http://www.python.org") +c2.setopt(c2.URL, "http://curl.haxx.se") +c3.setopt(c3.URL, "http://slashdot.org") +c1.body = open("doc1", "wb") +c2.body = open("doc2", "wb") +c3.body = open("doc3", "wb") +c1.setopt(c1.WRITEFUNCTION, c1.body.write) +c2.setopt(c2.WRITEFUNCTION, c2.body.write) +c3.setopt(c3.WRITEFUNCTION, c3.body.write) + +m = pycurl.CurlMulti() +m.add_handle(c1) +m.add_handle(c2) +m.add_handle(c3) + +# Number of seconds to wait for a timeout to happen +SELECT_TIMEOUT = 10 + +# Stir the state machine into action +while 1: + ret, num_handles = m.perform() + if ret != pycurl.E_CALL_MULTI_PERFORM: + break + +# Keep going until all the connections have terminated +while num_handles: + # The select method uses fdset internally to determine which file descriptors + # to check. + m.select(SELECT_TIMEOUT) + while 1: + ret, num_handles = m.perform() + if ret != pycurl.E_CALL_MULTI_PERFORM: + break + +# Cleanup +m.remove_handle(c3) +m.remove_handle(c2) +m.remove_handle(c1) +m.close() +c1.body.close() +c2.body.close() +c3.body.close() +c1.close() +c2.close() +c3.close() +print "http://www.python.org is in file doc1" +print "http://curl.haxx.se is in file doc2" +print "http://slashdot.org is in file doc3" + diff --git a/pycurl/tests/test_multi6.py b/pycurl/tests/test_multi6.py new file mode 100644 index 00000000..714c5c87 --- /dev/null +++ b/pycurl/tests/test_multi6.py @@ -0,0 +1,62 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_multi6.py 5574 2007-10-25 20:33:17Z thierry $ + +import sys, select, time +import pycurl + +c1 = pycurl.Curl() +c2 = pycurl.Curl() +c3 = pycurl.Curl() +c1.setopt(c1.URL, "http://www.python.org") +c2.setopt(c2.URL, "http://curl.haxx.se") +c3.setopt(c3.URL, "http://slashdot.org") +c1.body = open("doc1", "wb") +c2.body = open("doc2", "wb") +c3.body = open("doc3", "wb") +c1.setopt(c1.WRITEFUNCTION, c1.body.write) +c2.setopt(c2.WRITEFUNCTION, c2.body.write) +c3.setopt(c3.WRITEFUNCTION, c3.body.write) + +m = pycurl.CurlMulti() +m.add_handle(c1) +m.add_handle(c2) +m.add_handle(c3) + +# Number of seconds to wait for a timeout to happen +SELECT_TIMEOUT = 10 + +# Stir the state machine into action +while 1: + ret, num_handles = m.perform() + if ret != pycurl.E_CALL_MULTI_PERFORM: + break + +# Keep going until all the connections have terminated +while num_handles: + # The select method uses fdset internally to determine which file descriptors + # to check. + m.select(SELECT_TIMEOUT) + while 1: + ret, num_handles = m.perform() + # Print the message, if any + print m.info_read(1) + if ret != pycurl.E_CALL_MULTI_PERFORM: + break + +# Cleanup +m.remove_handle(c3) +m.remove_handle(c2) +m.remove_handle(c1) +m.close() +c1.body.close() +c2.body.close() +c3.body.close() +c1.close() +c2.close() +c3.close() +print "http://www.python.org is in file doc1" +print "http://curl.haxx.se is in file doc2" +print "http://slashdot.org is in file doc3" + diff --git a/pycurl/tests/test_multi_vs_thread.py b/pycurl/tests/test_multi_vs_thread.py new file mode 100644 index 00000000..8dac8064 --- /dev/null +++ b/pycurl/tests/test_multi_vs_thread.py @@ -0,0 +1,262 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_multi_vs_thread.py 5574 2007-10-25 20:33:17Z thierry $ + +import os, sys, time +from threading import Thread, RLock +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO +import pycurl + +# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see +# the libcurl tutorial for more info. +try: + import signal + from signal import SIGPIPE, SIG_IGN + signal.signal(signal.SIGPIPE, signal.SIG_IGN) +except ImportError: + pass + +# The conclusion is: the multi interface is fastest! + +NUM_PAGES = 30 +NUM_THREADS = 10 +assert NUM_PAGES % NUM_THREADS == 0 + +##URL = "http://pycurl.sourceforge.net/tests/testgetvars.php?%d" +URL = "http://pycurl.sourceforge.net/tests/teststaticpage.html?%d" + + +# +# util +# + +class Curl: + def __init__(self, url): + self.url = url + self.body = StringIO() + self.http_code = -1 + # pycurl API calls + self._curl = pycurl.Curl() + self._curl.setopt(pycurl.URL, self.url) + self._curl.setopt(pycurl.WRITEFUNCTION, self.body.write) + self._curl.setopt(pycurl.NOSIGNAL, 1) + + def perform(self): + self._curl.perform() + + def close(self): + self.http_code = self._curl.getinfo(pycurl.HTTP_CODE) + self._curl.close() + + +def print_result(items): + return # DO NOTHING + # + for c in items: + data = c.body.getvalue() + if 0: + print "**********", c.url, "**********" + print data + elif 1: + print "%-60s %3d %6d" % (c.url, c.http_code, len(data)) + + +### +### 1) multi +### + +def test_multi(): + clock1 = time.time() + + # init + handles = [] + m = pycurl.CurlMulti() + for i in range(NUM_PAGES): + c = Curl(URL %i) + m.add_handle(c._curl) + handles.append(c) + + clock2 = time.time() + + # stir state machine into action + while 1: + ret, num_handles = m.perform() + if ret != pycurl.E_CALL_MULTI_PERFORM: + break + + # get data + while num_handles: + m.select() + while 1: + ret, num_handles = m.perform() + if ret != pycurl.E_CALL_MULTI_PERFORM: + break + + clock3 = time.time() + + # close handles + for c in handles: + c.close() + m.close() + + clock4 = time.time() + print "multi interface: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1) + + # print result + print_result(handles) + + + +### +### 2) thread +### + +class Test(Thread): + def __init__(self, lock=None): + Thread.__init__(self) + self.lock = lock + self.items = [] + + def run(self): + if self.lock: + self.lock.acquire() + self.lock.release() + for c in self.items: + c.perform() + + +def test_threads(lock=None): + clock1 = time.time() + + # create and start threads, but block them + if lock: + lock.acquire() + + # init (FIXME - this is ugly) + threads = [] + handles = [] + t = None + for i in range(NUM_PAGES): + if i % (NUM_PAGES / NUM_THREADS) == 0: + t = Test(lock) + if lock: + t.start() + threads.append(t) + c = Curl(URL % i) + t.items.append(c) + handles.append(c) + assert len(handles) == NUM_PAGES + assert len(threads) == NUM_THREADS + + clock2 = time.time() + + # + if lock: + # release lock to let the blocked threads run + lock.release() + else: + # start threads + for t in threads: + t.start() + # wait for threads to finish + for t in threads: + t.join() + + clock3 = time.time() + + # close handles + for c in handles: + c.close() + + clock4 = time.time() + if lock: + print "thread interface [lock]: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1) + else: + print "thread interface: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1) + + # print result + print_result(handles) + + + +### +### 3) thread - threads grab curl objects on demand from a shared pool +### + +class TestPool(Thread): + def __init__(self, lock, pool): + Thread.__init__(self) + self.lock = lock + self.pool = pool + + def run(self): + while 1: + self.lock.acquire() + c = None + if self.pool: + c = self.pool.pop() + self.lock.release() + if c is None: + break + c.perform() + + +def test_thread_pool(lock): + clock1 = time.time() + + # init + handles = [] + for i in range(NUM_PAGES): + c = Curl(URL %i) + handles.append(c) + + # create and start threads, but block them + lock.acquire() + threads = [] + pool = handles[:] # shallow copy of the list, shared for pop() + for i in range(NUM_THREADS): + t = TestPool(lock, pool) + t.start() + threads.append(t) + assert len(pool) == NUM_PAGES + assert len(threads) == NUM_THREADS + + clock2 = time.time() + + # release lock to let the blocked threads run + lock.release() + + # wait for threads to finish + for t in threads: + t.join() + + clock3 = time.time() + + # close handles + for c in handles: + c.close() + + clock4 = time.time() + print "thread interface [pool]: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1) + + # print result + print_result(handles) + + + +lock = RLock() +if 1: + test_multi() + test_threads() + test_threads(lock) + test_thread_pool(lock) +else: + test_thread_pool(lock) + test_threads(lock) + test_threads() + test_multi() + diff --git a/pycurl/tests/test_post.py b/pycurl/tests/test_post.py new file mode 100644 index 00000000..17caff61 --- /dev/null +++ b/pycurl/tests/test_post.py @@ -0,0 +1,24 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_post.py 5574 2007-10-25 20:33:17Z thierry $ + +import urllib +import pycurl + +# simple +pf = {'field1': 'value1'} + +# multiple fields +pf = {'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'} + +# multiple fields with & in field +pf = {'field1':'value1', 'field2':'value2 with blanks and & chars', + 'field3':'value3'} + +c = pycurl.Curl() +c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testpostvars.php') +c.setopt(c.POSTFIELDS, urllib.urlencode(pf)) +c.setopt(c.VERBOSE, 1) +c.perform() +c.close() diff --git a/pycurl/tests/test_post2.py b/pycurl/tests/test_post2.py new file mode 100644 index 00000000..60f02fa9 --- /dev/null +++ b/pycurl/tests/test_post2.py @@ -0,0 +1,18 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_post2.py 5574 2007-10-25 20:33:17Z thierry $ + +import pycurl + +pf = [('field1', 'this is a test using httppost & stuff'), + ('field2', (pycurl.FORM_FILE, 'test_post.py', pycurl.FORM_FILE, 'test_post2.py')), + ('field3', (pycurl.FORM_CONTENTS, 'this is wei\000rd, but null-bytes are okay')) + ] + +c = pycurl.Curl() +c.setopt(c.URL, 'http://www.contactor.se/~dast/postit.cgi') +c.setopt(c.HTTPPOST, pf) +c.setopt(c.VERBOSE, 1) +c.perform() +c.close() diff --git a/pycurl/tests/test_post3.py b/pycurl/tests/test_post3.py new file mode 100644 index 00000000..8a9ea931 --- /dev/null +++ b/pycurl/tests/test_post3.py @@ -0,0 +1,32 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_post3.py 5574 2007-10-25 20:33:17Z thierry $ + +import urllib +POSTSTRING = urllib.urlencode({'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'}) + +class test: + + def __init__(self): + self.finished = False + + def read_cb(self, size): + assert len(POSTSTRING) <= size + if not self.finished: + self.finished = True + return POSTSTRING + else: + # Nothing more to read + return "" + +import pycurl +c = pycurl.Curl() +t = test() +c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testpostvars.php') +c.setopt(c.POST, 1) +c.setopt(c.POSTFIELDSIZE, len(POSTSTRING)) +c.setopt(c.READFUNCTION, t.read_cb) +c.setopt(c.VERBOSE, 1) +c.perform() +c.close() diff --git a/pycurl/tests/test_stringio.py b/pycurl/tests/test_stringio.py new file mode 100644 index 00000000..21a3153e --- /dev/null +++ b/pycurl/tests/test_stringio.py @@ -0,0 +1,25 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_stringio.py 5574 2007-10-25 20:33:17Z thierry $ + +import sys +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO +import pycurl + +url = "http://curl.haxx.se/dev/" + +print "Testing", pycurl.version + +body = StringIO() +c = pycurl.Curl() +c.setopt(c.URL, url) +c.setopt(c.WRITEFUNCTION, body.write) +c.perform() +c.close() + +contents = body.getvalue() +print contents diff --git a/pycurl/tests/test_xmlrpc.py b/pycurl/tests/test_xmlrpc.py new file mode 100644 index 00000000..918f76fd --- /dev/null +++ b/pycurl/tests/test_xmlrpc.py @@ -0,0 +1,29 @@ +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: test_xmlrpc.py 5574 2007-10-25 20:33:17Z thierry $ + +## XML-RPC lib included in python2.2 +import xmlrpclib +import pycurl + +# Header fields passed in request +xmlrpc_header = [ + "User-Agent: PycURL XML-RPC Test", "Content-Type: text/xml" + ] + +# XML-RPC request template +xmlrpc_template = """ +%s%s +""" + +# Engage +c = pycurl.Curl() +c.setopt(c.URL, 'http://betty.userland.com/RPC2') +c.setopt(c.POST, 1) +c.setopt(c.HTTPHEADER, xmlrpc_header) +c.setopt(c.POSTFIELDS, xmlrpc_template % ("examples.getStateName", xmlrpclib.dumps((5,)))) + +print 'Response from http://betty.userland.com/' +c.perform() +c.close() diff --git a/pycurl/tests/util.py b/pycurl/tests/util.py new file mode 100644 index 00000000..e4e9607d --- /dev/null +++ b/pycurl/tests/util.py @@ -0,0 +1,38 @@ +# -*- coding: iso-8859-1 -*- +# vi:ts=4:et +# $Id: util.py 5574 2007-10-25 20:33:17Z thierry $ + +import os, sys + +# +# prepare sys.path in case we are still in the build directory +# see also: distutils/command/build.py (build_platlib) +# + +def get_sys_path(p=None): + if p is None: p = sys.path + p = p[:] + try: + from distutils.util import get_platform + except ImportError: + return p + p0 = "" + if p: p0 = p[0] + # + plat = get_platform() + plat_specifier = "%s-%s" % (plat, sys.version[:3]) + ##print plat, plat_specifier + # + for prefix in (p0, os.curdir, os.pardir,): + if not prefix: + continue + d = os.path.join(prefix, "build") + for subdir in ("lib", "lib." + plat_specifier, "lib." + plat): + dir = os.path.normpath(os.path.join(d, subdir)) + if os.path.isdir(dir): + if dir not in p: + p.insert(1, dir) + # + return p + + -- 2.47.0