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.
+
+
+
+
+
+ $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.
+
+
+
+
+
+
+ $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.
+
+
+
+
+
+
+
+ $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
+
+
+
+
+
+
+ $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