starting new development to support slice conf files
authorTony Mack <tmack@cs.princeton.edu>
Fri, 10 Oct 2008 19:19:56 +0000 (19:19 +0000)
committerTony Mack <tmack@cs.princeton.edu>
Fri, 10 Oct 2008 19:19:56 +0000 (19:19 +0000)
31 files changed:
pycurl/COPYING [new file with mode: 0644]
pycurl/MANIFEST.in [new file with mode: 0644]
pycurl/Makefile [new file with mode: 0644]
pycurl/README [new file with mode: 0644]
pycurl/TODO [new file with mode: 0644]
pycurl/doc/callbacks.html [new file with mode: 0644]
pycurl/doc/curlmultiobject.html [new file with mode: 0644]
pycurl/doc/curlobject.html [new file with mode: 0644]
pycurl/doc/pycurl.html [new file with mode: 0644]
pycurl/python/curl/__init__.py [new file with mode: 0644]
pycurl/setup.py [new file with mode: 0644]
pycurl/tests/test.py [new file with mode: 0644]
pycurl/tests/test_cb.py [new file with mode: 0644]
pycurl/tests/test_debug.py [new file with mode: 0644]
pycurl/tests/test_getinfo.py [new file with mode: 0644]
pycurl/tests/test_gtk.py [new file with mode: 0644]
pycurl/tests/test_internals.py [new file with mode: 0644]
pycurl/tests/test_memleak.py [new file with mode: 0644]
pycurl/tests/test_multi.py [new file with mode: 0644]
pycurl/tests/test_multi2.py [new file with mode: 0644]
pycurl/tests/test_multi3.py [new file with mode: 0644]
pycurl/tests/test_multi4.py [new file with mode: 0644]
pycurl/tests/test_multi5.py [new file with mode: 0644]
pycurl/tests/test_multi6.py [new file with mode: 0644]
pycurl/tests/test_multi_vs_thread.py [new file with mode: 0644]
pycurl/tests/test_post.py [new file with mode: 0644]
pycurl/tests/test_post2.py [new file with mode: 0644]
pycurl/tests/test_post3.py [new file with mode: 0644]
pycurl/tests/test_stringio.py [new file with mode: 0644]
pycurl/tests/test_xmlrpc.py [new file with mode: 0644]
pycurl/tests/util.py [new file with mode: 0644]

diff --git a/pycurl/COPYING b/pycurl/COPYING
new file mode 100644 (file)
index 0000000..99dce33
--- /dev/null
@@ -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.
+\f
+  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.
+\f
+                  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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.
+\f
+  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
+\f
+           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.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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.
+
+  <signature of Ty Coon>, 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 (file)
index 0000000..f4e3837
--- /dev/null
@@ -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 (file)
index 0000000..9b2369d
--- /dev/null
@@ -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 (file)
index 0000000..bd04ab6
--- /dev/null
@@ -0,0 +1,12 @@
+LICENSE
+-------
+
+Copyright (C) 2001-2005 by Kjetil Jacobsen <kjetilja@cs.uit.no>
+Copyright (C) 2001-2005 by Markus F.X.J. Oberhumer <markus@oberhumer.com>
+
+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 (file)
index 0000000..7541535
--- /dev/null
@@ -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 (file)
index 0000000..c808646
--- /dev/null
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>PyCurl: Callbacks</title>
+  <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+  <meta name="revisit-after" content="30 days" />
+  <meta name="robots" content="noarchive, index, follow" />
+</head>
+<body>
+
+<h1>Callbacks</h1>
+
+<p>For more fine-grained control, libcurl allows a
+number of callbacks to be associated with each connection. In
+pycurl, callbacks are defined using the <code>setopt()</code> 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.</p>
+
+<p>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.
+</p>
+
+The signature of each callback used in pycurl is as follows:<br/>
+<br/>
+<code>WRITEFUNCTION(</code><em>string</em><code>) </code><em>-&gt; number of characters written<br/>
+</em>
+<br/>
+<code>READFUNCTION(</code><em>number of characters to read</em><code>)</code><em>-&gt;
+string</em><br/>
+<br/>
+<code>HEADERFUNCTION(</code><em>string</em><code>)</code><em> -&gt; number of characters written<br/>
+</em><br/>
+<code>PROGRESSFUNCTION(</code><em>download total, downloaded, upload total, uploaded</em><code>) </code><em>-&gt; status</em><br/>
+<br/>
+<code>DEBUGFUNCTION(</code><em>debug message type, debug message string</em><code>)</code>
+<em>-&gt; None<br/></em>
+<br/>
+<code>IOCTLFUNCTION(</code><em>ioctl cmd</em><code>)</code>
+<em>-&gt; status<br/></em>
+<br/>
+<hr/>
+
+<h2>Example: Callbacks for document header and body</h2>
+
+<p>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.</p>
+
+<pre>
+    ## 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()
+</pre>
+
+<h2>Example: Download/upload progress callback</h2>
+
+<p>This example shows how to use the progress callback.  When downloading
+a document, the arguments related to uploads are zero, and vice versa.</p>
+
+<pre>
+    ## 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()
+</pre>
+
+<h2>Example: Debug callbacks</h2>
+
+<p>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.</p>
+
+<pre>
+    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()
+</pre>
+
+<h2>Other examples</h2>
+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.</p>
+
+
+<hr />
+<p>
+  <a href="http://validator.w3.org/check/referer"><img align="right"
+     src="http://www.w3.org/Icons/valid-xhtml10"
+     alt="Valid XHTML 1.0!" height="31" width="88" border="0" /></a>
+  $Id: callbacks.html 5574 2007-10-25 20:33:17Z thierry $
+</p>
+
+</body>
+</html>
diff --git a/pycurl/doc/curlmultiobject.html b/pycurl/doc/curlmultiobject.html
new file mode 100644 (file)
index 0000000..7af3d54
--- /dev/null
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>PycURL: CurlMulti Objects</title>
+  <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+  <meta name="revisit-after" content="30 days" />
+  <meta name="robots" content="noarchive, index, follow" />
+</head>
+<body>
+
+<h1>CurlMulti Object</h1>
+
+<p>CurlMulti objects have the following methods: </p>
+
+<dl>
+<dt><code>close()</code> -&gt; <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_cleanup.html"><code>curl_multi_cleanup()</code></a> 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.</p>
+</dd>
+
+<dt><code>perform()</code> -&gt; <em>tuple of status and the number of active Curl objects</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_perform.html"><code>curl_multi_perform()</code></a> in libcurl.</p>
+</dd>
+
+<dt><code> add_handle(</code><em>Curl object</em><code>) </code>-&gt; <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_add_handle.html"><code>curl_multi_add_handle()</code></a> in libcurl.
+This method  adds an existing and valid Curl object to the CurlMulti
+object.</p>
+
+<p>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).</p>
+</dd>
+
+<dt><code>remove_handle(</code><em>Curl object</em><code>)</code> -&gt; <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_remove_handle.html"><code>curl_multi_remove_handle()</code></a> in libcurl.
+This method removes an existing and valid Curl object from the CurlMulti
+object.</p>
+
+<p>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).</p>
+</dd>
+
+<dt><code>fdset()</code> -&gt;
+<em>triple of lists with active file descriptors,
+readable,  writeable, exceptions.</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_fdset.html"><code>curl_multi_fdset()</code></a> in libcurl.
+This method extracts  the file descriptor information from a CurlMulti object.
+The returned  lists can be used with the <code>select</code> module to
+poll for events.</p>
+
+<p>Example usage:</p>
+
+<pre>
+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
+</pre>
+</dd>
+
+<dt><code>select(</code><em>[timeout]</em><code>)</code> -&gt;
+<em>number of ready file descriptors or -1 on timeout</em></dt>
+<dd>
+<p>This is a convenience function which simplifies the combined
+use of <code>fdset()</code> and the <code>select</code> module.</p>
+
+<p>Example usage:</p>
+
+<pre>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
+</pre>
+</dd>
+
+<dt><code>info_read(</code><em>[max]</em><code>)</code> -&gt;
+<em>numberof queued messages, a list of successful objects, a list of
+failed objects</em></dt>
+<dd>
+<p>Corresponds to the
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_info_read.html"><code>curl_multi_info_read()</code></a> function in libcurl.
+This method extracts at most <em>max</em> 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 <em>&lt;curl object, curl error number, curl
+error message&gt;</em> for each failed curl object. The number
+of queued messages after this method has been called is also
+returned.</p>
+</dd>
+</dl>
+
+<hr />
+<p>
+  <a href="http://validator.w3.org/check/referer"><img align="right"
+     src="http://www.w3.org/Icons/valid-xhtml10"
+     alt="Valid XHTML 1.0!" height="31" width="88" border="0" /></a>
+  $Id: curlmultiobject.html 5574 2007-10-25 20:33:17Z thierry $
+</p>
+
+</body>
+</html>
diff --git a/pycurl/doc/curlobject.html b/pycurl/doc/curlobject.html
new file mode 100644 (file)
index 0000000..b6ad774
--- /dev/null
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>PycURL: Curl Objects</title>
+  <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+  <meta name="revisit-after" content="30 days" />
+  <meta name="robots" content="noarchive, index, follow" />
+</head>
+<body>
+
+<h1>Curl Object</h1>
+
+<p>Curl objects have the following methods:</p>
+
+<dl>
+<dt><code>close()</code> -&gt; <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_easy_cleanup.html"><code>curl_easy_cleanup</code></a> 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.</p>
+</dd>
+
+<dt><code>perform()</code> -&gt; <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_easy_perform.html"><code>curl_easy_perform</code></a> in libcurl.</p>
+</dd>
+
+<dt><code>setopt(</code><em>option, value</em><code>)</code> -&gt; <em>None</em></dt>
+<dd>
+
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_easy_setopt.html"><code>curl_easy_setopt</code></a> in libcurl, where
+<em>option</em> is specified with the CURLOPT_* constants in libcurl,
+except that the CURLOPT_ prefix has been removed. The type for
+<em>value</em> depends on the option, and can be either a string,
+integer, long integer, file objects, lists, or functions.</p>
+
+<p>Example usage:</p>
+
+<pre>
+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()
+...
+</pre>
+</dd>
+
+<dt><code>getinfo(</code><em>option</em><code>) </code>-&gt; <em>Result</em></dt>
+<dd>
+
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html"><code>curl_easy_getinfo</code></a> in libcurl, where
+<em>option</em> is the same as the CURLINFO_* constants in libcurl,
+except that the CURLINFO_ prefix has been removed.
+<em>Result</em> contains an integer, float or string, depending on
+which option is given. The <code>getinfo</code> method should
+not be called unless <code>perform</code> has been called and
+finished.</p>
+
+<p>Example usage:</p>
+
+<pre>
+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)
+...
+--&gt; 200 "http://sourceforge.net/"
+</pre>
+</dd>
+
+<dt><code>errstr()</code> -&gt; <em>String</em></dt>
+<dd>
+<p>Returns the internal libcurl error buffer of this handle as a string.</p>
+</dd>
+</dl>
+
+
+<hr />
+<p>
+  <a href="http://validator.w3.org/check/referer"><img align="right"
+     src="http://www.w3.org/Icons/valid-xhtml10"
+     alt="Valid XHTML 1.0!" height="31" width="88" border="0" /></a>
+  $Id: curlobject.html 5574 2007-10-25 20:33:17Z thierry $
+</p>
+
+</body>
+</html>
diff --git a/pycurl/doc/pycurl.html b/pycurl/doc/pycurl.html
new file mode 100644 (file)
index 0000000..7d796d0
--- /dev/null
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>PycURL Documentation</title>
+  <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+  <meta name="revisit-after" content="30 days" />
+  <meta name="robots" content="noarchive, index, follow" />
+</head>
+<body>
+
+<h1><tt>pycurl</tt> &mdash; A Python interface to the cURL library</h1>
+
+<p>The pycurl package is a Python interface to libcurl (<a
+href="http://curl.haxx.se/libcurl/">http://curl.haxx.se/libcurl/</a>). pycurl
+has been successfully built and tested with Python versions from
+2.2 to the current 2.4.x releases.</p>
+
+<p>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.</p>
+
+<p>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
+(<a href="http://curl.haxx.se/libcurl/c/">http://curl.haxx.se/libcurl/c/</a>).</p>
+
+<hr/>
+
+<h1>Module Functionality</h1>
+
+<dl>
+<dt><code>pycurl.global_init(</code><em>option</em><code>)</code> -&gt;<em>None</em></dt>
+
+<dd><p><em>option</em> is one of the constants
+pycurl.GLOBAL_SSL, pycurl.GLOBAL_WIN32, pycurl.GLOBAL_ALL,
+pycurl.GLOBAL_NOTHING, pycurl.GLOBAL_DEFAULT.  Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_global_init.html"><code>curl_global_init()</code></a> in libcurl.</p>
+</dd>
+
+<dt><code>pycurl.global_cleanup()</code> -&gt; <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_global_cleanup.html"><code>curl_global_cleanup()</code></a> in libcurl.</p>
+</dd>
+
+<dt><code>pycurl.version</code></dt>
+
+<dd><p>This is a string with version information on libcurl,
+corresponding to
+<a href="http://curl.haxx.se/libcurl/c/curl_version.html"><code>curl_version()</code></a> in libcurl.</p>
+
+<p>Example usage:</p>
+<pre>
+>>> import pycurl
+>>> pycurl.version
+'libcurl/7.12.3 OpenSSL/0.9.7e zlib/1.2.2.1 libidn/0.5.12'
+</pre>
+</dd>
+
+<dt><code>pycurl.version_info()</code> -&gt; <em>Tuple</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_version_info.html"><code>curl_version_info()</code></a> in libcurl.
+Returns a tuple of information which is similar to the
+<code>curl_version_info_data</code> struct returned by
+<code>curl_version_info()</code> in libcurl.</p>
+
+<p>Example usage:</p>
+<pre>
+>>> 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')
+</pre>
+</dd>
+
+<dt><code>pycurl.Curl()</code> -&gt; <em>Curl object</em></dt>
+<dd>
+<p>This function creates a new
+<a href="curlobject.html">Curl object</a> which corresponds to a
+<code>CURL</code> 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.</p>
+</dd>
+
+<dt><code>pycurl.CurlMulti()</code> -&gt; <em>CurlMulti object</em></dt>
+<dd>
+<p>This function creates a new
+<a href="curlmultiobject.html">CurlMulti object</a> which corresponds to
+a <code>CURLM</code> handle in libcurl.</p>
+</dd>
+</dl>
+
+<hr/>
+
+<h1>Subsections</h1>
+
+<ul>
+  <li><a href="curlobject.html">Curl objects</a></li>
+  <li><a href="curlmultiobject.html">CurlMulti objects</a></li>
+  <li><a href="callbacks.html">Callbacks</a></li>
+</ul>
+
+<hr />
+<p>
+  <a href="http://validator.w3.org/check/referer"><img align="right"
+     src="http://www.w3.org/Icons/valid-xhtml10"
+     alt="Valid XHTML 1.0!" height="31" width="88" border="0" /></a>
+  $Id: pycurl.html 5574 2007-10-25 20:33:17Z thierry $
+</p>
+
+</body>
+</html>
diff --git a/pycurl/python/curl/__init__.py b/pycurl/python/curl/__init__.py
new file mode 100644 (file)
index 0000000..8fecb4d
--- /dev/null
@@ -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 (file)
index 0000000..8c7b68b
--- /dev/null
@@ -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 (file)
index 0000000..de787a6
--- /dev/null
@@ -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 <file with uris to fetch>" % 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 (file)
index 0000000..936293d
--- /dev/null
@@ -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 (file)
index 0000000..07bec02
--- /dev/null
@@ -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 (file)
index 0000000..927048b
--- /dev/null
@@ -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 (file)
index 0000000..1b306f6
--- /dev/null
@@ -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 <URL> <filename>" % 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 (file)
index 0000000..6a6c02f
--- /dev/null
@@ -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 (file)
index 0000000..a43ef06
--- /dev/null
@@ -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 (file)
index 0000000..0befd4e
--- /dev/null
@@ -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 (file)
index 0000000..6da93c2
--- /dev/null
@@ -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 (file)
index 0000000..8889246
--- /dev/null
@@ -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 (file)
index 0000000..5ab5be8
--- /dev/null
@@ -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 (file)
index 0000000..0ddb518
--- /dev/null
@@ -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 (file)
index 0000000..714c5c8
--- /dev/null
@@ -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 (file)
index 0000000..8dac806
--- /dev/null
@@ -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 (file)
index 0000000..17caff6
--- /dev/null
@@ -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 (file)
index 0000000..60f02fa
--- /dev/null
@@ -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 (file)
index 0000000..8a9ea93
--- /dev/null
@@ -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 (file)
index 0000000..21a3153
--- /dev/null
@@ -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 (file)
index 0000000..918f76f
--- /dev/null
@@ -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 = """
+<?xml version='1.0'?><methodCall><methodName>%s</methodName>%s</methodCall>
+"""
+
+# 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 (file)
index 0000000..e4e9607
--- /dev/null
@@ -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
+
+