/home/lnzliplg/public_html/doc.zip
PK���\1-��p�p�pycurl/ChangeLognu�[���Version 7.45.6 [requires libcurl-7.19.0 or better] - 2025-03-06
---------------------------------------------------------------

        * Re-enable building Linux wheels with CA bundle autodetection

Version 7.45.5 [requires libcurl-7.19.0 or better] - 2025-03-06
---------------------------------------------------------------

        * Enable GSS-API and brotli support in wheels (patch by Scott Talbert).
        * Add support for calling getinfo with CURLOPT_*_T arguments
          (patch by Scott Talbert)
        * Change wheels to build using shared libraries (vice static libraries)
          (patch by Scott Talbert)
        * Build wheels with curl 8.12.1 (mainly for security fixes)

Version 7.45.4 [requires libcurl-7.19.0 or better] - 2024-12-12
---------------------------------------------------------------

        * Add support for CURLOPT_HAPROXY_CLIENT_IP (patch by Scott Talbert).
        * Port tests from bottle to flask (patch by Miro Hrončok).
        * Add constant for CURL_HTTP_VERSION_3ONLY (patch by Pavel Horáček).
        * Add EFFECTIVE_METHOD info option (patch by Pavel Horáček).
        * Don't use `-flat_namespace` on macOS (patch by Michael Cho).
        * Add some missing GIL checks to callback functions
          (patch by Scott Talbert).
        * Fix assorted bugs in pycurl tests, including a segfault
          (patch by Scott Talbert).  All tests should now pass on Linux and
          macOS.
        * Fix minor bug in examples/multi-socket_action-select.py
          (patch by Oleg Broytman).
        * Build all wheels using the latest version of libcurl and its
          dependencies (patch by Scott Talbert).  All wheels should now have
          openssl, HTTP2, and SSH support.
        * Implement Certificate Authority path autodetection when building
          Linux wheels (patch by Scott Talbert).

Version 7.45.3 [requires libcurl-7.19.0 or better] - 2024-02-17
---------------------------------------------------------------

        * Add CURLOPT_REQUEST_TARGET option (patch by Marcel Brouwers).
        * Add missing 2nd parameters to METH_NOARGS functions
          (patch by Scott Talbert).
        * Add CURLOPT_AWS_SIGV4 option (patch by Scott Talbert).
        * Add consistent names for newer Curl version constants
          (patch by Scott Talbert).
        * Only run HTTP version 3 option constant test if curl supported
          (patch by Scott Talbert).
        * Expose COMPILE_SSL_LIB in Python and use for test filtering
          (patch by Scott Talbert).
        * Filter tests based on *compile* libcurl version not runtime version
          (patch by Scott Talbert).
        * Use print function in callbacks documentation
          (patch by Scott Talbert).
        * Add missing shebang to tests/ext/test-suite.sh
          (patch by Scott Talbert).
        * Officially declare support for Python 3.12
          (patch by Scott Talbert).
        * Fix curl_multi_info_read flow that loses messages
          (patch by Dom Sekotill).
        * Support using environment variables for setup on Windows
          (patch by Scott Talbert).
        * Add support for Schannel SSL backend (patch by Scott Talbert)
        * Skip HTTP2 tests based on a curl support check
          (patch by Scott Talbert).
        * Fix fake-curl tests so they work when run out of tree
          (patch by Scott Talbert).
        * xfail test_easy_pause_unpause unconditionally
          (patch by Scott Talbert).
        * Provide generic error strings in pycurl.error objects
          (patch by Scott Talbert).
        * Change URLs to new curl mailing list (patch by Michael C).
        * Add missing HTTPS proxy options (patch by Jean Hominal).
        * Add support for setting CURLOPT_SSLCERT_BLOB
          (patch by Vesa Jääskeläinen).
        * Add support for setting rest of CURLOPTTYPE_BLOB fields
          (patch by Vesa Jääskeläinen).
        * Build wheels on Linux/macOS/Windows (patch by Scott Talbert).

Version 7.45.2 [requires libcurl-7.19.0 or better] - 2022-12-16
---------------------------------------------------------------

        * Python 3.9 compatibility for Py_TRASHCAN_SAFE_BEGIN
          (patch by Scott Talbert).
        * Add support for CURL_HTTP_VERSION_3 (patch by Scott Talbert).
        * Add CURLOPT_TLS13_CIPHERS and CURLOPT_PROXY_TLS13_CIPHERS options
          (patch by Scott Talbert).
        * Added HTTP09_ALLOWED option (patch by Scott Talbert).
        * Removed use of distutils (patch by Scott Talbert).


Version 7.45.1 [requires libcurl-7.19.0 or better] - 2022-03-13
---------------------------------------------------------------

        * Fixed build against libcurl < 7.64.1 (patch by Scott Talbert).


Version 7.45.0 [requires libcurl-7.64.1 or better] - 2022-03-09
---------------------------------------------------------------

        * Add CURLOPT_MAXLIFETIME_CONN (patch by fsbs).

        * Easy handle duplication support (patch by fsbs).

        * Support for unsetting a number of multi options (patch by fsbs).

        * pycurl classes can now be subclassed (patch by fsbs).

        * Multi callbacks' thread state management fixed (patch by fsbs).

        * Add CURL_LOCK_DATA_PSL (patch by fsbs).

        * Add support for SecureTransport SSL backend (MacOS)
          (patch by Scott Talbert).


Version 7.44.1 [requires libcurl-7.19.0 or better] - 2021-08-15
---------------------------------------------------------------

        * Fixed Python thread initialization causing hangs on operations
          (patch by Scott Talbert).


Version 7.44.0 [requires libcurl-7.19.0 or better] - 2021-08-08
---------------------------------------------------------------

        * getinfo(CURLINFO_FTP_ENTRY_PATH) now handles NULL return from
          libcurl, returning None in this case.
        
        * Python 3.9 is now officially supported (patch by Bill Collins).
        
        * Added CURLOPT_DOH_URL (patch by resokou).
        
        * Best effort Python 2 support has been reinstated.
        
        * Added missing fields to curl_version_info struct (patch by Hasan).
        
        * Added CURLINFO_CONDITION_UNMET (patch by Dima Tisnek).
        
        * Exposed MAX_CONCURRENT_STREAMS in CurlMulti (patch by Alexandre Pion).
        
        * Compilation fixed against Python 3.10 alpha (patch by Kamil Dudka).


Version 7.43.0.6 [requires libcurl-7.19.0 or better] - 2020-09-02
-----------------------------------------------------------------

        * Fixed offset parameter usage in seek callback (patch by Scott Talbert).

        * Added support for libcurl SSL backend detection via
          `curl-config --ssl-backends` (patch by Scott Talbert).

        * Added support for libcurl MultiSSL (patch by Bo Anderson).

        * Added ability to unset CURLOPT_PROXY.

        * Added support for CURLOPT_UPLOAD_BUFFERSIZE (patch by Artur Sobierak).

        * Added support for CURLOPT_MAXAGE_CONN (patch by Artur Sobierak).

        * Added support for sharing connection cache in libcurl (patch by
          Artur Sobierak).

        * Added support for CURLOPT_HAPROXYPROTOCOL (patch by
          Russell McConnachie).

        * CC and CFLAGS environment variables are now respected when building
          (patch by Michał Górny).

        * Fixed OpenSSL detection on CentOS 7 and 8 (patch by Nicolas Pauss).

        * surrogateescape error handler is used in multi_info_read to handle
          invalid UTF-8.


Version 7.43.0.5 [requires libcurl-7.19.0 or better] - 2020-01-29
-----------------------------------------------------------------

        * Fixed build with recent Pythons on RHEL/CentOS.


Version 7.43.0.4 [requires libcurl-7.19.0 or better] - 2020-01-15
-----------------------------------------------------------------

        * Minimum supported Python 3 version is now 3.5.

        * Python 2 is no longer officially supported.
        
        * Improved thread safety of multi code.
        
        * Added Python 3.8 support (patch by Michael Treanor).
        
        * Fixed link order when linking statically against OpenSSL (patch by
          Ashley Whetter).
        
        * Fixed Darwin detection.
        
        * Added support for wolfSSL (patch by Eneas U de Queiroz).
        
        * Added PROXY_SSL_VERIFYHOST (patch by Amir Rossert).


Version 7.43.0.3 [requires libcurl-7.19.0 or better] - 2019-06-17
-----------------------------------------------------------------

        * Fixed use with libcurl 7.65+ when FTP support is disabled.

        * Added support for mbedTLS (patch by Josef Schlehofer).

        * Fixed string processing on Python 3 (patch by Dmitriy Taychenachev).

        * Added CURLOPT_TCP_FASTOPEN and CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE
          (patch by Khavish Anshudass Bhundoo).

        * Repaired inability to install PycURL when libcurl is using an SSL
          backend other than the ones PycURL explicitly recognizes and
          handles (OpenSSL, LibreSSL, BoringSSL, GnuTLS, NSS).
          The requirement for setup.py to detect an SSL backend if libcurl
          is configured to use SSL, added in 7.43.0.2, has been changed
          to a warning to allow this.


Version 7.43.0.2 [requires libcurl-7.19.0 or better] - 2018-06-02
-----------------------------------------------------------------

        * Official Windows builds now include HTTP 2 support via
          libnghttp2 and international domain name support via WINIDN.

        * Added perform_rb and perform_rs methods to Curl objects to
          return response body as byte string and string, respectively.

        * Added OPT_COOKIELIST constant for consistency with other
          option constants.

        * PycURL is now able to report errors triggered by libcurl
          via CURLOPT_FAILONERROR mechanism when the error messages are
          not decodable in Python's default encoding (GitHub issue #259).

        * Added getinfo_raw method to Curl objects to return byte strings
          as is from libcurl without attempting to decode them
          (GitHub issue #493).

        * When adding a Curl easy object to CurlMulti via add_handle,
          the easy objects now have their reference counts increased so that
          the application is no longer required to keep references to them
          to keep them from being garbage collected (GitHub issue #171).

        * PycURL easy, multi and share objects can now be weak referenced.

        * Python 3.2 and 3.3 support officially dropped as those versions
          are end of lifed.

        * set_ca_certs now accepts byte strings as it should have been
          all along.

        * PycURL now skips automatic SSL backend detection if curl-config
          indicates that libcurl is not built with SSL support, and will warn
          if an SSL backend is explicitly specified in this case.

        * PycURL now requires that SSL backend is determined by setup.py
          to provide earlier failure compared to the existing warning
          during compilation and failing during module import on mismatched
          SSL backends.

        * Use OpenSSL 1.1 and 1.0 specific APIs for controlling thread locks
          depending on OpenSSL version (patch by Vitaly Murashev).

        * Fixed a crash when closesocket callback failed (patch by
          Gisle Vanem and toddrme2178).

        * Added CURLOPT_PROXY_SSLCERT, CURLOPT_PROXY_SSLCERTTYPE,
          CURLOPT_PROXY_SSLKEY, CURLOPT_PROXY_SSLKEYTYPE,
          CURLOPT_PROXY_SSL_VERIFYPEER (libcurl 7.52.0+,
          patch by Casey Miller).

        * Added CURLOPT_PRE_PROXY (libcurl 7.52.0+, patch by ziggy).

        * Support for Python 2.6 officially dropped.

        * Added SOCKET_BAD constant and it is now recognized as a valid
          return value from OPENSOCKET callback.

        * BoringSSL is now recognized as equivalent to OpenSSL backend
          (patch by Gisle Vanem).


Version 7.43.0.1 [requires libcurl-7.19.0 or better] - 2017-12-07
-----------------------------------------------------------------

        * WRITEHEADER/WRITEFUNCTION and WRITEDATA/WRITEFUNCTION can now
          be set on the same handle. The last call will take precedence over
          previous calls. Previously some combinations were not allowed.

        * Fixed a crash when using WRITEDATA with a file-like object followed
          by WRITEDATA with a real file object (patch by Léo El Amri).

        * Fixed a theoretical memory leak in module initialization (patch by
          ideal).

        * Added support for CURL_SSLVERSION_MAX_* constants (libcurl 7.52.0+,
          patch by Jozef Melicher).

        * Added support for CURLSSH_AUTH_AGENT (libcurl 7.28.0+,
          patch by kxrd).

        * Added support for CURLOPT_CONNECT_TO (patch by Iain R. Learmonth).

        * Added support for CURLINFO_HTTP_VERSION (patch by Iain R. Learmonth).

        * Fixed build against OpenSSL l.1 on Windows.

        * Added set_ca_certs method to the Easy object to set CA certificates
          from a string (OpenSSL only, patch by Lipin Dmitriy).

        * Python 3.6 is now officially supported (patch by Samuel
          Dion-Girardeau).

        * Added support for CURLOPT_PROXY_CAPATH (libcurl 7.52.0+,
          patch by Jan Kryl).

        * C-Ares updated to 1.12.0 in Windows builds, fixing DNS resolution
          issues on Windows (patch by Wei C).

        * Added --openssl-lib-name="" option to support building against
          OpenSSL 1.1.0 on Windows.

        * Fixed a possible double free situation in all Curl objects
          due to a misuse of the trashcan API (patch by Benjamin Peterson).

        * High level Curl objects can now be reused.

        * LARGE options fixed under Windows and Python 3 (INFILESIZE,
          MAX_RECV_SPEED_LARGE, MAX_SEND_SPEED_LARGE, MAXFILESIZE,
          POSTFILESIZE, RESUME_FROM).

        * Fixed compilation on Solaris (patch by Yiteng Zhang).

        * ENCODING option can now be unset (patch by Yves Bastide).


Version 7.43.0 [requires libcurl-7.19.0 or better] - 2016-02-02
---------------------------------------------------------------

        * Added CURLINFO_RTSP_* constants (libcurl 7.20.0+).

        * Added CURLOPT_XOAUTH2_BEARER (libcurl 7.33.0+).

        * Added CURLOPT_SASL_IR (libcurl 7.31.0+).

        * Added CURLOPT_LOGIN_OPTIONS (libcurl 7.34.0+).

        * Added CURLOPT_FTP_USE_PRET (libcurl 7.20.0+).

        * Added setopt_string method to Curl objects to set arbitrary
          string options.

        * Switched to Bintray for hosting release distributions.

        * Added CURLOPT_DEFAULT_PROTOCOL (libcurl 7.45.0+).

        * Added CURLOPT_TLSAUTH_* options (libcurl 7.21.4+).

        * Added CURLPROTO_SMB and CURLPROTO_SMBS constants (libcurl 7.40.0+).

        * Added CURL_SOCKOPT_* constants (libcurl 7.21.5+).

        * Added CURL_HTTP_VERSION_2_0, CURL_HTTP_VERSION_2 and
          CURL_HTTP_VERSION_2TLS constants for CURLOPT_HTTP_VERSION
          (various libcurl versions required for these).

        * winbuild.py can now build binary wheels on Windows.

        * Added failed memory allocation handling during SSL lock initialization.

        * CURLOPT_IOCTLDATA option support has been removed.
          This option is used internally by PycURL and is not settable by
          applications.

        * HTTPHEADER and PROXYHEADER options can now be unset.

        * Added CURLPIPE_* constants (libcurl 7.43.0+).

        * Added CURLOPT_PIPEWAIT (libcurl 7.43.0+).

        * Added CURLOPT_PATH_AS_IS (libcurl 7.42.0+).

        * Added CURLOPT_PROXYHEADER and CURLOPT_HEADEROPT as well as
          CURLHEADER_UNIFIED and CURLHEADER_SEPARATE (libcurl 7.37.0+).

        * Added CURLOPT_EXPECT_100_TIMEOUT_MS (libcurl 7.36.0+).

        * Added CURLOPT_XFERINFOFUNCTION (libcurl 7.32.0+).

        * Added CURLM_ADDED_ALREADY error constant (libcurl 7.32.1+).

        * Added remaining CURLE_* constants through libcurl 7.46.0.

        * Unbroken `curl' module import on Windows - apparently Windows now
          has a `signal' Python module but no `SIGPIPE' (patch by Gabi Davar).

        * Added CURLMOPT_PIPELINING_SITE_BL and CURLMOPT_PIPELINING_SERVER_BL
          options (libcurl 7.30.0+).

        * Added CURLOPT_TCP_KEEPALIVE, CURLOPT_TCP_KEEPIDLE and
          CURLOPT_TCP_KEEPINTVL options (libcurl 7.25.0+).

        * Added CURLOPT_ACCEPTTIMEOUT_MS (libcurl 7.24.0+).

        * Added CURLOPT_ACCEPT_ENCODING and CURLOPT_TRANSFER_ENCODING
          options (libcurl 7.21.6+).

        * OPENSOCKETFUNCTION callback for AF_UNIX sockets was mistakenly
          invoked with the address as a `string' rather than `bytes' on
          Python 3. The callback now receives a `bytes' instance as was
          documented.


Version 7.21.5 [requires libcurl-7.19.0 or better] - 2016-01-05
---------------------------------------------------------------

        * --with-openssl and its --win-ssl alias setup.py options are now
          accepted under Windows in order to use OpenSSL's crypto locks
          when building against OpenSSL.

        * --with-openssl added as an alias for --with-ssl option to setup.py.

        * Official Windows builds are now linked against C-Ares and libssh2.

        * Official Windows builds are now linked against OpenSSL instead of
          WinSSL.

        * Official Windows builds are now statically linked against
          their dependencies (libcurl and zlib).

        * Added CURLOPT_USE_SSL and CURLUSESSL_* constants.

        * Added CURLOPT_APPEND, CURLOPT_COOKIESESSION, CURLOPT_DIRLISTONLY,
          CURLOPT_KEYPASSWD, CURLOPT_TELNETOPTIONS.

        * Several CURLE_* and CURLM_* constants added.

        * Add VERSION_* constants, corresponding to CURL_VERSION_*.

        * Breaking change: OPENSOCKETFUNCTION callback API now mirrors that
          of libcurl:
          1. The callback now takes two arguments, `purpose' and `address`.
             Previously the callback took `family', `socktype', `protocol`
             and `addr' arguments.
          2. The second argument to the callback, `address', is a
             `namedtuple' with `family', `socktype', `protocol' and
             `addr' fields.
          3. `addr' field on `address' for AF_INET6 family addresses is a
             4-tuple of (address, port, flow info, scope id) which matches
             Python's `socket.getaddrinfo' API.

          It seems that libcurl may mishandle error return from an
          opensocket callback, as would happen when code written for
          pre-PycURL 7.21.5 API is run with PycURL 7.21.5 or newer,
          resulting in the application hanging.

        * OPENSOCKETFUNCTION callback can now be unset.

        * Added CURLOPT_CLOSESOCKETFUNCTION (libcurl 7.21.7+).
          CURLOPT_CLOSESOCKETDATA is used internally by PycURL.

        * Added CURLOPT_SOCKOPTFUNCTION. CURLOPT_SOCKOPTDATA is used
          internally by PycURL.

        * Added CURLOPT_SSH_KEYFUNCTION (libcurl 7.19.6+).
          CURLOPT_SSH_KEYDATA is used internally by PycURL.

        * Added CURLOPT_SSL_OPTIONS (libcurl 7.25.0+).

        * Added CURLOPT_KRBLEVEL.

        * Added CURLOPT_SSL_FALSESTART (libcurl 7.42.0+).

        * Added CURLOPT_SSL_ENABLE_NPN (libcurl 7.36.0+).

        * Added CURLOPT_SSL_ENABLE_ALPN (libcurl 7.36.0+).

        * Added CURLOPT_UNIX_SOCKET_PATH (libcurl 7.40.0+).

        * Added CURLOPT_WILDCARDMATCH (libcurl 7.21.0+).

        * C module initialization changed to raise exceptions on failure
          rather than trigger a fatal error and abort the Python interpreter.

        * Added CURLOPT_PINNEDPUBLICKEY (libcurl 7.39.0-7.44.0+
          depending on SSL backend and encoding algorithm).

        * Fixed incorrect detection of libcurl 7.19.5 and 7.19.6
          (thanks to bataniya).


Version 7.19.5.3 [requires libcurl-7.19.0 or better] - 2015-11-03
-----------------------------------------------------------------

        * python and nosetests binaries can now be overridden when running
          the test suite (patch by Kamil Dudka).

        * Files needed to run the test suite are distributed in sdist
          (patch by Kamil Dudka).


Version 7.19.5.2 [requires libcurl-7.19.0 or better] - 2015-11-02
-----------------------------------------------------------------

        * C sources made 64-bit clean on Windows.

        * Support for building against Python 3.5 added to winbuild.py.

        * Fixed build on Windows when using MS SDK 8.1+ or MSVC 14/2015
          (patch by Gisle Vanem).

        * Added automatic SSL library detection on CentOS 6 by loading
          libcurl shared library in setup.py. This automatic detection is
          meant to permit installing pycurl seamlessly via `pip install pycurl`
          on CentOS; as such, it is only employed when no other configuration
          options or configuration environment variables are given to setup.py
          (original patch by Francisco Alves).

        * Added --libcurl-dll option to setup.py to take SSL library
          information out of libcurl shared library (original patch by
          Francisco Alves). This option is only usable
          with Python 2.5 or higher.

        * --with-ssl, --with-gnutls and --with-nss options to setup.py now
          result in PycURL explicitly linking against the respective SSL
          library. Previously setup.py relied on curl-config to supply the
          needed libraries in this case.

        * List and tuples are now accepted in all positions of HTTPPOST
          option values.

        * Tuples are now accepted for options taking list values (e.g.
          HTTPHEADER).

        * Fixed a use after free in HTTPPOST when using FORM_BUFFERPTR with
          a Unicode string (patch by Clint Clayton).

        * Fixed a memory leak in HTTPPOST for multiple FORM_BUFFERPTR
          (patch by Clint Clayton).

        * CURLMOPT_* option constants were mistakenly defined on Curl
          instances but not on CurlMulti instances. These option constants
          are now defined on CurlMulti instances and on pycurl module,
          but not on Curl instances.

        * Fixed several memory leaks when setting string options to
          Unicode values failed.

        * Fixed a memory leak when using POSTFIELDS with unicode objects
          on Python 2 (patch by Clint Clayton).

        * Official support for Python 2.4 and 2.5 dropped. PycURL is no
          longer tested against these Python versions on Travis.

        * Added CURLAUTH_NEGOTIATE (libcurl 7.38.0+), CURLAUTH_NTLM_WB
          (libcurl 7.22.0+), CURLAUTH_ONLY (libcurl 7.21.3+),

        * Added CURLOPT_SERVICE_NAME (libcurl 7.43.0+).

        * Added CURLOPT_PROXY_SERVICE_NAME (libcurl 7.43.0+).

        * Added CURLE_SSL_CRL_BADFILE, CURLE_SSL_INVALIDCERTSTATUS
          (libcurl 7.41.0+), CURLE_SSL_ISSUER_ERROR and
          CURLE_SSL_PINNEDPUBKEYNOTMATCH (libcurl 7.39.0+).

        * Added CURLOPT_SSL_VERIFYSTATUS (libcurl 7.41.0+).

        * Added CURL_SSLVERSION_TLSv1_0, CURL_SSLVERSION_TLSv1_1
          and CURL_SSLVERSION_TLSv1_2 (libcurl 7.34.0+).

        * The second argument of DEBUGFUNCTION callback is now of type bytes on
          Python 3. When response body contains non-ASCII data and
          DEBUGFUNCTION is enabled, this argument would receive non-ASCII data.
          Which encoding this data is in is unknown by PycURL, and e.g. in
          the case of HTTP requires parsing response headers. GitHub issue
          #210, patch by Barry Warsaw with help from Gregory Petukhov.

        * Fixed build on GCC 4.4.5 (patch by Travis Jensen).

        * Added CURLOPT_GSSAPI_DELEGATION, CURLGSSAPI_DELEGATION_FLAG,
          CURLGSSAPI_DELEGATION_NONE and CURLGSSAPI_DELEGATION_POLICY_FLAG
          (libcurl 7.22.0+, patch by Dmitry Ketov).


Version 7.19.5.1 [requires libcurl-7.19.0 or better] - 2015-01-06
-----------------------------------------------------------------

        * Added CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5_HOSTNAME.

        * setup.py now prints PycURL-specific option help when -h is used.

        * LibreSSL is now supported (patch by JiCiT).

        * Fixed an oversight that broke PycURL building against libcurl 7.19.4
          through 7.21.1. The bug was introduced in PycURL 7.19.5.

        * Tests are now included in source distributions again, thanks to
          Kamil Dudka and Johan Bergstroem.

        * Added CURLOPT_MAIL_FROM and CURLOPT_MAIL_RCPT (libcurl 7.20.0+)
          and CURLOPT_MAIL_AUTH (libcurl 7.25.0+).


Version 7.19.5 [requires libcurl-7.21.2 or better] - 2014-07-12
---------------------------------------------------------------

        * Tests removed from source and binary distributions.

        * Documentation greatly improved. Quickstart guide added.

        * pycurl.Curl, pycurl.CurlMulti and pycurl.CurlShare are now classes
          rather than factory functions. Previously, the classes were "hidden"
          (they were accessible as e.g. type(pycurl.Curl()), but could not be
          instantiated, nor could class methods be obtained from the classes.
          Please see this mailing list post for further information:
          https://curl.haxx.se/mail/curlpython-2014-06/0004.html

        * When passing a file-like object to READDATA option, PycURL was
          mistakenly looking for write method on this object. Now read method
          is looked up, as would be expected.

        * Python 3.4 is now officially supported.

        * Windows packages now build libcurl against zlib.

        * CherryPy is no longer required for the test suite, ssl module from
          the Python standard library is used instead.

        * Fixed a reference leak of SOCKET and TIMER callbacks on
          CurlMulti instances, thanks to Ben Darnell.

        * Fixed build against openssl on cygwin, where pycurl needs to link
          against libcrypto rather than libssl.

        * Added CURLOPT_SSH_KNOWNHOSTS (libcurl 7.19.6+).

        * Added CURLE_FTP_ACCEPT_FAILED (libcurl 7.24.0+).

        * Added CURLE_NOT_BUILT_IN and CURLE_UNKNOWN_OPTION (libcurl 7.21.5+).

        * Added CURL_SEEKFUNC_OK, CURL_SEEKFUNC_FAIL and
          CURL_SEEKFUNC_CANTSEEK. All constants require libcurl 7.19.5+;
          numeric values of CURL_SEEKFUNC_OK and CURL_SEEKFUNC_FAIL were
          understood earlier but constants only exist as of libcurl 7.19.5.

        * Added CURLINFO_CONDITION_UNMET (libcurl 7.19.4+).

        * Added CURLPROXY_HTTP_1_0 (libcurl 7.19.4+).

        * Added CURLOPT_SOCKS5_GSSAPI_SERVICE and
          CURLOPT_SOCKS5_GSSAPI_NEC (libcurl 7.19.4+).

        * Added CURLOPT_TFTP_BLKSIZE (libcurl 7.19.4+).

        * Added CURLOPT_PROTOCOLS, CURLOPT_REDIR_PROTOCOLS and associated
          CURLPROTO_* constants, which require libcurl 7.19.4+.

        * Fixed a reference leak of OPENSOCKET and SEEK callbacks, thanks to
          Ben Darnell.

        * C source is now split into several files.

        * Documentation is now processed by sphinx.


Version 7.19.3.1 [requires libcurl-7.19.0 or better] - 2014-02-05
-----------------------------------------------------------------

        * Added --avoid-stdio setup.py option to avoid passing FILE
          pointers from Python to libcurl. Applies to Python 2 only.

        * Added CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE,
          CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE, CURLMOPT_MAX_HOST_CONNECTIONS
          CURLMOPT_MAX_PIPELINE_LENGTH, CURLMOPT_MAX_TOTAL_CONNECTIONS
          multi options (patch by Jakob Truelsen).

        * SSL detection logic changed to consult `curl-config --static-libs`
          even if `curl-config --libs` succeeded. This should achieve
          pre-7.19.3 behavior with respect to automatic SSL detection
          (patch by Andjelko Horvat).


Version 7.19.3 [requires libcurl-7.19.0 or better] - 2014-01-09
---------------------------------------------------------------

        * Added CURLOPT_NOPROXY.

        * Added CURLINFO_LOCAL_PORT, CURLINFO_PRIMARY_PORT and
          CURLINFO_LOCAL_IP (patch by Adam Jacob Muller).

        * When running on Python 2.x, for compatibility with Python 3.x,
          Unicode strings containing ASCII code points only are now accepted
          in setopt() calls.

        * PycURL now requires that compile time SSL backend used by libcurl
          is the same as the one used at runtime. setup.py supports
          --with-ssl, --with-gnutls and --with-nss options like libcurl does,
          to specify which backend libcurl uses. On some systems PycURL can
          automatically figure out libcurl's backend.
          If the backend is not one for which PycURL provides crypto locks
          (i.e., any of the other backends supported by libcurl),
          no runtime SSL backend check is performed.

        * Default PycURL user agent string is now built at runtime, and will
          include the user agent string of libcurl loaded at runtime rather
          than the one present at compile time.

        * PycURL will now use WSAduplicateSocket rather than dup on Windows
          to duplicate sockets obtained from OPENSOCKETFUNCTION.
          Using dup may have caused crashes, OPENSOCKETFUNCTION should
          now be usable on Windows.

        * A new script, winbuild.py, was added to build PycURL on Windows
          against Python 2.6, 2.7, 3.2 and 3.3.

        * Added CURL_LOCK_DATA_SSL_SESSION (patch by Tom Pierce).

        * Added E_OPERATION_TIMEDOUT (patch by Romuald Brunet).

        * setup.py now handles --help argument and will print PycURL-specific
          configuration options in addition to distutils help.

        * Windows build configuration has been redone:
          PYCURL_USE_LIBCURL_DLL #define is gone, use --use-libcurl-dll
          argument to setup.py to build against a libcurl DLL.
          CURL_STATICLIB is now #defined only when --use-libcurl-dll is not
          given to setup.py, and PycURL is built against libcurl statically.
          --libcurl-lib-name option can be used to override libcurl import
          library name.

        * Added CURLAUTH_DIGEST_IE as pycurl.HTTPAUTH_DIGEST_IE.

        * Added CURLOPT_POSTREDIR option and CURL_REDIR_POST_301,
          CURL_REDIR_POST_302, CURL_REDIR_POST_303 and CURL_REDIR_POST_ALL
          constants. CURL_REDIR_POST_303 requires libcurl 7.26.0 or higher,
          all others require libcurl 7.19.1 or higher.

        * As part of Python 3 support, WRITEDATA option now accepts
          any object with a write method on Python 2 and Python 3.
          For non-file objects, c.setopt(c.WRITEDATA, buf) is equivalent to
          c.setopt(c.WRITEFUNCTION, buf.write).

        * PycURL now supports Python 3.1 through 3.3. Python 3.0 might
          work but it appears to ship with broken distutils, making virtualenv
          not function on it.

        * PycURL multi objects now have the multi constants defined on them.
          Previously the constants were only available on pycurl module.
          The new behavior matches that of curl and share objects.

        * PycURL share objects can now be closed via the close() method.

        * PycURL will no longer call `curl-config --static-libs` if
          `curl-config --libs` succeeds and returns output.
          Systems on which neither `curl-config --libs` nor
          `curl-config --static-libs` do the right thing should provide
          a `curl-config` wrapper that is sane.

        * Added CURLFORM_BUFFER and CURLFORM_BUFFERPTR.

        * pycurl.version and user agent string now include both
          PycURL version and libcurl version as separate items.

        * Added CURLOPT_DNS_SERVERS.

        * PycURL can now be dynamically linked against libcurl on Windows
          if PYCURL_USE_LIBCURL_DLL is #defined during compilation.

        * Breaking change: opensocket callback now takes an additional
          (address, port) tuple argument. Existing callbacks will need to
          be modified to accept this new argument.
          https://github.com/pycurl/pycurl/pull/18


Version 7.19.0.3 [requires libcurl-7.19.0 or better] - 2013-12-24
-----------------------------------------------------------------

        * Re-release of 7.19.0.2 with minor changes to build Windows packages
          due to botched 7.19.0.2 files on PyPi.
          https://curl.haxx.se/mail/curlpython-2013-12/0021.html


Version 7.19.0.2 [requires libcurl-7.19.0 or better] - 2013-10-08
-----------------------------------------------------------------

        * Fixed a bug in a commit made in 2008 but not released until 7.19.0.1
          which caused CURLOPT_POSTFIELDS to not correctly increment reference
          count of the object being given as its argument, despite libcurl not
          copying the data provided by said object.

        * Added support for libcurl pause/unpause functionality,
          via curl_easy_pause call and returning READFUNC_PAUSE from
          read callback function.


Version 7.19.0.1 [requires libcurl-7.19.0 or better] - 2013-09-23
-----------------------------------------------------------------

        * Test matrix tool added to test against all supported Python and
          libcurl versions.

        * Python 2.4 is now the minimum required version.

        * Source code, bugs and patches are now kept on GitHub.

        * Added CURLINFO_CERTINFO and CURLOPT_CERTINFO.

        * Added CURLOPT_RESOLVE.

        * PycURL can now be used with Python binaries without thread
          support.

        * gcrypt is no longer initialized when a newer version of gnutls
          is used.

        * Marked NSS as supported.

        * Fixed relative URL request logic.

        * Fixed a memory leak in util_curl_init.

        * Added CURLOPT_USERNAME and CURLOPT_PASSWORD.

        * Fixed handling of big timeout values.

        * Added GLOBAL_ACK_EINTR.

        * setopt(..., None) can be used as unsetopt().

        * CURLOPT_RANGE can now be unset.

        * Write callback can return -1 to signal user abort.

        * Reorganized tests into an automated test suite.

        * Added CURLOPT_SEEKFUNCTION and CURLOPT_SEEKDATA.

        * Cleaned up website.

        * Fix pycurl.reset() (patch by <johansen at sun.com>).

        * Fix install routine in setup.py where
          certain platforms (Solaris, Mac OSX, etc)
          would search for a static copy of libcurl (dbp).

        * Fixed build on OpenSolaris 0906 and other platforms on which
          curl-config does not have a --static-libs option.

        * No longer keep string options copies in the
          Curl Python objects, since string options are
          now managed by libcurl.


Version 7.19.0 [requires libcurl-7.19.0 or better]
--------------------------------------------------

        * Added CURLFILE, ADDRESS_SCOPE and ISSUERCERT options,
          as well as the APPCONNECT_TIME info.

        * Added PRIMARY_IP info (patch by
          Yuhui H <eyecat at gmail.com>).

        * Added support for curl_easy_reset through a
          new 'reset' method on curl objects
          (patch by Nick Pilon <npilon at oreilly.com>).

        * Added support for OPENSOCKET callbacks.
          See 'tests/test_opensocket.py' for example
          usage (patch by Thomas Hunger <teh at camvine.com>).


Version 7.18.2
--------------

        * Added REDIRECT_URL info and M_MAXCONNECTS option
          (patch by Yuhui H <eyecat at gmail.com>).

        * Added socket_action() method to CurlMulti objects.
          See 'tests/test_multi_socket_select.py' for example
          usage (patch by Yuhui H <eyecat at gmail.com>).

        * Added AUTOREFERER option.

        * Allow resetting some list operations (HTTPHEADER,
          QUOTE, POSTQUOTE, PREQUOTE) by passing an empty
          list to setopt (patch by Jim Patterson).


Version 7.18.1
--------------

        * Added POST301, SSH_HOST_PUBLIC_KEY_MD5,
          COPYPOSTFIELDS and PROXY_TRANSFER_MODE options.

        * Check for static libs in setup.py to better detect
          whether libcurl was linked with OpenSSL or GNUTLS.

        * PycURL is now dual licensed under the LGPL and
          a license similar to the cURL license (an MIT/X
          derivative).


Version 7.16.4
--------------

        * Allow any callable object as the callback function.
          This change comes handy when you would like to use objects
          which are callable but are not functions or methods, for
          example those objects created by the functions in the functools
          module (patch by Daniel Pena Arteaga <dpena at ph.tum.de>).

        * Added NEW_DIRECTORY_PERMS and NEW_FILE_PERMS options.


Version 7.16.2.1
----------------

        * Added IOCMD_NOP and IOCMD_RESTARTREAD for ioctl callback
          handling (patch by Mark Eichin).

        * Use Py_ssize_t where appropriate for Python 2.5 and 64-bit
          compatibility.  This fixes the problem reported by Aaron
          Hill, where the exception "pycurl.error: (2, '')" is thrown
          when calling setopt(pycurl.POSTFIELDS,...) on 64-bit
          platforms.


Version 7.16.2
--------------

        * Added options HTTP_TRANSFER_DECODING, HTTP_CONTENT_DECODING,
          TIMEOUT_MS, CONNECTTIMEOUT_MS from libcurl 7.16.2.

        * Right-strip URLs read from files in the test scripts
          to avoid sending requests with '\n' at the end.


Version 7.16.1
--------------

        * Added constants for all libcurl (error) return codes.  They
          are named the same as the macro constants in curl.h but prefixed
          with E_ instead of CURLE.  Return codes for the multi API are
          prefixed with M_ instead of CURLM.

        * Added CURLOPT_FTP_SSL_CCC, CURLOPT_SSH_PUBLIC_KEYFILE,
          CURLOPT_SSH_PRIVATE_KEYFILE, CURLOPT_SSH_AUTH_TYPES.

        * Removed CLOSEPOLICY and friends since this option is now
          deprecated in libcurl.

        * Set the _use_datetime attribute on the CURLTransport class
          to unbreak xmlrpc_curl.py on Python 2.5.


Version 7.16.0 [no public release]
--------------

        * Added CURLOPT_SSL_SESSIONID_CACHE.

        * Removed SOURCE_* options since they are no longer
          supported by libcurl.


Version 7.15.5.1
----------------

        * Added test for basic ftp usage (tests/test_ftp.py).

        * Fix broken ssl mutex lock function when using
          GNU TLS (Debian bug #380156, fix by Bastian Kleineidam)


Version 7.15.5
--------------

        * Added CURLOPT_FTP_ALTERNATIVE_TO_USER,
          CURLOPT_MAX_SEND_SPEED_LARGE,
          and CURLOPT_MAX_RECV_SPEED_LARGE.


Version 7.15.4.2
----------------

        * Use SSL locking callbacks, fixes random
          crashes for multithreaded SSL connections
          (patch by Jayne <corvine at gmail.com>).


Version 7.15.4.1
----------------

        * Fixed compilation problem with C compilers
          not allowing declarations in the middle of
          code blocks (patch by
          K.S.Sreeram <sreeram at tachyontech.net>).

        * Fixed bug in curl_multi_fdset wrapping,
          max_fd < 0 is not an error (patch by
          K.S.Sreeram <sreeram at tachyontech.net>).


Version 7.15.4
--------------

        * Added support for libcurl shares, patch from
        Victor Lascurain <bittor at eleka.net>.  See the
        file tests/test_share.py for example usage.

        * Added support for CURLINFO_FTP_ENTRY_PATH.


Version 7.15.2
--------------

        * Added CURLOPT_CONNECT_ONLY, CURLINFO_LASTSOCKET,
          CURLOPT_LOCALPORT and CURLOPT_LOCALPORTRANGE.


Version 7.15.1
--------------

2006-01-31 Kjetil Jacobsen <kjetilja>

        * Fixed memory leak for getinfo calls that return a
          list as result.  Patch by Paul Pacheco.


Version 7.15.0
--------------

2005-10-18  Kjetil Jacobsen  <kjetilja>

        * Added CURLOPT_FTP_SKIP_PASV_IP.


Version 7.14.1
--------------

2005-09-05  Kjetil Jacobsen  <kjetilja>

        * Added CURLOPT_IGNORE_CONTENT_LENGTH, CURLOPT_COOKIELIST as
          COOKIELIST and CURLINFO_COOKIELIST as INFO_COOKIELIST.


Version 7.14.0
--------------

2005-05-18  Kjetil Jacobsen  <kjetilja>

        * Added missing information returned from the info() method
          in the high-level interface.

        * Added the FORM_FILENAME option to the CURLFORM API
          with HTTPPOST.


Version 7.13.2
--------------

2005-03-30  Kjetil Jacobsen  <kjetilja>

        * Unbreak tests/test_gtk.py and require pygtk >= 2.0.

2005-03-15  Kjetil Jacobsen  <kjetilja>

        * Cleaned up several of the examples.

2005-03-11  Kjetil Jacobsen  <kjetilja>

        * WARNING: multi.select() now requires the previously optional
          timeout parameter.  Updated the tests and examples to reflect
          this change.  If the timeout is not set, select could block
          infinitely and cause problems for the internal timeout handling
          in the multi stack.  The problem was identified by
          <unknownsoldier93 at yahoo.com>.


Version 7.13.1
--------------

2005-03-04  Kjetil Jacobsen  <kjetilja>

        * Use METH_NOARGS where appropriate.

2005-03-03  Kjetil Jacobsen  <kjetilja>

        * Added support for CURLFORM API with HTTPPOST: Supports a
          a tuple with pairs of options and values instead of just
          supporting string contents.  See tests/test_post2.py
          for example usage.  Options are FORM_CONTENTS, FORM_FILE and
          FORM_CONTENTTYPE, corresponding to the CURLFORM_* options,
          and values are strings.

2005-02-13  Markus F.X.J. Oberhumer <mfx>

        * Read callbacks (pycurl.READFUNCTION) can now return
          pycurl.READFUNC_ABORT to immediately abort the current transfer.

        * The INFILESIZE, MAXFILESIZE, POSTFIELDSIZE and RESUME_FROM
          options now automatically use the largefile version to handle
          files > 2GB.

        * Added missing pycurl.PORT constant.


Version 7.13.0
--------------

2005-02-10  Kjetil Jacobsen  <kjetilja>

        * Added file_upload.py to examples, shows how to upload
          a file.

        * Added CURLOPT_IOCTLFUNCTION/DATA.

        * Added options from libcurl 7.13.0: FTP_ACCOUNT, SOURCE_URL,
          SOURCE_QUOTE.

        * Obsoleted options: SOURCE_HOST, SOURCE_PATH, SOURCE_PORT,
          PASV_HOST.


Version 7.12.3
--------------

2004-12-22  Markus F.X.J. Oberhumer <mfx>

        * Added CURLINFO_NUM_CONNECTS and CURLINFO_SSL_ENGINES.

        * Added some other missing constants.

        * Updated pycurl.version_info() to return a 12-tuple
          instead of a 9-tuple.


Version 7.12.2
--------------

2004-10-15  Kjetil Jacobsen  <kjetilja>

        * Added CURLOPT_FTPSSLAUTH (and CURLFTPAUTH_*).

        * Added CURLINFO_OS_ERRNO.

2004-08-17 Kjetil Jacobsen <kjetilja>

        * Use LONG_LONG instead of PY_LONG_LONG to make pycurl compile
          on Python versions < 2.3 (fix from Domenico Andreoli
          <cavok at libero.it>).


Version 7.12.1
--------------

2004-08-02  Kjetil Jacobsen  <kjetilja>

        * Added INFOTYPE_SSL_DATA_IN/OUT.

2004-07-16  Markus F.X.J. Oberhumer <mfx>

        * WARNING: removed deprecated PROXY_, TIMECOND_ and non-prefixed
          INFOTYPE constant names. See ChangeLog entry 2003-06-10.

2004-06-21  Kjetil Jacobsen  <kjetilja>

        * Added test program for HTTP post using the read callback (see
          tests/test_post3.py for details).

        * Use the new CURL_READFUNC_ABORT return code where appropriate
          to avoid hanging in perform() when read callbacks are used.

        * Added support for libcurl 7.12.1 CURLOPT features:
          SOURCE_HOST, SOURCE_USERPWD, SOURCE_PATH, SOURCE_PORT,
          PASV_HOST, SOURCE_PREQUOTE, SOURCE_POSTQUOTE.

2004-06-08  Markus F.X.J. Oberhumer <mfx>

        * Setting CURLOPT_POSTFIELDS now allows binary data and
          automatically sets CURLOPT_POSTFIELDSIZE for you. If you really
          want a different size you have to manually set POSTFIELDSIZE
          after setting POSTFIELDS.
          (Based on a patch by Martin Muenstermann).

2004-06-05  Markus F.X.J. Oberhumer <mfx>

        * Added stricter checks within the callback handlers.

        * Unify the behaviour of int and long parameters where appropriate.


Version 7.12
------------

2004-05-18  Kjetil Jacobsen  <kjetilja>

        * WARNING: To simplify code maintenance pycurl now requires
          libcurl 7.11.2 and Python 2.2 or newer to work.

        * GC support is now always enabled.


Version 7.11.3
--------------

2004-04-30  Kjetil Jacobsen  <kjetilja>

        * Do not use the deprecated curl_formparse function.
          API CHANGE: HTTPPOST now takes a list of tuples where each
          tuple contains a form name and a form value, both strings
          (see test/test_post2.py for example usage).

        * Found a possible reference count bug in the multithreading
          code which may have contributed to the long-standing GC
          segfault which has haunted pycurl.  Fingers crossed.


Version 7.11.2
--------------

2004-04-21  Kjetil Jacobsen  <kjetilja>

        * Added support for libcurl 7.11.2 CURLOPT features:
          CURLOPT_TCP_NODELAY.

2004-03-25 Kjetil Jacobsen   <kjetilja>

        * Store Python longs in off_t with PyLong_AsLongLong instead
          of PyLong_AsLong.  Should make the options which deal
          with large files behave a little better.  Note that this
          requires the long long support in Python 2.2 or newer to
          work properly.


Version 7.11.1
--------------

2004-03-16  Kjetil Jacobsen  <kjetilja>

        * WARNING: Removed support for the PASSWDFUNCTION callback, which
          is no longer supported by libcurl.

2004-03-15  Kjetil Jacobsen  <kjetilja>

        * Added support for libcurl 7.11.1 CURLOPT features:
          CURLOPT_POSTFIELDSIZE_LARGE.


Version 7.11.0
--------------

2004-02-11  Kjetil Jacobsen  <kjetilja>

        * Added support for libcurl 7.11.0 CURLOPT features:
          INFILESIZE_LARGE, RESUME_FROM_LARGE, MAXFILESIZE_LARGE
          and FTP_SSL.

        * Circular garbage collection support can now be enabled or
          disabled by passing the '--use-gc=[1|0]' parameter to setup.py
          when building pycurl.

        * HTTP_VERSION options are known as CURL_HTTP_VERSION_NONE,
          CURL_HTTP_VERSION_1_0, CURL_HTTP_VERSION_1_1 and
          CURL_HTTP_VERSION_LAST.

2003-11-16  Markus F.X.J. Oberhumer <mfx>

        * Added support for these new libcurl 7.11.0 features:
          CURLOPT_NETRC_FILE.


Version 7.10.8
--------------

2003-11-04  Markus F.X.J. Oberhumer <mfx>

        * Added support for these new libcurl 7.10.8 features:
          CURLOPT_FTP_RESPONSE_TIMEOUT, CURLOPT_IPRESOLVE,
          CURLOPT_MAXFILESIZE,
          CURLINFO_HTTPAUTH_AVAIL, CURLINFO_PROXYAUTH_AVAIL,
          CURL_IPRESOLVE_* constants.

        * Added support for these new libcurl 7.10.7 features:
          CURLOPT_FTP_CREATE_MISSING_DIRS, CURLOPT_PROXYAUTH,
          CURLINFO_HTTP_CONNECTCODE.


2003-10-28  Kjetil Jacobsen  <kjetilja>

        * Added missing CURLOPT_ENCODING option (patch by Martijn
          Boerwinkel <xim at xs4all.nl>)


Version 7.10.6
--------------

2003-07-29  Markus F.X.J. Oberhumer <mfx>

        * Started working on support for CURLOPT_SSL_CTX_FUNCTION and
          CURLOPT_SSL_CTX_DATA (libcurl-7.10.6) - not yet finished.

2003-06-10  Markus F.X.J. Oberhumer <mfx>

        * Added support for CURLOPT_HTTPAUTH (libcurl-7.10.6), including
          the new HTTPAUTH_BASIC, HTTPAUTH_DIGEST, HTTPAUTH_GSSNEGOTIATE
          and HTTPAUTH_NTML constants.

        * Some constants were renamed for consistency:

          All curl_infotype constants are now prefixed with "INFOTYPE_",
          all curl_proxytype constants are prefixed with "PROXYTYPE_" instead
          of "PROXY_", and all curl_TimeCond constants are now prefixed
          with "TIMECONDITION_" instead of "TIMECOND_".

          (The old names are still available but will get removed
          in a future release.)

        * WARNING: Removed the deprecated pycurl.init() and pycurl.multi_init()
          names - use pycurl.Curl() and pycurl.CurlMulti() instead.

        * WARNING: Removed the deprecated Curl.cleanup() and
          CurlMulti.cleanup() methods - use Curl.close() and
          CurlMulti.close() instead.


Version 7.10.5
--------------

2003-05-15  Markus F.X.J. Oberhumer <mfx>

        * Added support for CURLOPT_FTP_USE_EPRT (libcurl-7.10.5).

        * Documentation updates.

2003-05-07  Eric S. Raymond  <esr>

        * Lifted all HTML docs to clean XHTML, verified by tidy.

2003-05-02  Markus F.X.J. Oberhumer <mfx>

        * Fixed some `int' vs. `long' mismatches that affected 64-bit systems.

        * Fixed wrong pycurl.CAPATH constant.

2003-05-01  Markus F.X.J. Oberhumer <mfx>

        * Added new method Curl.errstr() which returns the internal
        libcurl error buffer string of the handle.


Version 7.10.4.2
----------------

2003-04-15  Markus F.X.J. Oberhumer <mfx>

        * Allow compilation against the libcurl-7.10.3 release - some
        recent Linux distributions (e.g. Mandrake 9.1) ship with 7.10.3,
        and apart from the new CURLOPT_UNRESTRICTED_AUTH option there is
        no need that we require libcurl-7.10.4.


Version 7.10.4
--------------

2003-04-01  Kjetil Jacobsen  <kjetilja>

        * Markus added CURLOPT_UNRESTRICTED_AUTH (libcurl-7.10.4).

2003-02-25  Kjetil Jacobsen  <kjetilja>

        * Fixed some broken test code and removed the fileupload test
        since it didn't work properly.

2003-01-28  Kjetil Jacobsen  <kjetilja>

        * Some documentation updates by Markus and me.

2003-01-22  Kjetil Jacobsen  <kjetilja>

        * API CHANGE: the CurlMulti.info_read() method now returns
        a separate array with handles that failed.  Each entry in this array
        is a tuple with (curl object, error number, error message).
        This addition makes it simpler to do error checking of individual
        curl objects when using the multi interface.


Version 7.10.3
--------------

2003-01-13  Kjetil Jacobsen  <kjetilja>

        * PycURL memory usage has been reduced.

2003-01-10  Kjetil Jacobsen  <kjetilja>

        * Added 'examples/retriever-multi.py' which shows how to retrieve
        a set of URLs concurrently using the multi interface.

2003-01-09  Kjetil Jacobsen  <kjetilja>

        * Added support for CURLOPT_HTTP200ALIASES.

2002-11-22  Kjetil Jacobsen  <kjetilja>

        * Updated pycurl documentation in the 'doc' directory.

2002-11-21  Kjetil Jacobsen  <kjetilja>

        * Updated and improved 'examples/curl.py'.

        * Added 'tests/test_multi6.py' which shows how to use the
        info_read method with CurlMulti.

2002-11-19  Kjetil Jacobsen  <kjetilja>

        * Added new method CurlMulti.info_read().


Version 7.10.2
--------------

2002-11-14  Kjetil Jacobsen <kjetilja>

        * Free options set with setopt after cleanup is called, as cleanup
        assumes that options are still valid when invoked.  This fixes the
        bug with COOKIEJAR reported by Bastiaan Naber
        <bastiaan at ricardis.tudelft.nl>.

2002-11-06  Markus F.X.J. Oberhumer <mfx>

        * Install documentation under /usr/share/doc instead of /usr/doc.
        Also, start shipping the (unfinished) HTML docs and some
        basic test scripts.

2002-10-30  Markus F.X.J. Oberhumer <mfx>

        * API CHANGE: For integral values, Curl.getinfo() now returns a
        Python-int instead of a Python-long.


Version 7.10.1
--------------

2002-10-03  Markus F.X.J. Oberhumer <mfx>

        * Added new module-level function version_info() from
        libcurl-7.10.


Version 7.10
------------

2002-09-13  Kjetil Jacobsen  <kjetilja>

        * Added commandline options to setup.py for specifying the path to
        'curl-config' (non-windows) and the curl installation directory
        (windows).  See the 'INSTALL' file for details.

        * Added CURLOPT_ENCODING, CURLOPT_NOSIGNAL and CURLOPT_BUFFERSIZE
        from libcurl-7.10 (by Markus Oberhumer).


Version 7.9.8.4
---------------

2002-08-28  Kjetil Jacobsen  <kjetilja>

        * Added a simple web-browser example based on gtkhtml and pycurl.
        See the file 'examples/gtkhtml_demo.py' for details.  The example
        requires a working installation of gnome-python with gtkhtml
        bindings enabled (pass --with-gtkhtml to gnome-python configure).

2002-08-14  Kjetil Jacobsen  <kjetilja>

        * Added new method 'select' on CurlMulti objects.  Example usage
        in 'tests/test_multi5.py'.  This method is just an optimization of
        the combined use of fdset and select.

2002-08-12  Kjetil Jacobsen  <kjetilja>

        * Added support for curl_multi_fdset.  See the file
        'tests/test_multi4.py' for example usage.  Contributed by Conrad
        Steenberg <conrad at hep.caltech.edu>.

        * perform() on multi objects now returns a tuple (result, number
        of handles) like the libcurl interface does.

2002-08-08  Kjetil Jacobsen  <kjetilja>

        * Added the 'sfquery' script which retrieves a SourceForge XML
        export object for a given project.  See the file 'examples/sfquery.py'
        for details and usage.  'sfquery' was contributed by Eric
        S. Raymond <esr at thyrsus.com>.

2002-07-20  Markus F.X.J. Oberhumer <mfx>

        * API enhancements: added Curl() and CurlMulti() as aliases for
        init() and multi_init(), and added close() methods as aliases
        for the cleanup() methods. The new names much better match
        the actual intended use of the objects, and they also nicely
        correspond to Python's file object.

        * Also, all constants for Curl.setopt() and Curl.getinfo() are now
        visible from within Curl objects.

        All changes are fully backward-compatible.


Version 7.9.8.3
---------------

2002-07-16  Markus F.X.J. Oberhumer <mfx>

        * Under Python 2.2 or better, Curl and CurlMulti objects now
        automatically participate in cyclic garbage collection
        (using the gc module).


Version 7.9.8.2
---------------

2002-07-05  Markus F.X.J. Oberhumer <mfx>

        * Curl and CurlMulti objects now support standard Python attributes.
        See tests/test_multi2.py for an example.

2002-07-02  Kjetil Jacobsen  <kjetilja>

        * Added support for the multi-interface.


Version 7.9.8.1
---------------

2002-06-25  Markus F.X.J. Oberhumer <mfx>

        * Fixed a couple of `int' vs. `size_t' mismatches in callbacks
        and Py_BuildValue() calls.

2002-06-25  Kjetil Jacobsen  <kjetilja>

        * Use 'double' type instead of 'size_t' for progress callbacks
        (by Conrad Steenberg <conrad at hep.caltech.edu>).  Also cleaned up
        some other type mismatches in the callback interfaces.

2002-06-24  Kjetil Jacobsen  <kjetilja>

        * Added example code on how to upload a file using HTTPPOST in
        pycurl (code by Amit Mongia <amit_mongia at hotmail.com>).  See the
        file 'test_fileupload.py' for details.


Version 7.9.8
-------------

2002-06-24  Kjetil Jacobsen  <kjetilja>

        * Resolved some build problems on Windows (by Markus Oberhumer).

2002-06-19  Kjetil Jacobsen  <kjetilja>

        * Added CURLOPT_CAPATH.

        * Added option constants for CURLOPT_NETRC: CURL_NETRC_OPTIONAL,
        CURL_NETRC_IGNORED and CURL_NETRC_REQUIRED.

        * Added option constants for CURLOPT_TIMECONDITION:
        TIMECOND_IFMODSINCE and TIMECOND_IFUNMODSINCE.

        * Added an simple example crawler, which downloads documents
        listed in a file with a configurable number of worker threads.
        See the file 'crawler.py' in the 'tests' directory for details.

        * Removed the redundant 'test_xmlrpc2.py' test script.

        * Disallow recursive callback invocations (by Markus Oberhumer).

2002-06-18  Kjetil Jacobsen  <kjetilja>

        * Made some changes to setup.py which should fix the build
        problems on RedHat 7.3 (suggested by Benji <benji at kioza.net>).

        * Use CURLOPT_READDATA instead of CURLOPT_INFILE, and
        CURLOPT_WRITEDATA instead of CURLOPT_FILE.  Also fixed some
        reference counting bugs with file objects.

        * CURLOPT_FILETIME and CURLINFO_FILETIME had a namespace clash
        which caused them not to work.  Use OPT_FILETIME for setopt() and
        INFO_FILETIME for getinfo().  See example usage in
        'test_getinfo.py' for details.


Version 7.9.7
-------------

2002-05-20  Kjetil Jacobsen  <kjetilja>

        * New versioning scheme.  Pycurl now has the same version number
        as the libcurl version it was built with.  The pycurl version
        number thus indicates which version of libcurl is required to run.

2002-05-17  Kjetil Jacobsen  <kjetilja>

        * Added CURLINFO_REDIRECT_TIME and CURLINFO_REDIRECT_COUNT.

2002-04-27  Kjetil Jacobsen  <kjetilja>

        * Fixed potential memory leak and thread race (by Markus
        Oberhumer).


Version 0.4.9
-------------

2002-04-15  Kjetil Jacobsen  <kjetilja>

        * Added CURLOPT_DEBUGFUNCTION to allow debug callbacks to be
        specified (see the file 'test_debug.py' for details on how to use
        debug callbacks).

        * Added CURLOPT_DNS_USE_GLOBAL_CACHE and
        CURLOPT_DNS_CACHE_TIMEOUT.

        * Fixed a segfault when finalizing curl objects in Python 1.5.2.

        * Now requires libcurl 7.9.6 or greater.

2002-04-12  Kjetil Jacobsen  <kjetilja>

        * Added 'test_post2.py' file which is another example on how to
        issue POST requests.

2002-04-11  Markus F.X.J. Oberhumer <mfx>

        * Added the 'test_post.py' file which demonstrates the use of
        POST requests.


Version 0.4.8
-------------

2002-03-07  Kjetil Jacobsen  <kjetilja>

        * Added CURLOPT_PREQUOTE.

        * Now requires libcurl 7.9.5 or greater.

        * Other minor code cleanups and bugfixes.

2002-03-05  Kjetil Jacobsen  <kjetilja>

        * Do not allow WRITEFUNCTION and WRITEHEADER on the same handle.


Version 0.4.7
-------------

2002-02-27  Kjetil Jacobsen  <kjetilja>

        * Abort callback if the thread state of the calling thread cannot
        be determined.

        * Check that the installed version of libcurl matches the
        requirements of pycurl.

2002-02-26  Kjetil Jacobsen  <kjetilja>

        * Clarence Garnder <clarence at silcom.com> found a bug where string
        arguments to setopt sometimes were prematurely deallocated, this
        should now be fixed.

2002-02-21  Kjetil Jacobsen  <kjetilja>

        * Added the 'xmlrpc_curl.py' file which implements a transport
        for xmlrpclib (xmlrpclib is part of Python 2.2).

        * Added CURLINFO_CONTENT_TYPE.

        * Added CURLOPT_SSLCERTTYPE, CURLOPT_SSLKEY, CURLOPT_SSLKEYTYPE,
        CURLOPT_SSLKEYPASSWD, CURLOPT_SSLENGINE and
        CURLOPT_SSLENGINE_DEFAULT.

        * When thrown, the pycurl.error exception is now a tuple consisting
        of the curl error code and the error message.

        * Now requires libcurl 7.9.4 or greater.

2002-02-19  Kjetil Jacobsen  <kjetilja>

        * Fixed docstring for getopt() function.

2001-12-18  Kjetil Jacobsen  <kjetilja>

        * Updated the INSTALL information for Win32.

2001-12-12  Kjetil Jacobsen  <kjetilja>

        * Added missing link flag to make pycurl build on MacOS X (by Matt
        King <matt at gnik.com>).

2001-12-06  Kjetil Jacobsen  <kjetilja>

        * Added CURLINFO_STARTTRANSFER_TIME and CURLOPT_FTP_USE_EPSV from
        libcurl 7.9.2.

2001-12-01  Markus F.X.J. Oberhumer <mfx>

        * Added the 'test_stringio.py' file which demonstrates the use of
        StringIO objects as callback.

2001-12-01  Markus F.X.J. Oberhumer <mfx>

        * setup.py: Do not remove entries from a list while iterating
        over it.

2001-11-29  Kjetil Jacobsen  <kjetilja>

        * Added code in setup.py to install on Windows.  Requires some
        manual configuration (by Tino Lange <Tino.Lange at gmx.de>).

2001-11-27  Kjetil Jacobsen  <kjetilja>

        * Improved detection of where libcurl is installed in setup.py.
        Should make it easier to install pycurl when libcurl is not
        located in regular lib/include paths.

2001-11-05  Kjetil Jacobsen  <kjetilja>

        * Some of the newer options to setopt were missing, this should
        now be fixed.

2001-11-04  Kjetil Jacobsen  <kjetilja>

        * Exception handling has been improved and should no longer throw
        spurious exceptions (by Markus F.X.J. Oberhumer
        <markus at oberhumer.com>).

2001-10-15  Kjetil Jacobsen  <kjetilja>

        * Refactored the test_gtk.py script to avoid global variables.

2001-10-12  Kjetil Jacobsen  <kjetilja>

        * Added module docstrings, terse perhaps, but better than nothing.

        * Added the 'basicfirst.py' file which is a Python version of the
        corresponding Perl script by Daniel.

        * PycURL now works properly under Python 1.5 and 1.6 (by Markus
        F.X.J. Oberhumer <markus at oberhumer.com>).

        * Allow C-functions and Python methods as callbacks (by Markus
        F.X.J. Oberhumer <markus at oberhumer.com>).

        * Allow None as success result of write, header and progress
        callback invocations (by Markus F.X.J. Oberhumer
        <markus at oberhumer.com>).

        * Added the 'basicfirst2.py' file which demonstrates the use of a
        class method as callback instead of just a function.

2001-08-21  Kjetil Jacobsen  <kjetilja>

        * Cleaned up the script with GNOME/PycURL integration.

2001-08-20  Kjetil Jacobsen  <kjetilja>

        * Added another test script for shipping XML-RPC requests which
        uses py-xmlrpc to encode the arguments (tests/test_xmlrpc2.py).

2001-08-20  Kjetil Jacobsen  <kjetilja>

        * Added test script for using PycURL and GNOME (tests/test_gtk.py).

2001-08-20  Kjetil Jacobsen  <kjetilja>

        * Added test script for using XML-RPC (tests/test_xmlrpc.py).

        * Added more comments to the test sources.

2001-08-06  Kjetil Jacobsen  <kjetilja>

        * Renamed module namespace to pycurl instead of curl.

2001-08-06  Kjetil Jacobsen  <kjetilja>

        * Set CURLOPT_VERBOSE to 0 by default.

2001-06-29  Kjetil Jacobsen  <kjetilja>

        * Updated INSTALL, curl version 7.8 or greater is now mandatory to
        use pycurl.

2001-06-13  Kjetil Jacobsen  <kjetilja>

        * Set NOPROGRESS to 1 by default.

2001-06-07  Kjetil Jacobsen  <kjetilja>

        * Added global_init/cleanup.

2001-06-06  Kjetil Jacobsen  <kjetilja>

        * Added HEADER/PROGRESSFUNCTION callbacks (see files in tests/).

        * Added PASSWDFUNCTION callback (untested).

        * Added READFUNCTION callback (untested).

2001-06-05  Kjetil Jacobsen  <kjetilja>

        * WRITEFUNCTION callbacks now work (see tests/test_cb.py for details).

        * Preliminary distutils installation.

        * Added CLOSEPOLICY constants to module namespace.

2001-06-04  Kjetil Jacobsen  <kjetilja>

        * Return -1 on error from Python callback in WRITEFUNCTION callback.

2001-06-01  Kjetil Jacobsen  <kjetilja>

        * Moved source to src and tests to tests directory.

2001-05-31  Kjetil Jacobsen  <kjetilja>

        * Added better type checking for setopt.

2001-05-30  Kjetil Jacobsen  <kjetilja>

        * Moved code to sourceforge.

        * Added getinfo support.


# vi:ts=8:et
PK���\O�X_]]&pycurl/examples/opensocketexception.pynu�[���# Exposing rich exception information from callbacks example

import pycurl, random, socket

class ConnectionRejected(Exception):
    pass

def opensocket(curl, purpose, curl_address):
    if random.random() < 0.5:
        curl.exception = ConnectionRejected('Rejecting connection attempt in opensocket callback')
        return pycurl.SOCKET_BAD
    
    family, socktype, protocol, address = curl_address
    s = socket.socket(family, socktype, protocol)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
    return s

c = pycurl.Curl()
c.setopt(c.URL, 'http://pycurl.io')
c.exception = None
c.setopt(c.OPENSOCKETFUNCTION,
    lambda purpose, address: opensocket(c, purpose, address))

try:
    c.perform()
except pycurl.error as e:
    if e.args[0] == pycurl.E_COULDNT_CONNECT and c.exception:
        print(c.exception)
    else:
        print(e)
PK���\)�����;pycurl/examples/__pycache__/ssh_keyfunction.cpython-312.pycnu�[����

���g.��8�ddlZdZej�Zej	ej
e�ej	ejd�d�Zej	ejd�ej	eje�ej�y)�Nzsftp://web.sourceforge.netTc�"�tjS)N)�c�KHSTAT_FINE)�	known_key�	found_key�matchs   �c/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/ssh_keyfunction.py�keyfunctionr
	s���=�=��z.known_hosts)�pycurl�sftp_server�Curlr�setopt�URL�VERBOSEr
�SSH_KNOWNHOSTS�SSH_KEYFUNCTION�perform�rr	�<module>rsv��
�*���F�K�K�M����������������D�������	�	�>�*�����	�	�K�(��	�	�rPK���\����Fpycurl/examples/__pycache__/multi-socket_action-select.cpython-312.pycnu�[����

���g?���ddlZddlZddlZddlmZeej�dkDrejdZndZggdddddd�Zd�Z	d�Z
d�Zej�Z
e
jeje	�e
jej e�ej"�Zejej&e�ejej(d	�ejej*d	�ejej,d�e�Zejej0e�e
j3e�e
j5ej6d�Z	ed
dk(rnedZe�ed��e
e��!e
j?e�ejA�e
jA�ed
�ed��ed
r e!deejE��z�ye!dededfz�y)�N)�BytesIO�zhttps://www.python.org)�rlist�wlist�running�timeout�result�code�msgc���|tjk(s|tjk(rtdj	|�y|tj
k(s|tjk(rtdj	|�y|tjk(rH|tdvrtdj|�|tdvrtdj|�yytd|z��)NrrzUnknown value of what: %s)	�pycurl�POLL_IN�
POLL_INOUT�state�append�POLL_OUT�POLL_REMOVE�remove�	Exception)�what�sock_fd�multi�socketps    �n/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/multi-socket_action-select.py�	socket_fnrOs����v�~�~����):�):�!:�
�g����g�&�	
����	 �D�F�,=�,=�$=�
�g����g�&�	
��#�#�	#��e�G�n�$��'�N�!�!�'�*��e�G�n�$��'�N�!�!�'�*�%��3�d�:�;�;�c	��tjtdtdttd�ttd�z|�\}}}t|�dk(rDt|�dk(r6t|�dk(r(tjtjd�\}}n�|D])}tj|tj�\}}�+|D])}tj|tj�\}}�+|D])}tj|tj�\}}�+td��tdk7r�tj�\}}}	|dk(sJ�t|�dk(rt|	�dk(st|�dk(rt|	�dk(sJ�|r	dtd<|	r dtd<|	d\}
td	<td
<td<y)NrrrrrTr	Fr
r)�selectr�set�lenr�
socket_actionr
�SOCKET_TIMEOUT�
CSELECT_IN�CSELECT_OUT�CSELECT_ERR�	info_read)r�rready�wready�xready�_rr�qmsg�	successes�failures�_easys           r�workr/\s���#�]�]�
�g���g���E�'�N�(;�c�%��.�>Q�(Q�SZ�\��F�F�F��6�{�a��C��K�1�,��V���1A�
�(�(��)>�)>��B�
��7��G��,�,�W�f�6G�6G�H�J�A�w���G��,�,�W�f�6H�6H�I�J�A�w���G��,�,�W�f�6H�6H�I�J�A�w��
�Y��#��5��3C�(C�%*�O�O�$5�!��i���q�y��y��9�~��"�s�8�}��'9��	�N�a��C��M�Q�$6�	7�7��"�E�(�O��#�E�(�O�2:�!��.�E�5��=�%��,��E�)�rc�:�|dkr
dtd<y|dztd<y)Nrrg@�@)r)�
timeout_mss r�timer_fnr2�s$���A�~� ��i��%��.��i�r�rrz0Need to poll for I/O but the timeout is not set!r	z!Script finished without a result!z'Transfer successful, retrieved %d bytesz Transfer failed with code %d: %sr
r)#�sysrr
�iorr �argv�urlrrr/r2�	CurlMultir�setopt�M_SOCKETFUNCTION�M_TIMERFUNCTION�Curl�easy�URL�CONNECTTIMEOUT�LOW_SPEED_TIME�LOW_SPEED_LIMIT�_io�	WRITEDATA�
add_handler!r"�handlesrr�
remove_handle�close�print�getvalue�rr�<module>rKs���v�
���s�x�x�=�1��

�(�(�1�+�C�
"�C��
������		��<�4�l/�	���������V�
$�
$�i�0����V�
#�
#�X�.��v�{�{�}�����F�J�J������F�!�!�1�%����F�!�!�1�%����F�"�"�A�&�
�i�����F���c�"������
�
�
�f�3�3�Q�
7����Y��1��
��	�"���?��N�O�O��W�
�����D���
�
�����
�
	��?��
�7�
8�8���?�	�
3�c�#�,�,�.�6I�
I�J�	�
,��f�
�u�U�|�/L�
L�MrPK���\�g���7pycurl/examples/__pycache__/file_upload.cpython-312.pycnu�[����

���g��	���ddlZddlZddlZGd�d�Zeej
�dkredej
dz�e�ej
dZej
dZ	ejje	�s
ede	z�e�ej�Z
e
jeje�e
jej d�	e
jej"eee	d	��j&�ejj+e	�Ze
jej.e�ed
e	�de���e
j1�e
j3�y)�Nc��eZdZd�Zd�Zy)�
FileReaderc��||_y�N)�fp)�selfrs  �_/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/file_upload.py�__init__zFileReader.__init__
s	�����c�8�|jj|�Sr)r�read)r�sizes  r	�
read_callbackzFileReader.read_callbacks���w�w�|�|�D�!�!rN)�__name__�
__module__�__qualname__r
r�rr	rr	s���"rr�z Usage: %s <url> <file to upload>��z#Error: the file '%s' does not exist�rbzUploading file z to url )�os�sys�pycurlr�len�argv�print�
SystemExit�url�filename�path�exists�Curl�c�setopt�URL�UPLOAD�READFUNCTION�openrr
�getsize�filesize�
INFILESIZE�perform�closerrr	�<module>r/s*��
�
�"�"��s�x�x�=�1��	�
,�s�x�x��{�
:�;�
��	�h�h�q�k���8�8�A�;��	�w�w�~�~�h��	�
/�(�
:�;�
���F�K�K�M��������S������������H�H�V�
 �
 �*�T�(�D�-A�"B�"P�"P�Q�
�7�7�?�?�8�$������	�	�H�%��x��5�6��	�	�����	rPK���\�}O�����3pycurl/examples/__pycache__/linksys.cpython-312.pycnu�[����

���gU���ddlZddlZddlZddlZd�ZGd�dej
�ZGd�d�Zedk(reddl	Z	ddl
Z
Gd�d	e
j�Ze�Z
ejd
dD]Ze
j!e��dZes	e
j%�dZes�yyy#e$r/ej&�d
j(\ZZed
ez�Y�9wxYw)�Nc��tjj|�tjjd�y)N�
)�sys�stderr�write)�args �[/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/linksys.py�print_stderrr
%s&���J�J���S���J�J���T��c��eZdZd�Zy)�LinksysErrorc��||_y�N)�args)�selfrs  r	�__init__zLinksysError.__init__*s	����	rN)�__name__�
__module__�__qualname__r�rr	r
r
)s��rr
c��eZdZdZdZdZdZdZdZdZ	dd	d
ddd
�Z
d�Zd�Zd�Z
d�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd �Zd!�Zd"�Zd#�Z d$�Z!d%�Z"d&�Z#d'�Z$d(�Z%d)�Z&d*�Z'd+�Z(d,�Z)d-�Z*d.�Z+d/�Z,d0�Z-d1�Z.d2�Z/y3)4�LinksysSessionz/Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec�1�2�3�4�5�6zbasic setup functionszFor security reasons,z-You can configure the router to act as a DHCPz3There are some log settings and lists in this page.z5Port forwarding can be used to set up public services)��
Passwd.htmz	DHCP.htmlzLog.htmlzForward.htmc�<�g|_d|_d|_i|_y)Nzhttp://192.168.1.1F)�actions�host�	verbosity�	pagecache�rs r	rzLinksysSession.__init__As�����(��	������rc��||_yr)r$�r�flags  r	�
set_verbosityzLinksysSession.set_verbosityGs	����rc��||jvr�tj|j�}|j	|j
�|j
|�|j�|j|<|jd�rtdd��|jtj|�sD|j|=tdtjj|j|�zd��|j�yy)N�401zauthorization failure.Tz!check string for page %s missing!F)r%�curl�Curlr#r*r$�get�body�answeredr
r�
check_strings�os�path�join�close)r�page�fetchs   r	�
cache_loadzLinksysSession.cache_loadLs����t�~�~�%��I�I�d�i�i�(�E�������/��I�I�d�O�#(�:�:�<�D�N�N�4� ��~�~�e�$�"�#;�T�B�B��^�^�N�$@�$@��$F�G��N�N�4�(�"�#F������VZ�V_�V_�ae�If�#f�hm�n�n��K�K�M�&rc��i|_yr)r%r&s r	�cache_flushzLinksysSession.cache_flushXs	����rc��|j|�tj|�j|j|�}|r|jd�}|Sd}|S�N�)r9�re�compile�searchr%�group)rr7�template�match�results     r	�
screen_scrapezLinksysSession.screen_scrape\sS��������
�
�8�$�+�+�D�N�N�4�,@�A����[�[��^�F��
��F��
rc�,�|jd|dz�S)Nrz:[^M]*\(MAC Address: *([^)]*))rF)rr7�prefixs   r	�get_MAC_addresszLinksysSession.get_MAC_addressds���!�!�"�f�-M�&M�N�Nrc�~�|r|jj||d�y|jj||d�y)Nr�0�r"�append)rr7r)�values    r	�set_flagzLinksysSession.set_flagfs1����L�L����d�C�0��L�L����d�C�0rc
��d}|jd�D]5}|jjdd|t|dz�z|f�|dz
}�7y)Nr�.r�F1r>)�splitr"rM�repr)rr7�cgi�role�ip�ind�octets       r	�set_IP_addresszLinksysSession.set_IP_addressksJ�����X�X�c�]�E��L�L����T�4�$�s�1�u�+�+=�u� E�F��1�H�C�#rc�N�|jddtjzdz�S)Nrz
>([0-9.v]*, (z)[^<]*)<)rFr�monthsr&s r	�get_firmware_versionz#LinksysSession.get_firmware_versionrs4���!�!�"�o�"0�"7�"7�'8�:D�'E�H�	Hrc�&�|jdd�S)NrzLAN IP Address�rIr&s r	�get_LAN_MACzLinksysSession.get_LAN_MACxs���#�#�B�(9�:�:rc�&�|jdd�S)Nr�Wirelessr_r&s r	�get_Wireless_MACzLinksysSession.get_Wireless_MACzs���#�#�B��4�4rc�&�|jdd�S)NrzWAN Connection Typer_r&s r	�get_WAN_MACzLinksysSession.get_WAN_MAC|s���#�#�B�(>�?�?rc�@�|jjdd|f�y)Nr�hostNamerL�r�names  r	�
set_host_namezLinksysSession.set_host_name�s�������R��T�2�3rc�@�|jjdd|f�y)Nr�
DomainNamerLrhs  r	�set_domain_namezLinksysSession.set_domain_name�s�������R��t�4�5rc�*�|jdd|�y)Nr�ipAddr�rZ�rrWs  r	�
set_LAN_IPzLinksysSession.set_LAN_IP�s�����B��"�-rc��|jd�st�|jd�d}|dvrt�|jj	dd|�y)Nz255.255.255.rQ���)rK�128�192�240�252r�netMask)�
startswith�
ValueErrorrSr"rM)rrW�lastquads   r	�set_LAN_netmaskzLinksysSession.set_LAN_netmask�sK���}�}�^�,����8�8�C�=��$���<�<��������B�	�8�4rc�(�|jdd�y)Nr�wirelessStatus�rOr(s  r	�set_wirelesszLinksysSession.set_wireless�s���
�
�b�*�+rc�@�|jjdd|f�y)Nr�
wirelessESSIDrL)r�ssids  r	�set_SSIDzLinksysSession.set_SSID�s�������R��$�7�8rc�(�|jdd�y)Nr�
broadcastSSIDr�r(s  r	�set_SSID_broadcastz!LinksysSession.set_SSID_broadcast�s���
�
�b�/�*rc�@�|jjdd|f�y)Nr�wirelessChannelrL)r�channels  r	�set_channelzLinksysSession.set_channel�s�������R�!2�G�<�=rc�(�|jdd�y)Nr�WepTyper�r(s  r	�set_WEPzLinksysSession.set_WEP�s���
�
�b�)�$rc�@�|jjdd|f�y)Nr�WANConnectionTyperL)r�types  r	�set_connection_typez"LinksysSession.set_connection_type�s�������R�!4�d�;�<rc�*�|jdd|�y)Nr�aliasIPrprqs  r	�
set_WAN_IPzLinksysSession.set_WAN_IP�s�����B�	�2�.rc�*�|jdd|�y)Nr�aliasMaskIPrprqs  r	�set_WAN_netmaskzLinksysSession.set_WAN_netmask�s�����B�
�r�2rc�*�|jdd|�y)Nr�routerIPrprqs  r	�set_WAN_gateway_addressz&LinksysSession.set_WAN_gateway_address�s�����B�
�B�/rc�6�|jddd|z|�y)Nr�dns�ABCrp�r�indexrWs   r	�set_DNS_serverzLinksysSession.set_DNS_server�s�����B���e�� 4�b�9rc�x�|jjdd|�|jjdd|�y)Nr �	sysPasswd�sysPasswdConfirmrL)r�strs  r	�set_passwordzLinksysSession.set_password�s/�������L��c�:������L�);�S�Arc�(�|jdd�y)Nr �	UPnP_Workr�r(s  r	�set_UPnPzLinksysSession.set_UPnP�s���
�
�l�K�0rc�<�|jjdd�y)Nr �FactoryDefaultsrLr&s r	�resetzLinksysSession.reset�s�������L�*;�<rc�~�|r|jjddd�y|jjddd�y)N�DHCP.htm�
dhcpStatus�Enable�DisablerLr(s  r	�set_DHCPzLinksysSession.set_DHCP�s1����L�L���
�<��A��L�L���
�<�	�Brc�P�|jjddt|��y)Nr��dhcpS4�r"rMr��r�vals  r	�set_DHCP_starting_IPz#LinksysSession.set_DHCP_starting_IP�s�������J�x��S��:rc�P�|jjddt|��y)Nr��dhcpLenr�r�s  r	�set_DHCP_userszLinksysSession.set_DHCP_users�s�������J�y�#�c�(�;rc�P�|jjddt|��y)Nr��	leaseTimer�r�s  r	�set_DHCP_lease_timez"LinksysSession.set_DHCP_lease_time���������J�{�C��H�=rc�6�|jddd|z|�y)Nr�r�r�rpr�s   r	�set_DHCP_DNS_serverz"LinksysSession.set_DHCP_DNS_server�s�����J���e��(<�b�Arc�~�|r|jjddd�y|jjddd�y)NzLog.htm�rLogr�r�rLr(s  r	�set_loggingzLinksysSession.set_logging�s1����L�L���	�6�8�<��L�L���	�6�9�=rc�P�|jjddt|��y)Nr��	trapAddr3r�r�s  r	�set_log_addresszLinksysSession.set_log_address�r�rc
�F�|j�rg}|j�|jD]�\}}}|j|�|j|j	|�dk(r;td|�dtjj|j|��d���t|j||f���g|_tj|j�}|j|j�|jdt!|��|j#�yy)z+Write configuration changes to the Linksys.rtzlinksys: field z" not found where expected in page �!z
Gozila.cgiN)r"r;r9r%�findr
r3r4r5r#rMr-r.r*r$r/�tupler6)r�fieldsr7�fieldrN�transactions      r	�	configurezLinksysSession.configure�s����<�<��F�����(,���$��u�e�����%��>�>�$�'�,�,�U�3�r�9� �]b�df�dk�dk�dp�dp�qu�qz�qz�}A�eB�"C�D���M�M�5�%�.�1�
)5��D�L��)�)�D�I�I�.�K��%�%�d�n�n�5��O�O�L�%��-�8�����!rN)0rrrr\�WAN_CONNECT_AUTO�WAN_CONNECT_STATIC�WAN_CONNECT_PPOE�WAN_CONNECT_RAS�WAN_CONNECT_PPTP�WAN_CONNECT_HEARTBEATr2rr*r9r;rFrIrOrZr]r`rcrerjrmrrr}r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�rrr	rr-s��
>�F��������O�����
.�-�E�K�M�
�M���
���O�1�
�H�;�5�@�4�6�.�5�,�9�+�>�%�=�/�3�0�:�B�1�=�C�
;�<�>�B�
>�
>� rr�__main__c���eZdZdZd�Zd�Zd�Zd�Zd�Zd�Z	d�Z
d	�Zd
�Zd�Z
d�Zd
�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Zd�Z d�Z!d �Z"d!�Z#d"�Z$d#�Z%d$�Z&d%�Z'd&�Z(d'�Z)d(�Z*d)�Z+d*�Z,d+�Z-d,�Z.d-�Z/d.�Z0d/�Z1d0�Z2d1�Z3d2�Z4d3�Z5d4�Z6d5�Z7d6�Z8d7�Z9d8�Z:d9�Z;d:�Z<d;�Z=d<�Z>d=�Z?d>�Z@d?�ZAd@�ZBdA�ZCdB�ZDdC�ZEdD�ZFdE�ZGdF�ZHdG�ZIdH�ZJdI�ZKdJ�ZLyK)L�LinksysInterpreterz:Interpret commands to perform LinkSys programming actions.c��tjj|�t�|_tjd�r*td�|jjdz|_	yd|_	td�y)NrzType ? or `help' for help.�: r�Bar1)
�cmd�Cmdrr�sessionr3�isatty�printr#�promptr&s r	rzLinksysInterpreter.__init__�sV���G�G���T�"�)�+�D�L��y�y��|��2�3�"�l�l�/�/�$�6��� ����f�
rc��|j�dvr	|d�y|j�dvr	|d�ytd�y)N)�on�enable�yesT)�off�disable�noFzlinksys: unknown switch valuer)�stripr
)r�func�lines   r	�flag_commandzLinksysInterpreter.flag_command�sH���z�z�|�6�6��T�
�
�	����!9�9��U����<�=�rc��|j�}|rJ||j_|jj�|jjdz|_yt|jj�y)Nr�r)r�r�r#r;r�r�)rr��newhosts   r	�
do_connectzLinksysInterpreter.do_connect�s^���j�j�l�G��$+����!����(�(�*�"�l�l�/�/�$�6�����d�l�l�'�'�(�rc�F�td�td�td�y)Nz!Usage: connect [<hostname-or-IP>]z+Connect to a Linksys by name or IP address.z0If no argument is given, print the current host.�r�r&s r	�help_connectzLinksysInterpreter.help_connect�s���5�6��?�@��D�Erc��|jjd�d|jjvr�td|jj	��td|jj��td|jj
��td|jj��td�y)Nrz	Firmware:zLAN MAC:z
Wireless MAC:zWAN MAC:rQr)r�r9r%r�r]r`rcre�rr�s  r	�	do_statuszLinksysInterpreter.do_statuss����L�L�#�#�B�'��T�\�\�+�+�+��k�4�<�<�#D�#D�#F�G��j�$�,�,�":�":�"<�=��o�t�|�|�'D�'D�'F�G��j�$�,�,�":�":�"<�=��c�
�rc�\�td�td�td�td�y)Nz
Usage: statusz3The status command shows the status of the Linksys.z2It is mainly useful as a sanity check to make surez the box is responding correctly.r�r&s r	�help_statuszLinksysInterpreter.help_statuss%���/�"��G�H��F�G��4�5rc�P�|j|jj|�yr)r�r�r*r�s  r	�
do_verbosezLinksysInterpreter.do_verboses�����d�l�l�8�8�$�?rc�0�td�td�y)Nz-Usage: verbose {on|off|enable|disable|yes|no}z!Enables display of HTTP requests.r�r&s r	�help_verbosezLinksysInterpreter.help_verboses���A�B��5�6rc�:�|jj|�y�Nr)r�rjr�s  r	�do_hostzLinksysInterpreter.do_hosts���L�L�&�&�t�,�rc�0�td�td�y)NzUsage: host <hostname>z-Sets the Host field to be queried by the ISP.r�r&s r	�	help_hostzLinksysInterpreter.help_hosts���*�+��A�Brc�P�td�|jj|�y)NzUsage: host <domainname>r)r�r�rmr�s  r	�	do_domainzLinksysInterpreter.do_domains ���,�-��L�L�(�(��.�rc��td�y)Nz/Sets the Domain field to be queried by the ISP.r�r&s r	�help_domainzLinksysInterpreter.help_domain#s���C�Drc�:�|jj|�yr�)r�rrr�s  r	�do_lan_addressz!LinksysInterpreter.do_lan_address&����L�L�#�#�D�)�rc�0�td�td�y)NzUsage: lan_address <ip-address>zSets the LAN IP address.r�r&s r	�help_lan_addressz#LinksysInterpreter.help_lan_address)����3�4��,�-rc�:�|jj|�yr�)r�r}r�s  r	�do_lan_netmaskz!LinksysInterpreter.do_lan_netmask-����L�L�(�(��.�rc�0�td�td�y)NzUsage: lan_netmask <ip-mask>�Sets the LAN subnetwork mask.r�r&s r	�help_lan_netmaskz#LinksysInterpreter.help_lan_netmask0����0�1��1�2rc�P�|j|jj|�yr�)r�r�r�r�s  r	�do_wirelesszLinksysInterpreter.do_wireless4s�����d�l�l�7�7��>�rc�0�td�td�y)Nz.Usage: wireless {on|off|enable|disable|yes|no}z.Switch to enable or disable wireless features.r�r&s r	�
help_wirelessz LinksysInterpreter.help_wireless7s���B�C��B�Crc�:�|jj|�yr�)r�r�r�s  r	�do_ssidzLinksysInterpreter.do_ssid;s���L�L�!�!�$�'�rc�0�td�td�y)NzUsage: ssid <string>z.Sets the SSID used to control wireless access.r�r&s r	�	help_ssidzLinksysInterpreter.help_ssid>s���(�)��B�Crc�P�|j|jj|�yr�)r�r�r�r�s  r	�do_ssid_broadcastz$LinksysInterpreter.do_ssid_broadcastBs�����d�l�l�=�=�t�D�rc�0�td�td�y)Nz4Usage: ssid_broadcast {on|off|enable|disable|yes|no}z+Switch to enable or disable SSID broadcast.r�r&s r	�help_ssid_broadcastz&LinksysInterpreter.help_ssid_broadcastEs���H�I��?�@rc�:�|jj|�yr�)r�r�r�s  r	�
do_channelzLinksysInterpreter.do_channelIs���L�L�$�$�T�*�rc�0�td�td�y)NzUsage: channel <number>zSets the wireless channel.r�r&s r	�help_channelzLinksysInterpreter.help_channelLs���+�,��.�/rc�P�|j|jj|�yr�)r�r�r�r�s  r	�do_wepzLinksysInterpreter.do_wepPs�����d�l�l�2�2�D�9�rc�0�td�td�y)Nz)Usage: wep {on|off|enable|disable|yes|no}z)Switch to enable or disable WEP security.r�r&s r	�help_wepzLinksysInterpreter.help_wepSs���=�>��=�>rc���	td|j�j�z�}|jj	|�y#t
$rt
d�YywxYw)NzLinksysSession.WAN_CONNECT_z!linksys: unknown connection type.r)�evalr��upperr�r�r{r
)rr�r�s   r	�do_wan_typezLinksysInterpreter.do_wan_typeWs]��
B��7��
�
��8J�8J�8L�L�M�����0�0��6����
B��@�A��
B�s�AA�A�Ac�0�td�td�y)Nz5Usage: wan_type {auto|static|ppoe|ras|pptp|heartbeat}zSet the WAN connection type.r�r&s r	�
help_wan_typez LinksysInterpreter.help_wan_type^s���I�J��0�1rc�:�|jj|�yr�)r�r�r�s  r	�do_wan_addressz!LinksysInterpreter.do_wan_addressbrrc�0�td�td�y)NzUsage: wan_address <ip-address>zSets the WAN IP address.r�r&s r	�help_wan_addressz#LinksysInterpreter.help_wan_addresserrc�:�|jj|�yr�)r�r�r�s  r	�do_wan_netmaskz!LinksysInterpreter.do_wan_netmaskirrc�0�td�td�y)NzUsage: wan_netmask <ip-mask>zSets the WAN subnetwork mask.r�r&s r	�help_wan_netmaskz#LinksysInterpreter.help_wan_netmasklrrc�:�|jj|�yr�)r��set_WAN_gatewayr�s  r	�do_wan_gatewayz!LinksysInterpreter.do_wan_gatewayprrc�0�td�td�y)NzUsage: wan_gateway <ip-address>rr�r&s r	�help_wan_gatewayz#LinksysInterpreter.help_wan_gatewayss���3�4��1�2rc��|j�\}}|dvr&|jjt|�|�yt	d�y�N)rrrz$linksys: server index out of bounds.r)rSr�r�r(r
�rr�r��addresss    r	�do_dnszLinksysInterpreter.do_dnswsE��#�z�z�|��U�G���'����+�+�D��K��A���C�D�rc�0�td�td�y)NzUsage: dns {1|2|3} <ip-mask>z:Sets a primary, secondary, or tertiary DNS server address.r�r&s r	�help_dnszLinksysInterpreter.help_dns~s���0�1��N�Orc�:�|jj|�yr�)r�r�r�s  r	�do_passwordzLinksysInterpreter.do_password�s���L�L�%�%�d�+�rc�0�td�td�y)NzUsage: password <string>zSets the router password.r�r&s r	�
help_passwordz LinksysInterpreter.help_password�s���,�-��-�.rc�P�|j|jj|�yr�)r�r�r�r�s  r	�do_upnpzLinksysInterpreter.do_upnp�s�����d�l�l�3�3�T�:�rc�0�td�td�y)Nz*Usage: upnp {on|off|enable|disable|yes|no}z4Switch to enable or disable Universal Plug and Play.r�r&s r	�	help_upnpzLinksysInterpreter.help_upnp�s���>�?��H�Irc�8�|jj�yr)r�r�r�s  r	�do_resetzLinksysInterpreter.do_reset�s���L�L��� rc�0�td�td�y)NzUsage: resetz+Reset Linksys settings to factory defaults.r�r&s r	�
help_resetzLinksysInterpreter.help_reset�s���.�!��?�@rc�P�|j|jj|�yr)r�r�r�r�s  r	�do_dhcpzLinksysInterpreter.do_dhcp�s�����d�l�l�3�3�T�:rc�0�td�td�y)Nz*Usage: dhcp {on|off|enable|disable|yes|no}z*Switch to enable or disable DHCP features.r�r&s r	�	help_dhcpzLinksysInterpreter.help_dhcp�s���>�?��>�?rc�:�|jj|�yr)r�r�r�s  r	�
do_dhcp_startz LinksysInterpreter.do_dhcp_start�s���L�L�-�-�d�3rc�0�td�td�y)NzUsage: dhcp_start <number>z'Set the start address of the DHCP pool.r�r&s r	�help_dhcp_startz"LinksysInterpreter.help_dhcp_start�s���.�/��;�<rc�:�|jj|�yr)r�r�r�s  r	�
do_dhcp_usersz LinksysInterpreter.do_dhcp_users�����L�L�'�'��-rc�0�td�td�y)NzUsage: dhcp_users <number>�9Set number of address slots to allocate in the DHCP pool.r�r&s r	�help_dhcp_usersz"LinksysInterpreter.help_dhcp_users�����.�/��M�Nrc�:�|jj|�yr)r��set_DHCP_leaser�s  r	�
do_dhcp_leasez LinksysInterpreter.do_dhcp_lease�rWrc�0�td�td�y)NzUsage: dhcp_lease <number>rYr�r&s r	�help_dhcp_leasez"LinksysInterpreter.help_dhcp_lease�r[rc��|j�\}}|dvr&|jjt|�|�yt	d�yr;)rSr�r�r(r
r<s    r	�do_dhcp_dnszLinksysInterpreter.do_dhcp_dns�sE��#�z�z�|��U�G���'����0�0��e��g�F���C�D�rc�0�td�td�y)Nz!Usage: dhcp_dns {1|2|3} <ip-mask>z8Sets primary, secondary, or tertiary DNS server address.r�r&s r	�
help_dhcp_dnsz LinksysInterpreter.help_dhcp_dns�s���5�6��L�Mrc�P�|j|jj|�yr)r�r�r�r�s  r	�
do_loggingzLinksysInterpreter.do_logging�s�����d�l�l�6�6��=rc�0�td�td�y)Nz-Usage: logging {on|off|enable|disable|yes|no}z,Switch to enable or disable session logging.r�r&s r	�help_loggingzLinksysInterpreter.help_logging�s���A�B��@�Arc�:�|jj|�yr)r��set_Log_addressr�s  r	�do_log_addressz!LinksysInterpreter.do_log_address�s���L�L�(�(��.rc�0�td�td�y)NzUsage: log_address <number>z1Set the last quad of the address to which to log.r�r&s r	�help_log_addressz#LinksysInterpreter.help_log_address�s���/�0��E�Frc�8�|jj�yr�)r�r�r�s  r	�do_configurezLinksysInterpreter.do_configure�s���L�L�"�"�$�rc�0�td�td�y)NzUsage: configurez(Writes the configuration to the Linksys.r�r&s r	�help_configurez!LinksysInterpreter.help_configure�s���$�%��<�=rc�B�t|jj�yr)r�r�r%r�s  r	�do_cachezLinksysInterpreter.do_cache�s���$�,�,�(�(�)rc�0�td�td�y)NzUsage: cachezDisplay the page cache.r�r&s r	�
help_cachezLinksysInterpreter.help_cache�s���.�!��+�,rc��yr=rr�s  r	�do_quitzLinksysInterpreter.do_quit�s��rc�0�td�td�y)Nz2The quit command ends your linksys session withoutz-writing configuration changes to the Linksys.r�r�s  r	�	help_quitzLinksysInterpreter.help_quit�s���F�G��A�Brc�N�td�|jj�y)Nrr>)r�r�r�r�s  r	�do_EOFzLinksysInterpreter.do_EOF�s���"�I��L�L�"�"�$�rc�0�td�td�y)Nz7The EOF command writes the configuration to the linksyszand ends your session.r�r&s r	�help_EOFzLinksysInterpreter.help_EOF�s���K�L��*�+rc�.�tj|�y)z5Pass the command through to be executed by the shell.r)r3�systemr�s  r	�defaultzLinksysInterpreter.default�s���I�I�d�O�rc�0�td�td�y)Nz/On-line help is available through this command.z"? is a convenience alias for help.r�r&s r	�	help_helpzLinksysInterpreter.help_help�s���C�D��6�7rc��td�y)Na�
This program supports changing the settings on Linksys blue-box routers.  This
capability may come in handy when they freeze up and have to be reset.  Though
it can be used interactively (and will command-prompt when standard input is a
terminal) it is really designed to be used in batch mode. Commands are taken
from the command line first, then standard input.

By default, it is assumed that the Linksys is at http://192.168.1.1, the
default LAN address.  You can connect to a different address or IP with the
'connect' command.  Note that your .netrc must contain correct user/password
credentials for the router.  The entry corresponding to the defaults is:

machine 192.168.1.1
    login ""
    password admin

Most commands queue up changes but don't actually send them to the Linksys.
You can force pending changes to be written with 'configure'.  Otherwise, they
will be shipped to the Linksys at the end of session (e.g.  when the program
running in batch mode encounters end-of-file or you type a control-D).  If you
end the session with `quit', pending changes will be discarded.

For more help, read the topics 'wan', 'lan', and 'wireless'.r�r&s r	�help_introductionz$LinksysInterpreter.help_introduction�s���@�
Arc��td�y)Nz�The `lan_address' and `lan_netmask' commands let you set the IP location of
the Linksys on your LAN, or inside.  Normally you'll want to leave these
untouched.r�r&s r	�help_lanzLinksysInterpreter.help_lans����
rc��td�y)Na{The WAN commands become significant if you are using the BEFSR41 or any of
the other Linksys boxes designed as DSL or cable-modem gateways.  You will
need to use `wan_type' to declare how you expect to get your address.

If your ISP has issued you a static address, you'll need to use the
`wan_address', `wan_netmask', and `wan_gateway' commands to set the address
of the router as seen from the WAN, the outside. In this case you will also
need to use the `dns' command to declare which remote servers your DNS
requests should be forwarded to.

Some ISPs may require you to set host and domain for use with dynamic-address
allocation.r�r&s r	�help_wanzLinksysInterpreter.help_wans����
rc��td�y)NzWThe channel, ssid, ssid_broadcast, wep, and wireless commands control
wireless routing.r�r&s r	�help_wireless_descz%LinksysInterpreter.help_wireless_descs����
rc�F�td�td�td�y)Nz8Switches may be turned on with 'on', 'enable', or 'yes'.z:Switches may be turned off with 'off', 'disable', or 'no'.z2Switch commands include: wireless, ssid_broadcast.r�r&s r	�
help_switchesz LinksysInterpreter.help_switchess���L�M��N�O��F�Grc�F�td�td�td�y)Nz/An address argument must be a valid IP address;z-four decimal numbers separated by dots, each zbetween 0 and 255.r�r&s r	�help_addressesz!LinksysInterpreter.help_addresses!s���C�D��A�B��&�'rc��yrrr&s r	�	emptylinezLinksysInterpreter.emptyline&s��rN)Mrrr�__doc__rr�r�r�r�r�r�r�r�rrrrr
r
rrrrrrrr r"r$r&r*r,r.r0r2r4r7r9r>r@rBrDrFrHrJrLrNrPrRrTrVrZr^r`rbrdrfrhrkrmrorqrsrurwryr{r}r�r�r�r�r�r�r�r�r�rrr	r�r��s���H�	�	�	�	F�
	�	6�	@�	7�	�	C�	�	E�	�	.�	�	3�	�	D�	�	D�	�	A�	�	0�	�	?�	�	2�	�	.�	�	3�	�	3�	�	P�	�	/�	�	J�	!�	A�	;�	@�	4�	=�	.�	O�	.�	O�	�	N�	>�	B�	/�	G�	�	>�	*�	-�	�	C�	�	,�	�
	8�	A�4	�
	�	�
	H�
	(�
	rr�r>FTz	linksys: )rr?r-�
exceptionsr
�	Exceptionr
rrr3r�r�r��interpreter�argvr�onecmd�fatal�cmdloop�exc_infor�messager�rrr	�<module>r�s���F!� ���:�'�'��n �n �`�z���G�S�W�W�G�R
%�&�K��x�x���|�����3����E��	)����!��E��a
��h
�	)�)�S�\�\�^�A�.�3�3�N�G�U��+��'�(�	)�s�B�1C�CPK���\�x���7pycurl/examples/__pycache__/xmlrpc_curl.cpython-312.pycnu�[����

���g���0�	ddlZddlmZmZejee�	ddlmZ	ddlZddl
Z
ddlZejddkDZ
Gd�dej�Zedk(rDej"de��	�Zee�	eej(j+d
��yy#e$rY��wxYw#e$r	ddlmZn#e$r	ddlmZYnwxYwY��wxYw#e$r	ddlm	ZY��wxYw#ej,$r ej.�dZede�YywxYw)
�N)�SIGPIPE�SIG_IGN)�StringIO�c�&�eZdZdZdgZdd�Zdd�Zy)�
CURLTransportz5Handles a cURL HTTP transaction to an XML-RPC server.zContent-Type: text/xmlNc��tj�|_|jjtjd�|jjtj
d�|jjtjd�|jjtj|j�|dk7r4|dk7r/|jjtj|�d|���d|_
y)N���:F)�pycurl�Curl�c�setopt�POST�NOSIGNAL�CONNECTTIMEOUT�
HTTPHEADER�xmlrpc_h�USERPWD�
_use_datetime)�self�username�passwords   �_/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/xmlrpc_curl.py�__init__zCURLTransport.__init__%s������������
�
�f�k�k�1�%����
�
�f�o�o�q�)����
�
�f�+�+�R�0����
�
�f�'�'����7��t���D� 0��F�F�M�M�&�.�.�X�x�*H�I�"���c���t�}|jjtjd|�|���|jjtj
|�|jjtj|j�|jjtj|�||_		|jj�|j%d�|j'|�S#tj$rKtj�d}tr|j}t!j"||z|d|dd��wxYw)Nzhttp://r
r)rrrr
�URL�
POSTFIELDS�
WRITEFUNCTION�write�VERBOSE�verbose�perform�error�sys�exc_info�PY3�args�	xmlrpclib�
ProtocolError�seek�parse_response)r�host�handler�request_bodyr$�b�vs       r�requestzCURLTransport.request/s���J�����
�
�f�j�j�4��"A�B����
�
�f�'�'��6����
�
�f�*�*�A�G�G�4����
�
�f�n�n�g�.����		��6�6�>�>��	
���q�	��"�"�1�%�%���|�|�	�����q�!�A���F�F���)�)��w���!��a��d�D��
�		�s
�
D�AE$)NN)r)�__name__�
__module__�__qualname__�__doc__rrr4�rrrr s��?�)�+�H�#�&rr�__main__zhttp://betty.userland.com)�	transport�)r
�ERROR)�signalrr�ImportError�	cStringIOr�ior+�
xmlrpc.client�clientr
r'�version_infor)�	Transportrr5�ServerProxy�server�print�examples�getStateName�Errorr(r3r9rr�<module>rLs1��$��'��F�M�M�'�7�#� �"�&���
�	���q��A���!&�I�'�'�!&�H�z��
"�Y�
"�
"�#>�-:�_�>�F�	�&�M��
�f�o�o�*�*�2�.�/�
��u�	��	��� � �%��� �� �� ���&�%�&��f�?�?���C�L�L�N�1���
�g�q���si�B&�B1�C�!C&�&B.�-B.�1C�7B>�=C�>C�	C�C�C�C�C#�"C#�&,D�DPK���\�^�p��0pycurl/examples/__pycache__/smtp.cpython-312.pycnu�[����

���gK��d�ddlmZddlZ	ddlmZddlZejddkDZ	dezZ
dZd	Zej�Zejej e
�ejej"e�ejej$eg�d
e�de�d�Ze	rej)d
�Zee�Zejej*e�ejej,d�ejej.d�ej1�y#e$r
ddlmZY��wxYw)�)�	localhost�N)�BytesIO)�StringIO�z	smtp://%szsender@example.orgzaddressee@example.netzFrom: z
To: z7
Subject: PycURL SMTP example

SMTP example via PycURL
�asciiT)�r�pycurl�ior�ImportErrorr�sys�version_info�PY3�mail_server�	mail_from�mail_to�Curl�c�setopt�URL�	MAIL_FROM�	MAIL_RCPT�message�encode�READDATA�UPLOAD�VERBOSE�perform���X/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/smtp.py�<module>r"s��
�
�-���	���q��A����I�%�� �	�
!���F�K�K�M����������������i� �������w�i� ��'�
����n�n�W�%�G�
�W���������R��������4��������D���	�	���K�-�,�,�-�s�D � D/�.D/PK���\���M;pycurl/examples/__pycache__/retriever-multi.cpython-312.pycnu�[����

���g
�	�	�ddlZddlZ	ddlZddlmZmZejee�dZ	ejddk(rejj�Z
n#eejd�j�Z
eej�dk\re
ejd�ZgZe
D]>Zej%�Zeredd	k(r�d
ee�dzzZej)eef��@esJd��ee�Zeee�ZdecxkrdksJd
��Jd
��edej.ej0fz�ededed�ej2�Zge_ee�D]�Zej<�Zde_ ejCejDd�ejCejFd�ejCejHd�ejCejJd�ejCejLd�ej6j)e���ej6ddZ'dZ(e(ek�r�er�e'r�ejSd�\ZZe'jS�Zeed�e_ ejCejTe�ejCejVej@�ejYe�ee_ee_ere'r��	ej[�\Z.Z/e.ej`k7rn�$	ejc�\Z2Z3Z4e3D]}Zej@jk�de_ ejme�edej&ej"ejoejp��e'j)e��e4D]i\ZZ9Z:ej@jk�de_ ejme�edej&ej"e9e:�e'j)e��ke(ee3�zee4�zZ(e2dk(rn�� ejwd�e(ekr���ej6D]?Zej@�!ej@jk�de_ ejk��Aejk�y#e$rY��wxYw#edejdz�e�xYw)�N)�SIGPIPE�SIG_IGN�
��-��zCUsage: %s <file with URLs to fetch> [<# of concurrent connections>]�#zdoc_%03d.datz
no URLs giveni'z(invalid number of concurrent connectionsz!PycURL %s (compiled against 0x%x)z
----- Gettingz
URLs usingzconnections -----��i,�wbzSuccess:zFailed: g�?)<�sys�pycurl�signalrr�ImportError�num_conn�argv�stdin�	readlines�urls�open�len�int�print�
SystemExit�queue�url�strip�filename�append�num_urls�min�version�COMPILE_LIBCURL_VERSION_NUM�	CurlMulti�m�handles�range�i�Curl�c�fp�setopt�FOLLOWLOCATION�	MAXREDIRS�CONNECTTIMEOUT�TIMEOUT�NOSIGNAL�freelist�
num_processed�pop�URL�	WRITEDATA�
add_handle�perform�ret�num_handles�E_CALL_MULTI_PERFORM�	info_read�num_q�ok_list�err_list�close�
remove_handle�getinfo�
EFFECTIVE_URL�errno�errmsg�select���c/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/retriever-multi.py�<module>rKsO���
�$��'��F�M�M�'�7�#�
��	�
�x�x��{�c���y�y�"�"�$���C�H�H�Q�K� �*�*�,��
�3�8�8�}����s�x�x��{�#��	���C�

�)�)�+�C��#�a�&�C�-����U��a��0�H�	�L�L�#�x��!��
��o��u��u�:���x��"���H����I�I�I��I�I�I���)�V�^�^�V�=_�=_�,`�`�a��o�x��x�9L�M��F�������	�	�x��A�����
�A��A�D��H�H�V�
"�
"�A�&��H�H�V�
�
�q�!��H�H�V�
"�
"�B�'��H�H�V�^�^�S�!��H�H�V�_�_�a� ��I�I���Q��
�
�9�9�Q�<���
��h��
�H��	�	�!��
��X��L�L�N���H�d�#���	������S�!�	����!�!�1�4�4�(�	���Q����
�����H���9�9�;���[��&�-�-�-���
�#$�;�;�=� ��w���A�
�D�D�J�J�L��A�D�
�O�O�A���*�a�j�j�!�%�%����6�;O�;O�1P�Q��O�O�A���!)��A�u�f�
�D�D�J�J�L��A�D�
�O�O�A���*�a�j�j�!�%�%���?��O�O�A��!)�&��G��4�s�8�}�D�
��A�:��!�(�H�H�S�M�M�h��T
���A��t�t��	���
�
������G�G�I�	
�
���	��Q�	��	�� �	�
O�RU�RZ�RZ�[\�R]�
]�^�
��s�R �A:R,� R)�(R)�,SPK���\{*�W6pycurl/examples/__pycache__/basicfirst.cpython-312.pycnu�[����

���g!���ddlZddlZejddkDZGd�d�Zej
j
dejz�e�Zej�Z
e
je
jd�e
je
jej�e
j�e
j!�eej$�y)�N�c��eZdZd�Zd�Zy)�Testc�`�d|_tr!|jjd�|_yy)N��ascii)�contents�PY3�encode)�selfs �^/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/basicfirst.py�__init__z
Test.__init__s'����
�� �M�M�0�0��9�D�M��c�,�|j|z|_y)N)r	)r�bufs  r
�
body_callbackzTest.body_callbacks���
�
��+��
rN)�__name__�
__module__�__qualname__rr�rr
rr
s��:�
,rrzTesting %s
zhttps://curl.haxx.se/dev/)�sys�pycurl�version_infor
r�stderr�write�version�t�Curl�c�setopt�URL�
WRITEFUNCTIONr�perform�close�printr	rrr
�<module>r&s����
�	���q��A���,�,��
�
����&�.�.�0�1��F���F�K�K�M��������+�,�������!�/�/�*��	�	�����	��a�j�j�rPK���\������5pycurl/examples/__pycache__/retriever.cpython-312.pycnu�[����

���gi
��D�ddlZddlZ	ddlZddlZ	ddlZddlmZmZejee�dZ		ejddk(rejj�Z
n#eejd�j�Z
eej�dk\reejd�Z	ej�Ze
D]HZej)�Zeredd	k(r�d
eej�dzzZej-eef��JejsJd��eej�Zee	e�Z	de	cxkrdksJd
��Jd
��edej2ej4fz�edede	d�Gd�dej6�ZgZee	�D]+Zee�Z e jC�ejEe ��-eD]Z#e#jI��y#e$rddlZY���wxYw#e$rY���wxYw#edejdz�e�xYw)�N)�SIGPIPE�SIG_IGN�
��-��zCUsage: %s <file with URLs to fetch> [<# of concurrent connections>]�#zdoc_%03d.datz
no URLs giveni'z(invalid number of concurrent connectionsz!PycURL %s (compiled against 0x%x)z
----- Gettingz
URLs usingzconnections -----c��eZdZd�Zd�Zy)�WorkerThreadc�P�tjj|�||_y)N)�	threading�Thread�__init__�queue)�selfrs  �]/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/retriever.pyrzWorkerThread.__init__>s�����!�!�$�'���
�c���		|jj�\}}t|d�}t
j�}|jtj|�|jtjd�|jtjd�|jtjd�|jtjd�|jtjd�|jtj|�	|j!�|j-�|j-�t&j.j1d�t&j.j+����#tj$rt�wxYw#ddl}|j%t&j(��t&j(j+�Y��xYw)	Nr�wb��i,r)�file�.)r�
get_nowait�Queue�Empty�
SystemExit�open�pycurl�Curl�setopt�URL�FOLLOWLOCATION�	MAXREDIRS�CONNECTTIMEOUT�TIMEOUT�NOSIGNAL�	WRITEDATA�perform�	traceback�	print_exc�sys�stderr�flush�close�stdout�write)r�url�filename�fp�curlr+s      r�runzWorkerThread.runBsQ���
!� $�
�
� 5� 5� 7�
��X��h��%�B��;�;�=�D��K�K��
�
�C�(��K�K��-�-�q�1��K�K��(�(�!�,��K�K��-�-�r�2��K�K�����,��K�K�����+��K�K��(�(�"�-�
#�����

�J�J�L��H�H�J��J�J���S�!��J�J����/���;�;�
!� � �
!��
#� ��#�#����#�4��
�
� � �"�s�F�!F-�F*�-AG3N)�__name__�
__module__�__qualname__rr7�rrrr=s���rr)%r-rr�ImportErrorrr �signalrr�num_conn�argv�stdin�	readlines�urlsr�len�int�printrr3�stripr4�put�num_urls�min�version�COMPILE_LIBCURL_VERSION_NUMrr�threads�range�dummy�t�start�append�thread�joinr;rr�<module>rTs&������$��'��F�M�M�'�7�#���	�
�x�x��{�c���y�y�"�"�$���C�H�H�Q�K� �*�*�,��
�3�8�8�}����s�x�x��{�#��	����
���C�

�)�)�+�C��#�a�&�C�-����U�[�[�!1�A�!5�6�H�	�I�I�s�H�o���
�{�{�#�O�#�{��u�{�{����x��"���H����I�I�I��I�I�I���)�V�^�^�V�=_�=_�,`�`�a��o�x��x�9L�M��9�#�#��B
��
�8�_�E��U��A��G�G�I��N�N�1����F�
�K�K�M���s������	��	���	�
O�RU�RZ�RZ�[\�R]�
]�^�
��s.�G'�G7�A:H�'	G4�3G4�7H�?H�HPK���\l�~ܻ�?pycurl/examples/__pycache__/opensocketexception.cpython-312.pycnu�[����

���g]����ddlZddlZddlZGd�de�Zd�Zej�Zejejd�de_
ejejd��	ej�y#ej$rRZejdej k(rejreej�n
ee�YdZ[yYdZ[ydZ[wwxYw)�Nc��eZdZy)�ConnectionRejectedN)�__name__�
__module__�__qualname__���g/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/opensocketexception.pyrrs��r	rc��tj�dkr td�|_tjS|\}}}}tj
|||�}|j
t
jt
jd�|S)Ng�?z3Rejecting connection attempt in opensocket callback�)	�randomr�	exception�pycurl�
SOCKET_BAD�socket�
setsockopt�
SOL_SOCKET�SO_KEEPALIVE)�curl�purpose�curl_address�family�socktype�protocol�address�ss        r
�
opensocketrsj��
�}�}����+�,a�b���� � � �*6�'�F�H�h���
�
�f�h��1�A��L�L��"�"�F�$7�$7��;��Hr	zhttp://pycurl.ioc�$�tt||�S)N)r�c)rrs  r
�<lambda>r s
��Z��7�G�<r	)rr
r�	Exceptionrr�Curlr�setopt�URLr�OPENSOCKETFUNCTION�perform�error�e�args�E_COULDNT_CONNECT�printrr	r
�<module>r,s�����	��	�
��F�K�K�M��������"�#��������	�	�<�>���I�I�K��
�|�|���v�v�a�y�F�,�,�,����
�a�k�k��
�a���	���s�-A>�>C�
AC�CPK���\���y3pycurl/examples/__pycache__/sfquery.cpython-312.pycnu�[����

���g�	���ddlZddlZddlZGd�dej�Zedk(�r�eej�dk(rdZnejdZ	ej�jd�Z
e
\ZZZ
ed�Zej#d�ej%ee
�ej'd
�rEej(j+deej-��z�ej.d�yej'dez�r]ej1e�ej2j+ej-��ej5�ej.d�yej(j+deej-��z�ej.d�yy#eej�dkred	ejdz�e�ejd
ZejdZ
Y���xYw)�Nc��eZdZd�Zd�Zd�Zy)�SourceForgeUserSessionc�:�|jdd|fd|fdddf�y)zEstablish a login session.zaccount/login.php�form_loginname�form_pw)�	return_to�)�stay_in_ssl�1)�loginzLogin With SSLN)�post)�self�name�passwords   �[/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/sfquery.pyrzSourceForgeUserSession.logins1���	�	�%�)9�4�(@�)2�H�(=�(9�(<�(C�	(E�	F�c�&�|jd�y)zLog out of SourceForge.zaccount/logout.phpN��get)rs r�logoutzSourceForgeUserSession.logouts�����%�&rc�,�|jd|z�y)Nz!export/xml_export.php?group_id=%sr)r�numids  r�	fetch_xmlz SourceForgeUserSession.fetch_xmls�����4�u�<�=rN)�__name__�
__module__�__qualname__rrr�rrrrs��F�'�>rr�__main__��28236zsourceforge.net�z,Usage: %s <project id> <username> <password>��zhttps://sourceforge.net/zInvalid Password or User Namez'Login/password not accepted (%d bytes)
zPersonal Page For: zUnexpected page (%d bytes)
)�sys�netrc�curl�Curlrr�len�argv�
project_id�authenticators�authr�accountr�print�
SystemExit�session�
set_verbosityr�answered�stderr�write�body�exitr�stdoutrrrr�<module>r8s�����
>�T�Y�Y�
>��z��
�3�8�8�}����
��X�X�a�[�
���u�{�{�}�+�+�,=�>��"&���g�x�%�%?�@�G����!���M�M�$��!����7�8��
�
���C�c�'�,�,�.�FY�Y�Z�������	�	�	�/�$�6�	7����*�%��
�
�������(�����������	�
�
���7��G�L�L�N�8K�K�L�������A����s�x�x�=�1���@�3�8�8�A�;�N�O����x�x��{���8�8�A�;��s
�&F.�.AHPK���\^�7�.."pycurl/examples/ssh_keyfunction.pynu�[���import pycurl

sftp_server = 'sftp://web.sourceforge.net'

c = pycurl.Curl()
c.setopt(c.URL, sftp_server)
c.setopt(c.VERBOSE, True)

def keyfunction(known_key, found_key, match):
    return c.KHSTAT_FINE

c.setopt(c.SSH_KNOWNHOSTS, '.known_hosts')
c.setopt(c.SSH_KEYFUNCTION, keyfunction)

c.perform()
PK���\ѹ�+��pycurl/examples/file_upload.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import os, sys
import pycurl

# Class which holds a file reference and the read callback
class FileReader:
    def __init__(self, fp):
        self.fp = fp
    def read_callback(self, size):
        return self.fp.read(size)

# Check commandline arguments
if len(sys.argv) < 3:
    print("Usage: %s <url> <file to upload>" % sys.argv[0])
    raise SystemExit
url = sys.argv[1]
filename = sys.argv[2]

if not os.path.exists(filename):
    print("Error: the file '%s' does not exist" % filename)
    raise SystemExit

# Initialize pycurl
c = pycurl.Curl()
c.setopt(pycurl.URL, url)
c.setopt(pycurl.UPLOAD, 1)

# Two versions with the same semantics here, but the filereader version
# is useful when you have to process the data which is read before returning
if 1:
    c.setopt(pycurl.READFUNCTION, FileReader(open(filename, 'rb')).read_callback)
else:
    c.setopt(pycurl.READFUNCTION, open(filename, 'rb').read)

# Set size of file to be uploaded.
filesize = os.path.getsize(filename)
c.setopt(pycurl.INFILESIZE, filesize)

# Start transfer
print('Uploading file %s to url %s' % (filename, url))
c.perform()
c.close()
PK���\FX�UUpycurl/examples/linksys.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
#
# linksys.py -- program settings on a Linkys router
#
# This tool is designed to help you recover from the occasional episodes
# of catatonia that afflict Linksys boxes. It allows you to batch-program
# them rather than manually entering values to the Web interface.  Commands
# are taken from the command line first, then standard input.
#
# The somewhat spotty coverage of status queries is because I only did the
# ones that were either (a) easy, or (b) necessary.  If you want to know the
# status of the box, look at the web interface.
#
# This code has been tested against the following hardware:
#
#   Hardware    Firmware
#   ----------  ---------------------
#   BEFW11S4v2  1.44.2.1, Dec 20 2002
#
# The code is, of course, sensitive to changes in the names of CGI pages
# and field names.
#
# Note: to make the no-arguments form work, you'll need to have the following
# entry in your ~/.netrc file.  If you have changed the router IP address or
# name/password, modify accordingly.
#
# machine 192.168.1.1
#   login ""
#   password admin
#
# By Eric S. Raymond, August April 2003.  All rites reversed.

import sys, re, curl, exceptions

def print_stderr(arg):
    sys.stderr.write(arg)
    sys.stderr.write("\n")

class LinksysError(exceptions.Exception):
    def __init__(self, *args):
        self.args = args

class LinksysSession:
    months = 'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec'

    WAN_CONNECT_AUTO = '1'
    WAN_CONNECT_STATIC = '2'
    WAN_CONNECT_PPOE = '3'
    WAN_CONNECT_RAS = '4'
    WAN_CONNECT_PPTP = '5'
    WAN_CONNECT_HEARTBEAT = '6'

    # Substrings to check for on each page load.
    # This may enable us to detect when a firmware change has hosed us.
    check_strings = {
        "":           "basic setup functions",
        "Passwd.htm": "For security reasons,",
        "DHCP.html":  "You can configure the router to act as a DHCP",
        "Log.html":   "There are some log settings and lists in this page.",
        "Forward.htm":"Port forwarding can be used to set up public services",
        }

    def __init__(self):
        self.actions = []
        self.host = "http://192.168.1.1"
        self.verbosity = False
        self.pagecache = {}

    def set_verbosity(self, flag):
        self.verbosity = flag

    # This is not a performance hack -- we need the page cache to do
    # sanity checks at configure time.
    def cache_load(self, page):
        if page not in self.pagecache:
            fetch = curl.Curl(self.host)
            fetch.set_verbosity(self.verbosity)
            fetch.get(page)
            self.pagecache[page] = fetch.body()
            if fetch.answered("401"):
                raise LinksysError("authorization failure.", True)
            elif not fetch.answered(LinksysSession.check_strings[page]):
                del self.pagecache[page]
                raise LinksysError("check string for page %s missing!" % os.path.join(self.host, page), False)
            fetch.close()
    def cache_flush(self):
        self.pagecache = {}

    # Primitives
    def screen_scrape(self, page, template):
        self.cache_load(page)
        match = re.compile(template).search(self.pagecache[page])
        if match:
            result = match.group(1)
        else:
            result = None
        return result
    def get_MAC_address(self, page, prefix):
        return self.screen_scrape("", prefix+r":[^M]*\(MAC Address: *([^)]*)")
    def set_flag(self, page, flag, value):
        if value:
            self.actions.append(page, flag, "1")
        else:
            self.actions.append(page, flag, "0")
    def set_IP_address(self, page, cgi, role, ip):
        ind = 0
        for octet in ip.split("."):
            self.actions.append(("", "F1", role + repr(ind+1), octet))
            ind += 1

    # Scrape configuration data off the main page
    def get_firmware_version(self):
        # This is fragile.  There is no distinguishing tag before the firmware
        # version, so we have to key off the pattern of the version number.
        # Our model is ">1.44.2.1, Dec 20 2002<"
        return self.screen_scrape("", ">([0-9.v]*, (" + \
                                  LinksysSession.months + ")[^<]*)<", )
    def get_LAN_MAC(self):
        return self.get_MAC_address("", r"LAN IP Address")
    def get_Wireless_MAC(self):
        return self.get_MAC_address("", r"Wireless")
    def get_WAN_MAC(self):
        return self.get_MAC_address("", r"WAN Connection Type")

    # Set configuration data on the main page
    def set_host_name(self, name):
        self.actions.append(("", "hostName", name))
    def set_domain_name(self, name):
        self.actions.append(("", "DomainName", name))
    def set_LAN_IP(self, ip):
        self.set_IP_address("", "ipAddr", ip)
    def set_LAN_netmask(self, ip):
        if not ip.startswith("255.255.255."):
            raise ValueError
        lastquad = ip.split(".")[-1]
        if lastquad not in ("0", "128", "192", "240", "252"):
            raise ValueError
        self.actions.append("", "netMask", lastquad)
    def set_wireless(self, flag):
        self.set_flag("", "wirelessStatus")
    def set_SSID(self, ssid):
        self.actions.append(("", "wirelessESSID", ssid))
    def set_SSID_broadcast(self, flag):
        self.set_flag("", "broadcastSSID")
    def set_channel(self, channel):
        self.actions.append(("", "wirelessChannel", channel))
    def set_WEP(self, flag):
        self.set_flag("", "WepType")
    # FIXME: Add support for setting WEP keys
    def set_connection_type(self, type):
        self.actions.append(("", "WANConnectionType", type))
    def set_WAN_IP(self, ip):
        self.set_IP_address("", "aliasIP", ip)
    def set_WAN_netmask(self, ip):
        self.set_IP_address("", "aliasMaskIP", ip)
    def set_WAN_gateway_address(self, ip):
        self.set_IP_address("", "routerIP", ip)
    def set_DNS_server(self, index, ip):
        self.set_IP_address("", "dns" + "ABC"[index], ip)

    # Set configuration data on the password page
    def set_password(self, str):
        self.actions.append("Passwd.htm","sysPasswd", str)
        self.actions.append("Passwd.htm","sysPasswdConfirm", str)
    def set_UPnP(self, flag):
        self.set_flag("Passwd.htm", "UPnP_Work")
    def reset(self):
        self.actions.append("Passwd.htm", "FactoryDefaults")

    # DHCP features
    def set_DHCP(self, flag):
        if flag:
            self.actions.append("DHCP.htm","dhcpStatus","Enable")
        else:
            self.actions.append("DHCP.htm","dhcpStatus","Disable")
    def set_DHCP_starting_IP(self, val):
        self.actions.append("DHCP.htm","dhcpS4", str(val))
    def set_DHCP_users(self, val):
        self.actions.append("DHCP.htm","dhcpLen", str(val))
    def set_DHCP_lease_time(self, val):
        self.actions.append("DHCP.htm","leaseTime", str(val))
    def set_DHCP_DNS_server(self, index, ip):
        self.set_IP_address("DHCP.htm", "dns" + "ABC"[index], ip)
    # FIXME: add support for setting WINS key

    # Logging features
    def set_logging(self, flag):
        if flag:
            self.actions.append("Log.htm", "rLog", "Enable")
        else:
            self.actions.append("Log.htm", "rLog", "Disable")
    def set_log_address(self, val):
        self.actions.append("DHCP.htm","trapAddr3", str(val))

    # The AOL parental control flag is not supported by design.

    # FIXME: add Filters and other advanced features

    def configure(self):
        "Write configuration changes to the Linksys."
        if self.actions:
            fields = []
            self.cache_flush()
            for (page, field, value) in self.actions:
                self.cache_load(page)
                if self.pagecache[page].find(field) == -1:
                    print_stderr("linksys: field %s not found where expected in page %s!" % (field, os.path.join(self.host, page)))
                    continue
                else:
                    fields.append((field, value))
            # Clearing the action list before fieldsping is deliberate.
            # Otherwise we could get permanently wedged by a 401.
            self.actions = []
            transaction = curl.Curl(self.host)
            transaction.set_verbosity(self.verbosity)
            transaction.get("Gozila.cgi", tuple(fields))
            transaction.close()

if __name__ == "__main__":
    import os, cmd

    class LinksysInterpreter(cmd.Cmd):
        """Interpret commands to perform LinkSys programming actions."""
        def __init__(self):
            cmd.Cmd.__init__(self)
            self.session = LinksysSession()
            if os.isatty(0):
                print("Type ? or `help' for help.")
                self.prompt = self.session.host + ": "
            else:
                self.prompt = ""
                print("Bar1")

        def flag_command(self, func, line):
            if line.strip() in ("on", "enable", "yes"):
                func(True)
            elif line.strip() in ("off", "disable", "no"):
                func(False)
            else:
                print_stderr("linksys: unknown switch value")
            return 0

        def do_connect(self, line):
            newhost = line.strip()
            if newhost:
                self.session.host = newhost
                self.session.cache_flush()
                self.prompt = self.session.host + ": "
            else:
                print(self.session.host)
            return 0
        def help_connect(self):
            print("Usage: connect [<hostname-or-IP>]")
            print("Connect to a Linksys by name or IP address.")
            print("If no argument is given, print the current host.")

        def do_status(self, line):
            self.session.cache_load("")
            if "" in self.session.pagecache:
                print("Firmware:", self.session.get_firmware_version())
                print("LAN MAC:", self.session.get_LAN_MAC())
                print("Wireless MAC:", self.session.get_Wireless_MAC())
                print("WAN MAC:", self.session.get_WAN_MAC())
                print(".")
            return 0
        def help_status(self):
            print("Usage: status")
            print("The status command shows the status of the Linksys.")
            print("It is mainly useful as a sanity check to make sure")
            print("the box is responding correctly.")

        def do_verbose(self, line):
            self.flag_command(self.session.set_verbosity, line)
        def help_verbose(self):
            print("Usage: verbose {on|off|enable|disable|yes|no}")
            print("Enables display of HTTP requests.")

        def do_host(self, line):
            self.session.set_host_name(line)
            return 0
        def help_host(self):
            print("Usage: host <hostname>")
            print("Sets the Host field to be queried by the ISP.")

        def do_domain(self, line):
            print("Usage: host <domainname>")
            self.session.set_domain_name(line)
            return 0
        def help_domain(self):
            print("Sets the Domain field to be queried by the ISP.")

        def do_lan_address(self, line):
            self.session.set_LAN_IP(line)
            return 0
        def help_lan_address(self):
            print("Usage: lan_address <ip-address>")
            print("Sets the LAN IP address.")

        def do_lan_netmask(self, line):
            self.session.set_LAN_netmask(line)
            return 0
        def help_lan_netmask(self):
            print("Usage: lan_netmask <ip-mask>")
            print("Sets the LAN subnetwork mask.")

        def do_wireless(self, line):
            self.flag_command(self.session.set_wireless, line)
            return 0
        def help_wireless(self):
            print("Usage: wireless {on|off|enable|disable|yes|no}")
            print("Switch to enable or disable wireless features.")

        def do_ssid(self, line):
            self.session.set_SSID(line)
            return 0
        def help_ssid(self):
            print("Usage: ssid <string>")
            print("Sets the SSID used to control wireless access.")

        def do_ssid_broadcast(self, line):
            self.flag_command(self.session.set_SSID_broadcast, line)
            return 0
        def help_ssid_broadcast(self):
            print("Usage: ssid_broadcast {on|off|enable|disable|yes|no}")
            print("Switch to enable or disable SSID broadcast.")

        def do_channel(self, line):
            self.session.set_channel(line)
            return 0
        def help_channel(self):
            print("Usage: channel <number>")
            print("Sets the wireless channel.")

        def do_wep(self, line):
            self.flag_command(self.session.set_WEP, line)
            return 0
        def help_wep(self):
            print("Usage: wep {on|off|enable|disable|yes|no}")
            print("Switch to enable or disable WEP security.")

        def do_wan_type(self, line):
            try:
                type=eval("LinksysSession.WAN_CONNECT_"+line.strip().upper())
                self.session.set_connection_type(type)
            except ValueError:
                print_stderr("linksys: unknown connection type.")
            return 0
        def help_wan_type(self):
            print("Usage: wan_type {auto|static|ppoe|ras|pptp|heartbeat}")
            print("Set the WAN connection type.")

        def do_wan_address(self, line):
            self.session.set_WAN_IP(line)
            return 0
        def help_wan_address(self):
            print("Usage: wan_address <ip-address>")
            print("Sets the WAN IP address.")

        def do_wan_netmask(self, line):
            self.session.set_WAN_netmask(line)
            return 0
        def help_wan_netmask(self):
            print("Usage: wan_netmask <ip-mask>")
            print("Sets the WAN subnetwork mask.")

        def do_wan_gateway(self, line):
            self.session.set_WAN_gateway(line)
            return 0
        def help_wan_gateway(self):
            print("Usage: wan_gateway <ip-address>")
            print("Sets the LAN subnetwork mask.")

        def do_dns(self, line):
            (index, address) = line.split()
            if index in ("1", "2", "3"):
                self.session.set_DNS_server(eval(index), address)
            else:
                print_stderr("linksys: server index out of bounds.")
            return 0
        def help_dns(self):
            print("Usage: dns {1|2|3} <ip-mask>")
            print("Sets a primary, secondary, or tertiary DNS server address.")

        def do_password(self, line):
            self.session.set_password(line)
            return 0
        def help_password(self):
            print("Usage: password <string>")
            print("Sets the router password.")

        def do_upnp(self, line):
            self.flag_command(self.session.set_UPnP, line)
            return 0
        def help_upnp(self):
            print("Usage: upnp {on|off|enable|disable|yes|no}")
            print("Switch to enable or disable Universal Plug and Play.")

        def do_reset(self, line):
            self.session.reset()
        def help_reset(self):
            print("Usage: reset")
            print("Reset Linksys settings to factory defaults.")

        def do_dhcp(self, line):
            self.flag_command(self.session.set_DHCP, line)
        def help_dhcp(self):
            print("Usage: dhcp {on|off|enable|disable|yes|no}")
            print("Switch to enable or disable DHCP features.")

        def do_dhcp_start(self, line):
            self.session.set_DHCP_starting_IP(line)
        def help_dhcp_start(self):
            print("Usage: dhcp_start <number>")
            print("Set the start address of the DHCP pool.")

        def do_dhcp_users(self, line):
            self.session.set_DHCP_users(line)
        def help_dhcp_users(self):
            print("Usage: dhcp_users <number>")
            print("Set number of address slots to allocate in the DHCP pool.")

        def do_dhcp_lease(self, line):
            self.session.set_DHCP_lease(line)
        def help_dhcp_lease(self):
            print("Usage: dhcp_lease <number>")
            print("Set number of address slots to allocate in the DHCP pool.")

        def do_dhcp_dns(self, line):
            (index, address) = line.split()
            if index in ("1", "2", "3"):
                self.session.set_DHCP_DNS_server(eval(index), address)
            else:
                print_stderr("linksys: server index out of bounds.")
            return 0
        def help_dhcp_dns(self):
            print("Usage: dhcp_dns {1|2|3} <ip-mask>")
            print("Sets primary, secondary, or tertiary DNS server address.")

        def do_logging(self, line):
            self.flag_command(self.session.set_logging, line)
        def help_logging(self):
            print("Usage: logging {on|off|enable|disable|yes|no}")
            print("Switch to enable or disable session logging.")

        def do_log_address(self, line):
            self.session.set_Log_address(line)
        def help_log_address(self):
            print("Usage: log_address <number>")
            print("Set the last quad of the address to which to log.")

        def do_configure(self, line):
            self.session.configure()
            return 0
        def help_configure(self):
            print("Usage: configure")
            print("Writes the configuration to the Linksys.")

        def do_cache(self, line):
            print(self.session.pagecache)
        def help_cache(self):
            print("Usage: cache")
            print("Display the page cache.")

        def do_quit(self, line):
            return 1
        def help_quit(self, line):
            print("The quit command ends your linksys session without")
            print("writing configuration changes to the Linksys.")
        def do_EOF(self, line):
            print("")
            self.session.configure()
            return 1
        def help_EOF(self):
            print("The EOF command writes the configuration to the linksys")
            print("and ends your session.")

        def default(self, line):
            """Pass the command through to be executed by the shell."""
            os.system(line)
            return 0

        def help_help(self):
            print("On-line help is available through this command.")
            print("? is a convenience alias for help.")

        def help_introduction(self):
            print("""\

This program supports changing the settings on Linksys blue-box routers.  This
capability may come in handy when they freeze up and have to be reset.  Though
it can be used interactively (and will command-prompt when standard input is a
terminal) it is really designed to be used in batch mode. Commands are taken
from the command line first, then standard input.

By default, it is assumed that the Linksys is at http://192.168.1.1, the
default LAN address.  You can connect to a different address or IP with the
'connect' command.  Note that your .netrc must contain correct user/password
credentials for the router.  The entry corresponding to the defaults is:

machine 192.168.1.1
    login ""
    password admin

Most commands queue up changes but don't actually send them to the Linksys.
You can force pending changes to be written with 'configure'.  Otherwise, they
will be shipped to the Linksys at the end of session (e.g.  when the program
running in batch mode encounters end-of-file or you type a control-D).  If you
end the session with `quit', pending changes will be discarded.

For more help, read the topics 'wan', 'lan', and 'wireless'.""")

        def help_lan(self):
            print("""\
The `lan_address' and `lan_netmask' commands let you set the IP location of
the Linksys on your LAN, or inside.  Normally you'll want to leave these
untouched.""")

        def help_wan(self):
            print("""\
The WAN commands become significant if you are using the BEFSR41 or any of
the other Linksys boxes designed as DSL or cable-modem gateways.  You will
need to use `wan_type' to declare how you expect to get your address.

If your ISP has issued you a static address, you'll need to use the
`wan_address', `wan_netmask', and `wan_gateway' commands to set the address
of the router as seen from the WAN, the outside. In this case you will also
need to use the `dns' command to declare which remote servers your DNS
requests should be forwarded to.

Some ISPs may require you to set host and domain for use with dynamic-address
allocation.""")

        def help_wireless_desc(self):
            print("""\
The channel, ssid, ssid_broadcast, wep, and wireless commands control
wireless routing.""")

        def help_switches(self):
            print("Switches may be turned on with 'on', 'enable', or 'yes'.")
            print("Switches may be turned off with 'off', 'disable', or 'no'.")
            print("Switch commands include: wireless, ssid_broadcast.")

        def help_addresses(self):
            print("An address argument must be a valid IP address;")
            print("four decimal numbers separated by dots, each ")
            print("between 0 and 255.")

        def emptyline(self):
            pass

    interpreter = LinksysInterpreter()
    for arg in sys.argv[1:]:
        interpreter.onecmd(arg)
    fatal = False
    while not fatal:
        try:
            interpreter.cmdloop()
            fatal = True
        except LinksysError:
            message, fatal = sys.exc_info()[1].args
            print("linksys: " + message)

# The following sets edit modes for GNU EMACS
# Local Variables:
# mode:python
# End:
PK���\��	�	pycurl/examples/sfquery.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
#
# sfquery -- Source Forge query script using the ClientCGI high-level interface
#
# Retrieves a SourceForge XML export object for a given project.
# Specify the *numeric* project ID. the user name, and the password,
# as arguments. If you have a valid ~/.netrc entry for sourceforge.net,
# you can just give the project ID.
#
# By Eric S. Raymond, August 2002.  All rites reversed.

import sys, netrc
import curl

class SourceForgeUserSession(curl.Curl):
    # SourceForge-specific methods.  Sensitive to changes in site design.
    def login(self, name, password):
        "Establish a login session."
        self.post("account/login.php", (("form_loginname", name),
                                        ("form_pw", password),
                                        ("return_to", ""),
                                        ("stay_in_ssl", "1"),
                                        ("login", "Login With SSL")))
    def logout(self):
        "Log out of SourceForge."
        self.get("account/logout.php")
    def fetch_xml(self, numid):
        self.get("export/xml_export.php?group_id=%s" % numid)

if __name__ == "__main__":
    if len(sys.argv) == 1:
        project_id = '28236'    # PyCurl project ID
    else:
        project_id = sys.argv[1]
    # Try to grab authenticators out of your .netrc
    try:
        auth = netrc.netrc().authenticators("sourceforge.net")
        name, account, password = auth
    except:
        if len(sys.argv) < 4:
            print("Usage: %s <project id> <username> <password>" % sys.argv[0])
            raise SystemExit
        name = sys.argv[2]
        password = sys.argv[3]
    session = SourceForgeUserSession("https://sourceforge.net/")
    session.set_verbosity(0)
    session.login(name, password)
    # Login could fail.
    if session.answered("Invalid Password or User Name"):
        sys.stderr.write("Login/password not accepted (%d bytes)\n" % len(session.body()))
        sys.exit(1)
    # We'll see this if we get the right thing.
    elif session.answered("Personal Page For: " + name):
        session.fetch_xml(project_id)
        sys.stdout.write(session.body())
        session.logout()
        sys.exit(0)
    # Or maybe SourceForge has changed its site design so our check strings
    # are no longer valid.
    else:
        sys.stderr.write("Unexpected page (%d bytes)\n"%len(session.body()))
        sys.exit(1)

PK���\�!#�i
i
pycurl/examples/retriever.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

#
# Usage: python retriever.py <file with URLs to fetch> [<# of
#          concurrent connections>]
#

import sys, threading
try:
    import Queue
except ImportError:
    import queue as Queue
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
except ImportError:
    pass
else:
    signal.signal(SIGPIPE, SIG_IGN)


# Get args
num_conn = 10
try:
    if sys.argv[1] == "-":
        urls = sys.stdin.readlines()
    else:
        urls = open(sys.argv[1]).readlines()
    if len(sys.argv) >= 3:
        num_conn = int(sys.argv[2])
except:
    print("Usage: %s <file with URLs to fetch> [<# of concurrent connections>]" % sys.argv[0])
    raise SystemExit


# Make a queue with (url, filename) tuples
queue = Queue.Queue()
for url in urls:
    url = url.strip()
    if not url or url[0] == "#":
        continue
    filename = "doc_%03d.dat" % (len(queue.queue) + 1)
    queue.put((url, filename))


# Check args
assert queue.queue, "no URLs given"
num_urls = len(queue.queue)
num_conn = min(num_conn, num_urls)
assert 1 <= num_conn <= 10000, "invalid number of concurrent connections"
print("PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM))
print("----- Getting", num_urls, "URLs using", num_conn, "connections -----")


class WorkerThread(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        while 1:
            try:
                url, filename = self.queue.get_nowait()
            except Queue.Empty:
                raise SystemExit
            fp = open(filename, "wb")
            curl = pycurl.Curl()
            curl.setopt(pycurl.URL, url)
            curl.setopt(pycurl.FOLLOWLOCATION, 1)
            curl.setopt(pycurl.MAXREDIRS, 5)
            curl.setopt(pycurl.CONNECTTIMEOUT, 30)
            curl.setopt(pycurl.TIMEOUT, 300)
            curl.setopt(pycurl.NOSIGNAL, 1)
            curl.setopt(pycurl.WRITEDATA, fp)
            try:
                curl.perform()
            except:
                import traceback
                traceback.print_exc(file=sys.stderr)
                sys.stderr.flush()
            curl.close()
            fp.close()
            sys.stdout.write(".")
            sys.stdout.flush()


# Start a bunch of threads
threads = []
for dummy in range(num_conn):
    t = WorkerThread(queue)
    t.start()
    threads.append(t)


# Wait for all threads to finish
for thread in threads:
    thread.join()
PK���\%�ק

"pycurl/examples/retriever-multi.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

#
# Usage: python retriever-multi.py <file with URLs to fetch> [<# of
#          concurrent connections>]
#

import sys
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
except ImportError:
    pass
else:
    signal.signal(SIGPIPE, SIG_IGN)



# Get args
num_conn = 10
try:
    if sys.argv[1] == "-":
        urls = sys.stdin.readlines()
    else:
        urls = open(sys.argv[1]).readlines()
    if len(sys.argv) >= 3:
        num_conn = int(sys.argv[2])
except:
    print("Usage: %s <file with URLs to fetch> [<# of concurrent connections>]" % sys.argv[0])
    raise SystemExit


# Make a queue with (url, filename) tuples
queue = []
for url in urls:
    url = url.strip()
    if not url or url[0] == "#":
        continue
    filename = "doc_%03d.dat" % (len(queue) + 1)
    queue.append((url, filename))


# Check args
assert queue, "no URLs given"
num_urls = len(queue)
num_conn = min(num_conn, num_urls)
assert 1 <= num_conn <= 10000, "invalid number of concurrent connections"
print("PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM))
print("----- Getting", num_urls, "URLs using", num_conn, "connections -----")


# Pre-allocate a list of curl objects
m = pycurl.CurlMulti()
m.handles = []
for i in range(num_conn):
    c = pycurl.Curl()
    c.fp = None
    c.setopt(pycurl.FOLLOWLOCATION, 1)
    c.setopt(pycurl.MAXREDIRS, 5)
    c.setopt(pycurl.CONNECTTIMEOUT, 30)
    c.setopt(pycurl.TIMEOUT, 300)
    c.setopt(pycurl.NOSIGNAL, 1)
    m.handles.append(c)


# Main loop
freelist = m.handles[:]
num_processed = 0
while num_processed < num_urls:
    # If there is an url to process and a free curl object, add to multi stack
    while queue and freelist:
        url, filename = queue.pop(0)
        c = freelist.pop()
        c.fp = open(filename, "wb")
        c.setopt(pycurl.URL, url)
        c.setopt(pycurl.WRITEDATA, c.fp)
        m.add_handle(c)
        # store some info
        c.filename = filename
        c.url = url
    # Run the internal curl state machine for the multi stack
    while 1:
        ret, num_handles = m.perform()
        if ret != pycurl.E_CALL_MULTI_PERFORM:
            break
    # Check for curl objects which have terminated, and add them to the freelist
    while 1:
        num_q, ok_list, err_list = m.info_read()
        for c in ok_list:
            c.fp.close()
            c.fp = None
            m.remove_handle(c)
            print("Success:", c.filename, c.url, c.getinfo(pycurl.EFFECTIVE_URL))
            freelist.append(c)
        for c, errno, errmsg in err_list:
            c.fp.close()
            c.fp = None
            m.remove_handle(c)
            print("Failed: ", c.filename, c.url, errno, errmsg)
            freelist.append(c)
        num_processed = num_processed + len(ok_list) + len(err_list)
        if num_q == 0:
            break
    # Currently no more I/O is pending, could do something in the meantime
    # (display a progress bar, etc.).
    # We just call select() to sleep until some more data is available.
    m.select(1.0)


# Cleanup
for c in m.handles:
    if c.fp is not None:
        c.fp.close()
        c.fp = None
    c.close()
m.close()

PK���\�p�KKpycurl/examples/smtp.pynu�[���# Based on the simple libcurl SMTP example:
# https://github.com/bagder/curl/blob/master/docs/examples/smtp-mail.c
# There are other SMTP examples in that directory that you may find helpful.

from . import localhost
import pycurl
try:
    from io import BytesIO
except ImportError:
    from StringIO import StringIO as BytesIO
import sys

PY3 = sys.version_info[0] > 2

mail_server = 'smtp://%s' % localhost
mail_from = 'sender@example.org'
mail_to = 'addressee@example.net'

c = pycurl.Curl()
c.setopt(c.URL, mail_server)
c.setopt(c.MAIL_FROM, mail_from)
c.setopt(c.MAIL_RCPT, [mail_to])

message = '''\
From: %s
To: %s
Subject: PycURL SMTP example

SMTP example via PycURL
''' % (mail_from, mail_to)

if PY3:
    message = message.encode('ascii')

# libcurl does not perform buffering, therefore
# we need to wrap the message string into a BytesIO or StringIO.
io = BytesIO(message)
c.setopt(c.READDATA, io)

# If UPLOAD is not set, libcurl performs SMTP VRFY.
# Setting UPLOAD to True sends a message.
c.setopt(c.UPLOAD, True)

# Observe SMTP conversation.
c.setopt(c.VERBOSE, True)
c.perform()
PK���\p$���(pycurl/examples/quickstart/put_buffer.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl
try:
    from io import BytesIO
except ImportError:
    from StringIO import StringIO as BytesIO

c = pycurl.Curl()
c.setopt(c.URL, 'https://httpbin.org/put')

c.setopt(c.UPLOAD, 1)
data = '{"json":true}'
# READDATA requires an IO-like object; a string is not accepted
# encode() is necessary for Python 3
buffer = BytesIO(data.encode('utf-8'))
c.setopt(c.READDATA, buffer)

c.perform()
c.close()
PK���\��~,��-pycurl/examples/quickstart/follow_redirect.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl

c = pycurl.Curl()
# Redirects to https://www.python.org/.
c.setopt(c.URL, 'http://www.python.org/')
# Follow redirect.
c.setopt(c.FOLLOWLOCATION, True)
c.perform()
c.close()
PK���\h�PW��4pycurl/examples/quickstart/file_upload_real_fancy.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl

c = pycurl.Curl()
c.setopt(c.URL, 'https://httpbin.org/post')

c.setopt(c.HTTPPOST, [
    ('fileupload', (
        # upload the contents of this file
        c.FORM_FILE, __file__,
        # specify a different file name for the upload
        c.FORM_FILENAME, 'helloworld.py',
        # specify a different content type
        c.FORM_CONTENTTYPE, 'application/x-python',
    )),
])

c.perform()
c.close()
PK���\�S��||:pycurl/examples/quickstart/__pycache__/get.cpython-312.pycnu�[����

���gG��j�ddlZ	ddlmZe�Zej�Zejejd�ejeje�ej�ej�ej�Zeej!d��y#e$r	ddlmZY��wxYw)�N)�BytesIO)�StringIOzhttp://pycurl.io/z
iso-8859-1)�pycurl�ior�ImportErrorr�buffer�Curl�c�setopt�URL�	WRITEDATA�perform�close�getvalue�body�print�decode���b/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/get.py�<module>rs���
�-��
����F�K�K�M��������#�$�������f���	�	�����	�
������d�k�k�,�� ��!�-�,�-�s�B$�$B2�1B2PK���\��Gpycurl/examples/quickstart/__pycache__/file_upload_real.cpython-312.pycnu�[����

���g#���ddlZej�Zejejd�ejej
dejeffg�ej�ej�y)�Nzhttps://httpbin.org/post�
fileupload)
�pycurl�Curl�c�setopt�URL�HTTPPOST�	FORM_FILE�__file__�perform�close���o/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/file_upload_real.py�<module>rsk��
��F�K�K�M��������*�+��������	���X������	�	�����	rPK���\���5��Bpycurl/examples/quickstart/__pycache__/get_python2.cpython-312.pycnu�[����

���g���(�ddlZddlmZe�Zej�Zejejd�ejeje�ej�ej�ej�Zee�y)�N)�StringIOzhttp://pycurl.io/)
�pycurlr�buffer�Curl�c�setopt�URL�	WRITEDATA�perform�close�getvalue�body�print���j/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/get_python2.py�<module>rso��
��	����F�K�K�M��������#�$�������f���	�	�����	�
������d�rPK���\0�_���Ipycurl/examples/quickstart/__pycache__/file_upload_buffer.cpython-312.pycnu�[����

���g7���ddlZej�Zejejd�ejej
dejdejdffg�ej�ej�y)�Nzhttps://httpbin.org/post�
fileuploadz
readme.txtzThis is a fancy readme file)
�pycurl�Curl�c�setopt�URL�HTTPPOST�FORM_BUFFER�FORM_BUFFERPTR�perform�close���q/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/file_upload_buffer.py�<module>rsw��
��F�K�K�M��������*�+��������	�
�
�|�	���7������	�	�����	rPK���\�6��Gpycurl/examples/quickstart/__pycache__/response_headers.cpython-312.pycnu�[����

���g1��|�ddlZddlZ	ddlmZiZd�Ze�Zej�Z
e
je
jd�e
je
jej�e
je
je�e
j!�e
j#�dZdevrDedj'�Zej*de�Zerej/d�Zed	ez�e�
d
Zedez�ej3�Zeej7e��y#e$r
ddlmZY��*wxYw)�N)�BytesIO)�StringIOc���|jd�}d|vry|jdd�\}}|j�}|j�}|j�}|t|<y)N�
iso-8859-1�:�)�decode�split�strip�lower�headers)�header_line�name�values   �o/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/response_headers.py�header_functionr
si���$�$�\�2�K�
�+����#�#�C��+�K�D�%�
�:�:�<�D��K�K�M�E��:�:�<�D��G�D�M�zhttp://pycurl.iozcontent-typez
charset=(\S+)rzDecoding using %srzAssuming encoding is %s)�pycurl�re�ior�ImportErrorrr
r�buffer�Curl�c�setopt�URL�
WRITEFUNCTION�write�HEADERFUNCTION�perform�close�encodingr�content_type�search�match�group�print�getvalue�bodyr	�rr�<module>r+s(��
�	�-��
���8
����F�K�K�M��������"�#�������&�,�,�'�����	�	�?�+��	�	�����	����W���>�*�0�0�2�L��B�I�I�o�|�4�E���;�;�q�>��
�!�H�,�-����H�	�
#�h�
.�/�
������d�k�k�(����w�-�,�,�-�s�D,�,D;�:D;PK���\ɓ��ppHpycurl/examples/quickstart/__pycache__/get_python2_https.cpython-312.pycnu�[����

���g����ddlZddlZddlmZe�Zej�Zej
ejd�ej
eje�ej
ejej��ej�ej�ej�Zee�y)�N)�StringIOzhttp://pycurl.io/)�pycurl�certifir�buffer�Curl�c�setopt�URL�	WRITEDATA�CAINFO�where�perform�close�getvalue�body�print���p/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/get_python2_https.py�<module>rs���
���	����F�K�K�M��������#�$�������f��������=�7�=�=�?�#��	�	�����	�
������d�rPK���\���))@pycurl/examples/quickstart/__pycache__/form_post.cpython-312.pycnu�[����

���g4��&�ddlZ	ddlmZej
�Zejejd�ddiZ	ee	�Z
ejeje
�ej�ej�y#e$r	ddlmZY��wxYw)�N)�	urlencodezhttps://httpbin.org/post�field�value)�pycurl�urllib.parser�ImportError�urllib�Curl�c�setopt�URL�	post_data�
postfields�
POSTFIELDS�perform�close���h/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/form_post.py�<module>rs���
�!�&�
�F�K�K�M��������*�+�
�g��	�
�y�
!�
�������z�"��	�	�����	��!�!� �!�s�B�B�BPK���\I��44Mpycurl/examples/quickstart/__pycache__/file_upload_real_fancy.cpython-312.pycnu�[����

���g��
�,�ddlZej�Zejejd�ejej
dejeejdejdffg�ej�ej�y)�Nzhttps://httpbin.org/post�
fileuploadz
helloworld.pyzapplication/x-python)�pycurl�Curl�c�setopt�URL�HTTPPOST�	FORM_FILE�__file__�
FORM_FILENAME�FORM_CONTENTTYPE�perform�close���u/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/file_upload_real_fancy.py�<module>rs���
��F�K�K�M��������*�+��������	���X�	����	���2�
��	�	��	�	�����	rPK���\�5���Apycurl/examples/quickstart/__pycache__/write_file.cpython-312.pycnu�[����

���ge���ddlZedd�5Zej�Zejejd�ejeje�ej�ej�ddd�y#1swYyxYw)�Nzout.html�wbzhttp://pycurl.io/)
�pycurl�open�f�Curl�c�setopt�URL�	WRITEDATA�perform�close���i/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/write_file.py�<module>rsh��
�
�*�d��q�����
�A��H�H�Q�U�U�'�(��H�H�Q�[�[�!���I�I�K��G�G�I����s�A*B�B
PK���\#g��Apycurl/examples/quickstart/__pycache__/put_buffer.cpython-312.pycnu�[����

���g���x�ddlZ	ddlmZej
�Zejejd�ejejd�dZ
ee
jd��Zejeje�ej�ej�y#e$r	ddlmZY��wxYw)�N)�BytesIO)�StringIOzhttps://httpbin.org/put�z
{"json":true}zutf-8)�pycurl�ior�ImportErrorr�Curl�c�setopt�URL�UPLOAD�data�encode�buffer�READDATA�perform�close���i/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/put_buffer.py�<module>rs���
�-���F�K�K�M��������)�*�������1����
����W�%�	&��������V���	�	�����	���-�,�-�s�B+�+B9�8B9PK���\��Bpycurl/examples/quickstart/__pycache__/get_python3.cpython-312.pycnu�[����

���g���F�ddlZddlmZe�Zej�Zej
ejd�ej
eje�ej�ej�ej�Ze
ejd��y)�N)�BytesIOzhttp://pycurl.io/z
iso-8859-1)�pycurl�ior�buffer�Curl�c�setopt�URL�	WRITEDATA�perform�close�getvalue�body�print�decode���j/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/get_python3.py�<module>rsw��
��	����F�K�K�M��������#�$�������f���	�	�����	�
������d�k�k�,�� rPK���\(�fCMMFpycurl/examples/quickstart/__pycache__/follow_redirect.cpython-312.pycnu�[����

���g�����ddlZej�Zejejd�ejej
d�ej
�ej�y)�Nzhttp://www.python.org/T)�pycurl�Curl�c�setopt�URL�FOLLOWLOCATION�perform�close���n/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/follow_redirect.py�<module>rsP��
��F�K�K�M��������(�)�����	�	�4� ��	�	�����	rPK���\W�G��Hpycurl/examples/quickstart/__pycache__/get_python3_https.cpython-312.pycnu�[����

���g����ddlZddlZddlmZe�Zej
�Zejejd�ejeje�ejejej��ej�ej�ej�Zeej#d��y)�N)�BytesIOzhttp://pycurl.io/z
iso-8859-1)�pycurl�certifi�ior�buffer�Curl�c�setopt�URL�	WRITEDATA�CAINFO�where�perform�close�getvalue�body�print�decode���p/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/get_python3_https.py�<module>rs���
���	����F�K�K�M��������#�$�������f��������=�7�=�=�?�#��	�	�����	�
������d�k�k�,�� rPK���\-�k��?pycurl/examples/quickstart/__pycache__/put_file.cpython-312.pycnu�[����

���g,��F�ddlZej�Zejejd�ejej
d�ee�Zejeje�ej�ej�ej�y)�Nzhttps://httpbin.org/put�)�pycurl�Curl�c�setopt�URL�UPLOAD�open�__file__�file�READDATA�perform�close���g/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/put_file.py�<module>rsr��
��F�K�K�M��������)�*�������1���H�~��������T���	�	�����	��
�
�rPK���\
�����Dpycurl/examples/quickstart/__pycache__/response_info.cpython-312.pycnu�[����

���g����ddlZ	ddlmZe�Zej�Zejejd�ejeje�ej�edejej�z�edejej�z�ej!�y#e$r	ddlmZY��wxYw)�N)�BytesIO)�StringIOzhttp://pycurl.io/z
Status: %dzTime: %f)�pycurl�ior�ImportErrorr�buffer�Curl�c�setopt�URL�	WRITEDATA�perform�print�getinfo�
RESPONSE_CODE�
TOTAL_TIME�close���l/opt/hc_python/lib64/python3.12/site-packages/../../../share/doc/pycurl/examples/quickstart/response_info.py�<module>rs���
�-��
����F�K�K�M��������#�$�������f���	�	���l�Q�Y�Y�q���/�/�0��j�1�9�9�Q�\�\�*�*�+����	���-�,�-�s�C�C�CPK���\�ԃ+,,&pycurl/examples/quickstart/put_file.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl

c = pycurl.Curl()
c.setopt(c.URL, 'https://httpbin.org/put')

c.setopt(c.UPLOAD, 1)
file = open(__file__)
c.setopt(c.READDATA, file)

c.perform()
c.close()
# File must be kept open while Curl object is using it
file.close()
PK���\���/pycurl/examples/quickstart/get_python3_https.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl
import certifi
from io import BytesIO

buffer = BytesIO()
c = pycurl.Curl()
c.setopt(c.URL, 'http://pycurl.io/')
c.setopt(c.WRITEDATA, buffer)
c.setopt(c.CAINFO, certifi.where())
c.perform()
c.close()

body = buffer.getvalue()
# Body is a byte string.
# We have to know the encoding in order to print it to a text file
# such as standard output.
print(body.decode('iso-8859-1'))
PK���\���ee(pycurl/examples/quickstart/write_file.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl

# As long as the file is opened in binary mode, both Python 2 and Python 3
# can write response body to it without decoding.
with open('out.html', 'wb') as f:
    c = pycurl.Curl()
    c.setopt(c.URL, 'http://pycurl.io/')
    c.setopt(c.WRITEDATA, f)
    c.perform()
    c.close()
PK���\����)pycurl/examples/quickstart/get_python3.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl
from io import BytesIO

buffer = BytesIO()
c = pycurl.Curl()
c.setopt(c.URL, 'http://pycurl.io/')
c.setopt(c.WRITEDATA, buffer)
c.perform()
c.close()

body = buffer.getvalue()
# Body is a byte string.
# We have to know the encoding in order to print it to a text file
# such as standard output.
print(body.decode('iso-8859-1'))
PK���\WJ�A44'pycurl/examples/quickstart/form_post.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl
try:
    # python 3
    from urllib.parse import urlencode
except ImportError:
    # python 2
    from urllib import urlencode

c = pycurl.Curl()
c.setopt(c.URL, 'https://httpbin.org/post')

post_data = {'field': 'value'}
# Form data must be provided already urlencoded.
postfields = urlencode(post_data)
# Sets request method to POST,
# Content-Type header to application/x-www-form-urlencoded
# and data to send in request body.
c.setopt(c.POSTFIELDS, postfields)

c.perform()
c.close()
PK���\[�770pycurl/examples/quickstart/file_upload_buffer.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl

c = pycurl.Curl()
c.setopt(c.URL, 'https://httpbin.org/post')

c.setopt(c.HTTPPOST, [
    ('fileupload', (
        c.FORM_BUFFER, 'readme.txt',
        c.FORM_BUFFERPTR, 'This is a fancy readme file',
    )),
])

c.perform()
c.close()
PK���\�%���/pycurl/examples/quickstart/get_python2_https.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl
import certifi
from StringIO import StringIO

buffer = StringIO()
c = pycurl.Curl()
c.setopt(c.URL, 'http://pycurl.io/')
c.setopt(c.WRITEDATA, buffer)
# For older PycURL versions:
#c.setopt(c.WRITEFUNCTION, buffer.write)
c.setopt(c.CAINFO, certifi.where())
c.perform()
c.close()

body = buffer.getvalue()
# Body is a string in some encoding.
# In Python 2, we can print it without knowing what the encoding is.
print(body)
PK���\�~(�##.pycurl/examples/quickstart/file_upload_real.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl

c = pycurl.Curl()
c.setopt(c.URL, 'https://httpbin.org/post')

c.setopt(c.HTTPPOST, [
    ('fileupload', (
        # upload the contents of this file
        c.FORM_FILE, __file__,
    )),
])

c.perform()
c.close()
PK���\�b,��)pycurl/examples/quickstart/get_python2.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl
from StringIO import StringIO

buffer = StringIO()
c = pycurl.Curl()
c.setopt(c.URL, 'http://pycurl.io/')
c.setopt(c.WRITEDATA, buffer)
# For older PycURL versions:
#c.setopt(c.WRITEFUNCTION, buffer.write)
c.perform()
c.close()

body = buffer.getvalue()
# Body is a string in some encoding.
# In Python 2, we can print it without knowing what the encoding is.
print(body)
PK���\�YQ11.pycurl/examples/quickstart/response_headers.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl
import re
try:
    from io import BytesIO
except ImportError:
    from StringIO import StringIO as BytesIO

headers = {}
def header_function(header_line):
    # HTTP standard specifies that headers are encoded in iso-8859-1.
    # On Python 2, decoding step can be skipped.
    # On Python 3, decoding step is required.
    header_line = header_line.decode('iso-8859-1')

    # Header lines include the first status line (HTTP/1.x ...).
    # We are going to ignore all lines that don't have a colon in them.
    # This will botch headers that are split on multiple lines...
    if ':' not in header_line:
        return

    # Break the header line into header name and value.
    name, value = header_line.split(':', 1)

    # Remove whitespace that may be present.
    # Header lines include the trailing newline, and there may be whitespace
    # around the colon.
    name = name.strip()
    value = value.strip()

    # Header names are case insensitive.
    # Lowercase name here.
    name = name.lower()

    # Now we can actually record the header name and value.
    headers[name] = value

buffer = BytesIO()
c = pycurl.Curl()
c.setopt(c.URL, 'http://pycurl.io')
c.setopt(c.WRITEFUNCTION, buffer.write)
# Set our header function.
c.setopt(c.HEADERFUNCTION, header_function)
c.perform()
c.close()

# Figure out what encoding was sent with the response, if any.
# Check against lowercased header name.
encoding = None
if 'content-type' in headers:
    content_type = headers['content-type'].lower()
    match = re.search('charset=(\S+)', content_type)
    if match:
        encoding = match.group(1)
        print('Decoding using %s' % encoding)
if encoding is None:
    # Default encoding for HTML is iso-8859-1.
    # Other content types may have different default encoding,
    # or in case of binary data, may have no encoding at all.
    encoding = 'iso-8859-1'
    print('Assuming encoding is %s' % encoding)

body = buffer.getvalue()
# Decode using the encoding we figured out.
print(body.decode(encoding))
PK���\i%���+pycurl/examples/quickstart/response_info.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl
try:
    from io import BytesIO
except ImportError:
    from StringIO import StringIO as BytesIO

buffer = BytesIO()
c = pycurl.Curl()
c.setopt(c.URL, 'http://pycurl.io/')
c.setopt(c.WRITEDATA, buffer)
c.perform()

# HTTP response code, e.g. 200.
print('Status: %d' % c.getinfo(c.RESPONSE_CODE))
# Elapsed time for the transfer.
print('Time: %f' % c.getinfo(c.TOTAL_TIME))

# getinfo must be called before close.
c.close()
PK���\�� �GG!pycurl/examples/quickstart/get.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

import pycurl
try:
    from io import BytesIO
except ImportError:
    from StringIO import StringIO as BytesIO

buffer = BytesIO()
c = pycurl.Curl()
c.setopt(c.URL, 'http://pycurl.io/')
c.setopt(c.WRITEDATA, buffer)
# For older PycURL versions:
#c.setopt(c.WRITEFUNCTION, buffer.write)
c.perform()
c.close()

body = buffer.getvalue()
# Body is a string on Python 2 and a byte string on Python 3.
# If we know the encoding, we can always decode the body and
# end up with a Unicode string.
print(body.decode('iso-8859-1'))
PK���\8�!!pycurl/examples/basicfirst.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et
import sys
import pycurl

PY3 = sys.version_info[0] > 2


class Test:
    def __init__(self):
        self.contents = ''
        if PY3:
            self.contents = self.contents.encode('ascii')

    def body_callback(self, buf):
        self.contents = self.contents + buf


sys.stderr.write("Testing %s\n" % pycurl.version)

t = Test()
c = pycurl.Curl()
c.setopt(c.URL, 'https://curl.haxx.se/dev/')
c.setopt(c.WRITEFUNCTION, t.body_callback)
c.perform()
c.close()

print(t.contents)
PK���\�8���pycurl/examples/xmlrpc_curl.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see
# the libcurl tutorial for more info.
try:
    import signal
    from signal import SIGPIPE, SIG_IGN
except ImportError:
    pass
else:
    signal.signal(SIGPIPE, SIG_IGN)

try:
    from cStringIO import StringIO
except ImportError:
    try:
        from StringIO import StringIO
    except ImportError:
        from io import StringIO
try:
    import xmlrpclib
except ImportError:
    import xmlrpc.client as xmlrpclib
import pycurl
import sys

PY3 = sys.version_info[0] > 2


class CURLTransport(xmlrpclib.Transport):
    """Handles a cURL HTTP transaction to an XML-RPC server."""

    xmlrpc_h = [ "Content-Type: text/xml" ]

    def __init__(self, username=None, password=None):
        self.c = pycurl.Curl()
        self.c.setopt(pycurl.POST, 1)
        self.c.setopt(pycurl.NOSIGNAL, 1)
        self.c.setopt(pycurl.CONNECTTIMEOUT, 30)
        self.c.setopt(pycurl.HTTPHEADER, self.xmlrpc_h)
        if username != None and password != None:
            self.c.setopt(pycurl.USERPWD, '%s:%s' % (username, password))
        self._use_datetime = False

    def request(self, host, handler, request_body, verbose=0):
        b = StringIO()
        self.c.setopt(pycurl.URL, 'http://%s%s' % (host, handler))
        self.c.setopt(pycurl.POSTFIELDS, request_body)
        self.c.setopt(pycurl.WRITEFUNCTION, b.write)
        self.c.setopt(pycurl.VERBOSE, verbose)
        self.verbose = verbose
        try:
           self.c.perform()
        except pycurl.error:
            v = sys.exc_info()[1]
            if PY3:
                v = v.args
            raise xmlrpclib.ProtocolError(
                host + handler,
                v[0], v[1], None
                )
        b.seek(0)
        return self.parse_response(b)


if __name__ == "__main__":
    ## Test
    server = xmlrpclib.ServerProxy("http://betty.userland.com",
                                   transport=CURLTransport())
    print(server)
    try:
        print(server.examples.getStateName(41))
    except xmlrpclib.Error:
        v = sys.exc_info()[1]
        print("ERROR", v)
PK���\3���??-pycurl/examples/multi-socket_action-select.pynu�[���#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vi:ts=4:et

# Retrieves a single URL using the CurlMulti.socket_action calls, using
# select as the I/O polling mechanism:
#
# First, create a Multi object, and set socket and timer callbacks on it.
# Observed side effect: this causes the timer callback to be immediately
# invoked with the zero value for the timeout.
#
# The timer callback is very simple - it stores the timeout value passed
# into it in the global state for future use by the select calls that
# we will be making.
#
# The socket callback is more complicated. Its job is to add and remove
# socket handles to/from the data structure that we use for waiting for
# activity on them. The callback is invoked with a socket handle and the
# needed action (add for reading, add for writing or remove).
# Since this script utilizes the select call for waiting for activity,
# the socket callback updates the list of sockets which we should be
# polling for readability and the list that we should be polling for
# writability, which are then passed to the select call (and both of the
# sets are passed as the sockets to wait for errors/exceptions on).
#
# Next, create a Curl object (mapping to a libcurl easy handle), set the URL
# we are going to retrieve as well as any transfer options. This script sets
# the timeout to 5 seconds to be able to test failing transfers easily.
#
# Add the Curl object to the Multi object.
#
# Invoke Multi.socket_action to start the retrieval operation.
# Observed side effect: this causes the timer callback to be invoked
# with a greater than zero value for the timeout.
#
# By now we should have initialized our own state, which this script does
# prior to invoking any libcurl functions. Importantly, the state includes
# the timeout value that was communicated to us by libcurl.
#
# Run a loop which waits for activity on any of the sockets used by libcurl.
# The sockets are set that the socket callback has produced as of the
# present moment; the timeout is the most recent timeout value received by
# the timer callback.
#
# Importantly, the loop should not simply sleep for the entire
# timeout interval, as that would cause the transfer to take a very long time.
# It is *required* to use something like a select call to wait for activity
# on any of the sockets currently active for *up to* the timeout value.
#
# The loop terminates when the number of active transfers (handles in libcurl
# parlance) reaches zero. This number is provided by each socket_action
# call, which is why each call (even the ones that are called due to
# timeout being reached, as opposed to any socket activity) must update
# the number of running handles.
#
# After the loop terminates, clean up everything: remove the easy object from
# the multi object, close the easy object, close the multi object.

import sys, select
import pycurl
from io import BytesIO

if len(sys.argv) > 1:
    url = sys.argv[1]
else:
    url = 'https://www.python.org'

state = {
    'rlist': [],
    'wlist': [],
    'running': None,
    'timeout': None,
    'result': None,
    # If the transfer failed, code and msg will be filled in.
    'code': None,
    'msg': None,
}

def socket_fn(what, sock_fd, multi, socketp):
    if what == pycurl.POLL_IN or what == pycurl.POLL_INOUT:
        state['rlist'].append(sock_fd)
    elif what == pycurl.POLL_OUT or what == pycurl.POLL_INOUT:
        state['wlist'].append(sock_fd)
    elif what == pycurl.POLL_REMOVE:
        if sock_fd in state['rlist']:
            state['rlist'].remove(sock_fd)
        if sock_fd in state['wlist']:
            state['wlist'].remove(sock_fd)
    else:
        raise Exception("Unknown value of what: %s" % what)

def work(timeout):
    rready, wready, xready = select.select(
        state['rlist'], state['wlist'], set(state['rlist']) | set(state['wlist']), timeout)
    
    if len(rready) == 0 and len(wready) == 0 and len(xready) == 0:
        # The number of running handles must be updated after each
        # call to socket_action, which includes those with the SOCKET_TIMEOUT
        # argument (otherwise e.g. a transfer which failed due to
        # exceeding the connection timeout would hang).
        _, running = multi.socket_action(pycurl.SOCKET_TIMEOUT, 0)
    else:
        for sock_fd in rready:
            # socket_action returns a tuple whose first element is always the
            # CURLE_OK value (0), ignore it and use the second element only.
            _, running = multi.socket_action(sock_fd, pycurl.CSELECT_IN)
        for sock_fd in wready:
            _, running = multi.socket_action(sock_fd, pycurl.CSELECT_OUT)
        for sock_fd in xready:
            _, running = multi.socket_action(sock_fd, pycurl.CSELECT_ERR)
    
    # Since we are only performing a single transfer, we could call
    # Multi.info_read after the I/O loop terminates.
    # In practice, you would probably use socket_action with multiple
    # transfers, and you may want to be notified about transfer completion
    # as soon as the result is available.
    if state['running'] is not None and running != state['running']:
        # Some handle has completed.
        #
        # Note that socket_action was potentially called multiple times
        # in this function (e.g. if both a read handle became ready and a
        # different write handle became ready), therefore it is possible
        # that multiple handles have completed. In this particular script
        # we are only performing a single transfer (one
        # Curl object / easy handle), therefore only one transfer can ever
        # possibly complete.
        qmsg, successes, failures = multi.info_read()
        # We should have retrieved all of the available statuses, leaving
        # none in the queue.
        assert qmsg == 0
        
        # We have only one transfer.
        assert len(successes) == 1 and len(failures) == 0 or \
            len(successes) == 0 and len(failures) == 1
        
        if successes:
            state['result'] = True
        if failures:
            state['result'] = False
            # The failures array contains tuples of
            # (easy object, CURLE code, error message).
            _easy, state['code'], state['msg'] = failures[0]
    
    state['running'] = running

def timer_fn(timeout_ms):
    if timeout_ms < 0:
        # libcurl passes a negative timeout value when no further
        # calls should be made.
        state['timeout'] = None
    else:
        state['timeout'] = timeout_ms / 1000.0

multi = pycurl.CurlMulti()
multi.setopt(pycurl.M_SOCKETFUNCTION, socket_fn)
multi.setopt(pycurl.M_TIMERFUNCTION, timer_fn)

easy = pycurl.Curl()
easy.setopt(pycurl.URL, url)
# Uncomment to see what libcurl is doing throughout the transfer.
#easy.setopt(pycurl.VERBOSE, 1)
easy.setopt(pycurl.CONNECTTIMEOUT, 5)
easy.setopt(pycurl.LOW_SPEED_TIME, 5)
easy.setopt(pycurl.LOW_SPEED_LIMIT, 1)
_io = BytesIO()
easy.setopt(pycurl.WRITEDATA, _io)

multi.add_handle(easy)

handles = multi.socket_action(pycurl.SOCKET_TIMEOUT, 0)
# This should invoke the timer function with a timeout value.

while True:
    if state['running'] == 0:
        break
    else:
        # By the time we get here, timer function should have been already
        # invoked at least once so that we have a libcurl-supplied
        # timeout value. But in case this hasn't happened, default the timeout
        # to 1 second.
        timeout = state['timeout']
        if timeout is None:
            raise Exception('Need to poll for I/O but the timeout is not set!')
        work(timeout)

multi.remove_handle(easy)
easy.close()
multi.close()

# Uncomment to print the retrieved contents.
#print(_io.getvalue().decode())

if state['result'] is None:
    raise Exception('Script finished without a result!')
if state['result']:
    print('Transfer successful, retrieved %d bytes' % len(_io.getvalue()))
else:
    print('Transfer failed with code %d: %s' % (state['code'], state['msg']))
PK���\�h�W�"�"pycurl/RELEASE-NOTES.rstnu�[���Release Notes
=============

PycURL 7.45.6 - 2025-03-06
--------------------------

The previous release was accidentally built without CA bundle autodetection in
Linux wheels - this restores that behavior (no changes to macOS or Linux).

PycURL 7.45.5 - 2025-03-06
--------------------------

This release is mainly to update the wheels to incorporate libcurl 8.12.1 for
security fixes, as well as enable some additional libraries in wheel builds.

PycURL 7.45.4 - 2024-12-12
--------------------------

This release fixes several minor issues and adds support for several libcurl
options.  Additionally, it fixes several issues with the wheels on
Linux/macOS/Windows.

PycURL 7.45.3 - 2024-02-17
--------------------------

This release fixes several minor issues and adds support for several libcurl
options.  Additionally, we are now building wheels for Linux/macOS/Windows.

PycURL 7.45.2 - 2022-12-16
--------------------------

This release fixes several minor issues and adds support for several libcurl
options.

PycURL 7.45.1 - 2022-03-13
--------------------------

This release fixes build when libcurl < 7.64.1 is used.

PycURL 7.45.0 - 2022-03-09
--------------------------

This release adds support for SecureTransport SSL backend (MacOS), adds
ability to unset a number of multi options, adds ability to duplicate easy
handles and permits pycurl classes to be subclassed.

PycURL 7.44.1 - 2021-08-15
--------------------------

This release repairs incorrect Python thread initialization logic which
caused operations to hang.

PycURL 7.44.0 - 2021-08-08
--------------------------

This release reinstates best effort Python 2 support, adds Python 3.9 and
Python 3.10 alpha support and implements support for several libcurl options.

Official Windows builds are currently not being produced.

PycURL 7.43.0.6 - 2020-09-02
----------------------------

This release improves SSL backend detection on various systems, adds support
for libcurl's multiple SSL backend functionality and adds support for several
libcurl options.

PycURL 7.43.0.5 - 2020-01-29
----------------------------

This release fixes a build issue on recent Pythons on CentOS/RHEL distributions.

It also brings back Windows binaries. Special thank you to Gisle Vanem for
contributing the nghttp2 makefile.


PycURL 7.43.0.4 - 2020-01-15
----------------------------

This release improves compatibility with Python 3.8 and removes support for
Python 2 and Python 3.4. It also adds wolfSSL support and thread safety of
the multi interface.


PycURL 7.43.0.3 - 2019-06-17
----------------------------

This release primarily fixes an OpenSSL-related installation issue, and
repairs the ability to use PycURL with newer libcurls compiled without FTP
support. Also, mbedTLS support has been contributed by Josef Schlehofer.


PycURL 7.43.0.2 - 2018-06-02
----------------------------

Highlights of this release:

1. Experimental perform_rs and perform_rb methods have been added to Curl
   objects. They return response body as a string and a byte string,
   respectively. The goal of these methods is to improve PycURL's usability
   for typical use cases, specifically removing the need to set up
   StringIO/BytesIO objects to store the response body.

2. getinfo_raw and errstr_raw methods have been added to Curl objects to
   return transfer information as byte strings, permitting applications to
   retrieve transfer information that is not decodable using Python's
   default encoding.

3. errstr and "fail or error" exceptions now replace undecodable bytes
   so as to provide usable strings; use errstr_raw to retrieve original
   byte strings.

4. There is no longer a need to keep references to Curl objects when they
   are used in CurlMulti objects - PycURL now maintains such references
   internally.

5. Official Windows builds now include HTTP/2 and international domain
   name support.

6. PycURL now officially supports BoringSSL.

7. A number of smaller improvements have been made and bugs fixed.


PycURL 7.43.0.1 - 2017-12-07
----------------------------

This release collects fixes and improvements made over the past two years,
notably updating Windows dependencies to address DNS resolution and
TLS connection issues.


PycURL 7.43.0 - 2016-02-02
--------------------------

Highlights of this release:

1. Binary wheels are now built for Windows systems.

2. setopt_string method added to Curl objects to permit setting string libcurl
   options that PycURL does not know about.

3. curl module can now be imported on Windows again.

4. OPENSOCKETFUNCTION callback is now invoked with the address as bytes on
   Python 3 as was documented.

5. Support for many libcurl options and constants was added.


PycURL 7.21.5 - 2016-01-05
--------------------------

Highlights of this release:

1. Socket callbacks are now fully implemented (``CURLOPT_OPENSOCKETFUNCTION``,
   ``CURLOPT_SOCKOPTFUNCTION``, ``CURLOPT_CLOSESOCKETFUNCTION``). Unfortunately
   this required changing ``OPENSOCKETFUNCTION`` API once again in a
   backwards-incompatible manner. Support for ``SOCKOPTFUNCTION`` and
   ``CLOSESOCKETFUNCTION`` was added in this release. ``OPENSOCKETFUNCTION``
   now supports Unix sockets.

2. Many other libcurl options and constants have been added to PycURL.

3. When ``pycurl`` module initialization fails, ``ImportError`` is raised
   instead of a fatal error terminating the process.

4. Usability of official Windows builds has been greatly improved:

   * Dependencies are linked statically, eliminating possible DLL conflicts.
   * OpenSSL is used instead of WinSSL.
   * libcurl is linked against C-Ares and libssh2.


PycURL 7.19.5.3 - 2015-11-03
----------------------------

PycURL 7.19.5.2 release did not include some of the test suite files in
its manifest, leading to inability to run the test suite from the sdist
tarball. This is now fixed thanks to Kamil Dudka.


PycURL 7.19.5.2 - 2015-11-02
----------------------------

Breaking change: DEBUGFUNCTION now takes bytes rather than (Unicode) string
as its argument on Python 3.

Breaking change: CURLMOPT_* option constants moved from Easy to Multi
class. They remain available in pycurl module.

SSL library detection improved again, --libcurl-dll option to setup.py added.

Options that required tuples now also accept lists, and vice versa.

This release fixes several memory leaks and one use after free issue.

Support for several new libcurl options and constants has been added.


PycURL 7.19.5.1 - 2015-01-06
----------------------------

This release primarily fixes build breakage against libcurl 7.19.4 through
7.21.1, such as versions shipped with CentOS.


PycURL 7.19.5 - 2014-07-12
--------------------------

PycURL C code has been significantly reorganized. Curl, CurlMulti and
CurlShare classes are now properly exported, instead of factory functions for
the respective objects. PycURL API has not changed.

Documentation has been transitioned to Sphinx and reorganized as well.
Both docstrings and standalone documentation are now more informative.

Documentation is no longer included in released distributions. It can be
generated from source by running `make docs`.

Tests are no longer included in released distributions. Instead the
documentation and quickstart examples should be consulted for sample code.

Official Windows builds now are linked against zlib.


PycURL 7.19.3.1 - 2014-02-05
----------------------------

This release restores PycURL's ability to automatically detect SSL library
in use in most circumstances, thanks to Andjelko Horvat.


PycURL 7.19.3 - 2014-01-09
--------------------------

This release brings official Python 3 support to PycURL.
Several GNU/Linux distributions provided Python 3 packages of PycURL
previously; these packages were based on patches that were incomplete and
in some places incorrect. Behavior of PycURL 7.19.3 and later may therefore
differ from behavior of unofficial Python 3 packages of previous PycURL
versions.

To summarize the behavior under Python 3, PycURL will accept ``bytes`` where
it accepted strings under Python 2, and will also accept Unicode strings
containing ASCII codepoints only for convenience. Please refer to
`Unicode`_ and `file`_ documentation for further details.

In the interests of compatibility, PycURL will also accept Unicode data on
Python 2 given the same constraints as under Python 3.

While Unicode and file handling rules are expected to be sensible for
all use cases, and retain backwards compatibility with previous PycURL
versions, please treat behavior of this versions under Python 3 as experimental
and subject to change.

Another potentially disruptive change in PycURL is the requirement for
compile time and runtime SSL backends to match. Please see the readme for
how to indicate the SSL backend to setup.py.

.. _Unicode: doc/unicode.html
.. _file: doc/files.html
PK���\H�wffpycurl/README.rstnu�[���PycURL -- A Python Interface To The cURL library
================================================

.. image:: https://github.com/pycurl/pycurl/workflows/CI/badge.svg
	   :target: https://github.com/pycurl/pycurl/actions

PycURL is a Python interface to `libcurl`_, the multiprotocol file
transfer library. Similarly to the urllib_ Python module,
PycURL can be used to fetch objects identified by a URL from a Python program.
Beyond simple fetches however PycURL exposes most of the functionality of
libcurl, including:

- Speed - libcurl is very fast and PycURL, being a thin wrapper above
  libcurl, is very fast as well. PycURL `was benchmarked`_ to be several
  times faster than requests_.
- Features including multiple protocol support, SSL, authentication and
  proxy options. PycURL supports most of libcurl's callbacks.
- Multi_ and share_ interfaces.
- Sockets used for network operations, permitting integration of PycURL
  into the application's I/O loop (e.g., using Tornado_).

.. _was benchmarked: http://stackoverflow.com/questions/15461995/python-requests-vs-pycurl-performance
.. _requests: http://python-requests.org/
.. _Multi: https://curl.haxx.se/libcurl/c/libcurl-multi.html
.. _share: https://curl.haxx.se/libcurl/c/libcurl-share.html
.. _Tornado: http://www.tornadoweb.org/


Requirements
------------

- Python 3.5-3.10.
- libcurl 7.19.0 or better.


Installation
------------

Download source and binary distributions from `PyPI`_.
Binary wheels are now available for 32 and 64 bit Windows versions.

Please see `INSTALL.rst`_ for installation instructions. If installing from
a Git checkout, please follow instruction in the `Git Checkout`_ section
of INSTALL.rst.

.. _PyPI: https://pypi.python.org/pypi/pycurl
.. _INSTALL.rst: http://pycurl.io/docs/latest/install.html
.. _Git Checkout: http://pycurl.io/docs/latest/install.html#git-checkout


Documentation
-------------

Documentation for the most recent PycURL release is available on
`PycURL website <http://pycurl.io/docs/latest/>`_.

Documentation for the development version of PycURL
is available `here <http://pycurl.io/docs/dev/>`_.

To build documentation from source, run ``make docs``.
Building documentation requires `Sphinx <http://sphinx-doc.org/>`_ to
be installed, as well as pycurl extension module built as docstrings are
extracted from it. Built documentation is stored in ``build/doc``
subdirectory.


Support
-------

For support questions please use `curl-and-python mailing list`_.
`Mailing list archives`_ are available for your perusal as well.

Although not an official support venue, `Stack Overflow`_ has been
popular with some PycURL users.

Bugs can be reported `via GitHub`_. Please use GitHub only for bug
reports and direct questions to our mailing list instead.

.. _curl-and-python mailing list: https://lists.haxx.se/listinfo/curl-and-python
.. _Stack Overflow: http://stackoverflow.com/questions/tagged/pycurl
.. _Mailing list archives: https://curl.haxx.se/mail/list.cgi?list=curl-and-python
.. _via GitHub: https://github.com/pycurl/pycurl/issues


Automated Tests
---------------

PycURL comes with an automated test suite. To run the tests, execute::

    make test

The suite depends on packages `pytest`_ and `flask`_, as well as `vsftpd`_.

Some tests use vsftpd configured to accept anonymous uploads. These tests
are not run by default. As configured, vsftpd will allow reads and writes to
anything the user running the tests has read and write access. To run
vsftpd tests you must explicitly set PYCURL_VSFTPD_PATH variable like so::

    # use vsftpd in PATH
    export PYCURL_VSFTPD_PATH=vsftpd

    # specify full path to vsftpd
    export PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd

.. _pytest: https://pytest.org/
.. _flask: https://flask.palletsprojects.com/
.. _vsftpd: http://vsftpd.beasts.org/


Test Matrix
-----------

The test matrix is a separate framework that runs tests on more esoteric
configurations. It supports:

- Testing against Python 2.4, which bottle does not support.
- Testing against Python compiled without threads, which requires an out of
  process test server.
- Testing against locally compiled libcurl with arbitrary options.

To use the test matrix, first start the test server from Python 2.5+ by
running::

    python -m tests.appmanager

Then in a different shell, and preferably in a separate user account,
run the test matrix::

    # run ftp tests, etc.
    export PYCURL_VSFTPD_PATH=vsftpd
    # create a new work directory, preferably not under pycurl tree
    mkdir testmatrix
    cd testmatrix
    # run the matrix specifying absolute path
    python /path/to/pycurl/tests/matrix.py

The test matrix will download, build and install supported Python versions
and supported libcurl versions, then run pycurl tests against each combination.
To see what the combinations are, look in
`tests/matrix.py <tests/matrix.py>`_.


Contribute
----------

For smaller changes:

#. Fork `the repository`_ on Github.
#. Create a branch off **master**.
#. Make your changes.
#. Write a test which shows that the bug was fixed or that the feature
   works as expected.
#. Send a pull request.
#. Check back after 10-15 minutes to see if tests passed on Travis CI.
   PycURL supports old Python and libcurl releases and their support is tested
   on Travis.

For larger changes:

#. Join the `mailing list`_.
#. Discuss your proposal on the mailing list.
#. When consensus is reached, implement it as described above.

Please contribute binary distributions for your system to the
`downloads repository`_.


License
-------

::

    Copyright (C) 2001-2008 by Kjetil Jacobsen <kjetilja at gmail.com>
    Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer <markus at oberhumer.com>
    Copyright (C) 2013-2022 by Oleg Pudeyev <code at olegp.name>

    All rights reserved.

    PycURL is dual licensed under the LGPL and an MIT/X derivative license
    based on the cURL license.  A full copy of the LGPL license is included
    in the file COPYING-LGPL.  A full copy of the MIT/X derivative license is
    included in the file COPYING-MIT.  You can redistribute and/or modify PycURL
    according to the terms of either license.

.. _PycURL: http://pycurl.io/
.. _libcurl: https://curl.haxx.se/libcurl/
.. _urllib: http://docs.python.org/library/urllib.html
.. _`the repository`: https://github.com/pycurl/pycurl
.. _`mailing list`: https://lists.haxx.se/listinfo/curl-and-python
.. _`downloads repository`: https://github.com/pycurl/downloads
PK���\]��a��pycurl/AUTHORSnu�[���Copyright (C) 2001-2008 by Kjetil Jacobsen <kjetilja at gmail.com>
Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer <markus at oberhumer.com>
Copyright (C) 2013-2022 by Oleg Pudeyev <code at olegp.name>

Please see README, COPYING-LGPL and COPYING-MIT for license information.

The following individuals contributed code to PycURL:

Aaron Hill <visine19 at hotmail.com>
Adam Guthrie <therigu at users.sourceforge.net>
Adam Jacob Muller <adam at isprime.com>
Akiomi Kamakura <akiomik at gmail.com>
Alexandre Pion <pion at afnic.fr>
Amir Rossert <amir.rossert at safebreach.com>
Amit Mongia <amit_mongia at hotmail.com>
Andjelko Horvat <comel at vingd.com>
Arshad Khan <khan.m.arshad at gmail.com>
Artur Sobierak <asobierak at gmail.com>
Ashley Whetter <ashleyw at activestate.com>
Barry Warsaw <barry at python.org>
Bastian Kleineidam
Benjamin Peterson <benjamin at python.org>
Bill Collins <bill.collins at hp.com>
Bo Anderson <mail at boanderson.me>
Casey Miller <camiller at linkedin.com>
Chih-Hsuan Yen <yan12125 at gmail.com>
Christian Clauss <cclauss at me.com>
Christopher Warner <cwarner at kernelcode.com>
Clint Clayton <clintclayton at me.com>
Conrad Steenberg <conrad at hep.caltech.edu>
Daniel Pena Arteaga <dpena at ph.tum.de>
Daniel Stenberg <daniel at haxx.se>
decitre <decitre at gmail.com>
Dima Tisnek <dimaqq at gmail.com>
Dmitriy Taychenachev <dmitriy.taychenachev at skypicker.com>
Dmitry Ketov <dketov at gmail.com>
Dom Sekotill <dom.sekotill at kodo.org.uk>
Domenico Andreoli <cavok at libero.it>
Dominique <curl-and-python at d242.net>
Eneas U de Queiroz <cotequeiroz at gmail.com>
Eric S. Raymond <esr at thyrsus.com>
Felix Yan <felixonmars at archlinux.org>
Francisco Alves <chico at corp.globo.com>
Gabi Davar <grizzly.nyo at gmail.com>
Gisle Vanem <gvanem at yahoo.no>
Gregory Petukhov <lorien at lorien.name>
Hasan <aliyevH at hotmail.com>
Hugo <hugovk at users.noreply.github.com>
Iain R. Learmonth <irl at fsfe.org>
ideal <idealities at gmail.com>
Jakob Truelsen <jakob at scalgo.com>
Jakub Wilk <jwilk at jwilk.net>
James Deucker <bitwisecook at users.noreply.github.com>
Jan Kryl <jan.kryl at nexenta.com>
Jayne <corvine at gmail.com>
James Deucker <bitwisecook at users.noreply.github.com>
Jean Hominal <jhominal at gmail.com>
JiCiT <jason at infinitebubble.com>
Jim Patterson
Josef Schlehofer <pepe.schlehofer at gmail.com>
Jozef Melicher <jozef.melicher at eset.sk>
K.S.Sreeram <sreeram at tachyontech.net>
Kamil Dudka <kdudka at redhat.com>
Kevin Ko <kevin.s.ko at gmail.com>
Kevin Schlosser <drschlosser at hotmail.com>
Khavish Anshudass Bhundoo <khavishbhundoo at users.noreply.github.com>
Kian-Meng Ang <kianmeng at cpan.org>
kxrd <onyeabor at riseup.net>
Lipin Dmitriy <blackwithwhite666 at gmail.com>
Léo El Amri <leo at superlel.me>
Marc Labranche <mlabranche at developertown.com>
Marcel Brouwers <marcel at marcelbrouwers.nl>
Marcelo Jorge Vieira <metal at alucinados.com>
Marien Zwart <marienz at users.sourceforge.net>
Mark Eichin
Markus <nepenthesdev at gmail.com>
Martin Muenstermann <mamuema at sourceforge.net>
Matt King <matt at gnik.com>
Michael C <michael at mchang.name>
Michael Cho <michael at michaelcho.dev>
Michael Coughlin <michael.w.coughlin at gmail.com>
Michael Treanor <26148512+skeptycal at users.noreply.github.com>
Michał Górny <mgorny at gentoo.org>
Miro Hrončok <miro at hroncok.cz>
Nelson Chen <crazysim at gmail.com>
Nick Pilon <npilon at oreilly.com>
Nicolas Pauss <nicolas.pauss at intersec.com>
Oleg Broytman <phd at phdru.name>
Oren <orenyomtov at users.noreply.github.com>
Orion Poplawski <orion at cora.nwra.com>
Oskari Saarenmaa <os at ohmu.fi>
Paul Pacheco
Pavel Horáček <horacek.pavel at protonmail.com>
Pierre Grimaud <grimaud.pierre at gmail.com>
René Dudfield <renesd at gmail.com>
resokou <resokou at gmail.com>
Roland Sommer <rol at ndsommer.de>
Romuald Brunet <romuald at gandi.net>
Romulo A. Ceccon <romuloceccon at gmail.com>
Russell McConnachie <okanaganrusty at mcconnachie.ca>
Russell McConnachie <pmcconna at cisco.com>
Samuel Dion-Girardeau <samuel.diongirardeau at gmail.com>
Samuel Henrique <samueloph at debian.org>
Scott Talbert <swt at techie.net>
Simon Legner <Simon.Legner at gmail.com>
Srinivas <spg349 at nyu.edu>
Steve Kowalik <steven at wedontsleep.org>
Subin <eourm20 at gmail.com>
Tal Einat <tal.einat at socialcodeinc.com>
Thomas Hunger <teh at camvine.org>
Tino Lange <Tino.Lange at gmx.de>
toddrme2178 <toddrme2178 at gmail.com>
Tom Pierce <tom.pierce0 at gmail.com>
Vesa Jääskeläinen <vesa.jaaskelainen at vaisala.com>
Victor Lascurain <bittor at eleka.net>
Vincent Philippon <Vincent.Philippon at ubisoft.com>
Vitaly Murashev <vitaly.murashev at gmail.com>
Vitezslav Cizek <vcizek at suse.com>
vmurashev <vitaly.murashev at gmail.com>
Wei C <gitsouler at users.noreply.github.com>
Whitney Sorenson <wsorenson at gmail.com>
Wim Lewis <wiml at users.sourceforge.net>
Yiteng Zhang <yiteng.zhang at oracle.com>
Yuhui H <eyecat at gmail.com>
Yuri Ushakov <yuri.ushakov at gmail.com>
Yves Bastide <yves at botify.com>
Zdenek Pavlas <zpavlas at redhat.com>
ziggy <ziggy at elephant-bird.net>
PK���\>X"V�g�gpycurl/COPYING-LGPLnu�[���                  GNU LESSER GENERAL PUBLIC LICENSE
                       Version 2.1, February 1999

 Copyright (C) 1991, 1999 Free Software Foundation, Inc.
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

[This is the first released version of the Lesser GPL.  It also counts
 as the successor of the GNU Library Public License, version 2, hence
 the version number 2.1.]

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.

  This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it.  You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.

  When we speak of free software, we are referring to freedom of use,
not price.  Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.

  To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights.  These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.

  For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you.  You must make sure that they, too, receive or can get the source
code.  If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it.  And you must show them these terms so they know their rights.

  We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.

  To protect each distributor, we want to make it very clear that
there is no warranty for the free library.  Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.

  Finally, software patents pose a constant threat to the existence of
any free program.  We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder.  Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.

  Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License.  This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License.  We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.

  When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library.  The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom.  The Lesser General
Public License permits more lax criteria for linking other code with
the library.

  We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License.  It also provides other free software developers Less
of an advantage over competing non-free programs.  These disadvantages
are the reason we use the ordinary General Public License for many
libraries.  However, the Lesser license provides advantages in certain
special circumstances.

  For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard.  To achieve this, non-free programs must be
allowed to use the library.  A more frequent case is that a free
library does the same job as widely used non-free libraries.  In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.

  In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software.  For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.

  Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.

  The precise terms and conditions for copying, distribution and
modification follow.  Pay close attention to the difference between a
"work based on the library" and a "work that uses the library".  The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.

                  GNU LESSER GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".

  A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.

  The "Library", below, refers to any such software library or work
which has been distributed under these terms.  A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language.  (Hereinafter, translation is
included without limitation in the term "modification".)

  "Source code" for a work means the preferred form of the work for
making modifications to it.  For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.

  Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it).  Whether that is true depends on what the Library does
and what the program that uses the Library does.

  1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.

  You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.

  2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) The modified work must itself be a software library.

    b) You must cause the files modified to carry prominent notices
    stating that you changed the files and the date of any change.

    c) You must cause the whole of the work to be licensed at no
    charge to all third parties under the terms of this License.

    d) If a facility in the modified Library refers to a function or a
    table of data to be supplied by an application program that uses
    the facility, other than as an argument passed when the facility
    is invoked, then you must make a good faith effort to ensure that,
    in the event an application does not supply such function or
    table, the facility still operates, and performs whatever part of
    its purpose remains meaningful.

    (For example, a function in a library to compute square roots has
    a purpose that is entirely well-defined independent of the
    application.  Therefore, Subsection 2d requires that any
    application-supplied function or table used by this function must
    be optional: if the application does not supply it, the square
    root function must still compute square roots.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.

In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library.  To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License.  (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.)  Do not make any other change in
these notices.

  Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.

  This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.

  4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.

  If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.

  5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library".  Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.

  However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library".  The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.

  When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library.  The
threshold for this to be true is not precisely defined by law.

  If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work.  (Executables containing this object code plus portions of the
Library will still fall under Section 6.)

  Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.

  6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.

  You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License.  You must supply a copy of this License.  If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License.  Also, you must do one
of these things:

    a) Accompany the work with the complete corresponding
    machine-readable source code for the Library including whatever
    changes were used in the work (which must be distributed under
    Sections 1 and 2 above); and, if the work is an executable linked
    with the Library, with the complete machine-readable "work that
    uses the Library", as object code and/or source code, so that the
    user can modify the Library and then relink to produce a modified
    executable containing the modified Library.  (It is understood
    that the user who changes the contents of definitions files in the
    Library will not necessarily be able to recompile the application
    to use the modified definitions.)

    b) Use a suitable shared library mechanism for linking with the
    Library.  A suitable mechanism is one that (1) uses at run time a
    copy of the library already present on the user's computer system,
    rather than copying library functions into the executable, and (2)
    will operate properly with a modified version of the library, if
    the user installs one, as long as the modified version is
    interface-compatible with the version that the work was made with.

    c) Accompany the work with a written offer, valid for at
    least three years, to give the same user the materials
    specified in Subsection 6a, above, for a charge no more
    than the cost of performing this distribution.

    d) If distribution of the work is made by offering access to copy
    from a designated place, offer equivalent access to copy the above
    specified materials from the same place.

    e) Verify that the user has already received a copy of these
    materials or that you have already sent this user a copy.

  For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it.  However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.

  It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system.  Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.

  7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:

    a) Accompany the combined library with a copy of the same work
    based on the Library, uncombined with any other library
    facilities.  This must be distributed under the terms of the
    Sections above.

    b) Give prominent notice with the combined library of the fact
    that part of it is a work based on the Library, and explaining
    where to find the accompanying uncombined form of the same work.

  8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License.  Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License.  However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.

  9. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Library or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.

  10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.

  11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all.  For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded.  In such case, this License incorporates the limitation as if
written in the body of this License.

  13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number.  If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation.  If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.

  14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission.  For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this.  Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.

                            NO WARRANTY

  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.

                     END OF TERMS AND CONDITIONS

           How to Apply These Terms to Your New Libraries

  If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change.  You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).

  To apply these terms, attach the following notices to the library.  It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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!
PK���\�0l���pycurl/COPYING-MITnu�[���COPYRIGHT AND PERMISSION NOTICE

Copyright (C) 2001-2008 by Kjetil Jacobsen <kjetilja at gmail.com>
Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer <markus at oberhumer.com>
Copyright (C) 2013-2022 by Oleg Pudeyev <code at olegp.name>

All rights reserved.

Permission to use, copy, modify, and distribute this software for any purpose
with or without fee is hereby granted, provided that the above copyright
notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of a copyright holder shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization of the copyright holder.
PK���\�W�z�/�/pycurl/INSTALL.rstnu�[���.. _install:

PycURL Installation
===================

NOTE: You need Python and libcurl installed on your system to use or
build pycurl.  Some RPM distributions of curl/libcurl do not include
everything necessary to build pycurl, in which case you need to
install the developer specific RPM which is usually called curl-dev.


Distutils
---------

Build and install pycurl with the following commands::

    (if necessary, become root)
    tar -zxvf pycurl-$VER.tar.gz
    cd pycurl-$VER
    python setup.py install

$VER should be substituted with the pycurl version number, e.g. 7.10.5.

Note that the installation script assumes that 'curl-config' can be
located in your path setting.  If curl-config is installed outside
your path or you want to force installation to use a particular
version of curl-config, use the '--curl-config' command line option to
specify the location of curl-config.  Example::

    python setup.py install --curl-config=/usr/local/bin/curl-config

If libcurl is linked dynamically with pycurl, you may have to alter the
LD_LIBRARY_PATH environment variable accordingly.  This normally
applies only if there is more than one version of libcurl installed,
e.g. one in /usr/lib and one in /usr/local/lib.


SSL
^^^

PycURL requires that the SSL library that it is built against is the same
one libcurl, and therefore PycURL, uses at runtime. PycURL's ``setup.py``
uses ``curl-config`` to attempt to figure out which SSL library libcurl
was compiled against, however this does not always work. If PycURL is unable
to determine the SSL library in use it will print a warning similar to
the following::

    src/pycurl.c:137:4: warning: #warning "libcurl was compiled with SSL support, but configure could not determine which " "library was used; thus no SSL crypto locking callbacks will be set, which may " "cause random crashes on SSL requests" [-Wcpp]

It will then fail at runtime as follows::

    ImportError: pycurl: libcurl link-time ssl backend (openssl) is different from compile-time ssl backend (none/other)

To fix this, you need to tell ``setup.py`` what SSL backend is used::

    python setup.py --with-[openssl|gnutls|nss|mbedtls|wolfssl|sectransp|schannel] install

Note: as of PycURL 7.21.5, setup.py accepts ``--with-openssl`` option to
indicate that libcurl is built against OpenSSL/LibreSSL/BoringSSL.
``--with-ssl`` is an alias
for ``--with-openssl`` and continues to be accepted for backwards compatibility.

You can also ask ``setup.py`` to obtain SSL backend information from installed
libcurl shared library, as follows:

    python setup.py --libcurl-dll=libcurl.so

An unqualified ``libcurl.so`` would use the system libcurl, or you can
specify a full path.


easy_install / pip
------------------

::

    easy_install pycurl
    pip install pycurl

If you need to specify an alternate curl-config, it can be done via an
environment variable::

    export PYCURL_CURL_CONFIG=/usr/local/bin/curl-config
    easy_install pycurl

The same applies to the SSL backend, if you need to specify it (see the SSL
note above)::

    export PYCURL_SSL_LIBRARY=[openssl|gnutls|nss|mbedtls|sectransp|schannel]
    easy_install pycurl


pip and cached pycurl package
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If you have already installed pycurl and are trying to reinstall it via
pip with different SSL options for example, pip may reinstall the package it
has previously compiled instead of recompiling pycurl with newly specified
options. More details are given in `this Stack Overflow post`_.

To force pip to recompile pycurl, run::

    # upgrade pip if necessary
    pip install --upgrade pip

    # remove current pycurl
    pip uninstall pycurl

    # set PYCURL_SSL_LIBRARY
    export PYCURL_SSL_LIBRARY=nss

    # recompile and install pycurl
    pip install --compile pycurl

.. _this Stack Overflow post: http://stackoverflow.com/questions/21487278/ssl-error-installing-pycurl-after-ssl-is-set


Windows
-------

There are currently no official binary Windows packages. You can build PycURL
from source or use third-party binary packages.


Building From Source
^^^^^^^^^^^^^^^^^^^^

Building PycURL from source is not for the faint of heart due to the multitude
of possible dependencies and each of these dependencies having its own
directory structure, configuration style, parameters and quirks.
Additionally different dependencies have different
settings for MSVCRT usage, and an application must have all of its parts
agreeing on a single setting. If you decide to build PycURL from source
it is advisable to look through the ``winbuild.py``
script - it is used to build the official binaries and contains a wealth
of information for compiling PycURL's dependencies on Windows.

If you are compiling PycURL from source it is recommended to compile all of its
dependencies from source as well. Using precompiled libraries may lead to
multiple MSVCRT versions mixed in the resulting PycURL binary, which will
not be good.

If PycURL is to be linked statically against its dependencies, OpenSSL must
be patched to link to the DLL version of MSVCRT. There is a patch for this in
``winbuild`` directory of PycURL source.

For a minimum build you will just need libcurl source. Follow its Windows
build instructions to build either a static or a DLL version of the library,
then configure PycURL as follows to use it::

    python setup.py --curl-dir=c:\dev\curl-7.33.0\builds\libcurl-vc-x86-release-dll-ipv6-sspi-spnego-winssl --use-libcurl-dll

Note that ``--curl-dir`` must point not to libcurl source but rather to headers
and compiled libraries.

If libcurl and Python are not linked against the same exact C runtime
(version number, static/dll, single-threaded/multi-threaded) you must use
``--avoid-stdio`` option (see below).

Additional Windows setup.py options:

- ``--use-libcurl-dll``: build against libcurl DLL, if not given PycURL will
  be built against libcurl statically.
- ``--libcurl-lib-name=libcurl_imp.lib``: specify a different name for libcurl
  import library. The default is ``libcurl.lib`` which is appropriate for
  static linking and is sometimes the correct choice for dynamic linking as
  well. The other possibility for dynamic linking is ``libcurl_imp.lib``.
- ``--with-openssl``: use OpenSSL/LibreSSL/BoringSSL crypto locks when libcurl
  was built against these SSL backends.
- ``--with-ssl``: legacy alias for ``--with-openssl``.
- ``--openssl-lib-name=""``: specify a different name for OpenSSL import
  library containing CRYPTO_num_locks. For OpenSSL 1.1.0+ this should be set
  to an empty string as given here.
- ``--avoid-stdio``: on Windows, a process and each library it is using
  may be linked to its own version of the C runtime (MSVCRT).
  FILE pointers from one C runtime may not be passed to another C runtime.
  This option prevents direct passing of FILE pointers from Python to libcurl,
  thus permitting Python and libcurl to be linked against different C runtimes.
  This option may carry a performance penalty when Python file objects are
  given directly to PycURL in CURLOPT_READDATA, CURLOPT_WRITEDATA or
  CURLOPT_WRITEHEADER options. This option applies only on Python 2; on
  Python 3, file objects no longer expose C library FILE pointers and the
  C runtime issue does not exist. On Python 3, this option is recognized but
  does nothing. You can also give ``--avoid-stdio`` option in
  PYCURL_SETUP_OPTIONS environment variable as follows::

    PYCURL_SETUP_OPTIONS=--avoid-stdio pip install pycurl

A good ``setup.py`` target to use is ``bdist_wininst`` which produces an
executable installer that you can run to install PycURL.

You may find the following mailing list posts helpful:

- https://curl.haxx.se/mail/curlpython-2009-11/0010.html
- https://curl.haxx.se/mail/curlpython-2013-11/0002.html


winbuild.py
^^^^^^^^^^^

This script is used to build official PycURL Windows packages. You can
use it to build a full complement of packages with your own options or modify
it to build a single package you need.

Prerequisites:

- `Git for Windows`_.
- Appropriate `Python versions`_ installed.
- MS Visual C++ 9/2008 for Python <= 3.2, MS Visual C++ 10/2010 for
  Python 3.3 or 3.4, MS Visual C++ 14/2015 for Python 3.5 through 3.8.
  Express versions of Visual Studio work fine for this,
  although getting 64 bit compilers to wok in some Express versions involves
  jumping through several hoops.
- NASM if building libcurl against OpenSSL.
- ActivePerl if building libcurl against OpenSSL. The perl shipping with
  Git for Windows handles forward and backslashes in paths in a way that is
  incompatible with OpenSSL's build scripts.

.. _Git for Windows: https://git-for-windows.github.io/
.. _Python versions: http://python.org/download/

``winbuild.py`` assumes all programs are installed in their default locations,
if this is not the case edit it as needed. ``winbuild.py`` itself can be run
with any Python it supports.


Using PycURL With Custom Python Builds
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

As of version 7.21.5, the official binary packages of PycURL are linked
statically against all of its dependencies except MSVCRT. This means that
as long as your custom Python build uses the same version of MSVC as the
corresponding official Python build as well as the same MSVCRT linking setting
(/MD et. al.), an official PycURL package should work.

If your Python build uses different MSVCRT settings or a different MSVC
version from the official Python builds, you will need to compile PycURL
from source.

If the C runtime library (MSVCRT.DLL) versions used by PycURL and Python
do not match, you will receive a message
like the following one when trying to import ``pycurl`` module::

    ImportError: DLL load failed: The specified procedure could not be found.

To identify which MSVCRT version your Python uses use the
`application profiling feature`_ of
`Dependency Walker`_ and look for `msvcrt.dll variants`_ being loaded.
You may find `the entire thread starting here`_ helpful.

.. _application profiling feature: https://curl.haxx.se/mail/curlpython-2014-05/0007.html
.. _Dependency Walker: http://www.dependencywalker.com/
.. _msvcrt.dll variants: https://curl.haxx.se/mail/curlpython-2014-05/0010.html
.. _the entire thread starting here: https://curl.haxx.se/mail/curlpython-2014-05/0000.html


Git Checkout
------------

In order to build PycURL from a Git checkout, some files need to be
generated. On Unix systems it is easiest to build PycURL with ``make``::

    make

To specify which curl or SSL backend to compile against, use the same
environment variables as easy_install/pip, namely ``PYCURL_CURL_CONFIG``
and ``PYCURL_SSL_LIBRARY``.

To generate generated files only you may run::

    make gen

This might be handy if you are on Windows. Remember to run ``make gen``
whenever you change sources.

To generate documentation, run::

    make docs

Generating documentation requires `Sphinx`_ to be installed.

.. _Sphinx: http://sphinx-doc.org/


A Note Regarding SSL Backends
-----------------------------

libcurl's functionality varies depending on which SSL backend it is compiled
against. For example, users have `reported`_ `problems`_ with GnuTLS backend.
As of this writing, generally speaking, OpenSSL backend has the most
functionality as well as the best compatibility with other software.

If you experience SSL issues, especially if you are not using OpenSSL
backend, you can try rebuilding libcurl and PycURL against another SSL backend.

.. _reported: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=515200
.. _problems: https://bugs.launchpad.net/ubuntu/+source/pycurl/+bug/1111673


SSL Certificate Bundle
----------------------

libcurl, and PycURL, by default verify validity of HTTPS servers' SSL
certificates. Doing so requires a CA certificate bundle, which libcurl
and most SSL libraries do not provide.

Here_ is a good resource on how to build your own certificate bundle.
certifie.com also has a `prebuilt certificate bundle`_.
To use the certificate bundle, use ``CAINFO`` or ``CAPATH`` PycURL
options.

.. _Here: http://certifie.com/ca-bundle/
.. _prebuilt certificate bundle: http://certifie.com/ca-bundle/ca-bundle.crt.txt
PK�*�\Z�\��,�,'alt-php84-ioncube-loader/USER-GUIDE.txtnu�[���ionCube Loader 15.5 User Guide
=====================================

This document describes the available php.ini configuration options of the
ionCube Loader that relate to processing of PHP encoded files, and also the
ionCube24 service. It also describes which encoded files can be run by each
ionCube Loader.

PERFORMANCE OF ENCODED FILES
----------------------------

We recommend that the encoded paths feature (see below) is used 
with encoded files in order to maximise performance.

ENCODED FILES  
-------------

INI entry: ioncube.loader.encoded_paths

Purpose:   Specify the locations of encoded files

  The ionCube Loader will normally examine a PHP file before processing
  to test whether it is encoded, and will run the file itself if necessary.
  Although this checking is very efficient, restricting which files the
  Loader tests for being encoded may give extra performance. If set to 
  a series of paths or files, only files in those locations are tested.

  Entries should be separated by a : on Unix and ; on Windows. 
  A path may be prefixed with + or - to add or remove that path from
  the possible locations. + is assumed if no character is given.


Examples: (... means ioncube.loader.encoded_paths)

  * Site with a single encoded module in /var/www/html/modules/RSS

... = "/var/www/html/modules/RSS"


  * As above, with a site configuration file encoded too.

... = "/var/www/html/modules/RSS:/var/www/html/config/config.php"


  * Encoded files may be anywhere except for /var/www/html/framework

... = "/:-/var/www/html/framework"


  * Site with most modules encoded except for one

... = "/var/www/html/modules:-/var/www/html/modules/plain"


  * As above, with an encoded config file in the plain directory

... = "/site/modules:-/site/modules/plain:/site/modules/plain/config.php"


Locations:

  The ioncube.loader.encoded_paths property can be set in a php.ini
  file, in a .htaccess file (when using Apache), in a .user.ini file
  (when using CGI PHP 5.3+) or using ini_set within a PHP script. In ini
  files only the last value will be used for the encoded_paths property. If
  you wish to build up the value in several lines then, for PHP 5.1+, you
  can use the following syntax:

ioncube.loader.encoded_paths = "/path1"  
ioncube.loader.encoded_paths = ${ioncube.loader.encoded_paths}":/path2"  
; etc...

LIMITATIONS OF LOADERS AND ENCODED FILES
----------------------------------------

Encoded files can, in general, run on versions of PHP equal to
or greater than the source language of the Encoder used to
produce them. So a file produced by the Encoder for PHP 7.2
can be run by the Loaders for PHP 7.2, 7.3 and 7.4, but 7.1. This 
means that the Loaders offer good backwards compatibility, 
however there are the following limitations:

  * The Loader for PHP 8.5 can run files produced by the PHP 8.2, 8.3
    and 8.4 Encoders.

  * The Loader for PHP 8.4 can run files produced by the PHP 8.2, 8.3
    and 8.4 Encoders.

  * The Loader for PHP 8.3 can run files produced by the PHP 8.2 and
    8.3 Encoders.

  * The Loader for PHP 8.2 can only run files produced for
    PHP 8.2. Updates for files produced for PHP 8.1 should
    be obtained to use them with PHP 8.2.

  * The Loader for PHP 8.1 can only run files produced for
    PHP 8.1.

  * The Loaders for PHP 7.1 through 7.4 can only run files 
    produced by the Encoders for PHP 7. 

  * The Loader for PHP 7.0 can only run files produced by the
    Encoder for PHP 5.6.

  * The Loaders for PHP 5.5 and PHP 5.6 cannot run files 
    produced by the PHP 4 Encoder.


IONCUBE24 : real-time intrusion protection and PHP error reporting
---------
### (Available for Linux 32 and 64 bit x86 servers using PHP 7)

ionCube24 (https://ioncube24.com) is an ionCube service that uses the
ionCube Loader to provide both real-time protection against the exploit of
website vulnerabilities and alerting of website errors.

Vulnerabilities in PHP applications are common, particularly in sites using 
Wordpress and other plugin based systems. Exploits result in website
defacement or customer data being compromised, and ionCube24 provides a 
uniquely powerful defense. 

PHP errors can cause intermittent or even persistent blank pages or errors for
visitors until discovered, and without active monitoring this could go
undetected indefinitely. ionCube24 active monitoring ensures you are always
aware of problems in your website code.

ionCube24 is free to try, with the server side support built into the Linux
Loaders as standard. With the Loader installed, ionCube24 can be activated
at any time to give active intrusion protection and error reporting.

## php.ini settings

ionCube24 has a powerful real-time web interface to configure, monitor and
manage things, and there are also settings that can be used in a php.ini
file as summarised below.

The setup process at https://ioncube24.com automatically gives the settings
that you need to get started, but you may wish to make changes yourself
once setup. The default values are given with each example.

### Global settings

INI entry: ic24.enable ; default 0

Purpose: Enable or disable all ionCube24 features. 

This defaults to 0 (off), and in this case no ionCube24 behaviour is
activated.

Example:

  ic24.enable = 1

----------

INI entry: ic24.api_access_key ; provided during setup

Purpose: An authentication key for adminstration requests. 

  This value is provided when adding a server to ionCube24.

----------

INI entry: ic24.api_check_ip ; default 1

Purpose: Specify whether the IP for admin requests should be validated

  If set, ionCube24 refuses access to API functions unless the calling IP
  is a known ionCube IP address. This option should be left with the
  default setting unless web requests pass through a proxy and your site
  appears to be accessed from the IP of the proxy instead of ionCube. Note
  that access to API functions will still be authenticated by access key.

----------

INI entry: ic24.api_max_timeout ; default 7

Purpose: Maximum timeout period when sending notifications to ionCube24.

  The actual period is adaptive so that a brief increase in typical latency
  will favour a timeout followed by a retry rather than a longer than usual
  timeout.

----------

INI entry: ic24.home_dir ; no default

Purpose: The home directory for ionCube24 related system files. 

  A location outside of the web root is recommended.  It should be writable
  by the web server during startup.

Example:

ic24.home_dir = /var/www/ic24_home

----------

INI entry: ic24.update_domains_retry_interval ; default 30

Purpose: The number of seconds to wait before retrying a fetch of the set
of domains being managed.


### Security related settings

INI entry: ic24.sec.enable ; default "auto"

Purpose: Enable the intrusion protection feature of ionCube24.

Accepted values:

   * "auto" (default) - allow setting from the ionCube24 control panel.
   * 1 : always enabled.
   * 0 : disabled.

----------

INI entry: ic24.sec.initial_state ; default 1

Purpose: The default for whether security should be enabled or
disabled. The default is to enable protection. Any files on a protected
domain will become blocked if they are changed, so setting this to 0 will
avoid accidental blocking when using ionCube24 for the first time.
Protection may be enabled and disabled using the ionCube24 control panel and
also via the User API.

Accepted values:

   * 1 : protection will be active when ionCube24 initialises.
   * 0 : protection will be disabled.

----------

INI entry: ic24.sec.initial_action ; default "block"

Purpose: The initial setting for how new and modified files should be
treated when about to execute. The default is to block. The action is taken
only if protection is enabled, and the setting may be changed via the
ionCube24 control panel.

Accepted values:

   * "block" : prevent execution of new or modified files
   * "allow" : allow execution of new or modified files

Note that depending on the notification settings, a notification may still
be generated when a new or modified file is about to execute even if it is
not blocked.

----------

INI entry: ic24.sec.initial_notify ; default "always"

Purpose: The initial setting for whether a notification is generated the 
first time an unacknowledged new or modified file is attempted to be
executed. This setting can be changed via the ionCube24 control panel.

Accepted values:

   * "always" : always notify of a new modification 
   * "once"   : only the first detected modification is reported
   * "never"  : never notify of new and modified files

----------

INI entry: ic24.sec.exclusion_key ; provided during setup

Purpose: A key that if present at the start of a file, will identify the
file as trusted. This value is provided when adding a server to ionCube24.

----------

INI entry: ic24.sec.trusted_include_paths ; no default

Purpose: List paths from where files can be included and automatically
trusted.

Example:

ic24.sec.trusted_include_paths = "/var/cache:/var/cache2"

Directories can be excluded from the list by prefixing with a minus
character -. e.g.

"/var/cache:-/var/cache/subdir"

This is useful if your site creates and/or modifies files by itself from
time to time, e.g. in a cache directory. Requests that *directly* access
files on a trusted include path will be blocked but the file itself will
not be blocked, so requests that use the file as intended will still work.
See ioncube24.com for more details once signed up.  As an alternative, if
possible we recommend producing files that include the exclusion key.

----------

INI entry: ic24.sec.block_uploaded_files ; default 1

Purpose: If set, block any uploaded files in ionCube24 that are processed
using the standard PHP mechanism for uploaded files. This applies even if
the file is subsequently included and where included files being
automatically approved with the previous setting.

----------

INI entry: ic24.sec.block_stdin ; default 1

Purpose: Refuse code that PHP sees via stdin.  If disabled, code via
stdin will run without security checking as there is no filepath. This
setting should be left on as PHP would normally never receive a script via
stdin.

### PHP Error reporting settings

INI entry: ic24.phperr.enable ; default "auto"

Purpose: Enable reporting of PHP errors to ionCube24.  When enabled, any
non-ignored errors are reported to ionCube24 in realtime, triggering
alerting so errors can be investigated as necessary.

Accepted values:

   * "auto" (default) - allow setting from the ionCube24 control panel.
   * 1 : always enabled.
   * 0 : disabled.

----------

### Deprecated settings

Deprecated settings are subject to removal in a future
release.

INI entry: ic24.phperr.ignore ; default 0

Purpose: Specify default error levels to always ignore for all domains.

Note that default and per-domain errors to ignore can also be set via the
web interface, and are combined with this setting. Leaving this unset and
using the web interface is recommended for maximum flexibility.

Example: 

ic24.phperr.ignore = E_NOTICE | E_DEPRECATED

(c) ionCube Ltd. 2026
PK�*�\�z)�)�*alt-php84-ioncube-loader/loader-wizard.phpnu�[���<?php // -*- c++ -*-

/** 
 * ionCube Loader install Wizard
 *
 * ionCube is a registered trademark of ionCube Ltd. 
 *
 * Copyright (c) ionCube Ltd. 2002-2022
 */


 

define ('ERROR_UNKNOWN_OS',1);
define ('ERROR_UNSUPPORTED_OS',2);
define ('ERROR_UNKNOWN_ARCH',3);
define ('ERROR_UNSUPPORTED_ARCH',4);
define ('ERROR_UNSUPPORTED_ARCH_OS',5);
define ('ERROR_WINDOWS_64_BIT',6);
define ('ERROR_PHP_UNSUPPORTED',7);
define ('ERROR_PHP_DEBUG_BUILD',8);
define ('ERROR_RUNTIME_EXT_DIR_NOT_FOUND',101);
define ('ERROR_RUNTIME_LOADER_FILE_NOT_FOUND',102);
define ('ERROR_INI_NOT_FIRST_ZE',201);
define ('ERROR_INI_WRONG_ZE_START',202);
define ('ERROR_INI_ZE_LINE_NOT_FOUND',203);
define ('ERROR_INI_LOADER_FILE_NOT_FOUND',204);
define ('ERROR_INI_NOT_FULL_PATH',205);
define ('ERROR_INI_NO_PATH',206);
define ('ERROR_INI_NOT_FOUND',207);
define ('ERROR_INI_NOT_READABLE',208);
define ('ERROR_INI_MULTIPLE_IC_LOADER_LINES',209);
define ('ERROR_INI_USER_INI_NOT_FOUND',210);
define ('ERROR_INI_USER_CANNOT_CREATE',211);
define ('ERROR_LOADER_UNEXPECTED_NAME',301);
define ('ERROR_LOADER_NOT_READABLE',302);
define ('ERROR_LOADER_PHP_MISMATCH',303);
define ('ERROR_LOADER_NONTS_PHP_TS',304);
define ('ERROR_LOADER_TS_PHP_NONTS',305);
define ('ERROR_LOADER_WRONG_OS',306);
define ('ERROR_LOADER_WRONG_ARCH',307);
define ('ERROR_LOADER_WRONG_GENERAL',308);
define ('ERROR_LOADER_WIN_SERVER_NONWIN',321);
define ('ERROR_LOADER_WIN_NONTS_PHP_TS',322);
define ('ERROR_LOADER_WIN_TS_PHP_NONTS',323);
define ('ERROR_LOADER_WIN_PHP_MISMATCH',324);
define ('ERROR_LOADER_WIN_COMPILER_MISMATCH',325);
define ('ERROR_LOADER_NOT_FOUND',380);
define ('ERROR_LOADER_PHP_VERSION_UNKNOWN',390);


define ('SERVER_UNKNOWN',0);
define ('HAS_PHP_INI',1);
define ('SERVER_SHARED',2); 
define ('SERVER_VPS',5); 
define ('SERVER_DEDICATED',7); 
define ('SERVER_LOCAL',9);

define ('IONCUBE_IP_ADDRESS',
			'94.101.154.134');
define  ('IONCUBE_ACCESS_ADDRESS',
			'lwaccess.ioncube.com');
define ('LOADERS_PAGE',
            'https://loaders.ioncube.com/'); 
define ('SUPPORT_SITE',
            'https://support.ioncube.com/');                                 
define ('WIZARD_SUPPORT_TICKET_DEPARTMENT',
			'3');
define ('LOADER_FORUM_URL',
            'https://forum.ioncube.com/viewforum.php?f=4');                  
define ('LOADERS_FAQ_URL',
            'https://www.ioncube.com/faqs/loaders.php');                     
define ('UNIX_ERRORS_URL',
            'https://www.ioncube.com/loaders/unix_startup_errors.php');      
define ('LOADER_WIZARD_URL',
            LOADERS_PAGE);                                                  
define ('ENCODER_URL',
            'https://www.ioncube.com/sa_encoder.php');                       
define ('LOADER_VERSION_URL',
            'https://www.ioncube.com/feeds/product_info/versions.php');    
define ('WIZARD_LATEST_VERSION_URL',
            LOADER_VERSION_URL . '?item=loader-wizard'); 
define ('PHP_COMPILERS_URL',
            LOADER_VERSION_URL . '?item=php-compilers');
define ('LOADER_PLATFORM_URL',
            LOADER_VERSION_URL . '?item=loader-platforms-all');   
define ('LOADER_LATEST_VERSIONS_URL',
            LOADER_VERSION_URL . '?item=loader-versions'); 
define ('LOADER_PHP_VERSION_URL',
            LOADER_VERSION_URL . '?item=loader-php-support'); 
define ('WIZARD_STATS_URL',
            'https://www.ioncube.com/feeds/stats/wizard.php');    
define ('IONCUBE_DOWNLOADS_SERVER',
            'https://downloads.ioncube.com/loader_downloads');          
define ('IONCUBE24_URL',
			'https://ioncube24.com');
define ('IONCUBE_CONNECT_TIMEOUT',4);

define ('DEFAULT_SELF','/ioncube/loader-wizard.php');
define ('LOADER_NAME_CHECK',true);
define ('LOADER_EXTENSION_NAME','ionCube Loader');
define ('LOADER_SUBDIR','ioncube');
define ('WINDOWS_IIS_LOADER_DIR', 'system32');
define ('ADDITIONAL_INI_FILE_NAME','00-ioncube.ini');
define ('UNIX_SYSTEM_LOADER_DIR','/usr/local/ioncube');
define ('RECENT_LOADER_VERSION','4.0.7');
define ('LATEST_LOADER_MAJOR_VERSION',12);
define ('LOADERS_PACKAGE_PREFIX','ioncube_loaders_');
define ('SESSION_LIFETIME_MINUTES',360);
define ('WIZARD_EXPIRY_MINUTES',2880);
define ('IONCUBE_WIZARD_EXPIRY_MINUTES',10080);
define ('MIN_INITIALISE_TIME',4);
define ('IC24_ENABLED_INI_PROPERTY',"ic24.enable");

    run();


function php4_http_build_query($formdata, $numeric_prefix = null, $key = null ) {
    $res = array();
    foreach ((array)$formdata as $k=>$v) {
        $tmp_key = urlencode(is_int($k) ? $numeric_prefix.$k : $k);
        if ($key) $tmp_key = $key.'['.$tmp_key.']';
        if ( is_array($v) || is_object($v) ) {
            $res[] = php4_http_build_query($v, null , $tmp_key);
        } else {
            $res[] = $tmp_key."=".urlencode($v);
        }
   }
   $separator = ini_get('arg_separator.output');
   return implode($separator, $res);
}


function script_version()
{
    return "2.74";
}

function retrieve_latest_wizard_version()
{
    $v = false;

    $s = trim(remote_file_contents(WIZARD_LATEST_VERSION_URL));
    if (preg_match('/^\d+([.]\d+)*$/', $s)) {
        $v = $s;
    }

    return $v;
}

function latest_wizard_version()
{
    if (!isset($_SESSION['latest_wizard_version'])) {
        $_SESSION['latest_wizard_version'] = retrieve_latest_wizard_version();
    } 
    return $_SESSION['latest_wizard_version'];
}

function update_is_available($lv)
{
    if (is_numeric($lv)) {
        $lv_parts = explode('.',$lv);
        $script_parts = explode('.',script_version());
        return ($lv_parts[0] > $script_parts[0] || ($lv_parts[0] == $script_parts[0] && $lv_parts[1] > $script_parts[1]));
    } else {
        return null;
    }
}

function check_for_wizard_update($echo_message = false)
{
    $latest_version = latest_wizard_version();
    $update_available = update_is_available($latest_version);

    if ($update_available) {
        if ($echo_message) {
            echo '<p class="alert">An updated version of this Wizard script is available <a href="' . LOADER_WIZARD_URL . '">here</a>.</p>';
        }
        return $latest_version;
    } else {
        return $update_available;
    }
}


function remote_file_contents($url)
{
    $remote_file_opening = ini_get('allow_url_fopen');
    $contents = false;
    if (isset($_SESSION['timing_out']) && $_SESSION['timing_out']) {
        return false;
    }
    @session_write_close();
    $timing_out = 0;
    if ($remote_file_opening) {
        $fh = @fopen($url,'rb');
        if ($fh) {
            stream_set_blocking($fh,0);
            stream_set_timeout($fh,IONCUBE_CONNECT_TIMEOUT);
            while (!feof($fh)) {
                $result = fread($fh, 8192);
                $info = stream_get_meta_data($fh);
                $timing_out = $info['timed_out']?1:0;
                if ($timing_out) {
                    break;
                }
                if ($result !== false) {
                    $contents .= $result;
                } else {
                    break;
                }
            }
            fclose($fh);
        } else {
            $timing_out = 1;
        }
    } elseif (extension_loaded('curl')) {
            $ch = curl_init();

            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_HEADER, 0);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT,IONCUBE_CONNECT_TIMEOUT);
            $output = curl_exec($ch);
            $info = curl_getinfo($ch);
            $timing_out = ($info['http_code'] >= 400)?1:0;
            curl_close($ch);

            if (is_string($output)) {
                $contents = $output;
            }
    } else {
        $timing_out = 1;
    }
    @session_start();
    $_SESSION['timing_out'] = $timing_out;
    return $contents;
}

function php_version()
{
    $v = explode('.',PHP_VERSION);

    return array(
           'major'      =>  $v[0],
           'minor'      =>  $v[1],
           'release'    =>  $v[2]);
}

function php_version_maj_min()
{
    $vprts = php_version();
    return ($vprts['major'] . '.' . $vprts['minor']);
}

function is_supported_php_version()
{
    $v = php_version(); 

    return ((($v['major'] == 4) && ($v['minor'] >= 1)) ||
      (($v['major'] == 5) && (($v['minor'] >= 1) || ($v['release'] >= 3))) ||
	  $v['major'] == 7 || ($v['major'] == 8 && $v['minor'] >= 1));
}

function is_php_version_or_greater($major,$minor,$release = 0)
{
    $version = php_version();
    return ($version['major'] > $major || 
            ($version['major'] == $major && $version['minor'] > $minor) ||
            ($version['major'] == $major && $version['minor'] == $minor && $version['release'] >= $release));
}

function ini_file_name()
{
    $sysinfo = get_sysinfo();
    return (!empty($sysinfo['PHP_INI'])?$sysinfo['PHP_INI_BASENAME']:'php.ini');
}

function get_remote_session_value($session_var,$remote_url,$default_function)
{
    if (!isset($_SESSION[$session_var])) {
        $serialised_res = remote_file_contents($remote_url);
        $unserialised_res = @unserialize($serialised_res);
        if (empty($unserialised_res)) {
            $unserialised_res = call_user_func($default_function);
        } else {
			$_SESSION['remote_access_successful'] = 1;
		}
        if (false === $unserialised_res) {
            $unserialised_res = '';
        }
        $_SESSION[$session_var] = $unserialised_res;
    }
    return $_SESSION[$session_var];
}

function get_file_contents($file)
{
    if (empty($file)) {
        return "";
    }
    if (function_exists('file_get_contents')) {
        $strs = @file_get_contents($file);
    } else {
        $lines = @file($file);
        $strs = join(' ',$lines);
    }
    return $strs;
}

function default_platform_list()
{
    $platforms = array();


    $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC6', 'is_legacy' => 1,       'os_mod' => '_vc6',     'arch'=>'x86',  'dirname'=>'win32', 'us1-dir'=>'windows_vc6/x86' );
    $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC6 (Non-TS)',   'is_legacy' => 1,  'os_mod' => '_nonts_vc6',   'arch'=>'x86',  'dirname'=>'win32-nonts', 'us1-dir'=>'windows_vc6/x86-nonts' );

    $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC9',        'os_mod' => '_vc9',     'arch'=>'x86',  'dirname'=>'win32_vc9', 'us1-dir'=>'windows_vc9/x86' );
    $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC9 (Non-TS)',   'os_mod' => '_nonts_vc9',   'arch'=>'x86',  'dirname'=>'win32-nonts_vc9', 'us1-dir'=>'windows_vc9/x86-nonts' );
	
	 $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC11',        'os_mod' => '_vc11',     'arch'=>'x86',  'dirname'=>'win32_vc11', 'us1-dir'=>'windows_vc11/x86' );
    $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC11 (Non-TS)',   'os_mod' => '_nonts_vc11',   'arch'=>'x86',  'dirname'=>'win32-nonts_vc11', 'us1-dir'=>'windows_vc11/x86-nonts' );
	
	$platforms[] = array('os'=>'win', 'os_human'=>'Windows VC11',        'os_mod' => '_vc11',     'arch'=>'x86-64',  'dirname'=>'win64_vc11', 'us1-dir'=>'windows_vc11/amd64' );
    $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC11 (Non-TS)',   'os_mod' => '_nonts_vc11',   'arch'=>'x86-64',  'dirname'=>'win64-nonts_vc11', 'us1-dir'=>'windows_vc11/amd64-nonts' );
	
	 $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC14',        'os_mod' => '_vc14',     'arch'=>'x86',  'dirname'=>'win32_vc14', 'us1-dir'=>'windows_vc14/x86' );
    $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC14 (Non-TS)',   'os_mod' => '_nonts_vc14',   'arch'=>'x86',  'dirname'=>'win32-nonts_vc14', 'us1-dir'=>'windows_vc14/x86-nonts' );
	
		$platforms[] = array('os'=>'win', 'os_human'=>'Windows VC14',        'os_mod' => '_vc14',     'arch'=>'x86-64',  'dirname'=>'win64_vc14', 'us1-dir'=>'windows_vc14/amd64' );
    $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC14 (Non-TS)',   'os_mod' => '_nonts_vc14',   'arch'=>'x86-64',  'dirname'=>'win64-nonts_vc14', 'us1-dir'=>'windows_vc14/amd64-nonts' );
	
		 $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC15',        'os_mod' => '_vc15',     'arch'=>'x86',  'dirname'=>'win32_vc15', 'us1-dir'=>'windows_vc15/x86' );
    $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC15 (Non-TS)',   'os_mod' => '_nonts_vc15',   'arch'=>'x86',  'dirname'=>'win32-nonts_vc15', 'us1-dir'=>'windows_vc15/x86-nonts' );
	
		$platforms[] = array('os'=>'win', 'os_human'=>'Windows VC15',        'os_mod' => '_vc15',     'arch'=>'x86-64',  'dirname'=>'win64_vc15', 'us1-dir'=>'windows_vc15/amd64' );
    $platforms[] = array('os'=>'win', 'os_human'=>'Windows VC15 (Non-TS)',   'os_mod' => '_nonts_vc15',   'arch'=>'x86-64',  'dirname'=>'win64-nonts_vc15', 'us1-dir'=>'windows_vc15/amd64-nonts' );

    $platforms[] = array('os'=>'lin', 'os_human'=>'Linux',              'arch'=>'x86',      'dirname'=>'linux_i686-glibc2.3.4', 'us1-dir'=>'linux/x86');
    $platforms[] = array('os'=>'lin', 'os_human'=>'Linux',              'arch'=>'x86-64',   'dirname'=>'linux_x86_64-glibc2.3.4', 'us1-dir'=>'linux/x86_64');
$platforms[] = array('os'=>'lin','os_human'=>'Linux',               'arch'=>'ppc',      'dirname'=>'linux_ppc-glibc2.3.4','us1-dir'=>'linux/ppc');
            $platforms[] = array('os'=>'lin','os_human'=>'Linux',               'arch'=>'ppc64',    'dirname'=>'linux_ppc64-glibc2.5','us1-dir'=>'linux/ppc64');
    

$platforms[] = array('os'=>'dra', 'os_human'=>'DragonFly', 'arch'=>'x86',      'dirname'=>'dragonfly_i386-1.7', 'us1-dir'=>'Dragonfly/x86');

$platforms[] = array('os'=>'fre', 'os_human'=>'FreeBSD 4', 'os_mod'=>'_4',  'arch'=>'x86',      'dirname'=>'freebsd_i386-4.8', 'us1-dir'=>'FreeBSD/v4');

    $platforms[] = array('os'=>'fre', 'os_human'=>'FreeBSD 6', 'os_mod'=>'_6',  'arch'=>'x86',      'dirname'=>'freebsd_i386-6.2', 'us1-dir'=>'FreeBSD/v6/x86');

    $platforms[] = array('os'=>'fre', 'os_human'=>'FreeBSD 6', 'os_mod'=>'_6',  'arch'=>'x86-64',   'dirname'=>'freebsd_amd64-6.2', 'us1-dir'=>'FreeBSD/v6/AMD64');


    $platforms[] = array('os'=>'fre', 'os_human'=>'FreeBSD 7', 'os_mod'=>'_7',  'arch'=>'x86',      'dirname'=>'freebsd_i386-7.3', 'us1-dir'=>'FreeBSD/v7/x86');
    $platforms[] = array('os'=>'fre', 'os_human'=>'FreeBSD 7', 'os_mod'=>'_7',  'arch'=>'x86-64',   'dirname'=>'freebsd_amd64-7.3', 'us1-dir'=>'FreeBSD/v7/AMD64');


    $platforms[] = array('os'=>'fre', 'os_human'=>'FreeBSD 8', 'os_mod'=>'_8',  'arch'=>'x86',      'dirname'=>'freebsd_i386-8.0', 'us1-dir'=>'FreeBSD/v8/x86');
    $platforms[] = array('os'=>'fre', 'os_human'=>'FreeBSD 8', 'os_mod'=>'_8',  'arch'=>'x86-64',   'dirname'=>'freebsd_amd64-8.0', 'us1-dir'=>'FreeBSD/v8/AMD64');
    
    $platforms[] = array('os'=>'bsd', 'os_human'=>'BSDi',     'is_legacy' => 1,           'arch'=>'x86',      'dirname'=>'bsdi_i386-4.3.1');
    $platforms[] = array('os'=>'net', 'os_human'=>'NetBSD',             'arch'=>'x86',      'dirname'=>'netbsd_i386-2.1','us1-dir'=>'NetBSD/x86');
    $platforms[] = array('os'=>'net', 'os_human'=>'NetBSD',             'arch'=>'x86-64',   'dirname'=>'netbsd_amd64-2.0','us1-dir'=>'NetBSD/x86_64');
    $platforms[] = array('os'=>'ope', 'os_human'=>'OpenBSD 4.2', 'os_mod'=>'_4.2',  'arch'=>'x86',  'dirname'=>'openbsd_i386-4.2', 'us1-dir'=>'OpenBSD/x86');

    $platforms[] = array('os'=>'ope', 'os_human'=>'OpenBSD 4.5', 'os_mod'=>'_4.5',  'arch'=>'x86',  'dirname'=>'openbsd_i386-4.5', 'us1-dir'=>'OpenBSD/x86');
    $platforms[] = array('os'=>'ope', 'os_human'=>'OpenBSD 4.6', 'os_mod'=>'_4.6',  'arch'=>'x86',  'dirname'=>'openbsd_i386-4.6', 'us1-dir'=>'OpenBSD/x86');

    $platforms[] = array('os'=>'ope', 'os_human'=>'OpenBSD 4.7', 'os_mod'=>'_4.7',  'arch'=>'x86-64', 'dirname'=>'openbsd_amd64-4.7', 'us1-dir' => 'OpenBSD/x86_64');

    $platforms[] = array('os'=>'dar', 'os_human'=>'OS X',    'is_legacy' => 1, 'arch'=>'ppc',      'dirname'=>'osx_powerpc-8.5','us1-dir'=>'OSX/ppc');

    $platforms[] = array('os'=>'dar', 'os_human'=>'OS X',               'arch'=>'x86',      'dirname'=>'osx_i386-8.11','us1-dir'=>'OSX/x86');

    $platforms[] = array('os'=>'dar', 'os_human'=>'OS X',               'arch'=>'x86-64',       'dirname'=>'osx_x86-64-10.2','us1-dir'=>'OSX/x86_64');

    $platforms[] = array('os'=>'sun', 'os_human'=>'Solaris',  'is_legacy' => 1,          'arch'=>'sparc',    'dirname'=>'solaris_sparc-5.9', 'us1-dir'=>'Solaris/sparc');

    $platforms[] = array('os'=>'sun', 'os_human'=>'Solaris',            'arch'=>'x86',      'dirname'=>'solaris_i386-5.10','us1-dir'=>'Solaris/x86');

    return $platforms;
}

function get_loader_platforms()
{
    return get_remote_session_value('loader_platform_info',LOADER_PLATFORM_URL,'default_platform_list');
}

function get_platforminfo()
{
    static $platforminfo;

    if (empty($platforminfo)) {
        $platforminfo = get_loader_platforms();
    }
    return $platforminfo;
}

function default_php_versions()
{
	return array();
}

function get_php_versions()
{
	return get_remote_session_value('php_version_info',LOADER_PHP_VERSION_URL,'default_php_versions');
}


function get_max_php_version_supported()
{
	static $max_php_version;
	
	if (empty($max_php_version)) {
		$php_versions = get_php_versions();
		
		$dirname = calc_dirname();
		
		if (array_key_exists($dirname,$php_versions)) {
			$max_php_version = $php_versions[$dirname];
		} else {
			$max_php_version = NULL;
		}
	}
	
	return $max_php_version;
}

function is_after_max_php_version_supported()
{
	$is_too_recent_php = false;
	
	$supported_php_version = get_max_php_version_supported();
	
	if (!is_null($supported_php_version)) {
		$pversion = php_version();
		
		$supported_parts = explode('.',$supported_php_version);
		$is_too_recent_php = ($supported_parts[0] < $pversion['major'] || ($supported_parts[0] == $pversion['major'] && $supported_parts[1] < $pversion['minor']));
	}
	
	if ($is_too_recent_php) {
		return $supported_php_version;
	} else {
		return false;
	}
}

function supported_os_variants($os_code,$arch_code)
{
    if (empty($os_code)) {
        return ERROR_UNKNOWN_OS;
    }
    if (empty($arch_code)) {
        return ERROR_UNKNOWN_ARCH;
    }

    $os_found = false;
    $arch_found = false;
    $os_arch_matches = array();
    $pinfo = get_platforminfo();

    foreach ($pinfo as $p) {
        if ($p['os'] == $os_code && $p['arch'] == $arch_code) {
            $os_arch_matches[$p['os_human']] = (isset($p['os_mod']))?(0 + (int) str_replace('_','',$p['os_mod'])):'';
        } 
        if ($p['os'] == $os_code) {
            $os_found = true;
        } elseif ($p['arch'] == $arch_code) {
            $arch_found = true;
        }
    }
    if (!empty($os_arch_matches)) {
        asort($os_arch_matches);
        return $os_arch_matches;
    } elseif (!$os_found) {
        return ERROR_UNSUPPORTED_OS;
    } elseif (!$arch_found) {
        return ERROR_UNSUPPORTED_ARCH;
    } else {
        return ERROR_UNSUPPORTED_ARCH_OS;
    }
}

function default_win_compilers()
{
    return array('VC6','VC9','VC11','VC14','VC15', 'VC16');
}

function supported_win_compilers()
{
    static $win_compilers;

    if (empty($win_compilers)) {
        $win_compilers = find_win_compilers();
    }
    return $win_compilers;
}

function find_win_compilers()
{
    return get_remote_session_value('php_compilers_info',PHP_COMPILERS_URL,'default_win_compilers');
}

function server_software_info()
{
    $ss = array('full' => '','short' => '');
    $ss['full'] = $_SERVER['SERVER_SOFTWARE'];

    if (preg_match('/apache/i', $ss['full'])) {
        $ss['short'] = 'Apache';
    } else if (preg_match('/IIS/',$ss['full'])) {
        $ss['short'] = 'IIS';
    } else {
        $ss['short'] = '';
    }
    return $ss;
}

function match_arch_pattern($str)
{
    $arch = null;
    $arch_patterns = array(
             'i.?86'        => 'x86',
             'x86[-_]64'    => 'x86',
             'x86'          => 'x86',
             'amd64'        => 'x86',
             'SMP Tue Jan 01 00:00:00 CEST 2000 all GNU\/Linux' => 'x86',
             'ppc64'        => 'ppc',
             'ppc'          => 'ppc',
             'powerpc'      => 'ppc',
             'sparc'        => 'sparc',
             'sun'          => 'sparc',
			 'armv7l'       => 'armv7l',
             'aarch64'      => 'aarch64'
         );

    foreach ($arch_patterns as $token => $a) {
        if (preg_match("/$token/i", $str)) {
          $arch = $a;
          break;
        }
    }
    return $arch;
}

function required_loader_arch($mach_info,$os_code,$wordsize)
{
    if ($os_code == 'win') {
        $arch = ($wordsize == 32)?'x86':'x86-64';
    } elseif (!empty($os_code)) {
        $arch = match_arch_pattern($mach_info);
        if ($wordsize == 64) {
            if ($arch == 'x86') {
                $arch = 'x86-64';
            } elseif ($arch == 'ppc') {
                $arch = 'ppc64';
            }
        }
    } else {
        $arch = ERROR_UNKNOWN_ARCH;
    }
    return $arch;
}

function uname($part = 'a')
{
    $result = '';
    if (!function_is_disabled('php_uname')) {
        $result = @php_uname($part);
    } elseif (function_exists('posix_uname') && !function_is_disabled('posix_uname')) {
        $posix_equivs = array(
                     'm' => 'machine',
                     'n' => 'nodename',
                     'r' => 'release',
                     's' => 'sysname'
                 );
        $puname = @posix_uname();
        if ($part == 'a' || !array_key_exists($part,$posix_equivs)) {
           $result = join(' ',$puname);
        } else {
           $result = $puname[$posix_equivs[$part]];
        }
    } else {
        if (!function_is_disabled('phpinfo')) {
            ob_start();
            phpinfo(INFO_GENERAL);
            $pinfo = ob_get_contents();
            ob_end_clean();
            if (preg_match('~System.*?(</B></td><TD ALIGN="left">| => |v">)([^<]*)~i',$pinfo,$match)) {
                $uname = $match[2];
                if ($part == 'r') {
                    if (!empty($uname) && preg_match('/\S+\s+\S+\s+([0-9.]+)/',$uname,$matchver)) {
                        $result = $matchver[1];
                    } else {
                        $result = '';
                    }
                } else {
                    $result = $uname;
                }
            }
        } else {
            $result = '';
        }
    }
    return $result;
}

function calc_word_size($os_code)
{
    $wordsize = null;
    if ('win' === $os_code) {
        ob_start();
        phpinfo(INFO_GENERAL);
        $pinfo = ob_get_contents();
        ob_end_clean();
        if (preg_match('~Compiler.*?(</B></td><TD ALIGN="left">| => |v">)([^<]*)~i',$pinfo,$compmatch)) {
            if (preg_match("/(VC[0-9]+)/i",$compmatch[2],$vcmatch)) {
                $compiler = strtoupper($vcmatch[1]);
            } elseif (stripos(trim($compmatch[2]),"Visual C++ 2019") === 0) {
                $compiler = 'VC16';
            } else {
                $compiler = 'VC6';
            }
        } else {
            $compiler = 'VC6';
        }
        if ($compiler === 'VC9' || $compiler === 'VC11' || $compiler === 'VC14' 
                || $compiler === 'VC15' || $compiler === 'VC16') {
			if (preg_match('~Architecture.*?(</B></td><TD ALIGN="left">| => |v">)([^<]*)~i',$pinfo,$archmatch)) {
				if (preg_match("/x64/i",$archmatch[2])) {
					$wordsize = 64;
				} else {
					$wordsize = 32;
				}
            } elseif (isset($_ENV['PROCESSOR_ARCHITECTURE']) && preg_match('~(amd64|x86-64|x86_64)~i',$_ENV['PROCESSOR_ARCHITECTURE'])) {
                if (preg_match('~Configure Command.*?(</B></td><TD ALIGN="left">| => |v">)([^<]*)~i',$pinfo,$confmatch)) {
                    if (preg_match('~(x64|lib64|system64)~i',$confmatch[2])) {
                        $wordsize = 64;
                    }
                }
            } else {
				$wordsize = 32;
			}
        }
    }
    if (empty($wordsize)) {
        $wordsize = ((-1^0xffffffff)?64:32);
    }
    return $wordsize;
}

function required_loader($unamestr = '')
{
    $un = empty($unamestr)?uname():$unamestr;

    $php_major_version = substr(PHP_VERSION,0,3);

    $os_name = substr($un,0,strpos($un,' '));
    $os_code = empty($os_name)?'':strtolower(substr($os_name,0,3));

    $wordsize = calc_word_size($os_code);

	if ($os_code == 'win' && $wordsize == 64 && $php_major_version < '5.5') {
        $arch = ERROR_WINDOWS_64_BIT;
	} else {
		$arch = required_loader_arch($un,$os_code,$wordsize);
	}
    if (!is_string($arch)) {
        return $arch;
    }
    $os_variants = supported_os_variants($os_code,$arch);
    if (!is_array($os_variants)) {
        return $os_variants;
    }

    $os_ver = '';
    if (preg_match('/([0-9.]+)/',uname('r'),$match)) {
        $os_ver = $match[1];
    }
    $os_ver_parts = preg_split('@\.@',$os_ver);

    $os_code_h = ($os_code == 'dar' ? 'mac' : $os_code);

    $loader_sfix = (($os_code == 'win') ? 'dll' : 'so');
    $file = "ioncube_loader_{$os_code_h}_{$php_major_version}.{$loader_sfix}";

    if ($os_code == 'win') {
        $os_name = 'Windows';
        $file_ts = $file;
        $os_name_qual = 'Windows';
    } else {
        $os_names = array_keys($os_variants);
        if (count($os_variants) > 1) {
            $parts = explode(" ",$os_names[0]); 
            $os_name = $parts[0];
            $os_name_qual = $os_name . ' ' . $os_ver_parts[0] . '.' . $os_ver_parts[1];
        } else {
            $os_name = $os_names[0];
            $os_name_qual = $os_name;
        }
        $file_ts = "ioncube_loader_{$os_code_h}_{$php_major_version}_ts.{$loader_sfix}";
    }

    return array(
           'uname'      =>  $un,
           'arch'       =>  $arch,
           'oscode'     =>  $os_code,
           'oscode_h'   =>  $os_code_h,
           'osname'     =>  $os_name,
           'osnamequal' =>  $os_name_qual,
           'osvariants' =>  $os_variants,
           'osver'      =>  $os_ver,
           'osver2'     =>  $os_ver_parts,
           'file'       =>  $file,
           'file_ts'    =>  $file_ts,
           'wordsize'   =>  $wordsize
       );
}

function ic_system_info()
{
    $thread_safe = null;
    $debug_build = null;
    $cgi_cli = false;
	$is_fpm = false;
    $is_cgi = false;
    $is_cli = false;
    $php_ini_path = '';
    $php_ini_dir = '';
    $php_ini_add = '';
    $is_supported_compiler = true;
    $php_compiler = is_ms_windows()?'VC6':'';

    ob_start();
    phpinfo(INFO_GENERAL);
    $php_info = ob_get_contents();
    ob_end_clean();

    $breaker = (php_sapi_name() == 'cli')?"\n":'</tr>';
    $lines = explode($breaker,$php_info);
    foreach ($lines as $line) {
        if (preg_match('/command/i',$line)) {
          continue;
        }

        if (preg_match('/thread safety/i', $line)) {
          $thread_safe = (preg_match('/(enabled|yes)/i', $line) != 0);
        }

        if (preg_match('/debug build/i', $line)) {
          $debug_build = (preg_match('/(enabled|yes)/i', $line) != 0);
        }

        if (preg_match('~configuration file.*(</B></td><TD ALIGN="left">| => |v">)([^ <]*)~i',$line,$match)) {
          $php_ini_path = $match[2];

          if (!@file_exists($php_ini_path)) {
                $php_ini_path = '';
          }
        }
        if (preg_match('~dir for additional \.ini files.*(</B></td><TD ALIGN="left">| => |v">)([^ <]*)~i',$line,$match)) {
            $php_ini_dir = $match[2];
            if (!@file_exists($php_ini_dir)) {
                $php_ini_dir = '';
            }
        }
        if (preg_match('~additional \.ini files parsed.*(</B></td><TD ALIGN="left">| => |v">)([^ <]*)~i',$line,$match)) {
            $php_ini_add = $match[2];
        }
        if (preg_match('/compiler/i',$line)) {
            $supported_match = join('|',supported_win_compilers());
            $is_supported_compiler = preg_match("/($supported_match)/i",$line);
            if (preg_match("/(VC[0-9]+)/i",$line,$match)) {
                $php_compiler = strtoupper($match[1]);
            } elseif (preg_match("/Visual C\+\+ 2017/i",$line)) {
				$php_compiler = "VC15";
				$is_supported_compiler = true;
            } elseif (preg_match("/Visual C\+\+ 2019/i",$line)) {
				$php_compiler = "VC16";
				$is_supported_compiler = true;
			} else {
                $php_compiler = '';
            }
        }
    }
    $is_cgi = strpos(php_sapi_name(),'cgi') !== false;
    $is_cli = strpos(php_sapi_name(),'cli') !== false;
	$is_fpm = strpos(php_sapi_name(),'fpm-fcgi') !== false;
    $cgi_cli = $is_cgi || $is_cli;

    $ss = server_software_info();
	
	if ($is_fpm) {
		$ss['short'] = 'PHP-FPM';
		$ss['full'] = 'PHP-FPM ' . $ss['full'];
	}

    if (!$php_ini_path && function_exists('php_ini_loaded_file')) {
        $php_ini_path = php_ini_loaded_file();
        if ($php_ini_path === false) {
            $php_ini_path = '';
        }
    }
    if (!empty($php_ini_path)) {
        $real_path = @realpath($php_ini_path);
        if (false !== $real_path) {
            $php_ini_path = $real_path;
        }
    }

    $php_ini_basename = basename($php_ini_path);

    return array(
           'THREAD_SAFE'        => $thread_safe,
           'DEBUG_BUILD'        => $debug_build,
           'PHP_INI'            => $php_ini_path,
           'PHP_INI_BASENAME'   => $php_ini_basename,
           'PHP_INI_DIR'        => $php_ini_dir,
           'PHP_INI_ADDITIONAL' => $php_ini_add,
           'PHPRC'              => getenv('PHPRC'),
           'CGI_CLI'            => $cgi_cli,
           'IS_CGI'             => $is_cgi,
           'IS_CLI'             => $is_cli,
		   'IS_FPM'				=> $is_fpm,
           'PHP_COMPILER'       => $php_compiler,
           'SUPPORTED_COMPILER' => $is_supported_compiler,
           'FULL_SS'            => $ss['full'],
           'SS'                 => $ss['short']);
}

function is_possibly_dedicated_or_local()
{
    $sys = get_sysinfo();

    return (empty($sys['PHP_INI']) || !@file_exists($sys['PHP_INI']) || (is_readable($sys['PHP_INI']) && (0 !== strpos($sys['PHP_INI'],$_SERVER['DOCUMENT_ROOT']))));
}

function is_local()
{
    $ret = false;
    if ($_SERVER["SERVER_NAME"] == 'localhost') {
        $ret = true;
    } else {
        $ip_address = strtolower($_SERVER["REMOTE_ADDR"]);
        if (strpos(':',$ip_address) === false) {
            $ip_parts = explode('.',$ip_address);
            $ret = (($ip_parts[0] == 10) || 
                    ($ip_parts[0] == 172 && $ip_parts[1] >= 16 &&  $ip_parts[1] <= 31) ||
                    ($ip_parts[0] == 192 && $ip_parts[1] == 168));
        } else {
            $ret = ($ip_address == '::1') || (($ip_address[0] == 'f') && ($ip_address[1] >= 'c' && $ip_address[1] <= 'f'));
        }
    }
    return $ret;
}

function is_shared()
{
    return !is_local() && !is_possibly_dedicated_or_local();
}

function find_server_type($chosen_type = '',$type_must_be_chosen = false,$set_session = false)
{
    $server_type = SERVER_UNKNOWN;
    if (empty($chosen_type)) {
        if ($type_must_be_chosen) {
            $server_type = SERVER_UNKNOWN;
        } else {
            if (isset($_SESSION['server_type']) && $_SESSION['server_type'] != SERVER_UNKNOWN) {
                $server_type = $_SESSION['server_type'];
            } elseif (is_local()) {
                $server_type = SERVER_LOCAL;
            } elseif (!is_possibly_dedicated_or_local()) {
                $server_type = SERVER_SHARED;
            } else {
                $server_type = SERVER_UNKNOWN;
            } 
        }
    } else {
        switch ($chosen_type)  {
            case 's':
                $server_type = SERVER_SHARED;
                break;
            case 'd':
                $server_type = SERVER_DEDICATED;
                break;
            case 'l':
                $server_type = SERVER_LOCAL;
                break;
            default:
                $server_type = SERVER_UNKNOWN;
                break;
        }
    }
    if ($set_session) {
        $_SESSION['server_type'] = $server_type;
    }
    return $server_type;
}

function server_type_string()
{
    $server_code = find_server_type();
    switch ($server_code) {
        case SERVER_SHARED:
            $server_string = 'SHARED';
            break;
        case SERVER_LOCAL:
            $server_string = 'LOCAL';
            break;
        case SERVER_DEDICATED:
            $server_string = 'DEDICATED';
            break;
        default:
            $server_string = 'UNKNOWN';
            break;
    }
    return $server_string;
}

function server_type_code()
{
    $server_code = find_server_type();
    switch ($server_code) {
        case SERVER_SHARED:
            $server_char = 's';
            break;
        case SERVER_LOCAL:
            $server_char = 'l';
            break;
        case SERVER_DEDICATED:
            $server_char = 'd';
            break;
        default:
            $server_char = '';
            break;
    }
    return $server_char;
}

function get_sysinfo()
{
    static $sysinfo;

    if (empty($sysinfo)) {
        $sysinfo = ic_system_info();
    }
    return $sysinfo;
}

function get_loaderinfo()
{
    static $loader;

    if (empty($loader)) {
        $loader = required_loader();
    }
    return $loader;
}

function is_ms_windows()
{
    $loader_info = get_loaderinfo();
    return ($loader_info['oscode'] == 'win');
}

function function_is_disabled($fn_name)
{
    $disabled_functions=explode(',',ini_get('disable_functions'));
    return in_array($fn_name, $disabled_functions);
}

function selinux_is_enabled()
{
    $se_enabled = false;

    if (!is_ms_windows()) {
        $cmd = (string) @shell_exec('sestatus');
        $se_enabled = preg_match('/enabled/i',$cmd);
    }

    return $se_enabled;
}

function grsecurity_is_enabled()
{
    $gr_enabled = false;

    if (!is_ms_windows()) {
        $cmd = (string) @shell_exec('gradm -S');
        $gr_enabled = preg_match('/enabled/i',$cmd);
    }

    return $gr_enabled;
}

function threaded_and_not_cgi()
{
    $sys = get_sysinfo();
    return($sys['THREAD_SAFE'] && !$sys['IS_CGI']);
}

function is_restricted_server($only_safe_mode = false)
{
    $disable_functions = ini_get('disable_functions');
    $open_basedir = ini_get('open_basedir');
    $php_restrictions = !empty($disable_functions) || !empty($open_basedir);
    $system_restrictions = selinux_is_enabled() || grsecurity_is_enabled();
    $non_safe_mode_restrictions = $php_restrictions || $system_restrictions;
    return (ini_get('safe_mode') || (!$only_safe_mode && $non_safe_mode_restrictions));
}

function server_restriction_warnings()
{
    $warnings = array();

    if (find_server_type() == SERVER_SHARED) {
        if (is_restricted_server()) {
            $warnings[] = "Server restrictions are in place which might affect the operation of this Loader Wizard or prevent the installation of the Loader.";
        }
    } else {
        $warning_suffix = "This may affect the operation of this Loader Wizard.";
        if (ini_get('safe_mode')) {
            $warnings[] = "Safe mode is in effect on the server. " . $warning_suffix;
        } 
        $disabled_functions = ini_get('disable_functions');
        if (!empty($disabled_functions)) {
            $warnings[] = "Some functions are disabled through disable_functions. " . $warning_suffix;
        }
        $open_basedir = ini_get('open_basedir');
        if (!empty($open_basedir)) {
            $warnings[] = "Open basedir restrictions are in effect. " . $warning_suffix;
        }
    }
    return $warnings;
}

function own_php_ini_possible($only_safe_mode = false)
{
    $sysinfo = get_sysinfo();
    return ($sysinfo['CGI_CLI'] && !is_ms_windows() && !is_restricted_server($only_safe_mode));
}

function extension_dir()
{
    $extdir = ini_get('extension_dir');
    if ($extdir == './' || ($extdir == '.\\' && is_ms_windows())) {
        $extdir = '.';
    }
    return $extdir;
}

function possibly_selinux()
{
    $loaderinfo = get_loaderinfo();
    $se_env = (getenv("SELINUX_INIT"));
    return (strtolower($loaderinfo['osname']) == 'linux' && $se_env && ($se_env == 'Yes' || $se_env == '1'));
}

function ini_same_dir_as_wizard()
{
    $sys = get_sysinfo();
    return dirname($sys['PHP_INI']) == dirname(__FILE__); 
}

function extension_dir_path()
{
    $ext_dir = extension_dir();
    if ($ext_dir == '.' || (dirname($ext_dir) == '.')) {
        $ext_dir_path = @realpath($ext_dir);
    } else {
        $ext_dir_path = $ext_dir;
    }
    return $ext_dir_path;
}

function get_loader_name()
{
    $u = uname();
    $sys = get_sysinfo();
    $os = substr($u,0,strpos($u,' '));
    $os_code = strtolower(substr($u,0,3));

    $os_code_h = ($os_code == 'dar' ? 'mac' : $os_code);

    $php_version = phpversion();
    $php_family = substr($php_version,0,3);

    $loader_sfix = (($os_code == 'win') ? '.dll' : (($sys['THREAD_SAFE'])?'_ts.so':'.so'));
    $loader_name="ioncube_loader_{$os_code_h}_{$php_family}{$loader_sfix}";

    return $loader_name;
}

function get_reqd_version($variants)
{
    $exact_match = false;
    $nearest_version = 0;
    $loader_info = get_loaderinfo();
    $os_version = $loader_info['osver2'][0] . '.' . $loader_info['osver2'][1];
    $os_version_major = $loader_info['osver2'][0];
    foreach ($variants as $v) {
        if ($v == $os_version || (is_int($v) && $v == $os_version_major)) {
            $exact_match = true;
            $nearest_version = $v;
            break;
        } elseif ($v > $os_version) {
            break;
        } else {
            $nearest_version = $v;
        }
    }
    return (array($nearest_version,$exact_match));
}

function get_default_loader_dir_webspace()
{
    return ($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . LOADER_SUBDIR);
}

function get_loader_location($loader_dir = '')
{
    if (empty($loader_dir)) {
        $loader_dir = get_default_loader_dir_webspace();
    }
    $loader_name = get_loader_name(); 
    return ($loader_dir . DIRECTORY_SEPARATOR . $loader_name);
}

function get_loader_location_from_ini($php_ini = '')
{
    $errors = array();
    if (empty($php_ini)) {
        $sysinfo = get_sysinfo();
        $php_ini = $sysinfo['PHP_INI'];
    }
    if (!@file_exists($php_ini)) {
        if (empty($php_ini)) {
            $errors[ERROR_INI_NOT_FOUND] = "The configuration file could not be found.";
        } else {
            $errors[ERROR_INI_NOT_FOUND] = "The $php_ini file could not be found.";
        }
    } elseif (!is_readable($php_ini)) {
        $errors[ERROR_INI_NOT_READABLE] = "The $php_ini file could not be read.";
    }
    if (!empty($errors)) {
        return array('location' => '', 'errors' => $errors);
    } 
    $lines = file($php_ini);
    $ext_start = zend_extension_line_start();
    $wrong_ext_start = ($ext_start == 'zend_extension')?'zend_extension_ts':'zend_extension';
    $loader_path = '';
    $loader_name_match = "ioncube_loader";
    foreach ($lines as $l) {
        if (preg_match("/^\s*$ext_start\s*=\s*\"?([^\"]+)\"?/i",$l,$corr_matches)) {
            if (preg_match("/$loader_name_match/i",$corr_matches[1])) {
                if (!empty($loader_path)) {
                    $errors[ERROR_INI_MULTIPLE_IC_LOADER_LINES] = "It appears that multiple $ext_start lines for the ionCube Loader have been included in the configuration file, $php_ini.";
                }
                $loader_path = $corr_matches[1];
            } else {
                if (empty($loader_path)) {
                    $errors[ERROR_INI_NOT_FIRST_ZE] = "The ionCube Loader must be the first Zend extension listed in the configuration file, $php_ini.";
                }
            }
        }
        if (empty($loader_path)) {
            if (preg_match("/^\s*$wrong_ext_start\s*=\s*\"?([^\"]+)\"?/i",$l,$bad_start_matches)) {
                if (preg_match("/$loader_name_match/i",$bad_start_matches[1])) {
                    $bad_zend_ext_msg = "The line for the ionCube Loader in the configuration file, $php_ini, should start with $ext_start and <b>not</b> $wrong_ext_start.";
                    $errors[ERROR_INI_WRONG_ZE_START] = $bad_zend_ext_msg;
                    $loader_path = $bad_start_matches[1];
                }
            }
        }
    }
    $loader_path = trim($loader_path);
    if ($loader_path === '') {
        $errors[ERROR_INI_ZE_LINE_NOT_FOUND] = "The necessary zend_extension line could not be found in the configuration file, $php_ini.";
    } elseif (!@file_exists($loader_path)) {
        $errors[ERROR_INI_LOADER_FILE_NOT_FOUND] = "The loader file  $loader_path, listed in the configuration file, $php_ini, does not exist or is not accessible.";
    } elseif (basename($loader_path) == $loader_path) {
        $errors[ERROR_INI_NOT_FULL_PATH] = "A full path must be specified for the loader file in the configuration file, $php_ini.";
    }
    return array('location' => $loader_path, 'errors' => $errors);
}

function zend_extension_line_missing($ini_path)
{
    $loader_loc = get_loader_location_from_ini($ini_path);
    return (!empty($loader_loc['errors']) && array_key_exists(ERROR_INI_ZE_LINE_NOT_FOUND,$loader_loc['errors']));
}

function find_additional_ioncube_ini()
{
    $sys = get_sysinfo();
    $ioncube_ini = '';

    if (!empty($sys['PHP_INI_ADDITIONAL']) && !preg_match('/(none)/i',$sys['PHP_INI_ADDITIONAL'])) {
        $ini_files = explode(',',$sys['PHP_INI_ADDITIONAL']);
        foreach ($ini_files as $f) {
            $fn = trim($f);
            $bfn = basename($fn);
            if (preg_match('/ioncube/i',$bfn)) {
                $ioncube_ini = $fn;
                break;
            }
        }
    }
    return $ioncube_ini;
}

function get_additional_ini_files()
{
    $sys = get_sysinfo();
    $ini_files = array();
    if (!empty($sys['PHP_INI_ADDITIONAL']) && !preg_match('/(none)/i',$sys['PHP_INI_ADDITIONAL'])) {
        $ini_files = explode(',',$sys['PHP_INI_ADDITIONAL']);
    }
    return (array_map('trim',$ini_files));
}

function all_ini_contents()
{
    $sys = get_sysinfo();
    $output = '';

    $output .= ";;; *MAIN INI FILE AT {$sys['PHP_INI']}* ;;;" . PHP_EOL;
    $output .= get_file_contents($sys['PHP_INI']);
    $other_inis = get_additional_ini_files();
    foreach ($other_inis as $inif) {
        $output .= ";;; *Additional ini file at $inif* ;;;" . PHP_EOL;
        $output .= get_file_contents($inif);
    }
    $here = unix_path_dir();
    $unrec_ini_files = unrecognised_inis_webspace($here);
    foreach ($unrec_ini_files as $urinif) {
        $output .= ";;; *UNRECOGNISED INI FILE at $urinif* ;;;" . PHP_EOL;
        $output .= get_file_contents($urinif);
    }
    return $output;
}

function scan_inis_for_loader()
{
    $ldloc = array('location' => '', 'errors' => array());
    $sysinfo = get_sysinfo();
    if (empty($sysinfo['PHP_INI'])) {
        $ini_files_not_found = array("Main ini file");
        $ini_file_list = get_additional_ini_files();
    } else {
        $ini_files_not_found = array();
        $ini_file_list = array_merge(array($sysinfo['PHP_INI']),get_additional_ini_files());
    }
    $server_type = find_server_type();
    $shared_server = SERVER_SHARED == $server_type;
    foreach ($ini_file_list as $f) {
        $ldloc = get_loader_location_from_ini($f);
        if (array_key_exists(ERROR_INI_ZE_LINE_NOT_FOUND,$ldloc['errors'])) {
            unset($ldloc['errors'][ERROR_INI_ZE_LINE_NOT_FOUND]);
        } 
        if ($shared_server && array_key_exists(ERROR_INI_NOT_FOUND,$ldloc['errors'])) {
            if (false == user_ini_space_path($f)) {
                $ldloc['errors'][ERROR_INI_NOT_FOUND] = "A system ini file cannot be found or read by the Wizard - you cannot do anything about this on your shared server.";
            } else {
                $ldloc['errors'][ERROR_INI_USER_INI_NOT_FOUND] = $ldloc['errors'][ERROR_INI_NOT_FOUND];
            }
        } elseif (array_key_exists(ERROR_INI_NOT_FOUND,$ldloc['errors'])) {
            $ini_files_not_found[] = $f;
        }
        if (!empty($ldloc['location'])) {
            break;
        }
    }
    if (!empty($ini_files_not_found)) {
        $plural = (count($ini_files_not_found) > 1)?"s":"";
        $ldloc['errors'][ERROR_INI_NOT_FOUND] = "The following ini file$plural could not be found by the Wizard: " . join(',',$ini_files_not_found);
        if (is_restricted_server()) {
            $ldloc['errors'][ERROR_INI_NOT_FOUND] .= "<br> This may be due to server restrictions in place.";
        }
    }
    if (empty($ldloc['location'])) {
        $ldloc['errors'][ERROR_INI_ZE_LINE_NOT_FOUND] = "The necessary zend_extension line could not be found in the configuration.";
    }
    return $ldloc;
}

function find_loader_filesystem()
{
    $ld_inst_dir = loader_install_dir(find_server_type());
    $loader_name = get_loader_name();
    $suggested_loader_path = $ld_inst_dir . DIRECTORY_SEPARATOR . $loader_name;
    if (@file_exists($suggested_loader_path)) {
        $location = $suggested_loader_path;
    } elseif (@file_exists($loader_name)) {
        $location = @realpath($loader_name);
    } else {
        $ld_loc = get_loader_location();
        if (@file_exists($ld_loc)) {
            $location = $ld_loc;
        } else {
            $location = '';
        }
    }
    return $location;
}

function find_loader($search_directories_if_not_ini = false)
{
    $sysinfo = get_sysinfo();
    $php_ini = $sysinfo['PHP_INI'];
    $rtl_path = get_runtime_loading_path_if_applicable();
    $location = '';
    $errors = array();

    if (!empty($rtl_path)) {
        $location = $rtl_path;
    } else {
        $loader_ini = scan_inis_for_loader();
        $location = $loader_ini['location'];
        $errors = $loader_ini['errors'];
    }
    if (empty($location) && (empty($errors) || $search_directories_if_not_ini)) {
        $errors = array(); 
        $location = find_loader_filesystem();
        if (empty($location)) {
            $errors[ERROR_LOADER_NOT_FOUND] = 'The loader file could not be found in standard locations.';
        }
    }
    if (!empty($errors)) {
        return $errors;
    } else {
        return $location;
    }
}

function zend_extension_line_start()
{
    $sysinfo = get_sysinfo();
    $is_53_or_later = is_php_version_or_greater(5,3);
    return (is_bool($sysinfo['THREAD_SAFE']) && $sysinfo['THREAD_SAFE'] && !$is_53_or_later ? 'zend_extension_ts' : 'zend_extension');
}

function ioncube_loader_version_information()
{
    $old_version = true;
    $liv = "";
    $lv = "";
    $mv = 0;
    if (function_exists('ioncube_loader_iversion')) {
        $liv = ioncube_loader_iversion();
        $lv = sprintf("%d.%d.%d", $liv / 10000, ($liv / 100) % 100, $liv % 100);

        $latest_version =  get_latestversion();

        $lat_parts = explode('.',$latest_version);
        $cur_parts = explode('.',$lv);

        if (($cur_parts[0] > $lat_parts[0]) || 
            ($cur_parts[0] == $lat_parts[0] && $cur_parts[1] > $lat_parts[1]) ||
             ($cur_parts[0] == $lat_parts[0] && $cur_parts[1] == $lat_parts[1] && $cur_parts[2] >= $lat_parts[2])) {
            $old_version = false;
        } else {
            $old_version = $latest_version;
        }
        $mv = $cur_parts[0];
    }
    return array($lv,$mv,$old_version);
}

function default_loader_version_info()
{
    return array();
}

function get_loader_version_info()
{
    return get_remote_session_value('loader_version_info',LOADER_LATEST_VERSIONS_URL,'default_loader_version_info');
}

function calc_platform()
{
    $platform = array();
    $platform_info = get_platforminfo();
    $loader = get_loaderinfo();
    $multiple_os_versions = false;
    if (is_array($loader) && array_key_exists('osvariants',$loader) && is_array($loader['osvariants'])) {
        $versions = array_values($loader['osvariants']);
        $multiple_os_versions = !empty($versions[0]);
    }
    if ($multiple_os_versions) {
        list($osvar,$exact_match) = get_reqd_version($loader['osvariants']);
    } else {
        $osvar = null;
        if (is_ms_windows()) {
            $sys = get_sysinfo();
            $phpc = (empty($sys['PHP_COMPILER']))?'vc6':strtolower($sys['PHP_COMPILER']); 
            $osvar = ($sys['THREAD_SAFE']?'':'nonts_') . $phpc;
        }
    }
    foreach ($platform_info as $p) {
        if ($p['os'] == $loader['oscode'] && $p['arch'] == $loader['arch'] && (empty($osvar) || $p['os_mod'] == "_" . $osvar)) {
            $platform = $p;
            break;
        }
    }
    return $platform;
}

function get_platform()
{
    static $this_platform;

    if (!isset($this_platform)) {
        $this_platform = calc_platform();
    }

    return $this_platform;
}

function is_legacy_platform()
{
    $platform = get_platform();
    return array_key_exists('is_legacy',$platform);
}

function calc_dirname()
{
    $dirname = '';
    $platform = get_platform();
    if (!empty($platform)) {
        $dirname = $platform['dirname'];
    }
    return $dirname;
}

function calc_loader_latest_version()
{
    $lv_info = get_loader_version_info();
    $latest_version = RECENT_LOADER_VERSION;
    if (!empty($lv_info)) {
        $dirname = calc_dirname();
      
        if (!empty($dirname)) {
            $compiler_specific_version = false;
            if (is_ms_windows()) {
                $sys = get_sysinfo();
                $phpc = strtolower($sys['PHP_COMPILER']);
                if (!empty($phpc)) {
                    $dirname_comp = $dirname . "_" . $phpc;
                    if (array_key_exists($dirname_comp,$lv_info)) {
                        $latest_version = $lv_info[$dirname_comp];
                        $compiler_specific_version = true;
                    }
                }
            }
            if (!$compiler_specific_version && array_key_exists($dirname,$lv_info)) {
                $latest_version = $lv_info[$dirname];
            }
        } 
    }
    return $latest_version;
}

function get_latestversion()
{
    static $latest_version;

    if (empty($latest_version)) {
        $latest_version = calc_loader_latest_version();
    }
    return $latest_version;
}


function runtime_loader_location()
{
    $loader_path = false;
    $ext_path = extension_dir_path();
    if ($ext_path !== false) {
        $id = $ext_path;
        $here = dirname(__FILE__);
        if (isset($id[1]) && $id[1] == ':') {
            $id = str_replace('\\','/',substr($id,2));
            $here = str_replace('\\','/',substr($here,2));
        }
        $rd=str_repeat('/..',substr_count($id,'/')).$here.'/';
        $i=strlen($rd);

        $loader_loc = DIRECTORY_SEPARATOR . basename($here) . DIRECTORY_SEPARATOR . get_loader_name();
        while($i--) {
            if($rd[$i]=='/') {
                $loader_path = runtime_location_exists($ext_path,$rd,$i,$loader_loc);
                if ($loader_path !== false) {
                    break;
                }
            }
        }

        if (!$loader_path && !empty($loader_loc) && @file_exists($loader_loc)) {
            $loader_path = basename($loader_loc);
        }
    }
    return $loader_path;
}

function runtime_location_exists($ext_dir,$path_str,$sep_pos,$loc_name)
{
    $sub_path = substr($path_str,0,$sep_pos);
    $lp = $sub_path . $loc_name;
    $fqlp = $ext_dir.$lp;

    if(@file_exists($fqlp)) {
        return $lp;
    } else {
        return false;
    }
}

function runtime_loading_is_possible() {
    return !((is_php_version_or_greater(5,2,5)) || is_restricted_server() || !ini_get('enable_dl') || !function_exists('dl') || function_is_disabled('dl') || threaded_and_not_cgi());
}

function shared_and_runtime_loading()
{
    return (find_server_type() == SERVER_SHARED && empty($_SESSION['use_ini_method']) && runtime_loading_is_possible());
}

function get_valid_runtime_loading_path($ignore_loading_check = false)
{
    if ($ignore_loading_check || runtime_loading_is_possible()) {
        return runtime_loader_location();
    } else {
        return false;
    }
}

function runtime_loading($rtl_path = null)
{
    if (empty($rtl_path)) {
        $rtl_path = get_valid_runtime_loading_path();
    }
    if (!empty($rtl_path) && @dl($rtl_path)) {
        return $rtl_path;
    } else {
        return false;
    }
}

function get_runtime_loading_path_if_applicable()
{
    $rtl = null;
    if (shared_and_runtime_loading()) {
        $rtl = get_valid_runtime_loading_path();
    }
    return $rtl;
}

function try_runtime_loading_if_applicable()
{
    $rtl_path = get_runtime_loading_path_if_applicable();
    if (!empty($rtl_path)) {
        return runtime_loading($rtl_path);
    } else {
        return $rtl_path;
    }
}

function runtime_loading_instructions()
{
    $default = get_default_address();
    echo '<h4>Runtime Loading Instructions</h4>';
    echo '<div class=panel>';
    echo '<p>On your shared server the Loader can be installed using the runtime loading method.';
    echo " (<a href=\"{$default}&amp;manual=1\">Please click here if you are <strong>not</strong> on a shared server</a>.)</p>";

    if ('.' == extension_dir()) {
        $dirphrase = is_ms_windows()?'folder':'directory';
        echo "Please note that on your system the Loader <em>must</em> be present in the same " . $dirphrase . " as the first encoded file accessed.";
    }
    echo '<ol>';
    loader_download_instructions(); 
    $loader_dir = loader_install_instructions(SERVER_SHARED,dirname(__FILE__));
    shared_test_instructions();
    echo '</ol>';
    echo '</div>';
}

function runtime_loading_errors()
{
    $errors = array();
    $ext_path = extension_dir_path();
    if (false === $ext_path) {
        $errors[ERROR_RUNTIME_EXT_DIR_NOT_FOUND] = "Extensions directory cannot be found.";
    } else {
        $expected_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . get_loader_name();
        if (!@file_exists($expected_file)) {
            $errors[ERROR_RUNTIME_LOADER_FILE_NOT_FOUND] = "The Loader file was expected to be at $expected_file but could not be found.";
        } else {
            $errors = loader_compatibility_test($expected_file);
        }
    }
    return $errors;
}


function windows_package_name()
{
    $sys = get_sysinfo();
	$loader = get_loaderinfo();
    return (LOADERS_PACKAGE_PREFIX . 'win' . '_' . ($sys['THREAD_SAFE']?'':'nonts_') . strtolower($sys['PHP_COMPILER']) .  '_' . $loader['arch']);
}

function unix_package_name()
{
    $sysinfo = get_sysinfo();
    $loader = get_loaderinfo();
    $multiple_os_versions = false;
    if (is_array($loader) && array_key_exists('osvariants',$loader) && is_array($loader['osvariants'])) {
        $versions = array_values($loader['osvariants']);
        $multiple_os_versions = !empty($versions[0]);
    }
    if ($multiple_os_versions) {
        list($reqd_version,$exact_match) = get_reqd_version($loader['osvariants']);
        if ($reqd_version) {
            $basename = LOADERS_PACKAGE_PREFIX . $loader['oscode'] . '_' . $reqd_version . '_' . $loader['arch'];
        } else {
            $basename = "";
        }
    } else {
        $basename = LOADERS_PACKAGE_PREFIX . $loader['oscode'] . '_' . $loader['arch'];
    }
    return array($basename,$multiple_os_versions);
}

function loader_download_instructions()
{
    $sysinfo = get_sysinfo();
    $loader = get_loaderinfo();
    $multiple_os_versions = false;

    if (is_ms_windows()) {
        if (is_bool($sysinfo['THREAD_SAFE'])) {
            $download_str = '<li>Download the following archive of Windows ' . $sysinfo['PHP_COMPILER'];
            if (!$sysinfo['THREAD_SAFE']) {
                $download_str .= ' non-TS';
            }
            $download_str .= ' ' . $loader['arch'] . ' Loaders:';
            echo $download_str;
            $basename = windows_package_name();
            echo make_archive_list($basename,array('zip'));
            echo 'A Loaders archive can also be downloaded from <a href="' . LOADERS_PAGE . '" target="loaders">' . LOADERS_PAGE . '</a>.';
        } else {
            echo '<li>Download a Windows Loaders archive from <a href="' . LOADERS_PAGE  . '" target=loaders>here</a>. If PHP is built with thread safety disabled, use the Windows non-TS Loaders.';
        }
    } else {
        list($basename,$multiple_os_versions) = unix_package_name(); 
        if ($basename == "") {
            echo '<li>Download a ' . $loader['osname'] . ' ' . $loader['arch'] . ' Loaders archive from <a href="' . LOADERS_PAGE . '" target="loaders">here</a>.';
            echo "<br>Your system appears to be {$loader['osnamequal']} for {$loader['wordsize']} bit. If Loaders are not available for that exact release of {$loader['osname']}, Loaders built for an earlier release should work. Note that you may need to install back compatibility libraries for the operating system.";
            echo '<br>If you cannot find a suitable loader then please raise a ticket at <a href="'. SUPPORT_SITE . '">our support helpdesk</a>.';
        } else {
            echo '<li>Download one of the following archives of Loaders for ' . $loader['osnamequal'] . ' ' . $loader['arch'] . ':'; 
            if (SERVER_SHARED == find_server_type()) {
                $archives = array('zip','tar.gz');
            } else {
                $archives = array('tar.gz','zip');
            }
            echo make_archive_list($basename,$archives);
            echo "</p>";
            if ($multiple_os_versions && !$exact_match) {
                echo "<p>Note that you may need to install back compatibility libraries for  {$loader['osname']}.</p>";
            }
        }
    }

    echo '</li>';
}

function ini_dir()
{
    $sysinfo = get_sysinfo();
    $parent_dir = '';
    if (!empty($sysinfo['PHP_INI'])) {
        $parent_dir = dirname($sysinfo['PHP_INI']);
    } else {
        $parent_dir = $_SERVER["PHPRC"];
        if (@is_file($parent_dir)) {
            $parent_dir = dirname($parent_dir);
        }
    }
    return $parent_dir;
}

function unix_install_dir()
{
    $ext_dir = extension_dir_path();
    $cur_dir = @realpath('.');
    if (empty($ext_dir) || $ext_dir == $cur_dir) {
        $loader_dir = UNIX_SYSTEM_LOADER_DIR;
    } else {
        $loader_dir = $ext_dir;
    }
    return $loader_dir;
}

function windows_install_dir()
{
    $sysinfo = get_sysinfo();
    if ($sysinfo['SS'] == 'IIS') {
        if (false === ($ext_dir = extension_dir_path())) {
            $parent_dir = ini_dir();
            $ext_dir = $parent_dir . '\\ext';
            if (!empty($parent_dir) && @file_exists($ext_dir)) {
                $loader_dir = $ext_dir;
            } else {
                $loader_dir = $_SERVER['windir'] . '\\' . WINDOWS_IIS_LOADER_DIR;
            }
        } else {
            $loader_dir = $ext_dir;
        }
    } else {
        if (false === ($ext_dir = extension_dir_path())) {
			$parent_dir = ini_dir();
			$loader_dir = $parent_dir . '\\' . 'ioncube';
		} else {
			$loader_dir = $ext_dir;
		}
    }
    return $loader_dir;
}

function loader_install_dir($server_type)
{
    if (SERVER_SHARED == $server_type && own_php_ini_possible()) {
        $loader_dir = get_default_loader_dir_webspace();
    } elseif (is_ms_windows()) {
        $loader_dir = windows_install_dir();
    } else {
        $loader_dir = unix_install_dir();
    }
    return $loader_dir;
}

function writeable_directories()
{
    $root_path = @realpath($_SERVER['DOCUMENT_ROOT']);
    $above_root_path = @realpath($_SERVER['DOCUMENT_ROOT'] . "/..");
    $root_path_cgi_bin = @realpath($_SERVER['DOCUMENT_ROOT'] . "/cgi-bin");
    $above_root_cgi_bin = @realpath($_SERVER['DOCUMENT_ROOT'] . "/../cgi-bin");

    $paths = array();
    foreach (array($root_path,$above_root_path,$root_path_cgi_bin,$above_root_cgi_bin) as $p) {
        if (@is_writeable($p)) {
            $paths[] = $p;
        }
    }
    return $paths;
}

function loader_install_instructions($server_type,$loader_dir = '')
{
    if (empty($loader_dir)) {
        $loader_dir = loader_install_dir($server_type);
    }
    if (SERVER_LOCAL == $server_type) {
        echo "<li>Put the Loader files in <code>$loader_dir</code></li>";
    } else {
        echo "<li>Transfer the Loaders to your web server and install in <code>$loader_dir</code></li>";
    }
    return $loader_dir;
}

function zend_extension_lines($loader_dir)
{
    $zend_extension_lines = array();
    $sysinfo = get_sysinfo();
    $qt = (is_ms_windows()?'"':'');
    $loader = get_loaderinfo();

    if (!is_bool($sysinfo['THREAD_SAFE']) || !$sysinfo['THREAD_SAFE']) {
        $path = $qt . $loader_dir . DIRECTORY_SEPARATOR . $loader['file'] . $qt;
        $zend_extension_lines[] = "zend_extension = " . $path;
    }
    if ((!is_bool($sysinfo['THREAD_SAFE']) && !is_php_version_or_greater(5,3)) || $sysinfo['THREAD_SAFE']) {
        $line_start = is_php_version_or_greater(5,3)?'zend_extension':'zend_extension_ts';
        $path = $qt . $loader_dir . DIRECTORY_SEPARATOR . $loader['file_ts'] . $qt;
        $zend_extension_lines[] = $line_start . " = " . $path;
    }
    return $zend_extension_lines;
}

function user_ini_base()
{
    $doc_root_path = realpath($_SERVER['DOCUMENT_ROOT']);
    $above_root_path = @realpath($_SERVER['DOCUMENT_ROOT'] . "/..");
    if (!empty($above_root_path) && @is_writeable($above_root_path)) {
        $start_path = $above_root_path;
    } else {
        $start_path = $doc_root_path;
    }
    return $start_path;
}

function user_ini_space_path($file)
{
    $user_base = user_ini_base();
    $fpath = @realpath($file);
    if (!empty($fpath) && (0 === strpos($fpath,$user_base))) {
        return $fpath;
    } else {
        return false;
    }
}

function default_ini_path()
{
    return (realpath($_SERVER['DOCUMENT_ROOT']));
}

function shared_ini_location()
{
    $phprc = getenv('PHPRC');
    if (!empty($phprc)) {
        $phprc_path = user_ini_space_path($phprc);
        if (false !== $phprc_path) {
            return $phprc_path;
        } else {
            return default_ini_path();
        }
    } else {
        return default_ini_path();
    }
}


function zend_extension_instructions($server_type,$loader_dir)
{
    $sysinfo = get_sysinfo();
    $base = get_base_address();
    $editing_ini = true;

    $php_ini_name = ini_file_name();

    if (isset($sysinfo['PHP_INI']) && @file_exists($sysinfo['PHP_INI'])) {
        $php_ini_path = $sysinfo['PHP_INI'];
    } else {
        $php_ini_path = '';
    }

    if (is_bool($sysinfo['THREAD_SAFE'])) {
        $kwd = zend_extension_line_start();
    } else {
        $kwd = 'zend_extension/zend_extension_ts';
    }

    $server_type_code = server_type_code();

    $zend_extension_lines = zend_extension_lines($loader_dir);

    if (SERVER_SHARED == $server_type && own_php_ini_possible()) {
        $ini_dir = shared_ini_location();
        $php_ini_path = $ini_dir . DIRECTORY_SEPARATOR . $php_ini_name;
        if (@file_exists($php_ini_path)) {
            $edit_line = "<li>Edit the <code>$php_ini_name</code> in the <code>$ini_dir</code> directory";
            if (zend_extension_line_missing($php_ini_path) && @is_writeable($php_ini_path) && @is_writeable($ini_dir)) {
                if (function_exists('file_get_contents')) {
                    $ini_strs = @file_get_contents($php_ini_path);
                } else {
                    $lines = @file($php_ini_path);
                    $ini_strs = join(' ',$lines);
                }
                $fh = @fopen($php_ini_path,"wb");
                if ($fh !== false) {
                    foreach ($zend_extension_lines as $zl) {
                        fwrite($fh,$zl . PHP_EOL);
                    }
                    fwrite($fh,$ini_strs);
                    fclose($fh);
                    $editing_ini = false;
                    echo "<li>Your php.ini file at $php_ini_path has been modified to include the necessary line for the ionCube Loader.";
                } else {
                    echo $edit_line;
                }
            } else {
               echo $edit_line;
            }
        } else {
            $download_ini_file = "<li><a href=\"$base&amp;page=phpconfig&amp;ininame=$php_ini_name&amp;stype=$server_type_code&amp;download=1&amp;prepend=1\">Save this  <code>$php_ini_name</code> file</a> and upload it to <code>$ini_dir</code> (full path on your server).";
            if (@is_writeable($ini_dir)) {
                $fh = @fopen($php_ini_path,"wb");
                if ($fh !== false) {
                    foreach ($zend_extension_lines as $zl) {
                       fwrite($fh,$zl . PHP_EOL);
                    }
                    if (!empty($sysinfo['PHP_INI']) && is_readable($sysinfo['PHP_INI'])) {
                        if (function_exists('file_get_contents')) {
                           $ini_strs = @file_get_contents($sysinfo['PHP_INI']);
                        } else {
                           $lines = @file($sysinfo['PHP_INI']);
                           $ini_strs = join(' ',$lines);
                        }
                        fwrite($fh,$ini_strs);
                    }
                    fclose($fh); 
                    echo "<li>A <code>$php_ini_name</code> file has been created for you in <code>$ini_dir</code>.";
                } else {
                    echo $download_ini_file;
                }
            } else {
                echo $download_ini_file;
            }
            $editing_ini = false;
        }
    } elseif (!empty($sysinfo['PHP_INI'])) {
        if (empty($sysinfo['PHP_INI_DIR'])) {
            echo "<li>Edit the file <code>{$sysinfo['PHP_INI']}</code>";
        } else {
            $php_ini_path = find_additional_ioncube_ini();
            if (empty($php_ini_path)) {
                $php_ini_name = ADDITIONAL_INI_FILE_NAME;
                echo "<li><a href=\"$base&amp;page=phpconfig&amp;download=1&amp;newlinesonly=1&amp;ininame=$php_ini_name&amp;stype=$server_type_code\">Save this $php_ini_name file</a> and put it in your ini files directory, <code>{$sysinfo['PHP_INI_DIR']}</code>";
                $editing_ini = false;
            } else {
                $php_ini_name = basename($php_ini_path);
                echo "<li>Edit the file <code>$php_ini_path</code>";
            }
        }
    } else {
        echo "<li>Edit the system <code>$php_ini_name</code> file";
    }
    if ($editing_ini) {
        echo " and <b>before</b> any other $kwd lines ensure that the following is included:<br>";
        foreach ($zend_extension_lines as $zl) {
            echo "<code>$zl</code><br>";
        }
        if (!empty($php_ini_path)) {
            if (zend_extension_line_missing($php_ini_path)) {
                echo "<a>Alternatively, replace your current <code>$php_ini_path</code> file with <a href=\"$base&amp;page=phpconfig&amp;ininame=$php_ini_name&amp;stype=$server_type_code&amp;download=1&amp;prepend=1\">this new $php_ini_name file</a>."; 
            }
        }
    }
    echo '</li>';
}

function server_restart_instructions()
{
    $sysinfo = get_sysinfo();
    $base = get_base_address();

    if ($sysinfo['SS']) {
		if ($sysinfo['SS'] == 'PHP-FPM') {
			echo "<li>Restart PHP-FPM.</li>";
		} else {
			echo "<li>Restart the {$sysinfo['SS']} server software.</li>";
		}
    } else {
        echo "<li>Restart the server software.</li>";
    }

    echo "<li>When the server software has restarted, <a href=\"$base&amp;page=loader_check\" onclick=\"showOverlay();\">click here to test the Loader</a>.</li>";

	if ($sysinfo['SS'] && $sysinfo['SS'] == 'PHP-FPM') {
		echo '<li>If the Loader installation failed, check the PHP-FPM error log file for errors.</li>';
    } elseif ($sysinfo['SS'] == 'Apache' && !is_ms_windows()) {
        echo '<li>If the Loader installation failed, check the Apache error log file for errors and see our guide to <a target="unix_errors" href="'. UNIX_ERRORS_URL . '">Unix related errors</a>.</li>';
    }
}

function shared_test_instructions()
{
    $base = get_base_address();
    echo "<li><a href=\"$base&amp;page=loader_check\" onclick=\"showOverlay();\">Click here to test the Loader</a>.</li>";
}

function link_to_php_ini_instructions()
{
    $default = get_default_address();
    echo "<p><a href=\"{$default}&amp;stype=s&amp;ini=1\">Please click here for instructions on using the php.ini method instead</a>.</p>";
}

function php_ini_instruction_list($server_type)
{
    echo '<h4>Installation Instructions</h4>';
    echo '<div class=panel>';
    echo '<ol>';

    loader_download_instructions(); 
    $loader_dir = loader_install_instructions($server_type);
    zend_extension_instructions($server_type,$loader_dir);
    if ($server_type != SERVER_SHARED || !own_php_ini_possible()) {
        server_restart_instructions();
    } else {
        shared_test_instructions();
    } 
    echo '</ol>';
    echo '</div>';
}

function php_ini_install_shared($give_preamble = true)
{
    $php_ini_name = ini_file_name();
    $default = get_default_address();
    if ($give_preamble) {
        echo "<p>On your <strong>shared</strong> server, the Loader should be installed using a <code>$php_ini_name</code> configuration file.";
        echo " (<a href=\"{$default}&amp;manual=1\">Please click here if you are <strong>not</strong> on a shared server</a>.)</p>";
    }

    if (own_php_ini_possible()) {
        echo '<p>With your hosting account, you may be able to use your own PHP configuration file.</p>';
    } else {
        echo "<p>It appears that you cannot install the ionCube Loader using the <code>$php_ini_name</code> file. Your server provider or system administrator should be able to perform the installation for you. Please refer them to the following instructions.</p>";
    }

    php_ini_instruction_list(SERVER_SHARED);
}

function php_ini_install($server_type_desc = null, $server_type = SERVER_DEDICATED, $required = true)
{
    $php_ini_name = ini_file_name();
    $default = get_default_address();

    echo '<p>';
    if ($server_type_desc) {
        echo "For a <strong>$server_type_desc</strong> server ";
    } else {
        echo "For this server ";
    }

    if ($required) {
        echo "you should install the ionCube Loader using the <code>$php_ini_name</code> configuration file.";
    } else {
        echo "installing the ionCube Loader using the <code>$php_ini_name</code> file is recommended.";
    }
    if ($server_type_desc) {
        echo " (<a href=\"{$default}&amp;manual=1\">Please click here if you are <strong>not</strong> on a $server_type_desc server</a>.)";
    }
    echo '</p>';
      
    php_ini_instruction_list($server_type);
}



function help_resources($error_list = array())
{
	$self = get_self();
    $base = get_base_address();
    $server_type_code = server_type_code();
    $server_type = find_server_type();
    $sysinfo = get_sysinfo();
    $resources = array(
            '<a target="_blank" href="' . LOADERS_FAQ_URL . '">ionCube Loaders FAQ</a>',
            '<a target="_blank" href="' . LOADER_FORUM_URL . '">ionCube Loader Forum</a>'
        );
    if (SERVER_SHARED != $server_type || own_php_ini_possible(true)) {
		$support_info = array ( 
			'department' 		=> WIZARD_SUPPORT_TICKET_DEPARTMENT,
			'subject' 			=> "ionCube Loader installation problem",
			'message' 			=> support_ticket_information()
		   );
		if (SERVER_LOCAL == $server_type && !info_should_be_disabled()) {
			$temp_files = system_info_temporary_files();
		} else {
			$temp_files = NULL;
		}
		if (!empty($temp_files)) {
			$support_info['ini'] = base64_encode(file_get_contents($temp_files['ini']));
			$support_info['phpinfo'] = base64_encode(file_get_contents($temp_files['phpinfo']));
			$support_info['additional'] = base64_encode(file_get_contents($temp_files['additional']));
			
			$loader_path = find_loader(true);
			if (is_string($loader_path)) {		
				$support_info['loader'] = base64_encode(file_get_contents($loader_path));
				$support_info['loader_name'] = basename($loader_path);
			} else {
				$support_info['loader'] = '';
				$support_info['loader_name'] = '';
			}
		} else {
			$support_info['ini'] = '';
			$support_info['phpinfo'] = '';
			$support_info['additional'] = '';
			$support_info['loader'] = '';
			$support_info['loader_name'] = '';
		}
		 
        $resources[2] = '<form action="' . SUPPORT_SITE . 'lw_index.php' .'" method="POST" id="support-ticket"><a href="" onclick="document.getElementById(\'support-ticket\').submit(); return false;">Raise a support ticket through our helpdesk</a>';
		$resources[2] .= '<input type="hidden" name="department" value="' . $support_info['department'] . '"/>';
		$resources[2] .= '<input type="hidden" name="subject" value="' . $support_info['subject'] . '"/>';
		$resources[2] .= '<input type="hidden" name="message" value="' . $support_info['message'] . '"/>';
		if (!empty($temp_files)) {
			$resources[2] .= '<input type="hidden" name="phpinfo" value="' . $support_info['phpinfo'] . '"/>';
			$resources[2] .= '<input type="hidden" name="ini" value="' . $support_info['ini'] . '"/>';
			$resources[2] .= '<input type="hidden" name="additional" value="' . $support_info['additional'] . '"/>';
			$resources[2] .= '<input type="hidden" name="loader" value="' . $support_info['loader'] . '"/>';
			$resources[2] .= '<input type="hidden" name="loader_name" value="' . $support_info['loader_name'] . '"/>';
		}
		$resources[2] .= '</form>';
    } 
	
    if (SERVER_SHARED == $server_type && own_php_ini_possible(true) && !user_ini_space_path($sysinfo['PHP_INI'])) {
        $resources[3] = '<strong>Please check with your host that you can create php.ini files that will override the system one.</strong>';
    }
    return $resources;
}

function system_info_temporary_files()
{
    $tmpfname_ini = get_tempnam("/tmp", "INI");
    $tmpfname_ini .= ".ini";
    $fh_ini = @fopen($tmpfname_ini,'wb');
    if ($fh_ini) {
        $config = all_ini_contents();
        fwrite($fh_ini,$config);
        fclose($fh_ini);
    } else {
        $tmpfname_ini = '';
    }

    $tmpfname_pinf = get_tempnam("/tmp", "PIN");
    $tmpfname_pinf .= ".html";
    $fh_pinfo = @fopen($tmpfname_pinf,'wb');
    if ($fh_pinfo) {
        ob_start();
        @phpinfo();
        $pinfo = ob_get_contents();
        ob_end_clean();
        fwrite($fh_pinfo,$pinfo);
        fclose($fh_pinfo);
    } else {
        $tmpfname_pinf = '';
    }

    $tmpfname_add = get_tempnam("/tmp", "ADD");
    $tmpfname_add .= ".html";
    $fh_add = @fopen($tmpfname_add,'wb');
    if ($fh_add) {
        ob_start();
        extra_page(false);
        $extra = ob_get_contents();
        ob_end_clean();
        fwrite($fh_add,$extra);
        fclose($fh_add);
    } else {
        $tmpfname_add = '';
    }

    if (empty($tmpfname_ini) || empty($tmpfname_pinf) || empty($tmpfname_add)) {
        return (array());
    } else {
        return (array('ini'           =>   $tmpfname_ini,
                      'phpinfo'       =>   $tmpfname_pinf,
                      'additional'    =>   $tmpfname_add));
    }
}

function get_tempnam($default_tmp_dir = '', $prefix = '')
{
	if (function_exists('sys_get_temp_dir')) {
		return tempnam(sys_get_temp_dir(),$prefix);
	} else {
		return @tempnam($default_tmp_dir, $prefix);
	}
}
function system_info_archive_page()
{
    info_disabled_check();
	$server_type = find_server_type();
	if (SERVER_LOCAL != $server_type) {
		exit;
	}
    $loader = find_loader(true);
    if (is_string($loader)) {
        $loader_file = $loader;
    } else {
        $loader_file = '';
    }
    $all_files = system_info_temporary_files();
    if (!empty($all_files)) {
        if (!empty($loader_file)) {
            $all_files['loader'] = $loader_file;
        }
        $archive_name =  get_tempnam('/tmp',"ARC");
        if (extension_loaded('zip')) {
            $archive_name .= '.zip';
            $zip = @new ZipArchive();
            $mode = @constant("ZIPARCHIVE::OVERWRITE");
            if (!$zip || $zip->open($archive_name, $mode)!==TRUE) {
                $archive_name = '';
            } else {
                foreach($all_files as $f) {
                    $zip->addFile($f,basename($f));
                }
                $zip->close();
            }
        } elseif (extension_loaded('zlib') && !is_ms_windows()) {
            $tar_name = $archive_name . ".tar";
            $all_files_str = join(' ',$all_files);
            $script = "tar -chf $tar_name $all_files_str";
            $result = @system($script,$retval);
            if ($result !== false) {
                $archive_name = $tar_name . '.gz';
                $zp = gzopen($archive_name,"w9");
                $tar_contents = get_file_contents($tar_name);
                gzwrite($zp,$tar_contents);
                gzclose($zp);
            } else {
                $archive_name = '';
            }
        } else {
            $archive_name = '';
        }
    } else {
        $archive_name = '';
    }
    if ($archive_name) {
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename='. $archive_name);
        @readfile($archive_name);
    } else {
        $self = get_self();
        $base = get_base_address();
        $server_type_code = server_type_code();
        heading();
        echo "<p>A downloadable archive of system information could not be created.<br> 
            <strong>Please save each of the following and then attach those files to the support ticket:</strong></p>"; 
        echo "<ul>";
        echo "<li><a href=\"$base&amp;page=phpinfo\" target=\"phpinfo\">phpinfo()</a></li>";
        echo "<li><a href=\"$base&amp;page=phpconfig\" target=\"phpconfig\">config</a></li>";
        echo "<li><a href=\"$base&amp;page=extra&amp;stype=$server_type_code\" target=\"extra\">additional information</a></li>";
        echo "<li><a href=\"$self?page=loaderbin\">loader file</a></li>";
        echo "</ul>";
        footer(true);
    }
}

function support_ticket_information($error_list = array())
{
    $sys = get_sysinfo();
    $ld = get_loaderinfo();

    $ticket_strs = array();
    $ticket_strs[] = "PLEASE DO NOT REMOVE THE FOLLOWING INFORMATION\r\n";
    $ticket_strs[] = "==============\r\n";
    if (!empty($error_list)) {
        $ticket_strs[] = "[hr]";
        $ticket_strs[] = "ERRORS";
        $ticket_strs[] = "[table]";
        $ticket_strs[] = '[tr][td]' . join('[/td][/tr][tr][td]',$error_list) . '[/td][/tr]';
        $ticket_strs[] = "[/table]";
    }
    $ticket_strs[] = "[hr]";
    $ticket_strs[] = "SYSTEM INFORMATION";
    $info_lines = array();
    $info_lines["Wizard version"] = script_version();
    $info_lines["PHP uname"] = $ld['uname'];
    $info_lines["Machine architecture"] = $ld['arch'];
    $info_lines["Word size"] = $ld['wordsize'];
    $info_lines["Operating system"] = $ld['osname'] . ' ' . $ld['osver'];
    if (selinux_is_enabled() || possibly_selinux()) {
        $info_lines["Security enhancements"] = "SELinux";
    } elseif (grsecurity_is_enabled()) {
        $info_lines["Security enhancements"] = "Grsecurity";
    } else {
        $info_lines["Security enhancements"] = "None";
    }
    $info_lines["PHP version"] = PHP_VERSION; 
    if ($sys['DEBUG_BUILD']) {
        $info_lines["DEBUG BUILD"] = "DEBUG BUILD OF PHP";
    }
    if (!$sys['SUPPORTED_COMPILER']) {
        $info_lines["SUPPORTED PHP COMPILER"] = "FALSE";
        $info_lines["PHP COMPILER"] = $sys['PHP_COMPILER'];
    }
    $info_lines["Is CLI?"] = ($sys['IS_CLI']?"Yes":"No");
    $info_lines["Is CGI?"] = ($sys['IS_CGI']?"Yes":"No");
    $info_lines["Is thread-safe?"] = ($sys['THREAD_SAFE']?"Yes":"No");
    $info_lines["Web server"] = $sys['FULL_SS'];
    $info_lines["Server type"] = server_type_string();
    $info_lines["PHP ini file"] = $sys['PHP_INI'];
    if (!@file_exists($sys['PHP_INI'])) {
        $info_lines["Ini file found"] = "INI FILE NOT FOUND";
    } else {
        if (is_readable($sys['PHP_INI'])) {
            $info_lines["Ini file found"] = "INI FILE READABLE";
        } else {
            $fh = @fopen($sys['PHP_INI'],"rb");
            if ($fh === false) {
                $info_lines["Ini file found"] = "INI FILE FOUND BUT POSSIBLY NOT READABLE";
            } else {
                $info_lines["Ini file found"] = "INI FILE READABLE";
            }
        }
    }
    $info_lines["PHPRC"] = $sys['PHPRC'];
    $loader_path = find_loader();
    if (is_string($loader_path)) {
        $info_lines["Loader path"] =  $loader_path;
        $info_lines["Loader file size"] = filesize($loader_path) . " bytes.";
        $info_lines["Loader MD5 sum"] =  md5_file($loader_path);
    } else {
        $info_lines["Loader path"] =  "LOADER PATH NOT FOUND";
    }
    $server_type_code = server_type_code();
    if (!empty($_SESSION['hostprovider'])) {
      $info_lines['Hosting provider'] = $_SESSION['hostprovider'];
      $info_lines['Provider URL'] = $_SESSION['hosturl'];
    }
    $info_lines["Wizard script path"] = '[url]http://' . $_SERVER["HTTP_HOST"] . get_self() . '?stype='. $server_type_code . '[/url]';
    $ticket_strs[] = "[table]";
    foreach ($info_lines as $h => $i) {
        $value = (empty($i))?'EMPTY':$i;
        $ticket_strs[] = '[tr][td]' . $h . '[/td]' . '[td]' . $value . '[/td][/tr]';
    }
    $ticket_strs[] = '[/table]';
    $ticket_strs[] = '[hr]';
    $ticket_strs[] = "\r\n==============\r\n";
    $ticket_strs[] = "PLEASE ENTER ANY ADDITIONAL INFORMATION BELOW\r\n";

    $support_ticket_str = join('',$ticket_strs);
    return urlencode($support_ticket_str);
}

function wizard_stats_data($page_id)
{
    $data = array();

    try_runtime_loading_if_applicable();
    $sysinfo = get_sysinfo();
    $ldinfo = get_loaderinfo();

    $data['sessionid'] = session_id();
    $data['wizard_version'] = script_version();
    $data['server_type'] = server_type_code();
    $data['hostprovider'] = (isset($_SESSION['hostprovider']))?$_SESSION['hostprovider']:'';
    $data['hosturl'] = (isset($_SESSION['hosturl']))?$_SESSION['hosturl']:'';
    $data['page_id'] = $page_id;
    $data['loader_state'] = (extension_loaded(LOADER_EXTENSION_NAME))?'installed':'failure';
    $data['ini_location'] = $sysinfo['PHP_INI'];
    $data['is_cgi'] = ($sysinfo['IS_CGI'])?"yes":"no";
    $data['is_ts'] = ($sysinfo['THREAD_SAFE'])?"yes":"no";
    $data['arch'] = $ldinfo['arch'];
    $data['php_version'] = PHP_VERSION;
    $data['os'] = $ldinfo['osname'];
    $data['word_size'] = $ldinfo['wordsize'];
    $data['referrer'] =  $_SERVER["HTTP_HOST"] . get_self();

    return $data;
}

function send_stats($page_id = 'default')
{
    $server_type = find_server_type();
    $res = false;

    if (SERVER_LOCAL != $server_type) {
        $stats_data = wizard_stats_data($page_id);

        if (!isset($_SESSION['stats_sent'][$page_id][$stats_data['loader_state']])) {
            $url = WIZARD_STATS_URL;

            if (!empty($stats_data)) {
                if(function_exists('http_build_query')) {
                    $qparams = http_build_query($stats_data);
                } else {
                    $qparams = php4_http_build_query($stats_data);
                }
                $url .= '?' . $qparams;
                $res = remote_file_contents($url);
            }
            $_SESSION['stats_sent'][$page_id][$stats_data['loader_state']] = 1;
        } else {
            $res = true;
        }
    } else {
        $res = 'LOCAL';
    }
    return $res;
}

function os_arch_string_check($loader_str)
{
    $errors = array();
    if (preg_match("/target os:\s*(([^_]+)_([^-]*)-([[:graph:]]*))/i",$loader_str,$os_matches)) {
        $loader_info = get_loaderinfo();
        $dirname = calc_dirname();
        $packed_osname = preg_replace('/\s/','',strtolower($loader_info['osname']));
        if (strtolower($dirname) != $os_matches[1] && $packed_osname != $os_matches[2]) {
            $errors[ERROR_LOADER_WRONG_OS] = "You have the wrong loader for your operating system, ". $loader_info['osname'] . ".";
        } else {
            $loader_wordsize = (strpos($os_matches[3],'64') === false)?32:64;
            if ($loader_info['arch'] != ($ap = required_loader_arch($os_matches[3],$loader_info['oscode'],$loader_wordsize))) {
                $err_str = "You have the wrong loader for your machine architecture.";
                $err_str .= " Your system is " . $loader_info['arch'];
                $err_str .= " but the loader you are using is for " . $ap . ".";
                $errors[ERROR_LOADER_WRONG_ARCH] = $err_str;
            }
        }
    }
    return $errors;
}

function get_loader_strings($loader_location)
{
    if (function_exists('file_get_contents')) {
        $loader_strs = @file_get_contents($loader_location);
    } else {
        $lines = @file($loader_location);
        $loader_strs = join(' ',$lines);
    }
    return $loader_strs;
}

function loader_system($loader_location)
{
    $loader_system = array();
    $loader_strs = get_loader_strings($loader_location);

    if (!empty($loader_strs)) {

        if (preg_match("/ioncube_loader_..?\.._(.)\.(.)\.(..?)(_nonts)?(_amd64)?\.dll/i",$loader_strs,$version_matches)) {
            $loader_system['oscode'] = 'win';
            $loader_system['thread_safe'] = (isset($version_matches[4]) && $version_matches[4] == '_nonts')?0:1;
			if (preg_match("/_localtime([0-9][0-9])/i",$loader_strs,$size_matches)) {
				$loader_system['wordsize'] = ($size_matches[1] == '64')?64:32;
			} else {
				$loader_system['wordsize'] = 32;
			}
            $loader_system['arch'] = ($loader_system['wordsize'] == 64)?'x86-64':'x86';
            $loader_system['php_version_major'] = $version_matches[1];
            $loader_system['php_version_minor'] = $version_matches[2];
			if ($loader_system['php_version_major'] == 8 && $loader_system['php_version_minor'] >= 1) {
				$loader_system['compiler'] = 'VC16';
			} elseif ($loader_system['php_version_major'] == 7 && $loader_system['php_version_minor'] >= 2) {
				$loader_system['compiler'] = 'VC15'; 
			} elseif ($loader_system['php_version_major'] == 7 && $loader_system['php_version_minor'] < 2) {
				$loader_system['compiler'] = 'VC14'; 
			} elseif ($loader_system['php_version_major'] == 5 && $loader_system['php_version_minor'] >= 5) {
				$loader_system['compiler'] = 'VC11'; 
			} elseif (preg_match("/assemblyIdentity.*version=\"([^.]+)\./",$loader_strs,$compiler_matches)) {
                $loader_system['compiler'] = "VC" . strtoupper($compiler_matches[1]);
            } else {
                $loader_system['compiler'] = 'VC6';
            }
        } elseif (preg_match("/php version:\s*(.)\.(.)\.(..?)(-ts)?/i",$loader_strs,$version_matches)) {
            $loader_system['thread_safe'] = (isset($version_matches[4]) && $version_matches[4] == '-ts')?1:0;
            $loader_system['php_version_major'] = $version_matches[1];
            $loader_system['php_version_minor'] = $version_matches[2];
            if (preg_match("/target os:\s*(([^_]+)_([^-]*)-([[:graph:]]*))/i",$loader_strs,$os_matches)) {
                $loader_system['oscode'] = strtolower(substr($os_matches[2],0,3));
                $loader_system['wordsize'] = (strpos($os_matches[3],'64') === false)?32:64;
                $loader_system['arch'] = required_loader_arch($os_matches[3],$loader_system['oscode'],$loader_system['wordsize']);
                $loader_system['compiler'] = $os_matches[4];
            }
        }
        if (preg_match("/ionCube Loader Version\s+(\S+)/",$loader_strs,$loader_version)) {
            $loader_system['loader_version'] = $loader_version[1];
		} elseif (preg_match("/ioncube_loader_(\d{1,2}\.\d\.\d{1,2})\./",$loader_strs,$loader_version)){
			$loader_system['loader_version'] = $loader_version[1];
        } else {
            $loader_system['loader_version'] = 'UNKNOWN';
        }
        if (isset($loader_system['php_version_major'])) {
            $loader_system['php_version'] = $loader_system['php_version_major'] . '.' . $loader_system['php_version_minor'];
        }
    }
    return $loader_system;
}

function loader_compatibility_test($loader_location)
{
    $errors = array();

    $sysinfo = get_sysinfo();
    if (LOADER_NAME_CHECK) {
        $installed_loader_name = basename($loader_location);
        $expected_loader_name = get_loader_name();
        if ($installed_loader_name != $expected_loader_name) {
            $errors[ERROR_LOADER_UNEXPECTED_NAME] = "The installed loader (<code>$installed_loader_name</code>) does not have the name expected (<code>$expected_loader_name</code>) for your system. Please check that you have the correct loader for your system.";
        }
    }
    if (empty($errors) && !is_readable($loader_location)) {
        $execute_error = "The loader at $loader_location does not appear to be readable.";
        $execute_error .= "<br>Please check that it exists and is readable.";
        $execute_error .= "<br>Please also check the permissions of the containing ";
        $execute_error .= (is_ms_windows()?'folder':'directory') . '.';
		if ($sysinfo['SS'] == 'PHP-FPM') {
			$execute_error .= "<br>Please also check that PHP-FPM has been restarted.";
        } elseif (($sysinfo['SS'] == 'IIS') || !($sysinfo['IS_CGI'] || $sysinfo['IS_CLI'])) {
            $execute_error .= "<br>Please also check that the web server has been restarted.";
        }
        $execute_error .= ".";
        $errors[ERROR_LOADER_NOT_READABLE] = $execute_error;
    }
    $loader_strs = get_loader_strings($loader_location);
    $phpv = php_version(); 
    if (preg_match("/php version:\s*(.)\.(.)\.(..?)(-ts)?/i",$loader_strs,$version_matches)) {
        if ($version_matches[1] != $phpv['major'] || $version_matches[2]  != $phpv['minor']) {
            $loader_php = $version_matches[1] . "." . $version_matches[2];
            $server_php =  $phpv['major'] . "." .  $phpv['minor'];
            $errors[ERROR_LOADER_PHP_MISMATCH] = "The installed loader is for PHP $loader_php but your server is running PHP $server_php.";
        }
        if (is_bool($sysinfo['THREAD_SAFE']) &&  $sysinfo['THREAD_SAFE'] && !is_ms_windows() && !(isset($version_matches[4]) && $version_matches[4] == '-ts')) {
            $errors[ERROR_LOADER_NONTS_PHP_TS] = "Your server is running a thread-safe version of PHP but the loader is not a thread-safe version.";
        } elseif (isset($version_matches[4]) && $version_matches[4] == '-ts' && !(is_bool($sysinfo['THREAD_SAFE']) &&  $sysinfo['THREAD_SAFE'])) {
            $errors[ERROR_LOADER_TS_PHP_NONTS] = "Your server is running a non-thread-safe version of PHP but the loader is a thread-safe version.";
        }
    } elseif (preg_match("/ioncube_loader_..?\.._(.)\.(.)\.(..?)(_nonts)?(_amd64)?\.dll/i",$loader_strs,$version_matches)) {
        if (!is_ms_windows()) {
            $errors[ERROR_LOADER_WIN_SERVER_NONWIN] = "You have a Windows loader but your server does not appear to be running Windows.";
        } else {
            if (isset($version_matches[4]) && $version_matches[4] == '_nonts' && is_bool($sysinfo['THREAD_SAFE']) &&  $sysinfo['THREAD_SAFE']) {
                $errors[ERROR_LOADER_WIN_NONTS_PHP_TS] = "You have the non-thread-safe version of the Windows loader but you need the thread-safe one.";
            } elseif (!(is_bool($sysinfo['THREAD_SAFE']) &&  $sysinfo['THREAD_SAFE']) && !(isset($version_matches[4]) && $version_matches[4] == '_nonts')) {
                $errors[ERROR_LOADER_WIN_TS_PHP_NONTS] = "You have the thread-safe version of the Windows loader but you need the non-thread-safe one."; 
            }
            if ($version_matches[1] != $phpv['major'] || $version_matches[2]  != $phpv['minor']) {
                $loader_php = $version_matches[1] . "." . $version_matches[2];
                $server_php =  $phpv['major'] . "." .  $phpv['minor'];
                $errors[ERROR_LOADER_WIN_PHP_MISMATCH] = "The installed loader is for PHP $loader_php but your server is running PHP $server_php.";
            }
                        
            if ($version_matches[1] == 8 && $version_matches[2] >= 1) {
                $loader_compiler = 'VC16';
            } elseif ($version_matches[1] == 7 && $version_matches[2] >= 2) {
                $loader_compiler = 'VC15'; 
            } elseif ($version_matches[1] == 7) {
                $loader_compiler = 'VC14'; 
            } elseif ($version_matches[1] == 5 && $version_matches[2] >= 5) {
                $loader_compiler = 'VC11'; 
            } elseif (preg_match("/assemblyIdentity.*version=\"([^.]+)\./",$loader_strs,$compiler_matches)) {
                $loader_compiler = "VC" . strtoupper($compiler_matches[1]);
            } else {
                $loader_compiler = 'VC6';
            }
            if ($loader_compiler != $sysinfo['PHP_COMPILER']) {
                $errors[ERROR_LOADER_WIN_COMPILER_MISMATCH] = "Your loader was built using $loader_compiler but you need the loader built using {$sysinfo['PHP_COMPILER']}.";
            }
        }
    } else {
            $errors[ERROR_LOADER_PHP_VERSION_UNKNOWN] = "The PHP version for the loader cannot be determined - please check that you have a valid ionCube Loader.";
    } 
    $errors += os_arch_string_check($loader_strs);

    return $errors;
}


function shared_server()
{
    if (!$rtl_path = runtime_loading()) {
        if (empty($_SESSION['use_ini_method']) && runtime_loading_is_possible()) {
            runtime_loading_instructions();
        } else {
            php_ini_install_shared();
        }
    } else {
        list($lv,$mv,$newer_version) = ioncube_loader_version_information();
        $phpv = php_version_maj_min();
        echo "<p>The ionCube Loader $lv for PHP $phpv has been successfully installed.</p>";
        $is_legacy_loader = loader_major_version_instructions($mv);
        if ($is_legacy_loader) {
            loader_upgrade_instructions($lv,$newer_version);
        }
        successful_install_end_instructions($rtl_path);
    }
}

function dedicated_server()
{
    php_ini_install('dedicated or VPS', SERVER_DEDICATED, true);
}

function local_install()
{
    php_ini_install('local',SERVER_LOCAL, true);
}


function unregister_globals()
{
    if (!ini_get('register_globals')) {
        return;
    }

    if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS'])) {
        die('GLOBALS overwrite attempt detected');
    }

    $noUnset = array('GLOBALS',  '_GET',
                     '_POST',    '_COOKIE',
                     '_REQUEST', '_SERVER',
                     '_ENV',     '_FILES');

    $input = array_merge($_GET,    $_POST,
                         $_COOKIE, $_SERVER,
                         $_ENV,    $_FILES,
                         isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array());

    foreach ($input as $k => $v) {
        if (!in_array($k, $noUnset) && isset($GLOBALS[$k])) {
            unset($GLOBALS[$k]);
        }
    }
}

function clear_session($persist = array())
{
    $persist['not_go_daddy'] = empty($_SESSION['not_go_daddy'])?0:1;
    $persist['use_ini_method'] = empty($_SESSION['use_ini_method'])?0:1;
    $persist['server_type'] = empty($_SESSION['server_type'])?SERVER_UNKNOWN:$_SESSION['server_type'];
    @session_destroy();
    $_SESSION = array();
    $_SESSION['CREATED'] = time();
    $_SESSION = $persist;
}

function can_archive()
{
	return (extension_loaded('zip') || (extension_loaded('zlib') && !is_ms_windows()));
}

function is_ioncube()
{
        return (($_SERVER["REMOTE_ADDR"] == IONCUBE_IP_ADDRESS) || ($_SERVER["REMOTE_ADDR"] == gethostbyname(IONCUBE_ACCESS_ADDRESS)));
}

function can_reach_ioncube()
{
	return (isset($_SESSION['remote_access_successful']));
}

function info_should_be_disabled($only_allow_ioncube = false)
{
    $elapsed = time() - max(filemtime(__FILE__),filectime(__FILE__));
	
	if (is_ioncube()) {
		$cutoff_time = IONCUBE_WIZARD_EXPIRY_MINUTES * 60;
	} else {
		if (!$only_allow_ioncube && !extension_loaded(LOADER_EXTENSION_NAME)) {
			$cutoff_time = WIZARD_EXPIRY_MINUTES * 60;
		} else {
			return true;
		}
	}
	
    return ($elapsed > $cutoff_time);
}

function info_disabled_text()
{
    return "The information you have tried to access has been disabled for security reasons. Please re-install this Loader Wizard script and try again.";
}

function info_disabled_check()
{
    if (info_should_be_disabled()) {
        heading();
        echo info_disabled_text();
        footer(true);
        exit;
    }
}

function run()
{

	$user_agent = $_SERVER['HTTP_USER_AGENT'];
	if (preg_match('/googlebot/i',$user_agent)) {
		exit;
	}
    unregister_globals();
    if (is_php_version_or_greater(4,3,0)) {
        ini_set('session.use_only_cookies',1);
    }
    $session_ok = @session_start();

    if (!defined('PHP_EOL')) {
        if (is_ms_windows()) {
            define('PHP_EOL',"\r\n");
        } else {
            define('PHP_EOL',"\n");
        }
    }

    if (!isset($_SESSION['CREATED'])) {
        $_SESSION['CREATED'] = time();
    } elseif (time() - $_SESSION['CREATED'] > SESSION_LIFETIME_MINUTES * 60 ) {
        clear_session(); 
    }
    if (!isset($_SERVER)) $_SERVER =& $HTTP_SERVER_VARS;

    (php_sapi_name() == 'cli') && die("This script should only be run by a web server.\n");

    $page = get_request_parameter('page');
    $host = get_request_parameter('host');
    $clear = get_request_parameter('clear');
    $ini = get_request_parameter('ini');
    $timeout = get_request_parameter('timeout');

    if ($timeout) {
        $_SESSION['timing_out'] = 1;
        $_SESSION['initial_run'] = 0;
    }

    if (!empty($host)) {
        if ($host == 'ngd') {
            $_SESSION['not_go_daddy'] = 1;
        }
    }
    if (!empty($ini)) {
        $_SESSION['use_ini_method'] = 1;
    }

    if (!empty($clear)) {
        clear_session();
        unset($_SESSION['not_go_daddy']);
        unset($_SESSION['use_ini_method']);
        unset($_SESSION['server_type']);
    } else {
        $stype = get_request_parameter('stype');
        $hostprovider = get_request_parameter('hostprovider');
        $hosturl = get_request_parameter('hosturl');
        if (!empty($hostprovider)) {
            $_SESSION['hostprovider'] = $hostprovider;
            $_SESSION['hosturl'] = $hosturl;
        }
        $server_type = find_server_type($stype,false,true);
    }
    if ($session_ok && !$timeout && !isset($_SESSION['initial_run']) && empty($page)) {
        $_SESSION['initial_run'] = 1;
        initial_page();
        @session_write_close();
        exit;
    } else {
        $_SESSION['initial_run'] = 0;
    }

    if (empty($_SESSION['server_type'])) {
        $_SESSION['server_type'] = SERVER_UNKNOWN;
    }

    if (empty($page) || !function_exists($page . "_page")) {
        $page = get_default_page();
    } 

    $fn = "{$page}_page";
    $fn();

    @session_write_close();
    exit(0);
}

function wizardversion_page()
{
    $start_time = time();
    $wizard_version_only = get_request_parameter('wizard_only');
    $clear_session_info = get_request_parameter('clear_info');
    if ($clear_session_info) {
        unset($_SESSION['timing_out']);
        unset($_SESSION['latest_wizard_version']);
    }
    $wizard_version = latest_wizard_version();
    $message = '';
    if (false === $wizard_version) {
        $message = "0";
    } elseif (update_is_available($wizard_version)) {
        $message = "$wizard_version";
    } else {
        $message = "1";
    }
    echo $message;
    @session_write_close();
    exit(0);
}

function platforminfo_page()
{
    $message = '';
    $platforms = get_loader_platforms();
    $message = empty($platforms)?0:1;
    echo $message;
    @session_write_close();
    exit(0);
}

function loaderversion_page()
{
    $message = '';
    $loader_versions = get_loader_version_info();
    $message = empty($loader_versions)?0:1;
    echo $message;
    @session_write_close();
    exit(0);
}

function compilerversion_page()
{
    $message = '';
    $compiler_versions = find_win_compilers();
    $message = empty($compiler_versions)?0:1;
    echo $message;
    @session_write_close();
    exit(0);
}

function initial_page()
{
    $self = get_self();
    $start_page = get_default_address(false);
    $stage_timeout = 7000;
    $step_lag = 500;

    echo <<<EOT
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title>ionCube Loader Wizard</title>
        <link rel="stylesheet" type="text/css" href="$self?page=css">
        <style type="text/css">
        body {
            height: 100%;
            width: 100%;
        }
        </style>
        <script type="text/javascript">
        var timingOut = 0;
        var xmlHttpTimeout;
        var ajax;
        var statusPar;
        var stage_timeout = $stage_timeout;
        var step_lag = $step_lag;

        function checkNextStep(ajax,expected,continuation) {
            if (ajax.readyState==4 && ajax.status==200)
            {
                clearTimeout(xmlHttpTimeout);
                if (ajax.responseText == expected) {
                   setTimeout('',step_lag);
                   continuation();
                } else {
                   statusPar.innerHTML = 'Unable to check for update<br>script continuing';
                   setTimeout("window.location.href = '$start_page&timeout=1'",1000);
                }
            }
        }

        function getXmlHttp() {
            if (window.XMLHttpRequest) {
                xmlhttp=new XMLHttpRequest();
            } else {
                xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
            }
            return xmlhttp;
        }
        var startMainLoaderWizard = function() {
            window.location.href = '$start_page';
        }
        var loaderVersionCheck = function() {
            statusPar.innerHTML = 'Stage 4/4: Getting latest loader versions';
            var xmlHttp = getXmlHttp();
            xmlHttp.onreadystatechange=function() {
                checkNextStep(xmlHttp,"1",startMainLoaderWizard);
            }
            xmlHttp.open("GET","$self?page=loaderversion",true);
            xmlHttp.send("");
            ajax = xmlHttp;
            xmlHttpTimeout=setTimeout('ajaxTimeout()',stage_timeout);
        }
        var platformCheck = function() {
            statusPar.innerHTML = 'Stage 3/4: Getting platform information';
            var xmlHttp = getXmlHttp();
            xmlHttp.onreadystatechange=function() {
                checkNextStep(xmlHttp,"1",loaderVersionCheck);
            }
            xmlHttp.open("GET","$self?page=platforminfo",true);
            xmlHttp.send("");
            ajax = xmlHttp;
            xmlHttpTimeout=setTimeout('ajaxTimeout()',stage_timeout);
        }
        var compilerVersionCheck = function() {
            statusPar.innerHTML = 'Stage 2/4: Getting compiler versions';
            var xmlHttp = getXmlHttp();
            xmlHttp.onreadystatechange=function() {
                checkNextStep(xmlHttp,"1",platformCheck);
            }
            xmlHttp.open("GET","$self?page=compilerversion",true);
            xmlHttp.send("");
            ajax = xmlHttp;
            xmlHttpTimeout=setTimeout('ajaxTimeout()',stage_timeout);
        }
        var startChecks = function() {
            statusPar = document.getElementById('status');
            statusPar.innerHTML = 'Stage 1/4: Getting Loader Wizard version';
            var xmlHttp = getXmlHttp();
            xmlHttp.onreadystatechange=function() {
                checkNextStep(xmlHttp,"1",compilerVersionCheck);
            }
            xmlHttp.open("GET","$self?page=wizardversion",true);
            xmlHttp.send("");
            ajax = xmlHttp;
            xmlHttpTimeout=setTimeout('ajaxTimeout()',stage_timeout);
        }
        function ajaxTimeout(){
           ajax.abort();
           statusPar.innerHTML = 'Cannot reach server<br>script continuing';
           setTimeout("window.location.href = '$start_page&timeout=1'",1000);
        }
        </script>
    </head>
    <body>

    <div id="loading"><script type="text/javascript">document.write('<p>Initialising<br>ionCube Loader Wizard<br><span id="status"></span></p>');</script><p id="noscript">Your browser does not support JavaScript so the ionCube Loader Wizard initialisation cannot be made now. This script can get the latest loader version information from the ionCube server when you go to the next page.<br>Please choose one of the following. <br>If the script appears to hang please restart the script and choose the "NO" option.<br><br><br><a href="$start_page">YES - my server DOES have internet access</a><br><br><a href="$start_page&timeout=1">NO - my server does NOT have internet access</a></p></div>
    <script type="text/javascript">
        document.getElementById('noscript').style.display = 'none';
        window.onload = startChecks;
    </script>
    </body>
    </html>
EOT;
}

function default_page($loader_extension = LOADER_EXTENSION_NAME)
{
    $self = get_self();
    foreach (array('self') as $vn) {
        if (empty($$vn)) {
			$server_data = print_r($_SERVER,true);
            error("Unable to initialise ($vn)". ' $_SERVER is: ' . $server_data);
        }
    }

    heading();

    $wizard_update = check_for_wizard_update(true);

    $rtl = try_runtime_loading_if_applicable();

    $server_type = find_server_type();

    if (extension_loaded($loader_extension) && $server_type != SERVER_UNKNOWN) {
        loader_already_installed($rtl);
    } else {
        loader_not_installed();
    }
    send_stats('default');

    footer($wizard_update);
}

function uninstall_wizard_instructions()
{
    echo '<p><strong>For security reasons we advise that you remove this Wizard script from your server now that the ionCube Loader is installed.</strong></p>';
}

function contact_script_provider_instructions()
{
    echo '<p>Please contact the script provider if you do experience any problems running encoded files.</p>';
}

function may_need_to_copy_ini()
{
    $sys = get_sysinfo();
    if (ini_same_dir_as_wizard() && $sys['IS_CGI']) {
        $dirphrase = is_ms_windows()?'folder':'directory';
        $ini = ini_file_name();
        echo "<p>Please note that if encoded files in a different $dirphrase from the Wizard fail then you should attempt to copy the $ini file to each $dirphrase in which you have encoded files.</p>";
    }
}

function ioncube_24_is_available()
{
	$loaderinfo = get_loaderinfo();
	$php_ver = php_version();
   
	return ($loaderinfo['oscode'] == 'lin' && (($php_ver['major'] == 5 && $php_ver['minor'] >= 3) || $php_ver['major'] > 5) );
}

function ioncube_24_is_enabled()
{
	$ic24_enabled = ini_get(IC24_ENABLED_INI_PROPERTY);
	return $ic24_enabled;
}

function ioncube_24_information()
{
    if (ioncube_24_is_available() && !ioncube_24_is_enabled()) {
        $self = get_self();
        echo '<div class="ic24">';
        echo '<div class="ic24graphic">';
        echo '<a target="_blank" href="' . IONCUBE24_URL . '"><img id="ic24logo" src="' . $self . '?page=ic24logo" alt="ionCube24 logo"></a>';
        echo '</div>';
        echo '<div id="ic24info">';
        echo '<p><strong>Bonus Features!</strong> The ionCube Loader can also give ';
        echo '<strong>real-time intrusion protection</strong> to protect against malware and <strong>PHP error reporting</strong> ';
        echo 'to alert when things go wrong on your website.</p>';
        echo '<p>These features are disabled by default but easily activated. ';
        echo '<strong><a target="_blank" href="' . IONCUBE24_URL . '">visit ioncube24.com</a></strong> to find out more.</p>';
        echo '</div>';
        echo '</div>';
    }
}

function cli_install_instructions()
{

	if (is_php_version_or_greater(5,3)) {
		$cli_loader_installed = shell_exec('php -r "echo extension_loaded(\"' . LOADER_EXTENSION_NAME . '\");"');
		
		if (!$cli_loader_installed) {
			$cli_php_ini_output = shell_exec("php --ini");
			
			$ini_loader_loc = scan_inis_for_loader();
		
			if (!is_null($cli_php_ini_output)) {
				echo '<div class="panel">';
				echo '<h4>Loader Installation for Command-Line (CLI) PHP</h4>';
				echo "<p>At present it does not look like the ionCube Loader is installed for command-line (CLI) PHP.</p>";
				echo "<p>Please note that if you need to run the CLI PHP, such as for <strong>cron jobs</strong>, then please ensure the zend_extension line for the ionCube Loader is included in your CLI PHP configuration.</p>";
				
				if (!empty($ini_loader_loc['location'])) {
					echo "<p>The zend_extension line that needs to be copied is:</p>";
					echo "<p><kbd>zend_extension = " . $ini_loader_loc['location'] . "</kbd></p>";
				}
				
				echo "<p>Your CLI PHP Configuration is:</p>";
				echo '<div class="terminal">';
				echo "<pre>";
				echo $cli_php_ini_output;
				echo "</pre>";
				echo '</div>';
				echo '</div>';
			}
		}
	}
}

function successful_install_end_instructions($rtl_path = null)
{
    if (empty($rtl_path)) {
        may_need_to_copy_ini();
    } elseif (is_string($rtl_path)) {
        echo "<p>The runtime loading method of installation was used with path <code>$rtl_path</code></p>";
    }
    contact_script_provider_instructions();
    if (is_legacy_platform()) {
        legacy_platform_instructions();
    }
	
	if (!is_ms_windows() && is_php_version_or_greater(5,3)) {
		cli_install_instructions();
	}
	
    uninstall_wizard_instructions();
	
	ioncube_24_information();
}

function loader_major_version_instructions($mv)
{
    if ($mv < LATEST_LOADER_MAJOR_VERSION) {
        echo "<p><strong>The installed version of the Loader cannot run files produced by the most recent ionCube Encoder.</strong>";
        echo " You will need a version " . LATEST_LOADER_MAJOR_VERSION . " ionCube Loader to run such files.</p>";
    }
    return ($mv < LATEST_LOADER_MAJOR_VERSION);
}

function loader_already_installed($rtl = null)
{
    list($lv,$mv,$newer_version) = ioncube_loader_version_information();
    $phpv = php_version_maj_min();
    $php_str = ' for PHP ' . $phpv;
    echo '<div class="success">';
    echo '<h4>Loader Installed</h4>';
    if ($newer_version) {
        echo '<p>The ionCube Loader version ' . $lv . $php_str . ' is <strong>already installed</strong> but it is an old version.';
        echo ' It is recommended that the Loader be upgraded to the latest version if possible.</p>';
        $know_latest_version = is_string($newer_version);
        $is_legacy_loader = loader_major_version_instructions($mv);
        echo '</div>';
        loader_upgrade_instructions($lv,$newer_version);
    } else {
        echo '<p>The ionCube Loader version ' . $lv . $php_str . ' is already installed and encoded files should run without problems.</p>'; 
        echo '</div>';
        $is_legacy_loader = loader_major_version_instructions($mv,true);
        if ($is_legacy_loader) {
            loader_upgrade_instructions($lv,true);
        }
    }

    successful_install_end_instructions($rtl);
}

function loader_upgrade_instructions($installed_version,$newer_version)
{
    if ($newer_version) {
        echo '<div class="panel">';
        echo '<h4>Loader Upgrade Instructions</h4>';
        $restart_needed = true;
        $server_type = find_server_type();
        if ($server_type == SERVER_SHARED || $server_type == SERVER_UNKNOWN) {
            $loader_path = find_loader(true);
            if (!is_string($loader_path) || false === user_ini_space_path($loader_path)) {
                $verb_case = ($server_type == SERVER_UNKNOWN)?"may":"will";
                echo "<p>Please note that you $verb_case need your system administrator to do the following to upgrade. The web server will need to be restarted after the loader file is changed.</p>";
            }
            $restart_needed = false;
        }
        if (is_string($newer_version)) {
            $version_str = "version $newer_version";
        } else {
            $version_str = "a newer version";
        }
        $loader_name =  get_loader_name();
        echo "<p>To upgrade from version $installed_version to $version_str of the ionCube Loader, please replace your existing loader file, $loader_name, with
            the file of the same name from one of the following packages:</p>";
        if (is_ms_windows()) {
            $basename = windows_package_name();
        } else {
            list($basename,$multiple_os_versions) = unix_package_name();
        }
        echo make_archive_list($basename,array('zip','tar.gz'));
        if ($restart_needed) {
            echo "<p>Once you have replaced the loader file please restart your web server.</p>";
        }
        echo '</div>';
    }
}

function legacy_platform_warning()
{
    $leg_warn = '<p><strong>You are on a platform on which ionCube Loaders are no longer being developed. ';
    $leg_warn .= 'Loaders on your platform may not be able to run files produced by the latest ionCube Encoder. ';
    $leg_warn .= 'Please switch, if possible, to a platform on which loaders are currently supported. ';
    $leg_warn .= 'A list of currently supported platforms is shown on our <a href="' . LOADERS_PAGE . '" target="loaders">loaders page</a>.</strong></p>';

    return $leg_warn;
}

function legacy_platform_instructions()
{
    echo legacy_platform_warning();
}

function loader_not_installed()
{
    $loader = get_loaderinfo();
    $sysinfo = get_sysinfo();

    $stype = get_request_parameter('stype');
    $manual_select = get_request_parameter('manual');
    $host_type = find_server_type($stype,$manual_select,true);

    if ($host_type != SERVER_UNKNOWN && is_array($loader) && !$sysinfo['DEBUG_BUILD']) {
        $warnings = server_restriction_warnings();
        if (is_legacy_platform()) {
            $warnings[] = legacy_platform_warning();
        }
        if (empty($_SESSION['use_ini_method']) && $host_type == SERVER_SHARED && runtime_loading_is_possible()) {
            $errors = runtime_loading_errors();
        } else {
            $errors = ini_loader_errors();
            $warnings = array_merge($warnings,ini_loader_warnings());
        }
        if (!empty($errors)) {
            if (count($errors) > 1) {
                $problem_str = "Please note that the following problems currently exist";
            } else {
                $problem_str = "Please note that the following problem currently exists";
            }
            echo '<div class="alert">' .$problem_str . ' with the ionCube Loader installation:';
            echo make_list($errors,"ul"); 
            echo '</div>';
        }
        if (!empty($warnings)) {
            $addword = empty($errors)?'':'also';
            $plural = (count($warnings)>1)?'s':'';
            echo '<div class="warning">';
            echo "Please note $addword the following issue$plural:";
            echo make_list($warnings,"ul"); 
            echo '</div>';
        }
    }
    if (!isset($stype)) {
        echo '<p>To use files that have been protected by the <a href="' . ENCODER_URL . '" target=encoder>ionCube PHP Encoder</a>, a component called the ionCube Loader must be installed.</p>';
    }

    if (!is_supported_php_version()) {
        echo '<p>Your server is running PHP version ' . PHP_VERSION . ' and is
                unsupported by ionCube Loaders.  Recommended PHP 4 versions are PHP 4.2 or higher, 
                PHP 5.1 or higher for PHP 5, PHP 7.1 or higher for PHP 7 and PHP 8.1 or higher for PHP 8. Please note that there is not an ionCube Loader for PHP 8.0.</p>';
	} elseif ($latest_supported_php_version = is_after_max_php_version_supported()) {
		echo '<strong>Your server is running PHP version ' . PHP_VERSION . ' and is
                currently unsupported by any ionCube Loaders. <br/>This may change in the future if a Loader is produced for your PHP platform.<br/>In the meantime please downgrade PHP to version ' . $latest_supported_php_version . '.</strong>';
    } elseif ($sysinfo['DEBUG_BUILD']) {
         echo '<p>Your server is currently running a debug build of PHP. The Loader cannot be installed with a debug build of PHP. Please ensure that PHP is reconfigured with debug disabled. Note that debug builds of PHP cannot help in debugging PHP scripts.</p>'; 
    } elseif (!is_array($loader)) {
        if ($loader == ERROR_WINDOWS_64_BIT) {
            echo '<p>Loaders for 64-bit PHP on Windows are not currently available. However, if you <b>install and run 32-bit PHP</b> the corresponding 32-bit loader for Windows should work.</p>';
            if ($sysinfo['THREAD_SAFE']) {
                echo '<li>Download one of the following archives of 32-bit Windows x86 loaders:';
            } else {
                echo '<li>Download one of the following archives of 32-bit Windows non-TS x86 loaders:';
            }
            echo make_archive_list(windows_package_name());
        } else {
            echo '<p>There may not be an ionCube Loader available for your type of system at the moment. However, if you create a <a href="'  . SUPPORT_SITE . '">support ticket</a> more advice and information may be available to assist. Please include the URL for this Wizard in your ticket.</p>';
        }
    } elseif (!$sysinfo['SUPPORTED_COMPILER']) {
        $supported_compilers = supported_win_compilers();
        $supported_compiler_string = join('/',$supported_compilers);
        echo '<p>At the current time the ionCube Loader requires PHP to be built with ' . $supported_compiler_string . '. Your PHP software has been built using ' . $sysinfo['PHP_COMPILER'] . '. Supported builds of PHP are available from <a href="https://windows.php.net/download/">PHP.net</a>.';
    } else {
        switch ($host_type) {
            case SERVER_SHARED:
                shared_server();
                break;
            case SERVER_DEDICATED:
                dedicated_server();
                break;
            case SERVER_LOCAL:
                local_install();
                break;
            default:
                echo server_selection_form();
                break;
        }
    }
}

function server_selection_form()
{
    $self = get_self();
    $timeout = (isset($_SESSION['timing_out']) && $_SESSION['timing_out'])?1:0;
    $hostprovider = (!empty($_SESSION['hostprovider']))?$_SESSION['hostprovider']:'';
    $hostprovider = htmlspecialchars($hostprovider, ENT_QUOTES, 'UTF-8');
    $hosturl = (!empty($_SESSION['hosturl']))?$_SESSION['hosturl']:'';
    $hosturl =  htmlspecialchars($hosturl, ENT_QUOTES, 'UTF-8');
    $form = <<<EOT
    <p>This Wizard will give you information on how to install the ionCube Loader.</p>
    <p>Please select the type of web server that you have and then click Next.</p>
    <script type=text/javascript>
        function trim(s) {
            return s.replace(/^\s+|\s+$/g,"");
        }
        function input_ok() {
            var l = document.getElementById('local');
            if (l.checked) {
                return true;
            } 

            var s = document.getElementById('shared');
            var d = document.getElementById('dedi');

            if (!s.checked && !d.checked) {
                alert("Please select one of the server types.");
                return false;
            } else {
                var hn = document.getElementById('hostprovider');
                var hu = document.getElementById('hosturl');
                var hostprovider = trim(hn.value);
                var hosturl = trim(hu.value);

                if (!hostprovider || !hosturl) {
                    alert("Please enter both a hosting provider name and their URL.");
                    return false;
                }
                if (hostprovider.length < 1) {
                    alert("The hosting provider name should be at least 1 character in length.");
                    return false;
                }
                if (!hosturl.match(/[A-Za-z0-9-_]+\.[A-Za-z0-9-_%&\?\/.=]+/)) {
                    alert("The hosting provider URL is invalid.");
                    return false;
                }
                if (hosturl.length < 4) {
                    alert("The hosting provider URL should be at least 4 characters in length.");
                    return false;
                }
            }
            return true;
        }
    </script>
    <form method=GET action=$self>
        <input type="hidden" name="page" value="default">
        <input type="hidden" name="timeout" value="$timeout">
        <input type=radio id=shared name=stype value=s onclick="document.getElementById('hostinginfo').style.display = 'block';"><label for=shared>Shared <small>(for example, server with FTP access only and no access to php.ini)</small></label><br>
        <input type=radio id=dedi name=stype value=d onclick="document.getElementById('hostinginfo').style.display = 'block';"><label for=dedi>Dedicated or VPS <small>(server with full root ssh access)</small></label><br>
        <div id="hostinginfo" style="display: none">If you are on a shared or dedicated server, please give your hosting provider and their URL:
            <table>
                <tr><td><label for=hostprovider>Name of your hosting provider</label></td><td><input type=text id="hostprovider" name=hostprovider value="$hostprovider"></td></tr>
                <tr><td><label for=hosturl>URL of your hosting provider</label></td><td><input type=text id="hosturl" name=hosturl value="$hosturl"></td></tr>
            </table>
        </div>
        <input type=radio id=local name=stype value=l onclick="document.getElementById('hostinginfo').style.display = 'none';"><label for=local>Local install</label>
        <p><input type=submit value=Next onclick="return (input_ok(this) && showOverlay());"></p>
    </form>
EOT;
    return $form;
}

function phpinfo_page()
{
    info_disabled_check();
    if (function_is_disabled('phpinfo')) {
        echo "phpinfo is disabled on this server";
    } else {
        @phpinfo();
    }
}

function loader_check_page($ext_name = LOADER_EXTENSION_NAME)
{
    heading();

    $rtl_path = try_runtime_loading_if_applicable();
	
    if (extension_loaded($ext_name)) {
        list($lv,$mv,$newer_version) = ioncube_loader_version_information();
        $phpv = php_version_maj_min();
        $php_str = ' for PHP ' . $phpv;
        echo '<div class="success">';
        echo '<h4>Loader Installed Successfully</h4>';
        echo '<p>The ionCube Loader version ' . $lv . $php_str . ' <strong>is installed</strong> and encoded files should run successfully.';
        if ($newer_version) {
            echo ' Please note though that you have an old version of the ionCube Loader.</p>';
            $is_legacy_loader = loader_major_version_instructions($mv);
            echo '</div>';
            loader_upgrade_instructions($lv,$newer_version);
        } else {
            echo '</p>';
            $is_legacy_loader = loader_major_version_instructions($mv);
            echo '</div>';
            if ($is_legacy_loader) {
                loader_upgrade_instructions($lv,true);
            }
        }
        successful_install_end_instructions($rtl_path);
    } else {
        echo '<div class="failure">';
        echo '<h4>Loader Not Installed</h4>';
        echo '<p>The ionCube Loader is <b>not</b> currently installed successfully.</p>';
	
        if (!is_null($rtl_path)) {
            echo '<p>Runtime loading was attempted but has failed.</p>';
            echo '</div>';
            $rt_errors = runtime_loading_errors();
            if (!empty($rt_errors)) {
                list_loader_errors($rt_errors);
            } 
            link_to_php_ini_instructions();
        } else {
            echo '</div>';
            list_loader_errors();
        }
    }
	
    send_stats('check');
    footer(true);
}

function ini_loader_errors()
{
    $errors = array();
    if (SERVER_SHARED == find_server_type() && !own_php_ini_possible(true)) {
        $errors[ERROR_INI_USER_CANNOT_CREATE] = "It appears that you are not be able to create your own ini files on your shared server. <br><strong>You will need to ask your server administrator to install the ionCube Loader for you.</strong>";
    }
    $loader_loc = find_loader(false);
    if (is_string($loader_loc)) {
        if (!shared_and_runtime_loading()) {
            $sys = get_sysinfo();
            if (empty($sys['PHP_INI'])) {
                $errors[ERROR_INI_NO_PATH] = 'No file path found for the PHP configuration file (php.ini).';
            } elseif (!@file_exists($sys['PHP_INI'])) {
                $errors[ERROR_INI_NOT_FOUND] = 'The PHP configuration file (' . $sys['PHP_INI'] .') cannot be found.';
            }
        }
        $errors = $errors + loader_compatibility_test($loader_loc);
    } else {
        $errors = $errors + $loader_loc;
        $fs_location = find_loader_filesystem();
        if (!empty($fs_location)) {
            $fs_loader_errors = loader_compatibility_test($fs_location);
            if (!empty($fs_loader_errors)) {
                $errors[ERROR_LOADER_WRONG_GENERAL] = "The loader file found at $fs_location is not the correct one for your system.";
            }
            $errors = $errors + $fs_loader_errors;
        }
    } 
    return $errors;
}

function unix_path_dir($dir = '')
{
    if (empty($dir)) {
        $dir = dirname(__FILE__);
    }
    if (is_ms_windows()) {
        $dir = str_replace('\\','/',substr($dir,2));
    }
    return $dir;
}

function unrecognised_inis_webspace($startdir)
{
    $ini_list = array();

    $ini_name = ini_file_name();
    $sys = get_sysinfo();
    $depth = substr_count($startdir,'/');

    $rel_path = '';
    $rootpath = realpath($_SERVER['DOCUMENT_ROOT']);
    for ($seps = 0; $seps < $depth; $seps++) {
        $full_ini_loc = @realpath($startdir . '/' . $rel_path) . DIRECTORY_SEPARATOR . $ini_name;
        if (@file_exists($full_ini_loc) && $sys['PHP_INI'] != $full_ini_loc) {
            $ini_list[] = @realpath($full_ini_loc);
        }

        if (dirname($full_ini_loc) == $rootpath) {
            break;
        }
        $rel_path .= '../';
    }
    return $ini_list;
}

function correct_loader_wrong_location()
{
    $loader_location_pair = array();
    $loader_location = find_loader_filesystem();
    if (is_string($loader_location) && !empty($loader_location)) {
        $loader_errors = loader_compatibility_test($loader_location);
        if (empty($loader_errors)) {
            $ini_loader = scan_inis_for_loader();
            if (!empty($ini_loader['location'])) {
                $ini_loader_errors = loader_compatibility_test($ini_loader['location']);
                if (!empty($ini_loader_errors)) {
                    $loader_location_pair['loader'] = $loader_location;
                    $loader_location_pair['newloc'] = dirname($ini_loader['location']);
                }
            } else {
                $std_dir = loader_install_dir(find_server_type());
                $std_ld_path = $std_dir . DIRECTORY_SEPARATOR . get_loader_name();
                if (@file_exists($std_ld_path)) {
                    $stdloc_loader_errors = loader_compatibility_test($std_ld_path);
                } else {
                    $stdloc_loader_errors = array("Loader file does not exist.");
                }
                if (!empty($stdloc_loader_errors)) {
                    $loader_location_pair['loader'] = $loader_location;
                    $loader_location_pair['newloc'] = $std_dir;
                }
            }
        }
    }
    return $loader_location_pair;
}

function ini_loader_warnings()
{
    $warnings = array();
    if (find_server_type() == SERVER_SHARED)
    {
        if (own_php_ini_possible()) {
            $sys = get_sysinfo();
            $ini_name = ini_file_name();
            $rootpath = realpath($_SERVER['DOCUMENT_ROOT']);
            $root_ini_file = $rootpath . DIRECTORY_SEPARATOR . $ini_name;
            $cgibinpath = @realpath($_SERVER['DOCUMENT_ROOT'] . "/cgi-bin");
            $cgibin_ini_file = (empty($cgibinpath))?'':$cgibinpath . DIRECTORY_SEPARATOR . $ini_name;
            $here = unix_path_dir();
            $ini_files = unrecognised_inis_webspace($here);
            $shared_ini_loc = shared_ini_location();
            $shared_ini_file = $shared_ini_loc . DIRECTORY_SEPARATOR . $ini_name;
            $ini_dir = dirname($sys['PHP_INI']);
            $all_ini_locations_used = !empty($ini_files);
            foreach ($ini_files as $full_ini_loc) {
                $advice = "The file $full_ini_loc is not being recognised by PHP.";
                $advice .= " Please check that the name and location of the file are correct.";
                if (!ini_same_dir_as_wizard()) {
                    $ini_loc_dir = dirname($full_ini_loc);
                    if (!@file_exists($shared_ini_file) && !empty($shared_ini_loc) && $ini_loc_dir != $shared_ini_loc && $ini_dir != $shared_ini_loc) {
                        $all_ini_locations_used = false;
                        $advice .= " Please try copying the <code>$full_ini_loc</code> file to <code>" . $shared_ini_loc . "</code>.";
                    } else {
                        if (!@file_exists($root_ini_file) && $rootpath != $shared_ini_loc && $full_ini_loc != $rootpath) {
                            $all_ini_locations_used = false;
                            $advice .= " Please try copying the <code>$full_ini_loc</code> file to <code>" . $rootpath . "</code>.";
                        } 
                        if (!empty($cgibin_ini_file) && !@file_exists($cgibin_ini_file) && $cgibinpath != $shared_ini_loc && $full_ini_loc != $cgibinpath && $cgibinpath != $rootpath) {
                            $all_ini_locations_used = false;
                            $advice .= "  Please try copying the <code>$full_ini_loc</code> file to <code>" . $cgibinpath . "</code>.";
                        }
                        $herepath = realpath($here);
                        $here_ini_file = $herepath . DIRECTORY_SEPARATOR . $ini_name;
                        if (!@file_exists($here_ini_file) && $herepath != $rootpath && $herepath != $cgibinpath) {
                            $all_ini_locations_used = false;
                            $advice .= " It may be necessary to copy the <code>$full_ini_loc</code> file to <code>$herepath</code> and to all " . (is_ms_windows()?'folders':'directories') . ' in which you have encoded files';
                        }
                    }
                } else {
                    $all_ini_locations_used = false;
                }
                $warnings[] = $advice;
            }
            if ($all_ini_locations_used) {
                $warnings[] = "<strong>It looks as if ini files are not being recognised in any of the standard locations in your webspace. Please contact your hosting provider to check whether you can create your own PHP ini file and where it should go.</strong>";
            }
        } else {
            if (own_php_ini_possible(true)) {
                $warnings[] = "You may not be able to create your own ini files on your shared server. <br><strong>You might need to ask your server administrator to install the ionCube Loader for you.</strong>";
            }
        }
    } else {
        $loader_dir_pair = correct_loader_wrong_location();
        if (!empty($loader_dir_pair)) {
            $advice = "The correct loader for your system has been found at <code>{$loader_dir_pair['loader']}</code>."; 
            if ($loader_dir_pair['loader'] != $loader_dir_pair['newloc']) {
                $advice .= " Please copy the loader from <code>{$loader_dir_pair['loader']}</code> to <code>{$loader_dir_pair['newloc']}</code>.";
            }
            $warnings[] = $advice;
        }
    }
    return $warnings;
}

function list_loader_errors($errors = array(),$warnings = array(),$suggest_restart = true)
{
    $default = get_default_address();
    $retry_message = '';

    
    if (empty($errors)) {
        $errors = ini_loader_errors();
        if (empty($warnings)) {
            $warnings = ini_loader_warnings();
        }
    }
	
    if (!empty($errors)) {
        $try_again = '<a href="#" onClick="window.location.href=window.location.href">try again</a>';
	
        echo '<div class="alert">';
        if (count($errors) > 1) {
            echo 'The following problems have been found with the ionCube Loader installation:';
            $retry_message = "Please correct those errors and $try_again.";
        } else {
            echo 'The following problem has been found with the ionCube Loader installation:';
            $retry_message = "Please correct that error and $try_again.";
        }
        if (array_key_exists(ERROR_INI_USER_CANNOT_CREATE,$errors)) {
            $retry_message = '';
        }
        echo make_list($errors,"ul");
        echo '</div>';
        if (!empty($warnings)) {
            echo '<div class="warning">';
            echo 'Please also note the following:';
            echo make_list($warnings,"ul");
            echo '</div>';
        }
    } elseif (!empty($warnings)) {
        echo '<div class="warning">';
        echo 'There are the following potential problems:';
        echo make_list($warnings,"ul");
        echo '</div>';
    } elseif ($suggest_restart) {
        if (SERVER_SHARED == find_server_type()) {
            echo "<p>Please contact your server administrator about installing the ionCube Loader.</p>";
        } else {
            if (selinux_is_enabled()) {
                echo "<p>It appears that SELinux is enabled on your server. This might be solved by running the command <code>restorecon [full path to loader file]</code> as root.</p>";
            } elseif (grsecurity_is_enabled()) {
                echo "<p>It appears that grsecurity is enabled on your server. Please run the command, <code>execstack -c [full path to loader file]</code> and then restart your web server.</p>";
            } else {
                $sysinfo = get_sysinfo();
                $ss = $sysinfo['SS'];
				if ($ss == 'PHP-FPM') {
					echo "<p>Please check that PHP-FPM has been restarted.</p>";
                } elseif (!$sysinfo['CGI_CLI'] || is_ms_windows()) {
                    echo "<p>Please check that the $ss web server software has been restarted.</p>";
                } 
            }
        }
    }
    echo '<div>';
    echo $retry_message;
    echo " You may wish to view the following for further help:";
    echo make_list(help_resources($errors),"ul");
    echo '<a href="' . $default . '">Click here to go back to the start of the Loader Wizard</a>.</div>';
}

function phpconfig_page()
{
    info_disabled_check();
    $sys = get_sysinfo();
    $download = get_request_parameter('download');
    $ini_file_name = '';
    if (!empty($download)) {
        $ini_file_name = get_request_parameter('ininame');
        if (empty($ini_file_name)) {
            $ini_file_name = ini_file_name();
        } else {
			if (!preg_match('`^.*\.ini$`',$ini_file_name) || preg_match('`/`',$ini_file_name) || preg_match('`\\\`',$ini_file_name)) {
				die("Illegal file name $ini_file_name");
			}
		}
        header('Content-Type: text/plain');
        header('Content-Disposition: attachment; filename=' . $ini_file_name);
    } else {
        header('Content-Type: text/plain');
    }
    $exclude_original = get_request_parameter('newlinesonly');
    $prepend = get_request_parameter('prepend');
    $stype = get_request_parameter('stype');
    $server_type = find_server_type($stype);
    if (!empty($exclude_original) || !empty($prepend)) {
        $loader_dir = loader_install_dir($server_type);
        $zend_lines = zend_extension_lines($loader_dir);
        echo join(PHP_EOL,$zend_lines);
        echo PHP_EOL;
    }
    if (empty($ini_file_name) || empty($sys['PHP_INI_DIR']) || ($sys['PHP_INI_BASENAME'] == $ini_file_name)) {
        $original_ini_file = isset($sys['PHP_INI'])?$sys['PHP_INI']:'';
    } else {
        $original_ini_file = $sys['PHP_INI_DIR'] . DIRECTORY_SEPARATOR . $ini_file_name;
    }
    if (empty($exclude_original) && !empty($original_ini_file) && @file_exists($original_ini_file)) {
        if (!empty($download)) {
            @readfile($original_ini_file);
        } else {
            echo all_ini_contents();
        } 
    }
}

function extra_page($check_access_to_info = true)
{
    if ($check_access_to_info) {
		info_disabled_check();
	}
    heading();
    $sys = get_sysinfo();
    $ini_loader = scan_inis_for_loader();
    $ini_loader_path = $ini_loader['location'];
    $loader_path = find_loader(true);
    $ldinf = get_loaderinfo();
    $self = get_self();
    echo "<h4>Additional Information</h4>";
    echo "<table>";
    $lines = array();
    if (is_string($loader_path)) {
        $lines['Loader is at'] = $loader_path;
        $loader_system = loader_system($loader_path);
        if (!empty($loader_system)) {
            $lines['Loader OS code'] = $loader_system['oscode'];
            $lines['Loader architecture'] = $loader_system['arch'];
            $lines['Loader word size'] = $loader_system['wordsize'];
            $lines['Loader PHP version'] = $loader_system['php_version'];
            $lines['Loader thread safety'] = $loader_system['thread_safe']?'Yes':'No';
            $lines['Loader compiler'] = $loader_system['compiler'];
            $lines['Loader version'] = $loader_system['loader_version'];
            $lines['File size is'] = filesize($loader_path) . " bytes.";
            $lines['MD5 sum is'] = md5_file($loader_path);
        }
        $lines['Loader file'] = "<a href=\"$self?page=loaderbin\">Download loader file</a>";
    } else {
        $lines['Loader file'] = "Loader cannot be found.";
    }
    $lines['Loader found in ini file'] = empty($ini_loader_path)?"No":"Yes";
    if (!empty($ini_loader_path) && (!is_string($loader_path) || $ini_loader_path != $loader_path)) {
        $lines['Loader location found in ini file'] =  $ini_loader_path;
        $loader_system = loader_system($ini_loader_path);
        if (!empty($loader_system)) {
            $lines['Ini Loader OS code'] = $loader_system['oscode'];
            $lines['Ini Loader architecture'] = $loader_system['arch'];
            $lines['Ini Loader word size'] = $loader_system['wordsize'];
            $lines['Ini Loader PHP version'] = $loader_system['php_version'];
            $lines['Ini Loader thread safety'] = $loader_system['thread_safe']?'Yes':'No';
            $lines['Ini Loader compiler'] = $loader_system['compiler'];
            $lines['Ini Loader version'] = $loader_system['loader_version'];
        }
    }
    $lines["OS extra security"] = (selinux_is_enabled() || possibly_selinux())?"SELinux":(grsecurity_is_enabled()?"Grsecurity":"None");
    $lines['PHPRC is'] = $sys['PHPRC'];
    $lines['INI DIR is'] = $sys['PHP_INI_DIR'];
    $lines['Additional INI files'] = $sys['PHP_INI_ADDITIONAL'];
    $stype = get_request_parameter('stype');
    $server_type = find_server_type($stype);
    $lines['Server type is'] = server_type_string();
    $lines["PHP uname"] = $ldinf['uname'];
    $lines['Server word size is'] = $ldinf['wordsize'];
    $lines['Disabled functions'] = ini_get('disable_functions');
    $writeable_dirs = writeable_directories();
    $lines['Writeable loader locations'] = (empty($writeable_dirs))?"<em>None</em>":join(", ",$writeable_dirs);
    if (!empty($_SESSION['hostprovider'])) {
        $lines['Hosting provider'] = $_SESSION['hostprovider'];
        $lines['Provider URL'] = $_SESSION['hosturl'];
    }
    foreach ($lines as $h => $i) {
        $v = (empty($i))?'<em>EMPTY</em>':$i;
        echo '<tr><th>'. $h . ':</th>' . '<td>' . $v . '</td></tr>';
    }
    echo "</table>";
    footer(true);
}

function loaderbin_page()
{
    info_disabled_check();
    $loader_path = find_loader(true);
    if (is_string($loader_path)) {
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename='. basename($loader_path));
        @readfile($loader_path);
    }
}



function GoDaddy_root($html_root = '')
{
    if (empty($_SESSION['not_go_daddy']) && empty($_SESSION['godaddy_root'])) {
        $godaddy_pattern = "[\\/]home[\\/]content[\\/][0-9a-z][\\/][0-9a-z][\\/][0-9a-z][\\/][0-9a-z]+[\\/]html";

        if (empty($html_root)) {
            $html_root =  $_SERVER['DOCUMENT_ROOT'];
        }
        if (preg_match("@$godaddy_pattern@i",$html_root,$matches)) {
            $_SESSION['godaddy_root'] = $matches[0];
        } else {
            $_SESSION['not_go_daddy'] = 1;
            $_SESSION['godaddy_root'] = '';
        } 
    } elseif (!empty($_SESSION['not_go_daddy'])) {
        $_SESSION['godaddy_root'] = '';
    }
    if (!empty($_SESSION['godaddy_root'])) {
        $_SESSION['hostprovider'] = 'GoDaddy';
        $_SESSION['hosturl'] = 'www.godaddy.com';
    }
    return $_SESSION['godaddy_root'];
}

function GoDaddy_windows_instructions()
{
    $instr = "It appears that you are hosted on a Windows server at GoDaddy.<br/>";
    $instr .= "Please change to a Linux hosting plan at GoDaddy.<br />";
    $instr .=  "If you <a href=\"https://help.godaddy.com/\">contact their support team</a> they should be able to switch you to a Linux server.";

    echo $instr;
}

function GoDaddy_linux_instructions($html_dir)
{
    $base = get_base_address();
    $loader_name = get_loader_name();
    $zend_extension_line="<code>zend_extension = $html_dir/ioncube/$loader_name</code>";
    $php_ini_name = is_php_version_or_greater(5,0)?'php5.ini':'php.ini';
    $ini_path = $html_dir . '/' . $php_ini_name;

    $instr = array();
    $instr[] = 'In your html directory, ' . $html_dir . ', create a sub-directory called <b>ioncube</b>.';
    if (@file_exists($ini_path)) {
       $instr[] = "Edit the $php_ini_name in your  $html_dir and add the following line to the <b>top</b> of the file:<br>" . $zend_extension_line ;
    } else {
        $instr[] = "<a href=\"$base&amp;page=phpconfig&amp;ininame=$php_ini_name&amp;stype=s&amp;download=1&amp;prepend=1\">Save this $php_ini_name file</a> and upload it to your html directory, $html_dir";
    }
    $instr[] = 'Download the <a target="_blank" href="' . IONCUBE_DOWNLOADS_SERVER . '"/ioncube_loaders_lin_x86.zip">Linux ionCube Loaders</a>.';
    $instr[] = 'Unzip the loaders and upload them into the ioncube directory you created previously.';
    $instr[] = 'The encoded files should now be working.';

    echo '<div class=panel>';
    echo (make_list($instr));
    echo '</div>';
}

function GoDaddy_page()
{
    $base = get_base_address();

    heading();

        $inst_str = '<h4>GoDaddy Installation Instructions</h4>';
        $inst_str .= '<p>It appears that you are hosted with GoDaddy (<a target="_blank" href="https://www.godaddy.com/">www.godaddy.com</a>). ';
        $inst_str .= "If that is <b>not</b> the case then please <a href=\"$base&amp;page=default&amp;host=ngd\">click here to go to the main page of this installation wizard</a>.</p>";
        $inst_str .= "<p>If you have already installed the loader then please <a href=\"$base&amp;page=loader_check\" onclick=\"showOverlay();\">click here to test the loader</a>.</p>";

        echo $inst_str;

        if (is_ms_windows()) {
            GoDaddy_windows_instructions();
        } else {
            GoDaddy_linux_instructions($_SESSION['godaddy_root']);
        }

    send_stats('gd_default');

    footer(true);
}



function get_request_parameter($param_name)
{
    static $request_array;

    if (!isset($request_array)) {
        if (isset($_GET)) {
            $request_array = $_GET;
        } elseif (isset($HTTP_GET_VARS)) {
            $request_array = $HTTP_GET_VARS;
        }
    }

    if (isset($request_array[$param_name])) {
        $return_value = strip_tags($request_array[$param_name]);
    } else {
        $return_value = null;
    }
    return $return_value;
}

function make_list($list_items,$list_type='ol')
{
    $html = '';
    if (!empty($list_items)) {
        $html .= "<$list_type>";
        $html .= '<li>';
        $html .= join('</li><li>',$list_items);
        $html .= '</li>';
        $html .= "</$list_type>";
    }
    return $html;
} 

function make_archive_list($basename,$archives_list = array(),$download_server = IONCUBE_DOWNLOADS_SERVER)
{
    if (empty($archives_list)) {
        $archives_list = array('tar.gz','zip');
    }

    foreach ($archives_list as $a) {
        $link_text = $a;
        $ext_sep = '.';
        $archive_list[] = "<a href=\"$download_server/$basename$ext_sep$a\">$link_text</a>";
    }

    return make_list($archive_list,"ul");
}

function error($m)
{
    die("<b>ERROR:</b> <span class=\"error\">$m</span><p>Please help us improve this script by <a href=\"". SUPPORT_SITE . "\">reporting this error</a> and including the URL to the script so that we can test it.");
}


function filter_server_input($server_var)
{
	$res = htmlspecialchars($_SERVER[$server_var], ENT_QUOTES, "UTF-8");
	return $res;
}

function failsafe_get_self()
{
    $result = '';
    $sfn = filter_server_input('SCRIPT_FILENAME');
    $dr = $_SERVER['DOCUMENT_ROOT'];
    if (!empty($sfn) && !empty($dr)) {
        if ($dr == '/' || $dr == '\\') {
            $result = $sfn;
        } else {
            $drpos = strpos($sfn,$dr);
            if ($drpos === 0) {
                $drlen = strlen($dr);
                $result = substr($sfn,$drlen);
            }
        }
        $result = str_replace('\\','/',$result);
    }
    if (empty($result)) {
        $result = DEFAULT_SELF;
    }
    return $result;
}

function get_self()
{ 
	$page = '';
    if (empty($_SERVER['PHP_SELF'])) {
        if (empty($_SERVER['SCRIPT_NAME'])) {
            if (empty($_SERVER['REQUEST_URI'])) {
                $page = failsafe_get_self();
            } else {
                $page = filter_server_input('REQUEST_URI');
            }
        } else {
            $page = filter_server_input('SCRIPT_NAME');
        }
    } else {
        $page = filter_server_input('PHP_SELF');
    }
	return $page;
}

function get_default_page()
{
    $godaddy_root = GoDaddy_root();
    if (empty($godaddy_root)) {
         $page = 'default';
    } else {
         $page = 'GoDaddy';
    }
    return $page;
}

function get_base_address()
{
    $self = get_self();
    $remote_timeout = (isset($_SESSION['timing_out']) && $_SESSION['timing_out'])?'timeout=1':'timeout=0';
    $using_ini = (isset($_SESSION['use_ini_method']) && $_SESSION['use_ini_method'])?'ini=1':'ini=0';
    return $self . '?' . $remote_timeout . '&' . $using_ini;
}

function get_default_address($include_timeout = true)
{
    if ($include_timeout) {
        $base =  get_base_address();
        $base .= "&amp;";
    } else {
        $base = get_self();
        $base .= "?";
    }
    $page = get_default_page();

    return $base . 'page=' . $page;
}

function heading()
{
    $self = get_self();

    echo <<<EOT
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <meta name="robots" content="noindex, nofollow">
    <head>
        <title>ionCube Loader Wizard</title>
        <link rel="stylesheet" type="text/css" href="$self?page=css">
        <script type="text/javascript">
            function showOverlay()
            {
                document.getElementById('overlay').style.display = 'block';
                return true;
            }

            function hideOverlay()
            {
                document.getElementById('overlay').style.display = 'none';
                return true;
            }
        </script>
    </head>
    <body onload="hideOverlay()">
    <div id="overlay">
        <div id="inner_overlay">Checking server configuration<br>Please wait</div>
    </div>
    <div id="header">
        <img src="?page=logo" alt="ionCube logo">
    </div>
	<div id="important">
	<h3 class="important">IMPORTANT: Ensure that This Script Is Removed When No Longer Required</h3>
	</div>
    <div id="main">
    <h2>ionCube Loader Wizard</h2>
EOT;
}

function footer($update_info = null)
{
    $self = get_self();
    $base = get_base_address();
    $default = get_default_address(false);
    $year = gmdate("Y");

    echo "</div>";
    echo "<div id=\"footer\">" .
    "Copyright ionCube Ltd. 2002-$year | " .
    "Loader Wizard version " . script_version() . " ";

    if ($update_info === true) {
        $update_info = check_for_wizard_update(false);  
    }
    $loader_wizard_loc = LOADER_WIZARD_URL;
    $wizard_version_string =<<<EOT
    <script type="text/javascript">
    var xmlhttp;
    function version_check()
    { 
        var body = document.getElementsByTagName('body')[0];
        var ldel = document.getElementById('loading');
        if (!ldel) {
            body.innerHTML += '<div id="loading"></div>';
            ldel = document.getElementById('loading');
        }
        ldel.innerHTML = '<p>Retrieving Wizard version information<br>Please wait</p>';
        ldel.style.display = 'block';
        ldel.style.height = '300px';
        ldel.style.left = '200px';
        ldel.style.border = '4px #660000 solid';
        if (window.XMLHttpRequest) {
            xmlhttp=new XMLHttpRequest();
        } else {
            xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
        }
        xmlhttp.onreadystatechange=function()
        {
            var loadedOkay = 0;
            if (xmlhttp.readyState==4 && xmlhttp.status==200)
            {
                var wizardversion = xmlhttp.responseText;
                var msg;
                clearTimeout(xmlHttpTimeout);
                buttons = '';
                if (wizardversion == '1') {
                    msg = 'You have the current version of the<br>ionCube Loader Wizard'; 
                } else if (wizardversion != '0') {
                    msg = 'A new version, ' + wizardversion + ', of the loader wizard is available';
                    buttons = '<button onclick="document.getElementById(\'loading\').style.display=\'none\'; window.open(\'$loader_wizard_loc\'); return false">Get new version</button> &nbsp;'; 
                } else {
                    msg = 'Wizard version information cannot be obtained from the<br>ionCube server';
                }
                buttons += '<button onclick="document.getElementById(\'loading\').style.display=\'none\'; return false">Close this box</button>'; 
                ldel.innerHTML = '<p>' + msg +  '<br>' + buttons + '</p>';
            }
        }
        xmlhttp.open("GET",'$self?page=wizardversion&wizard_only=1&clear_info=1',true);
        xmlhttp.send();
        var xmlHttpTimeout=setTimeout(ajaxTimeout,7000);
    }
    function ajaxTimeout(){
       xmlhttp.abort();
       msg = 'Wizard version information cannot be obtained from the<br>ionCube server';
       button = '<button onclick="document.getElementById(\'loading\').style.display=\'none\'; return false">Close this box</button>';
       var ldel = document.getElementById('loading');
       ldel.innerHTML = '<p>' + msg +  '<br>' + button + '</p>';
    }
    </script>
EOT;

    $wizard_version_string .= '('; 
    if ($update_info === null) {
        $wizard_version_string .= '<a target="_blank" href="' . $loader_wizard_loc . '" onclick="version_check();return false;">check for new version</a>';
    } else if ($update_info !== false) {
        $wizard_version_string .= '<a href="' . LOADERS_PAGE .'" target="_blank">download version ' . $update_info . '</a>';
    } else {
        $wizard_version_string .=  "current";
    }
    $wizard_version_string .= ')'; 
    echo $wizard_version_string;

    $server_type_code = server_type_code();
	
	if (!info_should_be_disabled(true)) {
		echo " | <a href=\"$base&amp;page=phpinfo\" target=\"phpinfo\">phpinfo</a>";
		echo " | <a href=\"$base&amp;page=phpconfig\" target=\"phpconfig\">config</a>";
		echo " | <a href=\"$base&amp;page=extra&amp;stype=$server_type_code\" target=\"extra\">additional</a>";
	}

    echo " | <a href=\"$default\" onclick=\"showOverlay();\">wizard start</a>";
    echo " | <a href=\"$base&amp;page=loader_check\" onclick=\"showOverlay();\">loader test</a>";
    echo ' | <a href="' . LOADERS_PAGE . '" target="loaders">loaders</a>';

    echo "</div>\n";
    echo "\n</body></html>\n";
}

function css_page()
{
    header('Content-Type: text/css');
    echo <<<EOT
    body {
        font-family: verdana, helvetica, arial, sans-serif;
        font-size: 10pt;
        line-height: 150%;
        margin: 0px;
        min-height: 400px;
        position: relative;
    }

    code {
        color: #c00080;
    }

    li {
        margin-top: 10px;
    }
    #overlay {
        display: block;
        z-index: 100;
        position: absolute;
        top: 0;
        left: 0;
        padding: 0;
        margin: 0;
        width: 100%;
        height: 100%;
        background-color: white;
    }
    #inner_overlay {
        display: block;
        z-index: 100;
        position: absolute;
        font-size: 200%;
        color: #660000;
        top: 50%;
        left: 25%;
        width: 460px;
        height: 460px;
        line-height: 200%;
        text-align: center;
        vertical-align: middle;
    }

    #loading {
        display: block;
        position: absolute;
        top: 33%;
        left: 25%;
        margin: auto;
        height: 320px;
        width: 460px;
        padding: 4px;
        color: #660000;
        background-color: white;
        z-index: 100;
    }

    #loading p {
        position: absolute;
        margin-top: 10px;
        text-align: center;
        vertical-align: middle;
        padding-left: 40px;
        padding-right: 30px;
        font-size: 200%;
        line-height: 200%;
    }

    #loading p span#status{
        font-size: 60%;
        line-height: 120%;
    }
    #loading p#noscript {
        font-size: 120%;
        line-height: 120%;
        position: absolute;
        text-align: left;
        padding-top: 10px;
        bottom: 0;
    }
    #loading p#noscript a {
        text-align: center;
    }

    #loading button {
        margin-top: 20px;
        line-height: 100%;
        padding-top: 4px;
        padding-bottom: 4px;
    }


    h4 {
        margin-bottom: 0;
        padding-bottom: 4px;
    }

    p,#main div {
        max-width: 1000px;
        width: 75%;
    }

    #hostinginfo {
        margin-top: 10px;
        margin-left: 20px;
    }
    #hostinginfo table {
        font-size: 1.00em;
    }
    #hostinginfo table td {
        padding-right: 4px;
    }
    #hostinginfo input {
        margin-top: 6px;
    }

    #hostinginfo label {
        margin-left: 6px;
    }

    th {
        text-align: left;
    }
	
	#important {
		margin-top: 12px;
	} 
	h3.important {
		margin: 0;
		border: 0;
        border-top: 1px solid #660000;
		border-bottom: 1px solid #660000;
        padding: 1ex 0 1ex 0;
        background-color: #CB2430;
		text-align: center;
        color: #ffffff; 
        width: 100%;
	}

    .alert {
        margin: 2ex 0;
        border: 1px solid #660000;
        padding: 1ex 1em;
        background-color: #ffeeee;
        color: #660000; 
        width: 75%;
    }

    .warning {
        margin: 2ex 0;
        border: 1px solid #FFBF00;
        padding: 1ex 1em;
        background-color: #FDF5E6;
        color: #000000; 
        width: 75%;
    }

    .success {
        margin: 2ex 0;
        border: 1px solid #006600;
        padding: 1ex 1em;
        background-color: #EEFFEE;
        color: #000000; 
        width: 75%;
    }

    .error {
        color: #FF0000;
    }

    .panel {
        border: 1px solid #c0c0c0;
        background-color: #f0f0f0;
        width: 75%;
        padding: 1ex 1em;
    }
	
	.terminal {
		border: none;
		background-color: #000000;
		color: #ffffff;
		width: 50%;
		padding: 1ex 1em;
	}

    #header {
        background: #fff;
    }

    #footer {
        border-top: 1px solid #404040;
        margin-top: 20px;
        padding-top: 10px;
        padding-left: 20px;
        font-size: 75%;
        text-align: left;
    }

    #main {
        margin: 20px;
    }
	
	
	#main .ic24 {
		position: relative;
		width: 75%;
		height: auto;
		border-width: 1px 1px 1px 1px;
		border-style: solid;
		border-color: #4B8DF8;   
		background-color: #EFEFFF;
		padding: 12px;
		padding-top: 16px;
		padding-bottom: 8px;
		margin-top: 20px;
		overflow: hidden;
	}
	
	#main .ic24 p {
		width: 100%;
	}
	
	
	#main .ic24graphic {
		position: relative;
		width: auto;
		height: auto;
		border: none;
		padding: 0px;
		padding-right: 16px;
		margin: 0px;
		float: left;
		
	}
	
	#main #ic24info {
		position: relative;
		width: auto;
		height: auto;
		float: left;
	}
	
	#main #ic24info a {
		color: #0B4DB8;
		text-decoration: none;
	}
	
	#main #ic24logo {
		max-width: 132px;
		max-height: 132px;
	}
	
EOT;
}

function logo_page()
{
$img_encoded = 'iVBORw0KGgoAAAANSUhEUgAAAakAAACABAMAAABD1osiAAAAKlBMVEUAAAAAAADnHCwAAAAAAAAAAAAAAAAAAABMCQ4AAADnHCznHCznHCwAAAAjcBE1AAAADHRSTlMAeDRHwSqg4BJl/PLTJLuIAAAF1UlEQVR42u2by4vTQBzHp3TTzR6EBtfXYS/+BZW6Pg6FFavgoRDBBx4KFd+HQgWFvQQqiuJhoeL7sP+LR0EPlj6yPfz+F5NMZ77TmmJjM3ZT5nNpOzvNzGcev5lMusxgMBgMBoPBYDAYDAaDwWDQwel5YRnC/jkvbZYdjFV2MFbZwVhlB2OVIVZyb2HIED/n5AfLEj/nhWUJY5UdjFV2MFbZwVgdMqzNZydXz2qrf59Kq2a1NmTsRnfVrLZOfj3VrrkrZuVb/dpBvZEJqzOOc5TNQ75rjXKDtV+ZsNoi6rJ52OhZwxONwiGwsi46zqnt1Kx8r7N8q/wmRfhP3BSsrK7VW/u13krDysGwT8o5kvilxa2YZ/U2eulEC0KhCTlLCo0UrPYff7Tfe+2lWt0glTT6qjB02e0eW6ZVjiZYaF4hq+eXlmll1yik75TL5eMeDVOxsj89hNQyrN5QyDFRm9GCVmCZVrYXBr4OE9w8ZFbBCNr+x646ycAhs/o3moFUj62Y1UY4/txVs9oLrAZs1azCAVhaNSsLgXNpVt/+dlNXZAplx4mLiXecU5hHhcBqN6lV/p3znk1xEYUltfr+t0J/4dN1jwKGWIg+VKuBdL5JAQ9EYj34ILOAjWq12lG+eE2xsk9EF/7CFN7WKOCpq9kK2/CTyp93mFUbpyKRZmwNi2oX4Y0dfgULd8QL4vRdvVavJ+6XYLVPIQjmHq9xAqvbJBTa8paTBCOtVpZHY1DrSmCF7flABotBIiuLJM+RQdJJO1qoVnUKqfLh1pBWrX10YVu0ciuRVXjlfpUiXGSmp85xdFaaT7thZUV95I5DRldaDYJPT8oXmyQqnYP0nFZetL23tgjtsT/e8uc9mKa3XsFqL3Rpy3YsCSufhwmrJgbeGmo/jxUCjd2UzWWFg1EuEzv6rJoY4ftyQapghBRElda5cxKrEfaPvGPWw+Esyx1ps8pHhaP0LqxK8p7KZwFHklt1kEqNcbsNcFfT12a1zgtEv7WFVZehB93xUGVJrPg7MXgPxotDUWlCV5dVhYtgjhV5KuLd+jixktjqYHoHmVcLw9fSt2ry8lDBlrAqKomN5FZI5aX0+Rztqmk7uqywtGKhRQ+KmbeT3AoDDN89gsJQBQ1WWFrFpmgkIruq2kpuhWCASFNBYXxN1GGFKk1XqqLWiXjeOvpv3n2gpBDm4dtL1aqnyaqAcA2bGCu0d3Ir5GkSPasKsFlO3WpNGf68P3wdVhs84tRIRZ/VEUwWfIyxwo4puRUiDh0+q2jntnJWOf6aplVv+VZ5VGMBq3tlhQuarNYnw3V9Zgzkr8PFYiByAi0xcM7ILva+7kJWNeyktVoV5l2FeSI1kluh8UKrlnar6dv2qNhejBVG6yDeaifOajg5X9tR4sH/sLIIBeFTjJV4JMImmd5KNmGFvHxfyV9Guq2mDvnQc9NWyIuOBWrD2BSzZ4fsHi6rzUq26cRdY2e2VSU+ChJ6IDdh1Zi+wylAVa9VfWqu+2y2VYFiO6uGzHsTVj01WOxgsOq3KqB0nMbMsLK96fNxKVASgrDCSogcHjpbq5WNg1WcVsRY4Zi3i1Xblqm7OLFXrHbRWn2GxUG/FduX0yIHwRlWFomD3ojrT+Vxje+KE3tYiQ6ym3JJKKidnW9rscJkuSwOiUdsphXO5P2724y9PPOI+njMMSyxOzWiTViF7/0v4kS6gzEcZA0545X0WbFmVClnk1B4vJXsDYArcPzXitUxCnhW5f070SyXHGfTw1jUYVUgMGKzrTBKQQk/LonYzSlWxToyFuOapaXRim2hqd2/WbFbJEBlLTx8k1a1QNmaai0eUMBAp5XVFFIdNtMqVqs/nhmvpGQuSJRWUmHoMsl5klzRacWsE4Sn3TOswMtH9Mfvbj+L36JNWrFzUgqcE6ofdf8X9PXN6qWjbF5eOverV51ye/ICd+NCWv549er0ha3o69vMYDAYDAaDwWAwGAwGg8FgSJffF2mwYDNbStYAAAAASUVORK5CYII=';

    header('Content-Type: image/png');
    header('Cache-Control: public');
    echo base64_decode($img_encoded);
}

function ic24logo_page()
{
	$img_encoded = 'PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6b3NiPSJodHRwOi8vd3d3Lm9wZW5zd2F0Y2hib29rLm9yZy91cmkvMjAwOS9vc2IiCiAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIKICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyIKICAgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIgogICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB2ZXJzaW9uPSIxLjAiCiAgIHdpZHRoPSI2OTAiCiAgIGhlaWdodD0iNjkxLjI1IgogICB2aWV3Qm94PSIwIDAgNTUyIDU1MyIKICAgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQgbWVldCIKICAgaWQ9InN2ZzMwMzUiCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNSByMTAwNDAiCiAgIHNvZGlwb2RpOmRvY25hbWU9ImlvbkN1YmUyNF9jdWJlLnN2ZyI+CiAgPGRlZnMKICAgICBpZD0iZGVmczMwODMiPgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ1MzQ5IgogICAgICAgb3NiOnBhaW50PSJzb2xpZCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMxMjczYjg7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNTM1MSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDUzNDMiCiAgICAgICBvc2I6cGFpbnQ9InNvbGlkIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzAwMDAwMDtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A1MzQ1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50NTMzNyIKICAgICAgIG9zYjpwYWludD0ic29saWQiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMTI3M2I4O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDUzMzkiIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQ1MzMxIgogICAgICAgb3NiOnBhaW50PSJzb2xpZCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMwMDAwMDA7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wNTMzMyIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDUzMjUiCiAgICAgICBvc2I6cGFpbnQ9InNvbGlkIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzEyNzNiODtzdG9wLW9wYWNpdHk6MDsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3A1MzI3IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaWQ9ImxpbmVhckdyYWRpZW50Mzg4NSIKICAgICAgIG9zYjpwYWludD0ic29saWQiPgogICAgICA8c3RvcAogICAgICAgICBzdHlsZT0ic3RvcC1jb2xvcjojMTI3M2I4O3N0b3Atb3BhY2l0eToxOyIKICAgICAgICAgb2Zmc2V0PSIwIgogICAgICAgICBpZD0ic3RvcDM4ODciIC8+CiAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPGxpbmVhckdyYWRpZW50CiAgICAgICBpZD0ibGluZWFyR3JhZGllbnQzODc5IgogICAgICAgb3NiOnBhaW50PSJzb2xpZCI+CiAgICAgIDxzdG9wCiAgICAgICAgIHN0eWxlPSJzdG9wLWNvbG9yOiMxMjczYjg7c3RvcC1vcGFjaXR5OjE7IgogICAgICAgICBvZmZzZXQ9IjAiCiAgICAgICAgIGlkPSJzdG9wMzg4MSIgLz4KICAgIDwvbGluZWFyR3JhZGllbnQ+CiAgICA8bGluZWFyR3JhZGllbnQKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDM4NzMiCiAgICAgICBvc2I6cGFpbnQ9InNvbGlkIj4KICAgICAgPHN0b3AKICAgICAgICAgc3R5bGU9InN0b3AtY29sb3I6IzEyNzNiODtzdG9wLW9wYWNpdHk6MTsiCiAgICAgICAgIG9mZnNldD0iMCIKICAgICAgICAgaWQ9InN0b3AzODc1IiAvPgogICAgPC9saW5lYXJHcmFkaWVudD4KICAgIDxsaW5lYXJHcmFkaWVudAogICAgICAgaW5rc2NhcGU6Y29sbGVjdD0iYWx3YXlzIgogICAgICAgeGxpbms6aHJlZj0iI2xpbmVhckdyYWRpZW50NTMzNyIKICAgICAgIGlkPSJsaW5lYXJHcmFkaWVudDUzNDEiCiAgICAgICB4MT0iNDQzNS40NDI0IgogICAgICAgeTE9IjI5NDkuMDQyIgogICAgICAgeDI9IjQ4MzQuMzkyMSIKICAgICAgIHkyPSIyOTQ5LjA0MiIKICAgICAgIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiAvPgogICAgPGNsaXBQYXRoCiAgICAgICBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJjbGlwUGF0aDMxNDIiPgogICAgICA8cGF0aAogICAgICAgICBkPSJtIDE2MDM0LDIyMzYgYyAtMywtOCAtMywtMzQwIC0xLC03MzggNSwtNzg5IDQsLTc4NiA2NiwtOTc3IDQyLC0xMzAgOTIsLTIxNCAxODUsLTMwNyAxMjgsLTEyOCAyNTcsLTE4MSA0NjcsLTE5MSAyNDYsLTEyIDQ2Miw2OSA2MjksMjM3IDM2LDM2IDgwLDg3IDk4LDExNCAxNywyNyAzMyw0OCAzMyw0NSAxLC0yIDcsLTgwIDEzLC0xNzQgbCAxMSwtMTcwIDE3OSwtMyAxNzgsLTIgLTYsNDIgYyAtNCwyNCAtOSw1MTQgLTEyLDEwOTEgbCAtNiwxMDQ3IC0xOTYsLTIgLTE5NywtMyAtNSwtNzMwIGMgLTQsLTUwOCAtOSwtNzQwIC0xNywtNzYyIC0xMDIsLTI4NCAtMzY2LC00NDUgLTY0NCwtMzkzIC0xNzgsMzQgLTI5OSwxNzIgLTM1MSw0MDAgLTIxLDkxIC0yMiwxMjMgLTI1LDc5MyBsIC00LDY5NyAtMTk1LDAgYyAtMTU4LDAgLTE5NiwtMyAtMjAwLC0xNCB6IgogICAgICAgICBpZD0icGF0aDMxNDQiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2NsaXBQYXRoPgogICAgPGNsaXBQYXRoCiAgICAgICBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJjbGlwUGF0aDMxNDYiPgogICAgICA8cGF0aAogICAgICAgICBkPSJtIDE2MDM0LDIyMzYgYyAtMywtOCAtMywtMzQwIC0xLC03MzggNSwtNzg5IDQsLTc4NiA2NiwtOTc3IDQyLC0xMzAgOTIsLTIxNCAxODUsLTMwNyAxMjgsLTEyOCAyNTcsLTE4MSA0NjcsLTE5MSAyNDYsLTEyIDQ2Miw2OSA2MjksMjM3IDM2LDM2IDgwLDg3IDk4LDExNCAxNywyNyAzMyw0OCAzMyw0NSAxLC0yIDcsLTgwIDEzLC0xNzQgbCAxMSwtMTcwIDE3OSwtMyAxNzgsLTIgLTYsNDIgYyAtNCwyNCAtOSw1MTQgLTEyLDEwOTEgbCAtNiwxMDQ3IC0xOTYsLTIgLTE5NywtMyAtNSwtNzMwIGMgLTQsLTUwOCAtOSwtNzQwIC0xNywtNzYyIC0xMDIsLTI4NCAtMzY2LC00NDUgLTY0NCwtMzkzIC0xNzgsMzQgLTI5OSwxNzIgLTM1MSw0MDAgLTIxLDkxIC0yMiwxMjMgLTI1LDc5MyBsIC00LDY5NyAtMTk1LDAgYyAtMTU4LDAgLTE5NiwtMyAtMjAwLC0xNCB6IgogICAgICAgICBpZD0icGF0aDMxNDgiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2NsaXBQYXRoPgogICAgPGNsaXBQYXRoCiAgICAgICBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJjbGlwUGF0aDMxNTAiPgogICAgICA8cGF0aAogICAgICAgICBkPSJtIDE2MDM0LDIyMzYgYyAtMywtOCAtMywtMzQwIC0xLC03MzggNSwtNzg5IDQsLTc4NiA2NiwtOTc3IDQyLC0xMzAgOTIsLTIxNCAxODUsLTMwNyAxMjgsLTEyOCAyNTcsLTE4MSA0NjcsLTE5MSAyNDYsLTEyIDQ2Miw2OSA2MjksMjM3IDM2LDM2IDgwLDg3IDk4LDExNCAxNywyNyAzMyw0OCAzMyw0NSAxLC0yIDcsLTgwIDEzLC0xNzQgbCAxMSwtMTcwIDE3OSwtMyAxNzgsLTIgLTYsNDIgYyAtNCwyNCAtOSw1MTQgLTEyLDEwOTEgbCAtNiwxMDQ3IC0xOTYsLTIgLTE5NywtMyAtNSwtNzMwIGMgLTQsLTUwOCAtOSwtNzQwIC0xNywtNzYyIC0xMDIsLTI4NCAtMzY2LC00NDUgLTY0NCwtMzkzIC0xNzgsMzQgLTI5OSwxNzIgLTM1MSw0MDAgLTIxLDkxIC0yMiwxMjMgLTI1LDc5MyBsIC00LDY5NyAtMTk1LDAgYyAtMTU4LDAgLTE5NiwtMyAtMjAwLC0xNCB6IgogICAgICAgICBpZD0icGF0aDMxNTIiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2NsaXBQYXRoPgogICAgPGNsaXBQYXRoCiAgICAgICBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJjbGlwUGF0aDMxNTQiPgogICAgICA8cGF0aAogICAgICAgICBkPSJtIDE2MDM0LDIyMzYgYyAtMywtOCAtMywtMzQwIC0xLC03MzggNSwtNzg5IDQsLTc4NiA2NiwtOTc3IDQyLC0xMzAgOTIsLTIxNCAxODUsLTMwNyAxMjgsLTEyOCAyNTcsLTE4MSA0NjcsLTE5MSAyNDYsLTEyIDQ2Miw2OSA2MjksMjM3IDM2LDM2IDgwLDg3IDk4LDExNCAxNywyNyAzMyw0OCAzMyw0NSAxLC0yIDcsLTgwIDEzLC0xNzQgbCAxMSwtMTcwIDE3OSwtMyAxNzgsLTIgLTYsNDIgYyAtNCwyNCAtOSw1MTQgLTEyLDEwOTEgbCAtNiwxMDQ3IC0xOTYsLTIgLTE5NywtMyAtNSwtNzMwIGMgLTQsLTUwOCAtOSwtNzQwIC0xNywtNzYyIC0xMDIsLTI4NCAtMzY2LC00NDUgLTY0NCwtMzkzIC0xNzgsMzQgLTI5OSwxNzIgLTM1MSw0MDAgLTIxLDkxIC0yMiwxMjMgLTI1LDc5MyBsIC00LDY5NyAtMTk1LDAgYyAtMTU4LDAgLTE5NiwtMyAtMjAwLC0xNCB6IgogICAgICAgICBpZD0icGF0aDMxNTYiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2NsaXBQYXRoPgogICAgPGNsaXBQYXRoCiAgICAgICBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJjbGlwUGF0aDMxNTgiPgogICAgICA8cGF0aAogICAgICAgICBkPSJtIDE2MDM0LDIyMzYgYyAtMywtOCAtMywtMzQwIC0xLC03MzggNSwtNzg5IDQsLTc4NiA2NiwtOTc3IDQyLC0xMzAgOTIsLTIxNCAxODUsLTMwNyAxMjgsLTEyOCAyNTcsLTE4MSA0NjcsLTE5MSAyNDYsLTEyIDQ2Miw2OSA2MjksMjM3IDM2LDM2IDgwLDg3IDk4LDExNCAxNywyNyAzMyw0OCAzMyw0NSAxLC0yIDcsLTgwIDEzLC0xNzQgbCAxMSwtMTcwIDE3OSwtMyAxNzgsLTIgLTYsNDIgYyAtNCwyNCAtOSw1MTQgLTEyLDEwOTEgbCAtNiwxMDQ3IC0xOTYsLTIgLTE5NywtMyAtNSwtNzMwIGMgLTQsLTUwOCAtOSwtNzQwIC0xNywtNzYyIC0xMDIsLTI4NCAtMzY2LC00NDUgLTY0NCwtMzkzIC0xNzgsMzQgLTI5OSwxNzIgLTM1MSw0MDAgLTIxLDkxIC0yMiwxMjMgLTI1LDc5MyBsIC00LDY5NyAtMTk1LDAgYyAtMTU4LDAgLTE5NiwtMyAtMjAwLC0xNCB6IgogICAgICAgICBpZD0icGF0aDMxNjAiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2NsaXBQYXRoPgogICAgPGNsaXBQYXRoCiAgICAgICBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJjbGlwUGF0aDMxNjIiPgogICAgICA8cGF0aAogICAgICAgICBkPSJtIDE2MDM0LDIyMzYgYyAtMywtOCAtMywtMzQwIC0xLC03MzggNSwtNzg5IDQsLTc4NiA2NiwtOTc3IDQyLC0xMzAgOTIsLTIxNCAxODUsLTMwNyAxMjgsLTEyOCAyNTcsLTE4MSA0NjcsLTE5MSAyNDYsLTEyIDQ2Miw2OSA2MjksMjM3IDM2LDM2IDgwLDg3IDk4LDExNCAxNywyNyAzMyw0OCAzMyw0NSAxLC0yIDcsLTgwIDEzLC0xNzQgbCAxMSwtMTcwIDE3OSwtMyAxNzgsLTIgLTYsNDIgYyAtNCwyNCAtOSw1MTQgLTEyLDEwOTEgbCAtNiwxMDQ3IC0xOTYsLTIgLTE5NywtMyAtNSwtNzMwIGMgLTQsLTUwOCAtOSwtNzQwIC0xNywtNzYyIC0xMDIsLTI4NCAtMzY2LC00NDUgLTY0NCwtMzkzIC0xNzgsMzQgLTI5OSwxNzIgLTM1MSw0MDAgLTIxLDkxIC0yMiwxMjMgLTI1LDc5MyBsIC00LDY5NyAtMTk1LDAgYyAtMTU4LDAgLTE5NiwtMyAtMjAwLC0xNCB6IgogICAgICAgICBpZD0icGF0aDMxNjQiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2NsaXBQYXRoPgogICAgPGNsaXBQYXRoCiAgICAgICBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJjbGlwUGF0aDMxNjYiPgogICAgICA8cGF0aAogICAgICAgICBkPSJtIDE2MDM0LDIyMzYgYyAtMywtOCAtMywtMzQwIC0xLC03MzggNSwtNzg5IDQsLTc4NiA2NiwtOTc3IDQyLC0xMzAgOTIsLTIxNCAxODUsLTMwNyAxMjgsLTEyOCAyNTcsLTE4MSA0NjcsLTE5MSAyNDYsLTEyIDQ2Miw2OSA2MjksMjM3IDM2LDM2IDgwLDg3IDk4LDExNCAxNywyNyAzMyw0OCAzMyw0NSAxLC0yIDcsLTgwIDEzLC0xNzQgbCAxMSwtMTcwIDE3OSwtMyAxNzgsLTIgLTYsNDIgYyAtNCwyNCAtOSw1MTQgLTEyLDEwOTEgbCAtNiwxMDQ3IC0xOTYsLTIgLTE5NywtMyAtNSwtNzMwIGMgLTQsLTUwOCAtOSwtNzQwIC0xNywtNzYyIC0xMDIsLTI4NCAtMzY2LC00NDUgLTY0NCwtMzkzIC0xNzgsMzQgLTI5OSwxNzIgLTM1MSw0MDAgLTIxLDkxIC0yMiwxMjMgLTI1LDc5MyBsIC00LDY5NyAtMTk1LDAgYyAtMTU4LDAgLTE5NiwtMyAtMjAwLC0xNCB6IgogICAgICAgICBpZD0icGF0aDMxNjgiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2NsaXBQYXRoPgogICAgPGNsaXBQYXRoCiAgICAgICBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJjbGlwUGF0aDMxNzAiPgogICAgICA8cGF0aAogICAgICAgICBkPSJtIDE2MDM0LDIyMzYgYyAtMywtOCAtMywtMzQwIC0xLC03MzggNSwtNzg5IDQsLTc4NiA2NiwtOTc3IDQyLC0xMzAgOTIsLTIxNCAxODUsLTMwNyAxMjgsLTEyOCAyNTcsLTE4MSA0NjcsLTE5MSAyNDYsLTEyIDQ2Miw2OSA2MjksMjM3IDM2LDM2IDgwLDg3IDk4LDExNCAxNywyNyAzMyw0OCAzMyw0NSAxLC0yIDcsLTgwIDEzLC0xNzQgbCAxMSwtMTcwIDE3OSwtMyAxNzgsLTIgLTYsNDIgYyAtNCwyNCAtOSw1MTQgLTEyLDEwOTEgbCAtNiwxMDQ3IC0xOTYsLTIgLTE5NywtMyAtNSwtNzMwIGMgLTQsLTUwOCAtOSwtNzQwIC0xNywtNzYyIC0xMDIsLTI4NCAtMzY2LC00NDUgLTY0NCwtMzkzIC0xNzgsMzQgLTI5OSwxNzIgLTM1MSw0MDAgLTIxLDkxIC0yMiwxMjMgLTI1LDc5MyBsIC00LDY5NyAtMTk1LDAgYyAtMTU4LDAgLTE5NiwtMyAtMjAwLC0xNCB6IgogICAgICAgICBpZD0icGF0aDMxNzIiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2NsaXBQYXRoPgogICAgPGNsaXBQYXRoCiAgICAgICBjbGlwUGF0aFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIKICAgICAgIGlkPSJjbGlwUGF0aDMxNzQiPgogICAgICA8cGF0aAogICAgICAgICBkPSJtIDE2MDM0LDIyMzYgYyAtMywtOCAtMywtMzQwIC0xLC03MzggNSwtNzg5IDQsLTc4NiA2NiwtOTc3IDQyLC0xMzAgOTIsLTIxNCAxODUsLTMwNyAxMjgsLTEyOCAyNTcsLTE4MSA0NjcsLTE5MSAyNDYsLTEyIDQ2Miw2OSA2MjksMjM3IDM2LDM2IDgwLDg3IDk4LDExNCAxNywyNyAzMyw0OCAzMyw0NSAxLC0yIDcsLTgwIDEzLC0xNzQgbCAxMSwtMTcwIDE3OSwtMyAxNzgsLTIgLTYsNDIgYyAtNCwyNCAtOSw1MTQgLTEyLDEwOTEgbCAtNiwxMDQ3IC0xOTYsLTIgLTE5NywtMyAtNSwtNzMwIGMgLTQsLTUwOCAtOSwtNzQwIC0xNywtNzYyIC0xMDIsLTI4NCAtMzY2LC00NDUgLTY0NCwtMzkzIC0xNzgsMzQgLTI5OSwxNzIgLTM1MSw0MDAgLTIxLDkxIC0yMiwxMjMgLTI1LDc5MyBsIC00LDY5NyAtMTk1LDAgYyAtMTU4LDAgLTE5NiwtMyAtMjAwLC0xNCB6IgogICAgICAgICBpZD0icGF0aDMxNzYiCiAgICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8L2NsaXBQYXRoPgogIDwvZGVmcz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgcGFnZWNvbG9yPSIjZmZmZmZmIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEiCiAgICAgb2JqZWN0dG9sZXJhbmNlPSIxMCIKICAgICBncmlkdG9sZXJhbmNlPSIxMCIKICAgICBndWlkZXRvbGVyYW5jZT0iMTAiCiAgICAgaW5rc2NhcGU6cGFnZW9wYWNpdHk9IjAiCiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIKICAgICBpbmtzY2FwZTp3aW5kb3ctd2lkdGg9IjE5MjAiCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iMTAxOCIKICAgICBpZD0ibmFtZWR2aWV3MzA4MSIKICAgICBzaG93Z3JpZD0iZmFsc2UiCiAgICAgaW5rc2NhcGU6em9vbT0iMC45NjUzODc0IgogICAgIGlua3NjYXBlOmN4PSI3MjQuNTI3MjIiCiAgICAgaW5rc2NhcGU6Y3k9IjMzMy4xMTQ1MSIKICAgICBpbmtzY2FwZTp3aW5kb3cteD0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LXk9Ii04IgogICAgIGlua3NjYXBlOndpbmRvdy1tYXhpbWl6ZWQ9IjEiCiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ic3ZnMzAzNSIKICAgICBmaXQtbWFyZ2luLXRvcD0iMCIKICAgICBmaXQtbWFyZ2luLWxlZnQ9IjAiCiAgICAgZml0LW1hcmdpbi1yaWdodD0iMCIKICAgICBmaXQtbWFyZ2luLWJvdHRvbT0iMCIgLz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGEzMDM3Ij4KQ3JlYXRlZCBieSBwb3RyYWNlIDEuMTEsIHdyaXR0ZW4gYnkgUGV0ZXIgU2VsaW5nZXIgMjAwMS0yMDEzCjxyZGY6UkRGPgogIDxjYzpXb3JrCiAgICAgcmRmOmFib3V0PSIiPgogICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICA8ZGM6dHlwZQogICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICA8L2NjOldvcms+CjwvcmRmOlJERj4KPC9tZXRhZGF0YT4KICA8ZwogICAgIHRyYW5zZm9ybT0ibWF0cml4KDAuMSwwLDAsLTAuMSwtNCw1NTcpIgogICAgIGlkPSJnMzAzOSIKICAgICBzdHlsZT0iZmlsbDojMDAwMDAwO3N0cm9rZTpub25lIj4KICAgIDxwYXRoCiAgICAgICBkPSJtIDQwLDQ3MDAgMCwtODcwIDg3MCwwIDg3MCwwIC0yLDg2OCAtMyw4NjcgLTg2NywzIC04NjgsMiAwLC04NzAgeiIKICAgICAgIGlkPSJwYXRoMzA0MSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgZD0ibSAxOTMwLDQ3MDAgMCwtODcwIDg3MCwwIDg3MCwwIDAsODcwIDAsODcwIC04NzAsMCAtODcwLDAgMCwtODcwIHoiCiAgICAgICBpZD0icGF0aDMwNDMiCiAgICAgICBzdHlsZT0iZmlsbDojYzAxZDJlO2ZpbGwtb3BhY2l0eToxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDM4MjcsNTU2MyBjIC00LC0zIC03LC0zOTUgLTcsLTg3MCBsIDAsLTg2MyA4NzAsMCA4NzAsMCAwLDg3MCAwLDg3MCAtODYzLDAgYyAtNDc1LDAgLTg2NywtMyAtODcwLC03IHoiCiAgICAgICBpZD0icGF0aDMwNDUiCiAgICAgICBzdHlsZT0iZmlsbDojYzAxZDJlO2ZpbGwtb3BhY2l0eToxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDQwLDI4MDAgMCwtODcwIDg2OCwyIDg2NywzIDMsODY4IDIsODY3IC04NzAsMCAtODcwLDAgMCwtODcwIHoiCiAgICAgICBpZD0icGF0aDMwNDciCiAgICAgICBzdHlsZT0iZmlsbDojYzAxZDJlO2ZpbGwtb3BhY2l0eToxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDE5MzAsMjgwMCAwLC04NzAgODcwLDAgODcwLDAgMCw4NzAgMCw4NzAgLTg3MCwwIC04NzAsMCAwLC04NzAgeiBtIDEwMzUsNjMwIGMgODAsLTMxIDE1NCwtMTAyIDE5MSwtMTgzIDI1LC01NCAyOCwtNzQgMjksLTE1NyAwLC0xOTAgLTc0LC0zMTggLTM0NCwtNTkyIGwgLTE3NCwtMTc4IDI3NiwwIDI3NywwIDAsLTgwIDAsLTgwIC00MDcsMiAtNDA4LDMgLTMsNTYgLTMsNTUgMTgxLDE3NCBjIDM1NSwzMzkgNDUyLDQ5MyA0MjMsNjY3IC0xOSwxMDYgLTcxLDE2MiAtMTcyLDE4NCAtOTIsMjAgLTIwMiwtNiAtMjkzLC02OSBsIC00NiwtMzEgLTI2LDU4IGMgLTE0LDMyIC0yNiw2MiAtMjYsNjYgMCwyMiAxNDcsOTkgMjI4LDEyMCA4MiwyMSAyMjEsMTQgMjk3LC0xNSB6IgogICAgICAgaWQ9InBhdGgzMDQ5IgogICAgICAgc3R5bGU9ImZpbGw6IzEyNzNiODtmaWxsLW9wYWNpdHk6MSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgZD0ibSAzODIyLDI4MDMgMywtODY4IDg2OCwtMyA4NjcsLTIgMCw4NzAgMCw4NzAgLTg3MCwwIC04NzAsMCAyLC04NjcgeiBtIDExNzgsMjQyIDAsLTM5NSA5MCwwIDkwLDAgMCwtNzAgMCwtNzAgLTkwLDAgLTkwLDAgMCwtMTcwIDAsLTE3MCAtODUsMCAtODUsMCAwLDE3MCAwLDE3MCAtMjkwLDAgLTI5MCwwIDAsNjMgMCw2NCAyODEsNDAxIDI4MSw0MDIgOTQsMCA5NCwwIDAsLTM5NSB6IgogICAgICAgaWQ9InBhdGgzMDUxIgogICAgICAgc3R5bGU9ImZpbGw6IzEyNzNiODtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6bm9uemVybyIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgZD0ibSA0NzkwLDMxNzMgYyAtMjQsLTQzIC0xMTEsLTE3MiAtMTk1LC0yODggLTgzLC0xMTUgLTE1NSwtMjE2IC0xNTksLTIyMiAtNiwtMTAgMzUsLTEzIDE5MywtMTMgbCAxOTksMCA0LDI5OCBjIDIsMTYzIDMsMjk4IDIsMzAwIC0xLDIgLTIxLC0zMiAtNDQsLTc1IHoiCiAgICAgICBpZD0icGF0aDMwNTMiCiAgICAgICBzdHlsZT0iZmlsbDp1cmwoI2xpbmVhckdyYWRpZW50NTM0MSk7ZmlsbC1vcGFjaXR5OjEiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMTg1MTYsMTc0MyBjIC0zLC04MzUgLTksLTE1NTMgLTEyLC0xNTk1IGwgLTYsLTc4IDE3MCwwIDE3MCwwIDcsODggYyAzLDQ4IDksMTI3IDEzLDE3NiBsIDcsODkgNDAsLTU5IGMgNTMsLTc3IDE2MCwtMTgxIDIyOSwtMjIzIDEyOCwtNzcgMjQ4LC0xMTEgNDIxLC0xMTggMjEwLC05IDM4NywzOCA1NTIsMTQ3IDI3NiwxODEgNDM4LDQ4MiA0NzQsODc5IDM5LDQzMyAtMTA1LDgzOSAtMzc1LDEwNTYgLTE1NSwxMjUgLTMzMCwxODUgLTU0MSwxODUgLTE5OSwwIC0zNTcsLTQwIC00OTMsLTEyNiAtNzEsLTQ1IC0xODMsLTE1MyAtMjI1LC0yMTkgbCAtMzIsLTUwIC0zLDY4MyAtMiw2ODIgLTE5NCwwIC0xOTQsMCAtNiwtMTUxNyB6IG0gMTE1NSwyMjMgYyAxNDksLTMyIDMwNSwtMTQ4IDM4OCwtMjg5IDc5LC0xMzUgMTIxLC0zMTMgMTIxLC01MTIgMCwtMTk2IC0zNSwtMzU2IC0xMDgsLTUwMCAtNDMsLTg0IC0xNzEsLTIxNyAtMjQ5LC0yNTggLTc3LC00MSAtMTkyLC02NyAtMjk0LC02NyAtMTE2LDAgLTE3NywxMyAtMjc4LDYyIC0xNDYsNjkgLTI1OCwyMDMgLTMxNywzNzggLTE3LDQ5IC0xOSw4OCAtMTksMzYwIDAsMzA1IDAsMzA1IDI3LDM4NSAzNywxMDkgOTEsMTk2IDE2OSwyNzUgNzQsNzQgMTkwLDE0MSAyODYsMTY0IDc2LDE5IDE5MSwxOSAyNzQsMiB6IgogICAgICAgaWQ9InBhdGgzMDU1IgogICAgICAgY2xpcC1wYXRoPSJ1cmwoI2NsaXBQYXRoMzE3NCkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMTQ2MTAsMzEzOSBjIC01MTgsLTY1IC05NDQsLTM1NyAtMTE2NCwtNzk3IC0xNDEsLTI4MCAtMjAxLC02MzYgLTE2NiwtOTgzIDcyLC03MTEgNDgwLC0xMTc3IDExNDcsLTEzMTAgMjExLC00MiA1NTcsLTM2IDgxMywxMiAxMTksMjMgMzIwLDg2IDMyNiwxMDMgNiwxNyAtNzIsMzExIC04MiwzMDkgLTUsLTEgLTQ5LC0xNiAtOTcsLTMzIC0xNDcsLTUyIC0yNjIsLTcxIC00NzAsLTc3IC0yMTAsLTYgLTMyMCw0IC00NTcsNDQgLTQzNywxMjYgLTcwNSw0NzIgLTc2MSw5NzkgLTE1LDE0MCAtNSwzODggMjAsNTE0IDYwLDI5OSAxOTgsNTM2IDQwMyw2OTAgMjIzLDE2OSA0NzIsMjM4IDgwOCwyMjcgMTg0LC02IDMwNywtMjggNDQyLC03OCA0NiwtMTYgODksLTMxIDk2LC0zMiA5LC0xIDMwLDQ5IDYyLDE1MyAyNyw4NSA0OCwxNTUgNDcsMTU2IC01Miw0MCAtMjc2LDEwMSAtNDU3LDEyMyAtOTcsMTMgLTQxNCwxMiAtNTEwLDAgeiIKICAgICAgIGlkPSJwYXRoMzA1NyIKICAgICAgIGNsaXAtcGF0aD0idXJsKCNjbGlwUGF0aDMxNzApIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDczNzAsMjg1NSAwLC0xOTUgMjEwLDAgMjEwLDAgMCwxOTUgMCwxOTUgLTIxMCwwIC0yMTAsMCAwLC0xOTUgeiIKICAgICAgIGlkPSJwYXRoMzA1OSIKICAgICAgIGNsaXAtcGF0aD0idXJsKCNjbGlwUGF0aDMxNjYpIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDIzODg2LDMwMjQgYyAtOTksLTE4IC0yNjQsLTczIC0zNDgsLTExNSAtNzEsLTM1IC0yMTgsLTEzMCAtMjM3LC0xNTMgLTEwLC0xMiAwLC00MCA1MCwtMTUwIDM0LC03NSA2MywtMTM2IDY1LC0xMzYgMSwwIDM2LDI0IDc3LDUzIDE2NiwxMTkgMzI0LDE3NiA1MTMsMTg0IDMwOCwxNCA1MDMsLTEwOCA1ODAsLTM2MiAxNCwtNDYgMTksLTkzIDE5LC0yMDAgLTEsLTE3MSAtMTksLTI0NyAtMTAwLC00MTAgLTEzMCwtMjYxIC0zODAsLTU0MyAtMTA0NCwtMTE4MCBsIC0yNTAsLTI0MCAtMSwtMTIyIDAsLTEyMyA5MzUsMCA5MzUsMCAwLDE2NSAwLDE2NSAtNjU3LDAgLTY1NywwIDEwOSwxMDEgYyA2MSw1NiAyMTgsMjEwIDM1MCwzNDMgMzQyLDM0NSA1MTgsNTYzIDYzNCw3ODYgMTc5LDM0NSAxOTgsNjc4IDU3LDk2NSAtODEsMTYzIC0xODgsMjcwIC0zNTEsMzUxIC0xNDEsNzAgLTIxOSw4NiAtNDI1LDkwIC0xMjUsMiAtMTk4LC0xIC0yNTQsLTEyIHoiCiAgICAgICBpZD0icGF0aDMwNjEiCiAgICAgICBzdHlsZT0iZmlsbDojMTI3M2I4O2ZpbGwtb3BhY2l0eToxIgogICAgICAgY2xpcC1wYXRoPSJ1cmwoI2NsaXBQYXRoMzE2MikiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMjY2ODEsMjk3NyBjIC02LC04IC0yOTksLTQyNSAtNjUxLC05MjggbCAtNjQwLC05MTQgMCwtMTMyIDAsLTEzMyA2ODAsMCA2ODAsMCAwLC00MDAgMCwtNDAwIDE4NSwwIDE4NSwwIDAsNDAwIDAsNDAwIDIwNSwwIDIwNSwwIDAsMTU1IDAsMTU1IC0yMDUsMCAtMjA1LDAgMCw5MDUgMCw5MDUgLTIxNCwwIGMgLTE2NiwwIC0yMTYsLTMgLTIyNSwtMTMgeiBtIDcxLC0xMDg0IC0zLC03MTMgLTQ4MCwwIGMgLTM4MiwwIC00NzksMyAtNDczLDEzIDUsNiAxNjYsMjMwIDM1OCw0OTcgMzQ3LDQ4MSAzOTksNTYwIDUzMCw3OTggMzgsNjggNjksMTIyIDcwLDEyMCAwLC0yIDAsLTMyNCAtMiwtNzE1IHoiCiAgICAgICBpZD0icGF0aDMwNjMiCiAgICAgICBzdHlsZT0iZmlsbDojMTI3M2I4O2ZpbGwtb3BhY2l0eToxIgogICAgICAgY2xpcC1wYXRoPSJ1cmwoI2NsaXBQYXRoMzE1OCkiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMTE5MjcsMjI4OCBjIC0xMDgsLTEwIC0yNDgsLTU1IC0zNDEsLTExMCAtODIsLTQ4IC0yMDMsLTE2MCAtMjQ3LC0yMjkgLTE3LC0yNyAtMzQsLTQ3IC0zOCwtNDQgLTMsNCAtMTAsODIgLTE2LDE3MyBsIC0xMCwxNjcgLTE3OSwzIC0xNzgsMiA2LC00NyBjIDQsLTI3IDksLTUxNyAxMiwtMTA5MCBsIDYsLTEwNDMgMTk5LDAgMTk4LDAgMyw3MjcgMyw3MjggMzEsNzIgYyAxMTMsMjYwIDM0MSwzOTggNTk4LDM2MiAxNjQsLTIyIDI3NiwtMTAzIDM0NiwtMjUxIDczLC0xNTQgNzIsLTE0OCA3NywtOTM1IGwgNSwtNzAzIDE5NCwwIDE5NCwwIDAsNzIzIGMgMCw3OTYgLTIsODI0IC02Miw5OTcgLTEyMSwzNDcgLTQyMCw1MzMgLTgwMSw0OTggeiIKICAgICAgIGlkPSJwYXRoMzA2NSIKICAgICAgIGNsaXAtcGF0aD0idXJsKCNjbGlwUGF0aDMxNTQpIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDczOTAsMTE4MCAwLC0xMTEwIDE5MCwwIDE5MCwwIDAsMTExMCAwLDExMTAgLTE5MCwwIC0xOTAsMCAwLC0xMTEwIHoiCiAgICAgICBpZD0icGF0aDMwNjciCiAgICAgICBjbGlwLXBhdGg9InVybCgjY2xpcFBhdGgzMTUwKSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgZD0ibSA5MTk5LDIyODAgYyAtMjIwLC0zNyAtNDE4LC0xMzggLTU3MCwtMjg5IC0xNTAsLTE1MSAtMjQyLC0zMjkgLTI5NSwtNTcxIC0yNiwtMTE5IC0yNywtNDI5IC0xLC01NDcgNTIsLTI0NCAxNDksLTQyNiAzMDUsLTU3NSAxODcsLTE3OCAzOTYsLTI2NCA2NjgsLTI3NSA1MDAsLTIxIDkxMiwyNTEgMTA2NSw3MDQgNTQsMTYxIDY0LDIzMCA2Myw0NDggMCwxNjcgLTMsMjE1IC0yMSwyOTEgLTEwMyw0NDEgLTM5MCw3MzAgLTgwMyw4MDggLTg3LDE3IC0zMjYsMjAgLTQxMSw2IHogbSAzMzQsLTMwNSBjIDI1NSwtNjYgNDM4LC0zMDggNDg3LC02NDQgMTcsLTExNiA4LC0zNDMgLTE4LC00NDIgLTY0LC0yNDMgLTE5NywtNDIzIC0zNzQsLTUwOCAtMTA1LC01MCAtMTg0LC02NiAtMjk2LC01OCAtMjIxLDE1IC0zOTMsMTM2IC01MDgsMzU5IC02NiwxMjkgLTk1LDI1MCAtMTAxLDQyNSAtMTEsMzA4IDY3LDU0NSAyMzYsNzE0IDgxLDgxIDE1OCwxMjYgMjYxLDE1MyA3MywxOSAyNDEsMjAgMzEzLDEgeiIKICAgICAgIGlkPSJwYXRoMzA2OSIKICAgICAgIGNsaXAtcGF0aD0idXJsKCNjbGlwUGF0aDMxNDYpIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICAgIDxwYXRoCiAgICAgICBkPSJtIDIxNzUwLDIyNzUgYyAtMzUyLC03MCAtNjExLC0zMDUgLTczOSwtNjY4IC01OCwtMTY1IC03NSwtMjcxIC03NSwtNDc3IC0xLC0yMDQgMTAsLTI3OSA2NiwtNDQ3IDExOSwtMzYwIDQyMCwtNTk4IDgyNiwtNjUzIDEyNywtMTggMzkyLC04IDU0MiwyMCAxMjIsMjIgMzYwLDk2IDM2MCwxMTEgMCwxOCAtNjMsMjY0IC02OSwyNzEgLTMsNCAtNTEsLTggLTEwNiwtMjcgLTE1NCwtNTEgLTI3MiwtNjggLTQ3NSwtNjggLTIwMywwIC0yNzgsMTUgLTQwOSw4MyAtMjE0LDExMSAtMzI4LDMwMiAtMzU2LDU5OCBsIC03LDcyIDc2NSwwIGMgNjg4LDAgNzY1LDIgNzcxLDE2IDEyLDMyIDYsMzAzIC05LDM5MCAtNDMsMjQ0IC0xMzQsNDMzIC0yNzcsNTcwIC0xMTUsMTEyIC0yMzUsMTc0IC00MDAsMjA4IC05NCwxOSAtMzE0LDIwIC00MDgsMSB6IG0gMzUzLC0yOTUgYyAyMDcsLTY0IDMzOCwtMjU3IDM2MywtNTM1IGwgNywtNzUgLTU3NywwIC01NzYsMCAwLDIzIGMgMCw1MiA0MiwxODcgODYsMjc1IDgyLDE2OCAyMjcsMjkyIDM3NCwzMjEgMzAsNiA2NCwxMyA3NSwxNSA0MSwxMCAxODUsLTUgMjQ4LC0yNCB6IgogICAgICAgaWQ9InBhdGgzMDcxIgogICAgICAgY2xpcC1wYXRoPSJ1cmwoI2NsaXBQYXRoMzE0MikiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gNDAsOTEwIDAsLTg3MCA4NjgsMiA4NjcsMyAzLDg2OCAyLDg2NyAtODcwLDAgLTg3MCwwIDAsLTg3MCB6IgogICAgICAgaWQ9InBhdGgzMDc1IgogICAgICAgc3R5bGU9ImZpbGw6I2MwMWQyZTtmaWxsLW9wYWNpdHk6MSIKICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiIC8+CiAgICA8cGF0aAogICAgICAgZD0ibSAxOTMwLDkxMCAwLC04NzAgODcwLDAgODcwLDAgMCw4NzAgMCw4NzAgLTg3MCwwIC04NzAsMCAwLC04NzAgeiIKICAgICAgIGlkPSJwYXRoMzA3NyIKICAgICAgIHN0eWxlPSJmaWxsOiNjMDFkMmU7ZmlsbC1vcGFjaXR5OjEiCiAgICAgICBpbmtzY2FwZTpjb25uZWN0b3ItY3VydmF0dXJlPSIwIiAvPgogICAgPHBhdGgKICAgICAgIGQ9Im0gMzgyMCw5MTAgMCwtODcwIDg3MCwwIDg3MCwwIDAsODcwIDAsODcwIC04NzAsMCAtODcwLDAgMCwtODcwIHoiCiAgICAgICBpZD0icGF0aDMwNzkiCiAgICAgICBzdHlsZT0iZmlsbDojYzAxZDJlO2ZpbGwtb3BhY2l0eToxIgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIgLz4KICA8L2c+Cjwvc3ZnPgo=';
	header('Content-Type: image/svg+xml');
    header('Cache-Control: public');
    echo base64_decode($img_encoded);
}
PK�*�\h������'alt-php84-ioncube-loader/USER-GUIDE.pdfnu�[���%PDF-1.4
1 0 obj
<<
/Title (��Markdown To PDF)
/Creator (��wkhtmltopdf 0.12.4)
/Producer (��Qt 4.8.7)
/CreationDate (D:20260312135604Z)
>>
endobj
3 0 obj
<<
/Type /ExtGState
/SA true
/SM 0.02
/ca 1.0
/CA 1.0
/AIS false
/SMask /None>>
endobj
4 0 obj
[/Pattern /DeviceRGB]
endobj
8 0 obj
[0 /XYZ 33  
813.500000  0]
endobj
9 0 obj
[0 /XYZ 33  
749.750000  0]
endobj
10 0 obj
[0 /XYZ 33  
700.250000  0]
endobj
11 0 obj
[0 /XYZ 33  
98.7500000  0]
endobj
12 0 obj
[0 /XYZ 33  
296  0]
endobj
13 0 obj
[0 /XYZ 33  
65  0]
endobj
14 0 obj
<<
/Type /Annot
/Subtype /Link
/Rect [71.2500000  34.2500000  144.750000  43.2500000 ]
/Border [0 0 0]
/A <<
/Type /Action
/S /URI
/URI (https://ioncube24.com)
>>
>>
endobj
5 0 obj
<<
/Type /Page
/Parent 2 0 R
/Contents 15 0 R
/Resources 17 0 R
/Annots 18 0 R
/MediaBox [0 0 595 842]
>>
endobj
17 0 obj
<<
/ColorSpace <<
/PCSp 4 0 R
/CSp /DeviceRGB
/CSpg /DeviceGray
>>
/ExtGState <<
/GSa 3 0 R
>>
/Pattern <<
>>
/Font <<
/F6 6 0 R
/F7 7 0 R
>>
/XObject <<
>>
>>
endobj
18 0 obj
[ 14 0 R ]
endobj
15 0 obj
<<
/Length 16 0 R
/Filter /FlateDecode
>>
stream
x��][�#9�~����Γ�+�p��j`��*؇e�����k��퟿y�|��O��(�i[=��e)��P�����_?})������ߋ���Ӯ*������?��m��	������{�}�q���������_�����_>=�g��
^�G���?�������3̔��L5���🬒��5�m�{5�g����_�o�<��V���e��f���eY3����m�s%�պ�.L����'����6�W���ߛ.�8&���<��Z��g�����Ȓi�������z��r3����>}��}Ѝd(>���_��sK���.>���ow�C�;]˸�ŋk�]Se�m+l�Z�o���ȾŖ����}���E�S�vo-�o���v�bݬ��sj?Z�O~�G�2�i?��<���k�K�?�Y��3ny�-�S
U�S���ʯSz�7��vT0�>�t�0�`�=�r�Mq��/ߊ������?�����?���C���ܒ�B�7O�<��y^~޽~>T�'��y�6��T�@u��Y�5tD?��to�������zL'��SvJu�YB���z��n��Ҏם)��'TWh����T����C�b�_��S���S4@���eYɱ��+�d���}s�4�t�^L�G�GM�I�G����}_C��� ��h��s �q'��TR�
�@�>�㡤��'�K�x���N`7b��Ӎ@�����sU�Y��~��K���-��7������S�>R	v���Q�>
�Ƹ�M�ۘnx�X;4S`n��GëM�f�P$�a�DЍ-�J��8I���-՘ָO`��MAI��`�Q0�8�-
V�ur� ��(2$����qMo-��tފ��E|�_s�^�{xO�C�>�|¹����(a���=
�}\��d(5H��hY�&ӷ$oE��5�k�ᝅuM�Ys��g��5��qw�Ej���?����(`1��@�8�A=a޶�;�b����p�i��}�u�g�����h�Ӗbt����'J��?g��r�g��M+�&�8�>���0�)����q�`���`>ஏ;����_���9�q����DJ�	y'\�NX���O�EeT��թ'�q}�[���=�'���$�h]��O�8O�X�ت��9��(g����h��v5~�{)��'�܋}?��kz�U7�
GFQV�؞[�W,��5H�)b�v���j�`#±i@�v@:��`	��vr�$>�ϡ���ao��#��CY�9vLu}�3^�B6VWr��5��Wl�j3��ng€p��	kG(}�ђ�U��Qbl�Wiy��%S��w[�fY�>?�Rق��`5k���Z�S��Ye���6�^MN[l��E�
[l���&��#ly"��0k<��M)����W�?�V���M���C%	.���NU�>MF.C>5���cg��P���b�V%|!���Ң���N"H���������z3q�D���0q��1��&rQ�'n�]�y�gMph5ox�l�T�9�9ZTjd��e�-y*WW��􂐘 :!Ђ֏���$�l4�Q�E��-��m%J,HfK���)�
�O�(3��B�O\��6��hzႪ��P���f�n��*�m�7�e���C�uTUH�R"�����T+KU���v�}I4�0L@`q�j,#I���{VK�L��s�-@H2lW�O<��rb�#A�
3���2}������V���T�"M�ᱤ�^Lk,�(�$�Ǟ�$)�������.�}5���x� 5P��Zڈ��׏��r�]\�>�%�W�H������-U�Q\�X�<�-F���˺��	�q�!��!)���s�(G�#H�w��i�w:��g�4�$ ��eڣ����íir]���P�`b��.
m医(fA���サ�f�3$��m6���i"�04��b���t��K:��]��%�(8�pUQ��k��[>�T�}}ond�C�S���R$vT@��dK���J\��W����XQL�[��%ZfqA�,�ˬ_fy�2�
'�WS�����5v�E=?�@���֭����ˇv��K=5g��D�����d�,����3�J��Yu��r4�e�9�"�^L���(Yv��θuJ�l��8�	N�p/�)��de�e����n��p��͜w�䋫;S�����vFrJ��ձ���!�t��y	5�����N؎�.�2�l���4].M��M���>�.�R���G��J�6a
)D�[����T�.�v�d�kk�u_wB��VRT�M��N�����sY��~,���3��͠�qc�c���Ztr��+Ց�/���7N:j�K��I��̍�۱�D�"R����S��hB��+V�H�!�?�����f@z�`x��הAPx��ڵ$rRɂ�[s�ے�&��﷥�h�w%�Á>(gY�e�
���{m��9���s6O-:;�#���]:�n�����c�n��ch󴿛A'��Z�橕�8py��88�.j(%��v����*�¶%���P�K\��~�,���)Am�%�1�-�
).�����-?C�}3�C_H�̣�x�t����5>��"g��X*��p�(����7��3�c��[s������b�Itcu���j�tT]|d�10��͠�qc-l:��)	�[�.dr�9r�<�E���ZA��F)m�#�(�K�FLt,gt���7�ݾ�M}(83VK��8�Ea�N�0���΍����J���^��w�g�#Qt=~l�A7K�,e�)e��tc���3��Lk����]:�n�΂ҕ���t���},��w3�d�X[P
H�d���騗�m9p�v.�ʶUEK��3�L"5�E{��E�H-r�V�}'��2��1��*>W�'�|�g�y'�ê8F�v�Z�Ej�z1�.�D7Vo�������n,5��~7�Nƍ��Ѣs��[�f���͘��L\�r�%D�O��?Qi�^�J�9J-�!���0mՅ�_��X_m�#]���1��6P��ʫp}�	%�3��A��<1n�}�;P�p6^�3n��2�b��QZ�J_��]]afnD��̋c��RSK�C��ZAa�Kj�xI-�V�>�.q���
#��P����$(3�wd���G��=D۸᭭��*����R�kD8�W��/Y����ӥJ��܎|�;U����|Ԗh�2�y;[�h`�[(��www�I�d��#WI�'B��-���Q���W��)w�P�����1x��؉Tm���Ι:g���s\{y�`��9}��8^�@e)O��x���cӽwة����}0�iF��)q|w��ꞣ&΃������#���q���Z�V*!Q�}BX���]��Lu�Wa��B{G�Gҕ�YNS��\�N �^<z@�����ĸRt��ep�bA�W���t�9���r>�}�J��Í��2�cd87�C��PBh�S��g�g�+��J�s�:U�!
�JUF**�L�.37@h����0�Ԥ2_�v�B�suAծ�#^38�q�3V^�������ԡ�%J��:�FZ]<o\DN'��wI�	�'ЇB�c���=˗a|�1PɰY"�œ��N��I%��.�"����
��k����[�dP��(�)�Y^����3�r"��t� ��Ԧ�p4�&p�}e�3_���3?ײz*q*p�Xy�L�@Z&i.���ϲ�0K�#}8\�W!לּ��pgq3�q��A2���HGo�Zo�gh�l�	{�ujp`��&k�+����Y�@焿?N�3���0�0�/��|���_�1�T��F��cKѓ��>.m��I�j�;�D9�A�>xn=;1YN|*O�e�t5̛�b	c�T3^��)��=h3�V?ҍ?�wt4�o�s[`��i0i�P�
��u�9�}�s�l�6�m
ӚBQ�@��<7��#�p��/ߊ������?�����?���C���3�t��,A��$K����ϧ�$��.k�����8&���{��~
f�p��ؕ($�D7<|fM9�\�A	�DRr���+���h���8��;��+��-nxR����5X�P�u��Ȫ��Q�P����M�
²�pT�RH	eoStp*9���6�aS�
�E�Q�ܼ��{q�AT�\@Hɟ��JR�6%yv�RD�o���P�8's�ðZ^�����t��I���I�JI���ʍ��puu��u��}yo�6�
G�.D�=x7�7��,&E)��"�7ũJ`L�|��e����!Q�~ ���e�z�H�7��J�Q(�����0�)P�"6	�Y��s���
$�a��o�4��Uq�rN���Q���g�U�K�lk�(,)���2�S��1m{��n��!_N�\"�	�R�,�_�^�_�^�z�2��V�����2��w1�$�����k�g.so~��j>��w��A'��Z�2�����]8�!��b�aO�#�fyS7R���6����ގ�%�`ӾY
E�o�ڂW�j.��l����#��M���մ���`��E��~��5���^'�8�o1�~Ə�0Rk��K�d���Z|ᓡZ�j�]���Pm�2C�y�&��j
���jB�Ъ�A��E��~<�D5�D�ƫ�P����3~�š��2T�P-�0�P-C�ն��)��[JY���<PR�J��J�������E��~<���J�Ȕ����?��@I��7(e�_�&J�ߡ���-A�,��P�Y����
�\�:�N8�� ˃Mȃ@��	%�9�q�������>�/�*��+Y\�uM��`l§��"��Q�nW���U`͜���޴�v�*h[Ġ���s�+�\֝�X9v�-f���w��ͮ��C��*Ȯ�lܚ<���NE3&gT4c�S���PEw-b�O��*��oFE7����HEw-f���Vь����'��m��-��F�z,�av'�މ[&��=��1��7�.3D��B0���\e偫ds�U�hJ>�m���x�
;\E�j?G��m1�~Ə�8p�2׼�N�Y�	��0�`�M��2J�nG*�`��|�ϻ��6(i;��@����4�a?я瀒Vs@I;��Vc�Զ�A?��[(����Cެ�BB��"�L���K=q��G��{s0rQo�J�O7
OSI���z���e��47�����?�s�|��ל����3���^č�fvq�mK��o��ŷL	���ʷL�]3��s��f�i0����8�1�c�	�53��o���q�AMG�~}&�����r�F�Q���4:e֘�9nW��`���Yc��`b3�ծJ;��>07��1��Yc�G�o�}�ԁ���dC�����␸��ɷL���^�p1��+�U�O�}�.��M�@��)������
K��9c�u��/ߊ������?�����?���C����LK:T��\VsY�e5��\Vs�Ws�??;gΊ#��`����;ʉ���w�če-����;�ˎ��t0�"3-�Ѵk��M�h�����NQ����TSW���3���G��s���ͽ�p}���h;u��V�A�S��X/�'xf�%�xM�<9ZO��a֐�|mpp��ܬ�G4k����4��w	��<�����r|`4��bϐ0�_���D_�8��'�	�N
9���^�bUVYad��FV��0�F�)S�/d��C�ϝ�]���Ὁ��c
�Vp=҉���eìFKS�滬���J+q��ptvy��O0E�{h��Q>Xh|�8t�-0���U�3�&���s���D|��>��`�w\	���a����dgD���F�ۖ�)�t)��)��-xM)aLqË(e�"�&
������ �T���5R��n�"�P[J���X"��Ywf,J�Ϻb:P�"��r��+�2�.� ��e�QD
x4`2���8�)V
�j{�U��@�=��$�5.U�q���Bi�W|SK� ��c�q��DUJ
endstream
endobj
16 0 obj
6245
endobj
20 0 obj
[1 /XYZ 33  
719  0]
endobj
21 0 obj
[1 /XYZ 33  
631.250000  0]
endobj
22 0 obj
[1 /XYZ 33  
117.500000  0]
endobj
23 0 obj
<<
/Type /Annot
/Subtype /Link
/Rect [102  654.500000  175.500000  663.500000 ]
/Border [0 0 0]
/A <<
/Type /Action
/S /URI
/URI (https://ioncube24.com)
>>
>>
endobj
19 0 obj
<<
/Type /Page
/Parent 2 0 R
/Contents 24 0 R
/Resources 26 0 R
/Annots 27 0 R
/MediaBox [0 0 595 842]
>>
endobj
26 0 obj
<<
/ColorSpace <<
/PCSp 4 0 R
/CSp /DeviceRGB
/CSpg /DeviceGray
>>
/ExtGState <<
/GSa 3 0 R
>>
/Pattern <<
>>
/Font <<
/F6 6 0 R
/F7 7 0 R
>>
/XObject <<
>>
>>
endobj
27 0 obj
[ 23 0 R ]
endobj
24 0 obj
<<
/Length 25 0 R
/Filter /FlateDecode
>>
stream
x��]�n�H}�W�y���n^��a v������>�d�$Hf2��%��,�:-�X�,�@,�M���.�.}�������\ݽ��<��w�������z����s����/���ŻŻ������|���Z��X��ݿ�O�Kl���ߓ����kx���/�Ҕ���&�����I�bYT������?�m��O��z�ҧ���:�K��W[����5�ee�"+��7�m�U����+�?u^|�}b�������?>.>՝?u�u��2�|���F�t�V>u�}n�eU��v�]7o�k��n��M��յ۝��!O�}:M1�q���z[$�&���^��=4�~e��?��\�l��$�/�76�lݒ櫖z�i}&�S�Zl���ꡧgz&-C��V
[-w��lY���>�:<��|��SKZJ�w�f��"��ϬG�Ny�Ԙ����o�V���^��vu��g�~"�F�)��p�����
��܆Q�%8�mv=�j�������04�G�W�9���u�B��]��?�Lv@�XˇuY��\��u�S��o3o�n�ÿ�r�j�����g(N��\��ٹ�zm�nj��m|�1G������1�nX��O�I��B���0�S���YQ�B"+�pXf��m6�q�G�#)iԙ�C��ހ�@���>`��[d�'s ��G�%���~L��R��D5,�oFx>/��E�ID/�RS^Qb�q?�G��XN�G�I�bK=�ř0��>��0G"d�)Dy�D�@I��8t�m��U�|��	������F.
���ᧄ�#+�#�زX�a�*���)l�2z)C!����#�ͬ����*�Z�*�e��?�0tm�����bP�O0�=]x�y'kU
n��tdG�"0}�t黑~�ڊ�5��TDit}º�i�m�>�pn���1jy��cct	F&�à�����m��w�њ�]όM�f:���Kb��Lav���Eq1�����ՁcsmKZ���y;�-Il���J�d���#����	�ȜҊ2�>���X��� ��`�
����A��=w�,`���LM���0��!��Uj�'Co��_)˟���c�%*��v�|�_�Jdi���"��0#��Pbp?�;?��$|��P���+�O|�	�:~d1�}��ۘh*f��؄WT4�G�I�`��kZ�O��@s^ZeQڠ��-sfּ��!Y����2��68���#�55�K�`v��blZI͗�x#3��Y��@�_ᣄ@+R[�#ɞƴ
�d�3G�l�;�3����6se}bL.��d�c�-�6&�gͽ0�9� �GE�:"��#����.Q���2)��c3(�y��Dc��lf�|#-˕��=h3
��d�t����X���A`d�V����]iu�2�F�����B%d}�[x�SuHS�–Y#-��N=�#��0/>�������ݝ1f��윺e�n�τ���'��ۙ����y��>I-ӽ�y[��++LK�>����0�	��o�èw���_�����㏿>|~�����&��In���&ANͼ�S����vHO�N!˭�M�]��76Q�)pAi��#����}P�.��o*�L:�P����Y΄R�&�g#�l�S�<*G7�"�ƌ@4�P+U�1�".�	�A�#��^��b''�U��x%��7Q'��:�dD�t�/����v"�M��TdY��a�4y�S1�|�49#S�ʹ����j�T	������#)�gc�@ш�!�$��_LIR�����#5��s���-UͅXy����?��qZ�QZ7�u��/Bˋ�I�k�"n��Z�U��+5�b�j/�"�Q�t��fE�&)ݍ)i�X�慺N���B���Jb�r2�lsֱZb2s���N�"��Z�LX�V�2��3e�g�#1�$?LU��
��"�-R>Q4e���T�ה�O0�Kj��=�6F�](|A��y;%�F�*��{�4�.���WW*�A—$)%R�J�T#FI��c#�p�<#gTm
ď�g�u��Z����V�-�q��ͪ
e�i�r�,ӝ��H?�=�	���e@o7�2zg��=ڀ-�����@�/ڳl�t�c:oܸ7j�tQl�
s뭡�
3��た��'Z���?�:��Z9a������YUj�٧kOLgٿ��7��[/w��W�L��/���L5k�K���|\|
8�5C�O	�0J[M.�ed�����o�?�`�k�Ƕ)Yp�6�d��
?��CN�[b
rb
`?x>v��
�쎸������F?ȸ��	��\&~V������"L`ud���e<�x9�%P6��*�\i6��2'Z��<1;YO.�q����F��3H��¸ťm�D��	\O���>Ѻ�P�jA�n�u���~�8��3� Z�ZX��_
H����n���Ƕ͔�`�i�L>1e4��9aL����r��g��T�����X�q����fz��"��='���h��{؂G
m�8�Hf(pl����g�,d�€:�����`�k@@���u��~�6<���=%h;""3�Wz���~9���a�=�p*7�M	���@)�WeB���$�[̙��Pq��x��ƭ�Zͫq���R��
�����+�kAx�I����k!zC������_��X���
��0Q'��\T�C5�s�G=�V5�UuvpZ5�xN-8
��@�";��aƆ��k0ǚ��ר.1ִ��9!S�g��=
d��eX����S�93�sI`	Sk�("�j%�+�0D�аצ����!�9W�������(x�Ff���G��)�`� �����V��S��7f�Dt��	W�S�ƨG?CPJ;o;rZ+Κr`���,���R_�*v���t��y�����#�G0�-IR��|<����u��!���Qԕ�g����*+�4�70��8��|��'����j�s�:�2��9SP�Zb�8��Ei�k����1�)�m��e�G�S����h���uS�WEC6#���=���t�Q�d�5����Jx`8c��Rș�yb�L#+:I	^��#�ǿ+g�rm��8�!�߉V!�$���rԡ��l��	��Ռ�]����хS5�c�i�H�99f��k�aXsv�1[t9�1'_�X<6��SFp�H��I@!�L�T�
3��g��rRm�d���5"E���@�V�B��ذ�eS�����1�ǘ�g��O��L+8�#���*β�� �yµ��L`M��iT��S��2�'&)�I�Pn&eM4H�����;���	�'(��k&(R֥��*s=��U
J�S`�*�du�+�gd�h���΃���>w���ZWN0�/�f&
=P��$p��I8�Z�1A��൓.������P�K[k���䐉Hyٵ��Sި�ֵuyg�r�6�]��Ĕ��Z~"��]ˑ����������W�be����'�:����u�}n��Ղ��LwJdc1Ÿ�㄁�����r\V�����dwA�REsj�p�-��$���n��D��);�L**:���.�xa��Ϩ�pZ�S�	��rG��#��E�dLX#�e�a�.�¢�j.��T�g��.`x·�\�U�&e�=/MV��%H�sɸ���Q)���%B�E�Mޙ�JVێ�GC�z�{c������n5Q��}b���guq�iw�͈��64���3p�#mc$�����Q�D�CP�;��z����|��a4��D���M)�����1o��QV|0l��v�2��a�-^��8C�s% vM3��%#v�L���|,n�X	�̮V�P�-�]��!�`ʁ�:������00���p�p�s�Əo����x�Չ��0���ҵ*{��ZIRc3�aG�䌑�Z�;�nXfa���8��]�O@�)|aˬ���1܏��Ú뉝�zm�Z߆��k��e�w���+��";=������]��qK����#��C\��+��	E�#�3���
�e���G��	4�OӁ�	<����|��"���N��S��.� Y�K�W�}��\_'W������ǿ$�7��Mr����4Lr&ޙxG&���v���K��A�7=�x�ޟ��Y���w� ��G����+�a���؅I��g8���Ì
�{j�X̀l=��.i�F��7H�䈨
e�b���׺�V��*h�+d�L*Q�	�=��U�D�rh��������](���C���%���n�cH�@�=��6c�<�>ս�Ym��ʚߖ��������<si��s���}��5��'ݧ\x�k���S�Rv�+�����i ��V�]UaY�
b��e�e3���*��e�� 8��]ڎ��w�f{,�)��U\Mɭ6�]�sW��x�E�T(Æ(�H�"���ǰ���_�����-��.y��?�zB�
endstream
endobj
25 0 obj
4552
endobj
29 0 obj
[2 /XYZ 33  
74  0]
endobj
28 0 obj
<<
/Type /Page
/Parent 2 0 R
/Contents 30 0 R
/Resources 32 0 R
/Annots 33 0 R
/MediaBox [0 0 595 842]
>>
endobj
32 0 obj
<<
/ColorSpace <<
/PCSp 4 0 R
/CSp /DeviceRGB
/CSpg /DeviceGray
>>
/ExtGState <<
/GSa 3 0 R
>>
/Pattern <<
>>
/Font <<
/F6 6 0 R
/F7 7 0 R
>>
/XObject <<
>>
>>
endobj
33 0 obj
[ ]
endobj
30 0 obj
<<
/Length 31 0 R
/Filter /FlateDecode
>>
stream
x��]]�۶����u�8")�HN�6 �z1�"H�E4k��珒(�C��yEI6��1M�/���)���ˇ��g�����M��e����^����je�s����[�m�a��f���篛���6�7������_���}�軷?����nۡU�m�O�նjTS����?�����쫛G���\k����2�����5�il�z�{��ֵ6�n
�>�e�º�&o��4Z��r�u�+��3���������^]W�L�6�l�6z�U����Օ.�z�5��0�EV��Xw�t�pO�Tnj)+��CS�ߺ������ear��1�~���]���g�^ڏ��ߍ��Z쨟�e�HI?�%���>n^��2��ǟ�~;_��_6E�v��u��c����w���_7�֚´D��U�Rw-z�TuGv-F-�ی��>���R�]�]�j�h�t-�6?�G�E�Rn+���z��}��/���}����R9ȴ��-���V�{���a���=ﻖ���5qUL3\�"?uU��@�}\��Ō��~<U�=uUܫ��<�*m����x�_�6�o#]|�
��E��4՞�͛�{n�z�cwȡ��cp�j�pY䠬���>t-�6?�Dig����1��8b���Ʒ�V�E㎻}�i���Ϡ�e��Gל�Q����X?�P>\��9<���
�X8Z�Z�
�
�'�RC�yO�y[���7<7<���z�-pGq�R�f�JK��{�z<������U=��mO�N�����X
�wD0s�[�CR ̐|0L��.��T~���+��aN�h��ѣ��x>�9	W�oxn�L1��`Y�9�R괉�0p��3յ:�k�6�a�;��bV�k;\������n�:�`�ѻ}��9���?��S�3���[��si�nc�
�(A��A8�>��7��~������6�@@��e���y���Rg����.*6��S�w�8퀵���ċ�|���k��3t�
��g��C�&����Ѧ�~���BVJdp}��al��|���J�ܰ����a�k��U+�T���()C��0{M�8X0Θ@�����8�_Ӌv�����0�4����ِ�X4-��)d���t�5�D�ai��Q���!��z_���9–Z���-º|Q�;`�Zq�
Cp��h6�
q���
ƻC�ݔ$������#��Y��c��cj�CU�ߋ�� ݚ��`��A@7��L�DeNaH�[�:,C�;�X"b�p���B;E��@���qܝ�������Dz �т`�A�͞g�3b.jU���Z5'b.j�w1��8�k1�~�����Uu"��}ۏ������Ŏ��a��c.j������R�k$!o*eY�Q3*
���Y/@e��z�i�pUe����b�sƵF&�v˓AS�"��fud���-f����y2h�)2h�O���Ŏ��a��ɠ{�'i�B�+2xkڼ(�W��5)�t7�9M�Fe�֫4}�h�«
4
��w����~�Rjp������>|��i��MM�̠]I����J��� ˩�

�����H浀;	>F�a��d��J!Ϥ��c�HƳh�	U�������Y�ֳ~�`"mU)�;�}oc� �%Cb$ۡp(r,x��C�_2+e\�p
R�U��:�If��Z�R��@��&&5�,���t��Xg����&ߟ�$��9��8&{[����o�Y�i��k����X�4�WW�9��Z̨����Ě�<�%��^b]����x{���~[�A�8���"}`n�{�afx��NUb,!C"!PG3���al��3���j���1r�-��>GD�o1�~��#B�)?&B�~�|L��;�g��&'B�:���I�<���Ϳ�DPAIe>�2
��0�v2���OY�0>q'��u�&�O�sc�W����R1�Ҏ�5��M%H���,PT�U��QV+�/
p�ɃQ�$���X�Y��v�"�!by�QqM�n�,Ϸd_ڥ��*6�z�@z����rm��{��@x]��x�8���5I�p�)�>�8����!�8�yC�J��[R��ͩm������Iu�յp�\R�NcP��~{@�S��`0<扈 :J]B`�%�
֋��LҴ0�,8B�ǃ��p�H)d
�6��SoO�^B���59�������*h���zR�s
qCb
q�eL�w��B�o&�����pO'
a�o����s]ֵ�Q?ӏ�G��D!��[?�A!��Ŏ��a����t>dّ�.���*�%ǝ�ڃ%+���7�1d�,]�be1F�g#���{:Q����_�y�����L?�G�
������[쨟ƛ��
�P&Ç��Y�=%GW�6����A���m�*>�Qm29e��D%m�����F���C⳩�ىQ��	�o�uXO�o1�~��S�S�;Y�?���b��Q1�	��\ F1�&<�ɬtw�Mݝ}��%}>�b�=�����0��$@푣_t�(�;��E��{,#	G�LAFc(+�-DG1��v'V��	�}��(;x�(܇���7x4"�kg���L�{�c��J�D٘;,d���SM�'�l��+��8Z.aѽ��<S��RO-�M("�q��XI��hJ(��II�dc���9�b���0��~���������mc�l�vkI��z�O���z���N$��pd��hG�6�����jhSWY��^�z@w���*1��cT�=�23���I*%�V�Զx=Lj"�	+Zq�!Y�:�$kՉ�&&z�<<�Gq���4�B�W8Y�Ũ5(�
�5�
�d����J��0���LِzF��>���$�� �T�=LZ�n��S>˄�\Vټ��ԼE��5<��H�H��ϖI^B��Ո"��������k��U+���$'	�z��~$`8k&ٰ��'�>����'�S"��t��iw6N2�R�f�����ņV�Ո�d����w$�i��)�ͯ=��O!�
�(%�ɺ8z��,�O��1�&��o	� ��Σ%�c�[��ßs{�--�t�rSgjU�r���?�	��B���8����C�g��:3���V��0��+��<%)0�@�yI����z�R���9��Zv�vefw��x��G4��	8	HDEX�9�5S�I4)3eC-�"\������~���T�A��dv_
Y�%=3�[�����b��uُ�&Dq��:����j�OF���Й@�[Lx��oٲ[O�/Y�l��a?�	�)����R����4�V[Ӹ�F�Ւ!���؄Rh���}b�e`��5~P6��h�E{ޚ�o\���uZ�.��	*d=�h��aم)�){[��0>��{̟<c�2��l�܄Ben	9>�L���Nr	�v����#ju��50�_fOe��P�I��v�Ѓ(����7X6jS�(�R�Ϝ�~B)+ƣ�[E���ka-�)"i�=��W��#(isBX�r[NZ��kSp��)���=�5b�e��1�Ɯ)�(�k�4�D�wPr6��<CwD�G���d��&���v����_�l�Z�~��,6�� �G?R�\��)\x!J�����/I	A�LZ�:Ӷ^S�N��	��(
���@pU(�;x=8ȹ*=R���:�5��JI*��‡Xi?������R�fPH�OrOLv)��1�c��Q�o�
���'u �;T��X�Cf@����z��Ez&Մ�s$J�U9�Q��e_�\B��l�\�L9.�~Be���-��$����$�Z��<�!��U�uPg!�D
��FHe�DJ����sp=���y�#xsJ� �̨�e�ί���	K.9r?���C6%��
~��@��rx�5p�`����5�Կ�|��l:ZY{q�)�������q4�9�=l�3�\���%w�|�T-&3)�А� b9A8�4��~� �lXT�Vٸd£~���hf#��d��6�\ޤ��օ�W��U�+Z�1f�K)'��2�����f4Y�4*�/���f�Ft��:��b�s�����Z7��
U��؂3u0�Cp4u$��c8=F��� j�P��D3$S.̳b�E|�n���kDN����yjV�
�RS�*KK0$��
e�]��H9�G���I�ֻ�E�P��b�*C�~�qV��D@�Q�����l.�9�Z)���z�F^)va$�[�E��dN�W���e5��7��Ѝ�l��	&TUW�-�ϣ3����q�K�UV���5x��h��멱R�"?����I�.�f,��r��G���r�<N�h?�����s�#)�#U
�����q`��G��򳮏����8������N=x�c����_~�����ի������o�?�������������"�UHW᦮�{8 X�lAP�s����W8�_a]��^[�>�~�8QR��8N��g���p�5~����1>��C/����37�R蓺_]�˾o�1ȟ�����7d��
endstream
endobj
31 0 obj
4638
endobj
35 0 obj
[3 /XYZ 33  
714.500000  0]
endobj
36 0 obj
<<
/__WKANCHOR_2 8 0 R
/__WKANCHOR_4 9 0 R
/__WKANCHOR_6 10 0 R
/__WKANCHOR_a 11 0 R
/__WKANCHOR_8 12 0 R
/__WKANCHOR_c 13 0 R
/__WKANCHOR_e 20 0 R
/__WKANCHOR_g 21 0 R
/__WKANCHOR_i 22 0 R
/__WKANCHOR_k 29 0 R
/__WKANCHOR_m 35 0 R
>>
endobj
39 0 obj
<</Title (��PERFORMANCE OF ENCODED FILES)
  /Parent 38 0 R
  /Dest /__WKANCHOR_4
  /Count 0
  /Next 40 0 R
>>
endobj
40 0 obj
<</Title (��ENCODED FILES)
  /Parent 38 0 R
  /Dest /__WKANCHOR_6
  /Count 0
  /Next 41 0 R
  /Prev 39 0 R
>>
endobj
41 0 obj
<</Title (��LIMITATIONS OF LOADERS AND ENCODED FILES)
  /Parent 38 0 R
  /Dest /__WKANCHOR_8
  /Count 0
  /Next 42 0 R
  /Prev 40 0 R
>>
endobj
44 0 obj
<</Title (��\(Available for Linux 32 and 64 bit x86 servers using PHP 7\))
  /Parent 42 0 R
  /Dest /__WKANCHOR_c
  /Count 0
>>
endobj
42 0 obj
<</Title (��IONCUBE24 : real-time intrusion protection and PHP error reporting)
  /Parent 38 0 R
  /Dest /__WKANCHOR_a
  /Count 0
  /Next 43 0 R
  /Prev 41 0 R
  /First 44 0 R
  /Last 44 0 R
>>
endobj
45 0 obj
<</Title (��Global settings)
  /Parent 43 0 R
  /Dest /__WKANCHOR_g
  /Count 0
  /Next 46 0 R
>>
endobj
46 0 obj
<</Title (��Security related settings)
  /Parent 43 0 R
  /Dest /__WKANCHOR_i
  /Count 0
  /Next 47 0 R
  /Prev 45 0 R
>>
endobj
47 0 obj
<</Title (��PHP Error reporting settings)
  /Parent 43 0 R
  /Dest /__WKANCHOR_k
  /Count 0
  /Next 48 0 R
  /Prev 46 0 R
>>
endobj
48 0 obj
<</Title (��Deprecated settings)
  /Parent 43 0 R
  /Dest /__WKANCHOR_m
  /Count 0
  /Prev 47 0 R
>>
endobj
43 0 obj
<</Title (��php.ini settings)
  /Parent 38 0 R
  /Dest /__WKANCHOR_e
  /Count 0
  /Prev 42 0 R
  /First 45 0 R
  /Last 48 0 R
>>
endobj
38 0 obj
<</Title (��ionCube Loader 15.5 User Guide)
  /Parent 37 0 R
  /Dest /__WKANCHOR_2
  /Count 0
  /First 39 0 R
  /Last 43 0 R
>>
endobj
37 0 obj
<</Type /Outlines /First 38 0 R
/Last 38 0 R>>
endobj
49 0 obj
<<
/Type /Catalog
/Pages 2 0 R
/Outlines 37 0 R
/PageMode /UseOutlines
/Dests 36 0 R
>>
endobj
34 0 obj
<<
/Type /Page
/Parent 2 0 R
/Contents 50 0 R
/Resources 52 0 R
/Annots 53 0 R
/MediaBox [0 0 595 842]
>>
endobj
52 0 obj
<<
/ColorSpace <<
/PCSp 4 0 R
/CSp /DeviceRGB
/CSpg /DeviceGray
>>
/ExtGState <<
/GSa 3 0 R
>>
/Pattern <<
>>
/Font <<
/F6 6 0 R
/F7 7 0 R
>>
/XObject <<
>>
>>
endobj
53 0 obj
[ ]
endobj
50 0 obj
<<
/Length 51 0 R
/Filter /FlateDecode
>>
stream
x��\_o�6ק�s�($�I ;ɀ�@�=i��h�f�}�Q&e[��}��IT�N$���ǣN�}'��.N����]�NoY�J���;Y}�]��S������A<7�M���h{�����q�">����Z�~}�.�S�慯�U�l�VU��e����.k�������O��7�>�!K'�֤IE\:�OHYJ[������i��7��ekcý��I�Zu:�C'���pN
(>6��4�r�9�Nf��u-���"�p/���T��{q�wy.f�[V����E���K驑�%d:��2p�u �"��jXi���B�J��d��MZoC�7��^�q-�^�@��qj}�g�ΧR�>KH�f�<�
�c�i�{�p!��8m���3�US����I�:�g�ްī�9$�
��`�1�1O1��9�Y�:��L,!�7��&q��u�`[��G˶M-!
A�u��īYp
���$eǦ�(�J����}�]鄥�#��ok'mo��8�:���}�XÙ�ȅ��ӄ�Ӎ�It��U�4	jl�rspc�5̹��
g��C��.��W���kp<G���`���fU��4�6Z��%�0u�:c�9�r�U�ۜ�<�z'�18��(=�j-�N�'E\�<fG���31�z�t�I�s�2An�l#�o��#����K�5�b0֌x1�
�E�k1�H�Kdڂ~,���u�(�+ǻ��!])�:�69i�O��&!��;]*i�ZPo�i���*^�
��ʐLO�:�(�׼'�{_+���M�kxo9R��N;�����ٓl� �a�*�ը$Z=��(-d,g��'˯[��{,t8Mˡ#�'�b�}�%�$4H�(8䩎�ȗ�G7NX�9)za2�*F��Y�1l�D�k��� `Ѐ��Ʋ�5���8����0�)_խ�Wv��W.:�p]s�
�:�(�7w����~�4�g�?Bl��m�ۻ���^�.ܜ�l��ɸ
�^'�VQG�34���"6���1�ؘz�1�ؘ:�1�VU�ظy��~]U��ʡ;�1�S�d$U��ʼn
�b�9űT���5M��;º����A[�AR�m�δ��L��s�>�텪4���z�$�/��Ac6���R�8i�8��8�QJ�>�J��4�v�e�^�{4�4N?i3E\HX�3��ۻ{qv&N/�~������?�ٹ8?��iSu#�$_�E��o��}�yI��'�H�-]�}���z�^;cd�\�Q��#�can�@i��v̟�j.8���:x1?l�~��	,��'ͨ�f�~svqoc�U/�#����z��o���v=�I��,ģ�9ͻ�B|m����ͽՕ�AD��x&$,��p*�r¨J-�����@~j� �
y��2�k��7!W���m$�`ʀ`�!�4ƚ D3p�3U����6��q�a�� �C�G�Pn�=�����W�LU�`�9�"�̓���(���D0�2B7VY����:έ�C>J�Y
*�W�pl��*N�K�(�%�شŋ��8��akI8I����8u�D(#��#Suv��|ȱ�����c��x�a��0����a�c
椉G�r�D��7̹A�,_�-�k�<�t�}��9�Y1������n�f���nk)8X?٦�٤?m�Ӭ�~���8�d�j�g��L֕���׏f�����%d�8d��E��ˉaX�*��[�ň-8��`i��e`#�K9�Rɫ����d��m#���gǃ3SN,���c>�t�k�b?��,�sX��t�W�l�X����
�1շ���E;ߕ���]�>�����h�FB���Ѱ"� �Қ �0�x�Ĩ��P��f�6��G���Lo�nu�'3���r]����r��p�'�9Y��j�sy;�q�)y|:2�f�<���Y��7�����
endstream
endobj
51 0 obj
1948
endobj
54 0 obj
<< /Type /FontDescriptor
/FontName /QCCAAA+Roboto-Regular
/Flags 4 
/FontBBox [-736.816406 -270.996093 1148.43750 1056.15234 ]
/ItalicAngle 0 
/Ascent 927.734375 
/Descent -244.140625 
/CapHeight 927.734375 
/StemV 48.8281250 
/FontFile2 55 0 R
>>
endobj
55 0 obj
<<
/Length1 6976 
/Length 58 0 R
/Filter /FlateDecode
>>
stream
x�}X	\G����{�F�1�q�
�/�S��@D�f�A@��BEŸI�
�A�1�`Iܨ�[s=�x�&����}�3�k��UwUu�W_}翚!Ċl&!���x�ל|{Z��f�[�+��~���5��vb�����c�J�,l�����/��sx��?c��i����2	?�����BFa���i�_6�1Bh;�`:-Rp2��+d-�	��������$F��C/!1ɈH�Zy�p�}�}��8)�AjӰ�9y�<J&�R)T��ت�
*n�i.}�[�L�`���4ʉ�8�ߨ�$�|Z���MU�А�VҊ�oh��I�-MeL�� KxװE�j��s�{k=��v�2�T�+$���q�����Q�=ّ��Y�NNγܦ�Ũ$���hbG\���{[h0"r�����zO��q�L�˒�v^oj�;�P��ƶ������b!>��1>�j:7o

:�aw�jcp80l}*���^0�C~2θ�>�u�wPzk�mj������'��K9#��IA.F޷�Xm��wʞЃ�{�e��G����@���W��ut#J5RU#Uܑ�^k���G;v�M� <���ķ-͓.<�f��N_�Ⓣ�p�۪�Ƴ�o8�����磣��kn���Sl݆���udAA��YP
�b��S�/57-���+V/]1���k�i�)�ߝ���EI{(wg�٨@akd9*Q᭵W�+4(k5JB�w..�l��6��Q\ho8�8����0�xж��ZFL�7��}�I8��	]�n���u�����'3�2�R')Y��Qة�-2QʀI^�CiѨ�M�
�5���Lj~Ų洠��F񓢈(�b4Zw�8���C���E������i��]��ĵ���dԅ~�~��梲S)��C_�~��a�,��!w�#w.�m�U#S#�zdP�ՙ% C��TLTڹ0Kԩ��2�=7Ma]��ь,ز�S�~����~>�i��ق�����h�n�Y�N��zX���XJrB��xh�c�"f̈́䔧���lws��ͥ���U��?G�ck�,�X
S���T-�V
T���`�6\<W^a����ظ�#���/��k�� )m8��@n���d�X�����aC�7Z��Ym��qf����W�#�W&�0ջa���Yg�L�{A碥���t�#��؛8���
E�F�mQ�dU̧��jXC�j��X~9�~ozqVN�ee�6|�ں
6=6a![���ԭ���K�C��Ͽ�R�9�V�_�|��+���4\s��OPN�`�#L�z\C)���u������)>>�i��NN��s�88O�$�^܍�cC���r�(�(K�Q��tӖ����	�.]z�	 �����Z�e�W��a�Υ�a��N�SF15�2�V�d��ܣ�٩-�Ţ��0���GA�ۼ=6N3�'Δ�.X�����jiJL�9
���lmz���ȇg-�N3�9M�1��7f��$�U){#0`�tT��8Ϙ�7/�5q9���q����O̚��]���qL��9�㭣zo�J5U��)�d��P�T����"�и��W-M�}z���Vg@Q�%Ktb�9������x{��~	y�T�GH�ϻZa�$9�8@��Qrl���
����V�0xO��9���X�s��BІ��n#w���[�V����A����
i�֥`	���[f	�t^zx�u��wnښ�5�����^Z_i�c��#2:*�v����	�����pՆ��9dz�fm��ܚ�z3!�J\�Y�?���e3j�f�4�d�+���e��҄
H��˻*+ hL����*��hwwL3�@�����L�'�
�1��9GY�ܡ���d֭�S3���a'iY�P�
�600y�B'���y���NS�"'�ض�M��N��B��"Yl���X~����U���W���C�k2�&� Ό�I�GL���͙ǜ��q�	�a�a�O�¢�]օ�������b�͹��t_�E�g�8ȟ�́���}�Y�\\n@f�v�e{ԙ�O.�Wi��p��g������Uv���N��)�lڬ��ӊw' �)b�#�<�̴�{�c�%��$��I��O��ff��e��]4XwUU_-+,�M-?^a�;_Z�rDĶ����Eb�6��h��yu�z��Xf؃1�)y��:��}�ul*�>�fM����U�%�jT��G�׸����b,?�C<D}M��n�[/.��#k��9s����#����i��.������.У��i-�ю�g�E>%�(	
�B-[�>;Yx��S�,��寻ךm|%r�ň�Ч�-��!�C��8���ìAA%M�QF���@E�xO\O?�J�7�D'��E�4_����=�������C�1�2�c�d1�Q��s���l��o����]��<��s?�Ёi��3���0��>+��d�iX��&<�ۖ�73q[[-��Q�o!m�ų���.r^����p3p������6H�R�F�S��G��
_�2���<W�����x��9�$�ȍk�]]��-�%ج
�C-R�n���tn�ɍV�F��t��Px����6��,����`�2�p�FEϋ��˟���"j �4;�aql��	�2�(�(,z�q�ZS�X�}xX���j�i�`=J_����>���K�i���q3�)�-X��c����A��_Q����Z��(�CC������;�S�54�<df�|&3���lc�ڵ��h\<$�u��}��K��}�mijj�9wx�.h�-P��3�0��E}�8G�:\��o�8u���E�����z�f(��o:bzˌ�~G)/�a���R�Sqܻ��Ƌt�1��o��<�*��M1�q���fRr2{�����y~�P'.z�ϴx6�'���,Z�$!)ͧ 9��2JJ삃���^M�������XBN�'t|+�o�b�A�0���4�p���cR�A<V�:�`-jx�Cd�8D:�Yp�9�InK��n�([��B|����1q�T�ZO���[m5�A!V�>��$"�6gg�����/c�ڨ�oX�?O���Wp2'���H�\5�?/g��1�QWwQXNd��K��KY�+���xl�ӗ�#=�ߕJ���>x��8�`����׃1���0�)��:q�'�%�Z:h�e��Ɩ�
�
��VVnqmMN���	e���w��/��������J��o�S��z �QtE�z�8iԶ��muf|'�N{����\_�9��La��4M]e��/
����率��rܝ��>�sǒ����޽���c����9v��CC�m��Gb'�08�!䋝�h6}g���\��Jf���}�@��?�_�?�������_�Zގ�:�9�H�P�����[j4�Ò/�$��c�y�X'#�)�F�kjb3ٹ��DO�YF���O���Q^��������Ȉ�u��Fؼ�w����T.Z�i�7m鍈ظ����{�9O(��ϕ�\H]�b����1��K� $�vS`��#Ѥ���O�㮝*���Ĝ^1�z�q=���̻��l�������q{��Q�1C&H�x�!Z�Us��W�S(��?�{��לʐH*��Q�dk�c=kf(�4�s3�H)+��3Xu���%eF���f��c��Ⱦ�[�])_y��dՋ��J'쀜\�Yj���=�����y�.��}$��&�g�6`T��t"��Xt7z̓쐅�n9LWZ�����d��_ZQ�M�߻�q3,\P�Z�T���WQ������V[��!p�� pQP�U�8�39����"<y�+OY�9'1!�l^�G�EGBdԆ����*�H@PIa@PP@aAH0ʴO>cp
v^�ه�ٖ٘��y����1����q���g\�c�E������b��N<y݇�����w;/P����Ass��ukq]�$Z	;��1G�Ke��g�/Nʒ�BcrZ<��M[l�/����U�{�"p�Y����8�iI���`
N6T߬��ۮ}�m��#b��Mf*��M|)�`eR[[rj�kd�o�"�;i%\��E�OP0���qAk��K���w+��/�<(]�&�o]	�bL����!~��3mu���S�Vm�7�2�!A�A���U<��<Ѕ:�q�4�(ftt�8	��N��ۄ�Z�ƌ֔�N����27��ٙ7���r����U��ݦo���'c�Y���@�8��P���
�vS���S�;�^{��b,G�GڶQM�����ӿ��yZ'��T'��ɺ���yߡ0�҄�V�V��/��L�o~�0���o�<:�g�����1CvW�<S�����D���8�@vW���+�k!k����E���"�H��r�&-�?I�!F4��W�|���З�#���A'�]�;��I�PB�����mw�RΑfz�Xs7�
�b��d.�S���K8E���8"�d,:�F��82�{���-��]r���q�I�l�O6G���Γ�cğ�#[�%���'�៤����HV�$�;Dx8I�#���R��K?D����p�d��$t'i��FΣ<<I�MN�AAP��^��N�s�����q�sI\��]��">����w�0[J�V�S8'|&ܗM�͓m��U���c��<I^'?.�D����*ʪ����Vw���������L�4&{L���1O�y_�RY�HZ���ˆ\|�?��Z�L���:E$����a�ۖ:�����@l�QK]F��BK}<�����G/ �$
���wY�G=c�s����c���.Wr�R�P[��I<��
���#�$�d�,RJ�Q���og�_
؟G2�F
H:����ؓ��؇�J�V�3�V9>��H`(\W���U������b0d�e8���v^����>�8�f�d�g��	���j,�XH�a�����\&)�%Ӑ8���,�K+�?���D��d׀�`[���"�{�a�܉�%نg��Z��Y�{�n�EZ��!KL�(Q_��9�B�g��B,�5[���_�t�)��V�=�(��'�n��H����wΜ����i�i�Y�
řs��3
J2J�D�E��y�Ʊ��
*�
endstream
endobj
58 0 obj
5241
endobj
56 0 obj
<< /Type /Font
/Subtype /CIDFontType2
/BaseFont /Roboto-Regular
/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>
/FontDescriptor 54 0 R
/CIDToGIDMap /Identity
/W [0 [440 241 566 547 646 547 557 526 246 534 540 559 336 557 557 261 643 512 676 592 546 519 869 324 481 241 557 344 557 626 707 195 557 557 270 745 469 564 611 548 682 866 647 707 651 589 880 339 345 492 240 503 557 562 448 210 564 557 557 557 557 618 274 409 631 317 237 ]
]
>>
endobj
57 0 obj
<< /Length 826 >>
stream
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def
/CMapName /Adobe-Identity-UCS def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
2 beginbfrange
<0000> <0000> <0000>
<0001> <0042> [<0069> <006F> <006E> <0043> <0075> <0062> <0065> <0020> <004C> <0061> <0064> <0072> <0031> <0035> <002E> <0055> <0073> <0047> <0054> <0068> <0063> <006D> <0074> <0076> <006C> <0070> <0066> <0067> <0050> <0048> <002C> <0032> <0034> <0049> <0077> <0079> <0045> <0052> <0046> <004F> <004D> <0041> <004E> <0044> <0053> <0057> <0028> <0029> <0078> <003A> <006B> <0033> <002B> <005F> <003B> <0071> <0037> <0038> <0030> <0036> <0042> <002D> <002F> <0056> <0022> <006A> ]
endbfrange
endcmap
CMapName currentdict /CMap defineresource pop
end
end

endstream
endobj
6 0 obj
<< /Type /Font
/Subtype /Type0
/BaseFont /Roboto-Regular
/Encoding /Identity-H
/DescendantFonts [56 0 R]
/ToUnicode 57 0 R>>
endobj
59 0 obj
<< /Type /FontDescriptor
/FontName /QHCAAA+Consolas
/Flags 4 
/FontBBox [-432.128906 -302.246093 677.246093 1011.23046 ]
/ItalicAngle 0 
/Ascent 742.675781 
/Descent -257.324218 
/CapHeight 742.675781 
/StemV 70.3125000 
/FontFile2 60 0 R
>>
endobj
60 0 obj
<<
/Length1 11900 
/Length 63 0 R
/Filter /FlateDecode
>>
stream
x��yytǙgW7�� �H$��M���/�%ID @�hZ�%��x��-;vd[����$���x2Il����;M6;���y��y����Yٱw=~�l�W�
�e�X�+tuu�W���jR����8�P�������`����%Vf�Ux�o������c�hde��e��X�<�L�=<�����}���u<�<�6�����ߚ(ʌ�/.��[�ڨ'(ʢ�g6^��ێ��\MQ�a�͢�(1E��(
�;�kj��

i����E-�J�7��՛@��c�R]TwS���'hZ:E�)�o���bލ2S"j��D��̀�
8EJ�7;��J��:\��`��_G0P���K���f�$��Pe�_���!frMD��*U��@p@���h����.�;�c{��~Ra�K
d�R���D)SH*�*d����5��n��g���x�mv����5FCqQ�`�r����`tnZZ�T�*�R��^�����A6{��*'CN�r��`�>��{�#�3j7��2�L&/S�f���T��j��L=���S�ĔL�R���Xƿ,3�-f�N���(�c��"�Q6����*�uא����w���4j~<��+���wN�[L�M���X���}�d:1>��b���TZgecS��PsyE�R�r9C�};�B�e��[
��8]�a��
<��90��r"�����L
���Zde���[�o��k���}��:u4�&��Rt��k�e�R��ܳR����rW�<32|y�'X+˖;]����
�r��P���ڽ���!T(��ܭk�x��n~¼���sb�
`}h�?�(��üпep���@�}�����������7���V�3�N������4*�щ��	��W:�Ln�hW��~�ď�M!�,e1���NY(��pI6,R��I�y󌤦ʍ�yvx�|��Zv����ݡ�J�ԾW���x��Z���~|�������LTӆo`i]���.~!U�9s�,�o�޽���s�`���XOW�W�}�c���^�pT�ܾ@������Se������� n�x�;�j�[�*�]�������g6�r�F���3hd
�V�x�
b�΂f>O��:���
~B���yv�I�(�IN�cD����oyk�g\Nvpr���[������D|d�)d1��⨫�i�Ֆ;�Z��5ώ�AbF")�>��:��g��Ӄ[����clr����]��ju.w�	�3����_�V��h*l5�kg�:MqQQ�Fge��U�+*F��H��ZQkQ�����q��Vi���7�xt��壍�G�P�:���1?'�F�<�`����(�
�+ǝ��q����o�t+����;���yi|dϤB����~���@����;=�Z�����P���j�vn�xh���.g0������PR�˟S(d�B�!�����1��])*�����`Vi
T�؅�I���9�n�(U>恟���p�Q=�*�#�b�Q��9�/�ռxF24:>3������!�3�u�r��;w���b��^g9�H���G~z�>PQ�ӵ��/�xr_k��*�0[�
d?)hؐ�2�+RG�-(v�y�����3#���*�pEy������\�Ku~������(6Z<�mkG�.�͊f��p
��,���x�}k�7�܀=�T0/�hw(y@�k���PwV�������.?+���X��5L|5���Ţ"nJ����y����~:���k�6X��6�W)��\i���z}�~�}d��~���֦{����%�4>�Z�8R�p?{��$�L��bdU6���|fh�7/{�G�=�.W��eI���$�'��OdZ��3��$7w��Cj)������5�Ȇ��6���F���]��Lks�ɿ����ll��W[W��=911��BPf xO_s�ۣ׫U���ᕖ��;�5�xkL�B.���{&;�+���˂��\�+)�R"��Qc�F�F��hL*��Q�u{�몽����Y��P&/Ui�&�V�Ҫ4P�X�+�5���������OT<�"�V�Y�.V(����ʚ,j�m�B���ػ�c���Xs��-���%��������Kp��r��q��Ľ��V:tZTTR�**)���A��6�����֎�S˨�.r����t���
:�����j�]Q��ZSӾg�Ζ�r�x�"j�j��hJ���W@i�q�/p��k/����p���i�$wg��w�4�ɡ3�v�Gw�T榛�~��ysm�E����]���ij@5S�O�����ןC���;�7�����o���\'�큹e�8�~�5�G��[�,��u��h����Ք�d���х�7��O�(�=��"���)��]�ʆM�_���Fy�-�^j*�L�K
eE��R�J�5؞��EQiY��t�����Qo4����G�Ӛ
v��u��>���N	
����Z;B���aW�H�4��lY;�\�Y��e�C�@
�T��e1�Kp�9�/�&8/9�z�ZL0�r�|N��rhR�ĥ���/tZ��dc]�/��{����2UyECco��t�D]����Xg��Y�Q��~֊�Ck��McuMg���`��g���}�aZ2l�v�Y͞*����5��1xȫV�E�N�$T�	�6�h�p�
�!����`&X�=�����:�\NU)Bf���������3[����2}�	(��.��|��OZ�u���v��jF3�1C��"\��^��(�x2\���jƶd�������:�ƈDi���hktVZ�0�hc��M�`����\e	�\��ru�3���'8���4�%#��
�k�`�g�҉��v��)J3A7:
΂$d���^'2}-r��p�?�^G�}k��wf��K��B�S�cHI���M��Խ�zh3P�]�U'�t�[Q���v��5(WF��)�Ѩ$<�ug�}LƤ,1[�C㝡&���@Ն
��;jk�d��uLM�l<Ԥ7�45`T��
��u���D�I,�`���
G�%֘��'w��)�����q7���H$���bv���X+U�-.wm��6Y�J�i���qgW��Vo,.R��Zi0"SZf����}�m5�F�Z��c�`S(�(S�4z��+�^�5�f����Z0n�W��6{I�Q����Huj�F��i���
6-V��R�����b���#��k	�x�G?"u

 ��\�e�|��ܴq�=���3�\�LJ����v�{���xg�������B�A�W/}㟿8�}#uS�\�M5C����`d6e9��ʅ�NU�q6 ��ñ�R5�8�SƆ`CMS������'�`��r������G�5vn�:����YWoc���ͪ�6�"��@@�PG{Q���t5���DZ({�H.s�F+[�in�ꮩ1�D}$Q�J.�JD�
���q55�h�h���6�S
_��-�'Z^5���&*,f�s�4v��1'�ߥ� =�^G��;��z@�@TK�ԣ�Co�?���5Y�k���Alt�
���P��߼��͢�����:��l�{X!7�+��G�z�>���W?�W�f����6�5��9R�rt�X�w獡S�sTB��ǵ��:0
�������
F�NKꚚ�C�Cm�͵6�w�M���zg��\T��B�7:+��>X�hߧV�ڵk��'�M>���BB^懰+�4��x�v���o�ݻ�
�\VTl4y�[G{��n�9x|bp~�U���!}�ڵ��
�
�j8��U�ʖ��ZBc]����m]�ݽ}#��J��Q�QF�.ɈAP����}y�a��ƻ\�kFo";�.�?̈́W��_Y�~?q�CQ1He�s�P�wɝ�9!A�u�pb�G��M�b��깕/��ї�#���:���XG��B��v�N[n�V5�6�\.�f�d�]����U�@_�	�ҏ<u���^=B��=#Ǐ]~��N�]�=ǎ���ag�X�[
ٹ�n��9p\�ZY3z:�q�Ve)��w���vl��Zf�%U�D
!�ڲp�Cͽ}û"F�ƀ1�頻he��_s���
��_�D:�-Vi�1�Ev[}� .�Μ��{n�aO9�/��0����>�>��Nr��&�6�:Ɲ@��O�Пq�v	���W�ז8�~���7��&+Z��!��y,�8�
�ܕW��o+�{�vÁ��3��T��x�j�@��ׇ�;۷�����P(��궎�'��R��%]W���-��.g��嬘�5l0��[#�z��ҨG��o�j�����\")+-��ڂ�*����Q�,���=�q���\^P {<mm��ݶ��@�wV���K��S�E�o�Kk���_Ҷ��tPlz�;���I��p��
%�"H�T���=��x
z�"S�$kܴش����K��k��%1�y{uwˑ��t�[�ET��<[������juW
���=�������J�\����Ѷ�J�J���\Ύ��=m�J��~��N��r�ښ�G��|���nO��vt�ȑ�dNo��U��S旦ý[�pb��O�!����ƩGT��!w�yU��GE�����k��&������b�-T��P���SO��N�S7���N�\�{�C�Tn>Ư+c�����~���'�tW�؞H|���;��ػ붆�ɂ��ߌ��>X׍��m�w���~���|����g��
w�W�Y��d������������������� �z ~�q��"**������‘/s�G#���S�!���qxW���u�́���Cm�j�^lz�,������
hU���}���~��2��"mX?�0�[��]�p�e��;���ze��o���sc�ûv�j�t�8�ؤ�������]{�����F4E�H*"\<`1�h�{���+��O��yY�߁#�'���~��)ķط�ׅ%m����v��ȳ�K��F��5���D�Ǐ�YB)�/c��^��s��
wܖD�����]�F	�;���`�3p�m�p�@�愶�%h)a-�B�П��S	��C��	h^��Vh[x~��К��o�
<bd�����D����>A���oL#s��>�D�!J�>��.�'��şK�%A�i���NH�)}��t�
�>Q�O�JY�짲��Z�G~^���mE�bX1���⇊�ElQ+�n�z��۟%��멜=�B���B���ӧB_D ��S
�.�%0>%�TzW�PzP�R��q�/��'�&��W���WPy��/Bۋ� �ƒK�	�F	W���R�i�Oì
}������Q��@�)=r}	�	})u�	��C��~!���B_&�M��rj\���WP�J�ѧ�cB��J��Q߃J�j�:*�T��Pi*Ee��RY�^|��a$�$ԉ,�M%�b��06G�ûy��=��oW��c�uvG<�NeR�Y�7�^L���x*�c�	vw|n>�aw�2���X��m0	�6�d&�C�0�~�����R"�۹o����:-�ͻl7N����PJ�@YxKg@6��Ja�+h��0�,Qk�X ��1�<~3�w6�y^��fG��a�)NL��`��g�0�M����p���e��<�̥SK�x8�ZX'㱌�N��c�@�Ѵ�X�PUDh7�������/E��7���T*{7]�A>�P,���g�x���,�s��������~�҄�y�
4��^J�u��ό���%s"D�,Y���`�B��=��]�(�������2�[�'4Ä���=�%c��>9/��b��8���𔀧Cdk,JV����V|�f5�7ssz��ě�9�7)b�X��X�0�:��bLk(#�a��(C�����=*�'ފmrX�����eBiC�Q��E�+dw�;<�_\+C�()�k(����ܕ�3A,�[=}��2M�� c1�>���#���uZ�ܻ{B�;���M_H��$��9�2y�_ ��z��
҄A�	���狷-�	<�1�՘�9�x,ː��9��ab�$p��!F}I�)mX{KB8���;-ț\K�x��%�J+ٛ��y�[5�!���Uy��*�*)�Fn~�{zR���B�`��s��s4#؟�WΧp��lI�ov=��=8A�&�s���2#D���躟�OY�_�̟!=LPae]�9��3dn>�-�q{�/Ģ�l*�.ebl<�.�Ss���B<9�ƒ���Tr!��\NF�d�4��#K�l8�e�e�A6YH%S��p��Kf1��b,��C�&I=2N�#YȒ�|�p���Lx!�.ǣ�j6?cS�(�]Y�-��xf5�>�y�ga��T*
dR�H��3R�p�07���'c��t:�YL%��C;
��@0�$؉x2�Z��<F��Dx�
'�ex�ē��%���<V�.���f"��M��Tzv���˂�$I�q<F7)!��ԛZJ�ci�	V9�,C�_H�� �B?�I����i!��X2
��NPVd"�X���Xr4�����%��I,A�n���4�Ľ��B,NdZ�|>�f��l�
D�?�������$dk0Of>��cB3 ?�5����[�,6��D*�����e����6�
g�1vf�=N�`�lО	�yG[��%�<��E(�j�Z&����[����B���E���8�n�/5��.���.//�r���P���q~�6�%��C��,A�4A���9�x�lx&?��R�^��|�]ɫS��8����G��Wr���@.�1(���P,���pQRje׫�e�W��;���o ���ǕE����r<-���ȸYn���OU��M2�]�?�U�6�~mP�Ȅ|���#�T��r�f��3
���%K�˕˜>/+_�$I~�QIy=�o�i�ߍ�k{�P
�H=|*Erp�[�����_W�g16T���'8��c�l{7���]�dq�X��G�����ш�:�BL/��0!� ����ީ$�^U���������w�L'`	�M�#<P����i��V�]��|I�aWH�D*��)��9ň�2^j)���"vg�3K,n�XmU-1.��%R���/BY��0`���lx)���3���;�	
endstream
endobj
63 0 obj
7274
endobj
61 0 obj
<< /Type /Font
/Subtype /CIDFontType2
/BaseFont /Consolas
/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>
/FontDescriptor 59 0 R
/CIDToGIDMap /Identity
/DW 545 >>
endobj
62 0 obj
<< /Length 742 >>
stream
/CIDInit /ProcSet findresource begin
12 dict begin
begincmap
/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def
/CMapName /Adobe-Identity-UCS def
/CMapType 2 def
1 begincodespacerange
<0000> <FFFF>
endcodespacerange
2 beginbfrange
<0000> <0000> <0000>
<0001> <0036> [<0069> <006F> <006E> <0063> <0075> <0062> <0065> <002E> <006C> <0061> <0064> <0072> <005F> <0070> <0074> <0068> <0073> <003A> <003B> <002B> <002D> <002F> <0076> <0077> <006D> <0052> <0053> <0020> <003D> <0022> <0066> <0067> <006B> <0031> <0024> <007B> <007D> <0032> <0034> <0030> <0079> <0078> <0037> <0033> <0045> <004E> <004F> <0054> <0049> <0043> <007C> <0044> <0050> <0041> ]
endbfrange
endcmap
CMapName currentdict /CMap defineresource pop
end
end

endstream
endobj
7 0 obj
<< /Type /Font
/Subtype /Type0
/BaseFont /Consolas
/Encoding /Identity-H
/DescendantFonts [61 0 R]
/ToUnicode 62 0 R>>
endobj
2 0 obj
<<
/Type /Pages
/Kids 
[
5 0 R
19 0 R
28 0 R
34 0 R
]
/Count 4
/ProcSet [/PDF /Text /ImageB /ImageC]
>>
endobj
xref
0 64
0000000000 65535 f 
0000000009 00000 n 
0000038393 00000 n 
0000000187 00000 n 
0000000282 00000 n 
0000000748 00000 n 
0000029475 00000 n 
0000038259 00000 n 
0000000319 00000 n 
0000000362 00000 n 
0000000405 00000 n 
0000000449 00000 n 
0000000493 00000 n 
0000000530 00000 n 
0000000566 00000 n 
0000001072 00000 n 
0000007393 00000 n 
0000000869 00000 n 
0000001045 00000 n 
0000007714 00000 n 
0000007414 00000 n 
0000007451 00000 n 
0000007495 00000 n 
0000007539 00000 n 
0000008039 00000 n 
0000012667 00000 n 
0000007836 00000 n 
0000008012 00000 n 
0000012724 00000 n 
0000012688 00000 n 
0000013042 00000 n 
0000017756 00000 n 
0000012846 00000 n 
0000013022 00000 n 
0000020135 00000 n 
0000017777 00000 n 
0000017821 00000 n 
0000019968 00000 n 
0000019794 00000 n 
0000018072 00000 n 
0000018226 00000 n 
0000018365 00000 n 
0000018761 00000 n 
0000019633 00000 n 
0000018558 00000 n 
0000019037 00000 n 
0000019165 00000 n 
0000019328 00000 n 
0000019497 00000 n 
0000020031 00000 n 
0000020453 00000 n 
0000022477 00000 n 
0000020257 00000 n 
0000020433 00000 n 
0000022498 00000 n 
0000022762 00000 n 
0000028115 00000 n 
0000028597 00000 n 
0000028094 00000 n 
0000029615 00000 n 
0000029873 00000 n 
0000037260 00000 n 
0000037465 00000 n 
0000037239 00000 n 
trailer
<<
/Size 64
/Info 1 0 R
/Root 49 0 R
>>
startxref
38512
%%EOF
PK�*�\&�**$alt-php84-ioncube-loader/LICENSE.txtnu�[���LICENCE AGREEMENT FOR THE IONCUBE PHP LOADER, PROVIDED TO ENABLE THE USE
OF IONCUBE ENCODED FILES AND AS PART OF THE IONCUBE24 SERVICE (ioncube24.com)

YOU SHOULD CAREFULLY READ THE FOLLOWING TERMS AND CONDITIONS BEFORE USING THE
LOADER SOFTWARE. THE INSTALLATION AND/OR USE OR COPYING OF THE IONCUBE PHP
LOADER SOFTWARE INDICATES YOUR ACCEPTANCE OF THIS LICENCE AGREEMENT.  IF YOU
DO NOT ACCEPT THE TERMS OF THIS LICENCE AGREEMENT, DO NOT INSTALL, COPY
AND/OR USE THE LOADER SOFTWARE.

DEFINITIONS

The following definitions shall apply in this document:

LOADER shall mean the ionCube PHP Loader software package or collection 
of Loaders, including any modifications or upgrades to the software, used for
executing PHP scripts previously encoded with the ionCube PHP Encoder
software to render them non-humanly readable, and any associated
documentation or electronic or online materials relating to the software.

ENCODER shall mean any ionCube PHP Encoder software or service used for the
purpose of producing non-humanly readable encoded files from PHP scripts.

ENCODED FILE shall mean a non-humanly readable file produced by the 
Encoder and being derived from humanly readable PHP script source.

PROVIDER shall mean ionCube Ltd.

USER/YOU shall mean any entity who has downloaded or obtained through any
other means a version of the Loader software.


1 LICENSE ENTITLEMENT 

1.1 The Loader is provided without charge.  Title to the Loader does not pass
to the user in any circumstances.  The Loader is supplied as object code.

1.2 The provider grants a personal, non-transferable, non-exclusive licence to
use the Loader in accordance with the terms and conditions of this Licence
Agreement.

1.3 The installation or downloading and use of the Loader entitles the user
to install and use the Loader for its own internal lawful purposes.


2 DISTRIBUTION 

2.1 The Loader may be freely distributed to third parties alone or as 
part of a distribution containing other items provided that this license
is also included. 

2.2 The Loader may under no circumstances be branded as another product, 
whether distributed or not. 

2.3 Distribution as part of a commercial product is permitted provided such
distribution is in accordance with clauses 2.1 and 2.2 with respect to the 
Loader.


3 ANALYSIS / REVERSE ENGINEERING / MODIFICATION 

Except insofar as the user is permitted to do so in accordance with applicable
law:

3.1 Any analysis of the Loader and embedded data by any means and by
any entity whether human or otherwise and including but without limitation to
discover details of internal operation, to reverse engineer, to de-compile
object code, or to modify for the purposes of modifying behaviour is
forbidden.

3.2 Any analysis of encoded files by any means and by any entity whether human
or otherwise and including but without limitation to discover details of file
format or for the purposes of modifying behaviour or scope of their usage is
forbidden.


4 WARRANTY

THE LOADER SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED 
WARRANTIES INCLUDING BUT WITHOUT LIMITATION THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR ANY PARTICULAR PURPOSE ARE
DISCLAIMED. THE PROVIDER DOES NOT WARRANT THAT THE LOADER IS UNINTERRUPTED
OR ERROR FREE, NOR THAT THE OPERATION OF THE LOADER WILL FUNCTION IN
CONJUNCTION WITH ANY OTHER PRODUCT.  


5 LIMITATION OF LIABILITY 

5.1 IN NO EVENT WILL THE PROVIDER OF THE LOADER BE LIABLE TO THE USER OR ANY
PARTY FOR ANY DIRECT, INDIRECT, PUNITIVE, SPECIAL, INCIDENTAL OR OTHER
CONSEQUENTIAL DAMAGES ARISING DIRECTLY OR INDIRECTLY FROM THIS LICENCE
AGREEMENT OR ANY USE OF THE LOADER OR ENCODED FILES, EVEN IF THE PROVIDER IS
EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

5.2 THE LOADER IS PROVIDED ON AN "AS IS" BASIS.  THE PROVIDER EXCLUDES ALL
WARRANTIES, CONDITIONS, TERMS, UNDERTAKINGS AND REPRESENTATIONS (EXCLUDING
FRAUDULENT MISREPRESENTATION) OF ANY KIND, EXPRESS OR IMPLIED, STATUTORY OR
OTHERWISE IN CONNECTION WITH THE LOADER TO THE FULLEST EXTENT PERMITTED BY
LAW.

5.3 DOWNLOADING THE LOADER IS AT YOUR OWN RISK AND THE PROVIDER DOES NOT
ACCEPT LIABILITY FOR ANY DIRECT OR INDIRECT LOSS OR DAMAGE HOWSOEVER CAUSED AS
A RESULT OF ANY COMPUTER VIRUSES, BUGS, TROJAN HORSES, WORMS, SOFTWARE BOMBS
OR OTHER SIMILAR PROGRAMS ARISING FROM YOUR USE OF THE LOADER.  WHILST THE
PROVIDER WILL DO ITS BEST TO ENSURE THAT THE LOADER IS FREE FROM SUCH
DESTRUCTIVE PROGRAMS, IT IS YOUR RESPONSIBILITY TO TAKE REASONABLE PRECAUTIONS
TO SCAN FOR SUCH DESTRUCTIVE PROGRAMS DOWNLOADED FROM THE INTERNET.

5.4 THE PROVIDER'S MAXIMUM LIABILITY FOR ANY LOSS OR DAMAGE ARISING FROM THIS
LICENCE AGREEMENT SHALL IN ANY EVENT BE LIMITED IN THE SOLE DISCRETION OF THE
PROVIDER TO THE REPLACEMENT OF THE LOADER PRODUCT.

5.5 DUE TO THE NATURE OF THE INTERNET, THE PROVIDER CANNOT GUARANTEE THAT ANY
E-MAILS OR OTHER ELECTRONIC TRANSMISSIONS WILL BE SENT TO YOU OR RECEIVED BY
THE PROVIDER OR THAT THE CONTENT OF SUCH TRANSMISSIONS WILL BE SECURE DURING
TRANSMISSION.


6 BUG FIXING AND PRODUCT SUPPORT 

6.1 The provider will use reasonable endeavours to provide support to users.
The provider will at their discretion only provide support for the latest
release.

6.2 Support comprises of fault reporting via tickets and fault diagnosis,
recommendations on workarounds, and where reasonably possible a timely
resolution.

6.3 The user accepts that on occasion the ability of the provider to meet
anticipated or published support schedules may be impaired due to, but without
limitation, Internet service provider failures or software failures that
affect the ability to communicate for an indeterminate period.

6.4 The provider reserves the right to refuse to provide support at any time.

6.5 The provider wishes to maintain and offer a product of the highest
possible quality, and accordingly may from time to time and at its discretion
make product changes for the purpose of correcting behaviour in variance to
the published specification or the user's reasonable expectations. 

6.6 The provider reserves the right to charge for support where the user does
not have a valid support plan in place, or where the support offered exceeds
the scope of the active support plan.


7 PRODUCT UPGRADES

7.1 The provider may from time to time release product upgrades. These will
be provided free of charge and attempts made to provide a timely notification
to customers of the existence of any new release.


8 ERRORS AND OMISSIONS

Whilst reasonable endeavours are made to ensure the accuracy of documentation
concerning the details of the Loader, the user accepts the possibility of
inaccuracies in information presented in any format, including email
communications and online services. The provider shall under no circumstances
be liable for any events that arise as a result of unintentional inaccuracies
or omissions.


9 USER INDEMNITY

You agree to fully indemnify, defend and hold the provider harmless
immediately upon demand from and against all actions, liability, claims,
losses, damages, costs and expenses (including legal/attorney fees) incurred
by the provider arising directly or indirectly as a result of your breach of
this Licence Agreement.


10 INTELLECTUAL PROPERTY RIGHTS

10.1 The user acknowledges that the Loader and associated documentation and
materials contain proprietary information of the provider and are and shall
remain the exclusive property of the provider and/or its licensors and all
title, copyright, trade marks, trade names, patents and other intellectual
property rights therein of whatever nature shall remain the sole property of
the provider and/or its licensors.

10.2 No title to or rights of ownership, copyright or other intellectual
property in the Loader is transferred to the user (other than the licence
rights expressly granted in this Licence Agreement).


11 TERMINATION

11.1 The provider reserves the right to terminate this Licence Agreement
immediately by notice in writing against the user if the user is in breach of
any terms and conditions of this Licence Agreement.

11.2 Termination of this Licence Agreement for any reason shall be without
prejudice to any other rights or remedies of the provider which may have
arisen on or before the date of termination under this Licence Agreement or in
law.

11.3 The provisions of the following clauses shall survive any termination of
this agreement; clause 3, 5, 10 and 13.


12 GENERAL

12.1 The provider reserves the right to transfer or assign all or any of its
rights and duties and responsibilities set out in this Licence Agreement to
another party.

12.2 Headings have been included for convenience only and will not be used in
construing any provision of this Licence Agreement.

12.3 No delay or failure by the provider to exercise any powers, rights or
remedies under this Licence Agreement will operate as a waiver of them nor
will any single or partial exercise of any such powers, rights or remedies
include any other or further exercise of them.

12.4 If any part of this Licence Agreement is found by a court of competent
jurisdiction or other competent authority to be invalid, unlawful or
unenforceable then such part shall be severed from the remainder of this
Licence Agreement which will continue to be valid and enforceable to the
fullest extent permitted by applicable law.

12.5 This Licence Agreement including the documents or other sources referred
to herein supersede all prior representations, understandings and agreements
between the user and the provider relating to the Loader and sets forth the
entire agreement and understanding between the user and the provider relating
to the Loader.

12.6 Nothing in this Licence Agreement shall be deemed to constitute a
partnership between you and the provider nor constitute either party being an
agent of the other party.

12.7 This Agreement does not create any rights or benefits enforceable by any
person not a party to it (within the meaning of the U.K.Contracts (Rights of
Third Parties) Act 1999) except that a person who under clause 12.1 is a
permitted successor or assignee of the rights or benefits of the provider may
enforce such rights or benefits.


13 GOVERNING LAW AND JURISDICTION

This License Agreement and any issues relating thereto shall be construed and
interpreted in accordance with the laws of England and subject to the
exclusive jurisdiction of the English courts.

Copyright (c) 2002-2024 ionCube Ltd.          Last revised 23-April-2015
PK�*�\a����#alt-php84-ioncube-loader/README.txtnu�[���                            The ionCube Loader 
                            ------------------

This package contains:

* ionCube Loaders

* a Loader Wizard script to assist with Loader installation (loader-wizard.php)

* the License document for use of the Loader and encoded files (LICENSE.txt)

* User Guide describing options that can be configured through a php.ini file.  
  There are options that may improve performance, particularly with files on
  a network drive. Options for the ionCube24 intrusion protection and PHP error
  reporting service (ioncube24.com) are also described.


INSTALLATION
============

Quick Guide for experienced system admins
-----------------------------------------

The Loader is a PHP engine extension, so should be referenced with 
a zend_extension line in a php.ini file. It must be the first engine
extension to be installed. 

The Loader must be for the correct operating system, match the 
PHP version, and for whether PHP is built as thread-safe (TS) or not. 
All information required for installing is available on a phpinfo page. 

For example, if your web server is 64 bit Linux, thread safety is disabled,
PHP is version 8.1.8, the main php.ini file is /etc/php.ini and you
have unpacked Loaders to /usr/local/ioncube, you would:

1) edit /etc/php.ini
2) at the top of the php.ini file add

zend_extension = /usr/local/ioncube/ioncube_loader_lin_8.1.so

3) restart the PHP environment (i.e. Apache, php-fpm, etc.)

4) Check a phpinfo page and the Loader should show up in the Zend Engine box.


Assisted Installation with the Loader Wizard
--------------------------------------------

1. Upload the contents of this package to a directory/folder called ioncube
   within the top level of your web scripts area. This is sometimes called the
   "web root" or "document root". Common names for this location are "www",
   "public_html", and "htdocs", but it may be different on your server.

2. Launch the Loader Wizard script in your browser. For example:
     https://yourdomain/ioncube/loader-wizard.php

   If the wizard is not found, check carefully the location on your server
   where you uploaded the Loaders and the wizard script. 

3. Follow the steps given by the Loader Wizard. If you have full access to the 
   server then installation should be easy. If your hosting plan is more limited, 
   you may need to ask your hosting provider for assistance. 

4. The Loader Wizard can automatically create a ticket in our support system
   if installation is unsuccessful, and we are happy to assist in that case.

   YouTube with a search for "ioncube loader wizard" also gives some helpful 
   examples of installation.


WHERE TO INSTALL THE LOADERS
============================

The Loader Wizard should be used to guide the installation process but the
following are the standard locations for the Loader files. Loader file
packages can be obtained from https://www.ioncube.com/loaders.php

Please check that you have the correct package of Loaders for your system.

Installing to a remote SHARED server
------------------------------------

* Upload the Loader files to a directory/folder called ioncube within your
  main web scripts area.  (This will probably be where you placed the
  loader-wizard.php script.)


Installing to a remote UNIX/LINUX DEDICATED or VPS server
---------------------------------------------------------

* Upload the Loader files to the PHP extensions directory or, if that is
  not set, /usr/local/ioncube


** Installing to a remote WINDOWS DEDICATED or VPS server

* Upload the Loader files to the PHP extensions directory or, if that is
  not set, C:\windows\system32


64-BIT LOADERS FOR WINDOWS
--------------------------

64-bit Loaders for Windows are available for PHP 5.5 upwards.
The Loader Wizard will not give directions for installing 64-bit Loaders for
any earlier version of PHP 5.

Copyright (c) 2002-2026 ionCube Ltd.           Last revised March 2026
PK�*�\5k��#�#"pear/XML_Util/examples/example.phpnu�[���<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * Examples (file #1)
 *
 * several examples for the methods of XML_Util
 * 
 * PHP versions 4 and 5
 *
 * LICENSE:
 *
 * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *    * The name of the author may not be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @category   XML
 * @package    XML_Util
 * @subpackage Examples
 * @author     Stephan Schmidt <schst@php.net>
 * @copyright  2003-2008 Stephan Schmidt <schst@php.net>
 * @license    http://opensource.org/licenses/bsd-license New BSD License
 * @version    CVS: $Id$
 * @link       http://pear.php.net/package/XML_Util
 */

    /**
     * set error level
     */
    error_reporting(E_ALL);

    require_once 'XML/Util.php';
    
    /**
     * replacing XML entities
     */
    print 'replace XML entities:<br>';
    print XML_Util::replaceEntities('This string contains < & >.');
    print "\n<br><br>\n";

    /**
     * reversing XML entities
     */
    print 'replace XML entities:<br>';
    print XML_Util::reverseEntities('This string contains &lt; &amp; &gt;.');
    print "\n<br><br>\n";

    /**
     * building XML declaration
     */
    print 'building XML declaration:<br>';
    print htmlspecialchars(XML_Util::getXMLDeclaration());
    print "\n<br><br>\n";

    print 'building XML declaration with additional attributes:<br>';
    print htmlspecialchars(XML_Util::getXMLDeclaration('1.0', 'UTF-8', true));
    print "\n<br><br>\n";

    /**
     * building document type declaration
     */
    print 'building DocType declaration:<br>';
    print htmlspecialchars(XML_Util::getDocTypeDeclaration('package', 
        'http://pear.php.net/dtd/package-1.0'));
    print "\n<br><br>\n";

    print 'building DocType declaration with public ID (does not exist):<br>';
    print htmlspecialchars(XML_Util::getDocTypeDeclaration('package', 
        array('uri' => 'http://pear.php.net/dtd/package-1.0', 
            'id' => '-//PHP//PEAR/DTD PACKAGE 0.1')));
    print "\n<br><br>\n";

    print 'building DocType declaration with internal DTD:<br>';
    print '<pre>';
    print htmlspecialchars(XML_Util::getDocTypeDeclaration('package', 
        'http://pear.php.net/dtd/package-1.0', 
        '<!ELEMENT additionalInfo (#PCDATA)>'));
    print '</pre>';
    print "\n<br><br>\n";

    /**
     * creating an attribute string
     */
    $att = array(
        'foo'  => 'bar',
        'argh' => 'tomato'
    );

    print 'converting array to string:<br>';
    print XML_Util::attributesToString($att);
    print "\n<br><br>\n";


    /**
     * creating an attribute string with linebreaks
     */
    $att = array(
        'foo'  => 'bar',
        'argh' => 'tomato'
    );

    print 'converting array to string (including line breaks):<br>';
    print '<pre>';
    print XML_Util::attributesToString($att, true, true);
    print '</pre>';
    print "\n<br><br>\n";


    /**
     * splitting a qualified tag name
     */
    print 'splitting qualified tag name:<br>';
    print '<pre>';
    print_r(XML_Util::splitQualifiedName('xslt:stylesheet'));
    print '</pre>';
    print "\n<br>\n";


    /**
     * splitting a qualified tag name (no namespace)
     */
    print 'splitting qualified tag name (no namespace):<br>';
    print '<pre>';
    print_r(XML_Util::splitQualifiedName('foo'));
    print '</pre>';
    print "\n<br>\n";

    /**
     * splitting a qualified tag name (no namespace, but default namespace specified)
     */
    print 'splitting qualified tag name '
        . '(no namespace, but default namespace specified):<br>';
    print '<pre>';
    print_r(XML_Util::splitQualifiedName('foo', 'bar'));
    print '</pre>';
    print "\n<br>\n";

    /**
     * verifying XML names
     */
    print 'verifying \'My private tag\':<br>';
    print '<pre>';
    print_r(XML_Util::isValidname('My Private Tag'));
    print '</pre>';
    print "\n<br><br>\n";
    
    print 'verifying \'-MyTag\':<br>';
    print '<pre>';
    print_r(XML_Util::isValidname('-MyTag'));
    print '</pre>';
    print "\n<br><br>\n";

    /**
     * creating an XML tag
     */
    $tag = array(
        'namespace'  => 'foo',
        'localPart'  => 'bar',
        'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
        'content'    => 'I\'m inside the tag'
    );

    print 'creating a tag with namespace and local part:<br>';
    print htmlentities(XML_Util::createTagFromArray($tag));
    print "\n<br><br>\n";

    /**
     * creating an XML tag
     */
    $tag = array(
        'qname'        => 'foo:bar',
        'namespaceUri' => 'http://foo.com',
        'attributes'   => array('key' => 'value', 'argh' => 'fruit&vegetable'),
        'content'      => 'I\'m inside the tag'
    );

    print 'creating a tag with qualified name and namespaceUri:<br>';
    print htmlentities(XML_Util::createTagFromArray($tag));
    print "\n<br><br>\n";

    /**
     * creating an XML tag
     */
    $tag = array(
        'qname'        => 'bar',
        'namespaceUri' => 'http://foo.com',
        'attributes'   => array('key' => 'value', 'argh' => 'fruit&vegetable')
    );

    print 'creating an empty tag without namespace but namespace Uri:<br>';
    print htmlentities(XML_Util::createTagFromArray($tag));
    print "\n<br><br>\n";

    /**
     * creating an XML tag with more namespaces
     */
    $tag = array(
        'namespace'   => 'foo',
        'localPart'   => 'bar',
        'attributes'  => array('key' => 'value', 'argh' => 'fruit&vegetable'),
        'content'     => 'I\'m inside the tag',
        'namespaces'  => array(
            'bar'  => 'http://bar.com',
            'pear' => 'http://pear.php.net',
        )
    );

    print 'creating an XML tag with more namespaces:<br />';
    print htmlentities(XML_Util::createTagFromArray($tag));
    print "\n<br><br>\n";

    /**
     * creating an XML tag with a CData Section
     */
    $tag = array(
        'qname'      => 'foo',
        'attributes' => array('key' => 'value', 'argh' => 'fruit&vegetable'),
        'content'    => 'I\'m inside the tag'
    );

    print 'creating a tag with CData section:<br>';
    print htmlentities(XML_Util::createTagFromArray($tag, XML_UTIL_CDATA_SECTION));
    print "\n<br><br>\n";

    /**
     * creating an XML tag with a CData Section
     */
    $tag = array(
        'qname'      => 'foo',
        'attributes' => array('key' => 'value', 'argh' => 't�t�'),
        'content'    => 
            'Also XHTML-tags can be created '
            . 'and HTML entities can be replaced � � � � <>.'
    );

    print 'creating a tag with HTML entities:<br>';
    print htmlentities(XML_Util::createTagFromArray($tag, XML_UTIL_ENTITIES_HTML));
    print "\n<br><br>\n";

    /**
    * creating an XML tag with createTag
    */
    print 'creating a tag with createTag:<br>';
    print htmlentities(XML_Util::createTag('myNs:myTag', 
        array('foo' => 'bar'), 
        'This is inside the tag', 
        'http://www.w3c.org/myNs#'));
    print "\n<br><br>\n";

    
    /**
     * trying to create an XML tag with an array as content
     */
    $tag = array(
        'qname'   => 'bar',
        'content' => array('foo' => 'bar')
    );
    print 'trying to create an XML tag with an array as content:<br>';
    print '<pre>';
    print_r(XML_Util::createTagFromArray($tag));
    print '</pre>';
    print "\n<br><br>\n";
    
    /**
     * trying to create an XML tag without a name
     */
    $tag = array(
        'attributes' => array('foo' => 'bar'),
    );
    print 'trying to create an XML tag without a name:<br>';
    print '<pre>';
    print_r(XML_Util::createTagFromArray($tag));
    print '</pre>';
    print "\n<br><br>\n";
?>
PK�*�\�f��#pear/XML_Util/examples/example2.phpnu�[���<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * Examples (file #2)
 *
 * several examples for the methods of XML_Util
 * 
 * PHP versions 4 and 5
 *
 * LICENSE:
 *
 * Copyright (c) 2003-2008 Stephan Schmidt <schst@php.net>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *    * The name of the author may not be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @category   XML
 * @package    XML_Util
 * @subpackage Examples
 * @author     Stephan Schmidt <schst@php.net>
 * @copyright  2003-2008 Stephan Schmidt <schst@php.net>
 * @license    http://opensource.org/licenses/bsd-license New BSD License
 * @version    CVS: $Id$
 * @link       http://pear.php.net/package/XML_Util
 */

    /**
     * set error level
     */
    error_reporting(E_ALL);

    require_once 'XML/Util.php';

    /**
     * creating a start element
     */
    print 'creating a start element:<br>';
    print htmlentities(XML_Util::createStartElement('myNs:myTag', 
        array('foo' => 'bar'), 'http://www.w3c.org/myNs#'));
    print "\n<br><br>\n";


    /**
     * creating a start element
     */
    print 'creating a start element:<br>';
    print htmlentities(XML_Util::createStartElement('myTag', 
        array(), 'http://www.w3c.org/myNs#'));
    print "\n<br><br>\n";

    /**
     * creating a start element
     */
    print 'creating a start element:<br>';
    print '<pre>';
    print htmlentities(XML_Util::createStartElement('myTag', 
        array('foo' => 'bar', 'argh' => 'tomato'), 
        'http://www.w3c.org/myNs#', true));
    print '</pre>';
    print "\n<br><br>\n";


    /**
     * creating an end element
     */
    print 'creating an end element:<br>';
    print htmlentities(XML_Util::createEndElement('myNs:myTag'));
    print "\n<br><br>\n";

    /**
     * creating a CData section
     */
    print 'creating a CData section:<br>';
    print htmlentities(XML_Util::createCDataSection('I am content.'));
    print "\n<br><br>\n";

    /**
     * creating a comment
     */
    print 'creating a comment:<br>';
    print htmlentities(XML_Util::createComment('I am a comment.'));
    print "\n<br><br>\n";

    /**
     * creating an XML tag with multiline mode
     */
    $tag = array(
        'qname'        => 'foo:bar',
        'namespaceUri' => 'http://foo.com',
        'attributes'   => array('key' => 'value', 'argh' => 'fruit&vegetable'),
        'content'      => 'I\'m inside the tag & contain dangerous chars'
    );

    print 'creating a tag with qualified name and namespaceUri:<br>';
    print '<pre>';
    print htmlentities(XML_Util::createTagFromArray($tag, 
        XML_UTIL_REPLACE_ENTITIES, true));
    print '</pre>';
    print "\n<br><br>\n";

    /**
     * create an attribute string without replacing the entities
     */
    $atts = array('series' => 'Starsky &amp; Hutch', 'channel' => 'ABC');
    print 'creating a attribute string, '
        . 'entities in values already had been replaced:<br>';
    print htmlentities(XML_Util::attributesToString($atts, 
        true, false, false, false, XML_UTIL_ENTITIES_NONE));
    print "\n<br><br>\n";

    /**
     * using the array-syntax for attributesToString()
     */
    $atts = array('series' => 'Starsky &amp; Hutch', 'channel' => 'ABC');
    print 'using the array-syntax for attributesToString()<br>';
    print htmlentities(XML_Util::attributesToString($atts, 
        array('entities' => XML_UTIL_ENTITIES_NONE)));
    print "\n<br><br>\n";


?>
PK�*�\�u����pear/PEAR/LICENSEnu�[���Copyright (c) 1997-2009,
 Stig Bakken <ssb@php.net>,
 Gregory Beaver <cellog@php.net>,
 Helgi Þormar Þorbjörnsson <helgi@php.net>,
 Tomas V.V.Cox <cox@idecnet.com>,
 Martin Jansen <mj@php.net>.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
PK�*�\�E���pear/PEAR/README.rstnu�[���*************************
PEAR - The PEAR Installer
*************************
.. image:: https://travis-ci.org/pear/pear-core.svg?branch=stable
    :target: https://travis-ci.org/pear/pear-core

=========================================
What is the PEAR Installer? What is PEAR?
=========================================
PEAR is the PHP Extension and Application Repository, found at
http://pear.php.net.

The **PEAR Installer** is this software, which contains executable
files and PHP code that is used to **download and install** PEAR code
from pear.php.net.

PEAR contains useful **software libraries and applications** such as
MDB2 (database abstraction), HTML_QuickForm (HTML forms management),
PhpDocumentor (auto-documentation generator), DB_DataObject
(Data Access Abstraction), and many hundreds more.
Browse all available packages at http://pear.php.net, the list is
constantly growing and updating to reflect improvements in the PHP language.

.. warning::
  Do not run PEAR without installing it - if you downloaded this
  tarball manually, you MUST install it.  Read the instructions in INSTALL
  prior to use.


=============
Documentation
=============
Documentation for PEAR can be found at http://pear.php.net/manual/.
Installation documentation can be found in the INSTALL file included
in this tarball.


=====
Tests
=====
Run the tests without installation as follows::

  $ ./scripts/pear.sh run-tests -r tests

You should have the ``Text_Diff`` package installed to get nicer error output.

To run the tests with another PHP version, modify ``php_bin`` and set the
``PHP_PEAR_PHP_BIN`` environment variable::

  $ pear config-set php_bin /usr/local/bin/php7
  $ PHP_PEAR_PHP_BIN=/usr/local/bin/php7 ./scripts/pear.sh run-tests -r tests

Happy PHPing, we hope PEAR will be a great tool for your development work!


Test dependencies
=================
* ``zlib``


=========
Releasing
=========
Create a PEAR package, as well as phars for pear-less installation,
simply run ``build-release.sh``).

``go-pear.phar`` contains the PEAR installer installer that asks where to install it.
It is available from http://pear.php.net/go-pear.phar.

``install-pear-nozlib.phar`` installs PEAR automatically without asking anything.
It is shipped with PHP itself.
PK�*�\���xxpear/PEAR/INSTALLnu�[���PEAR - The PEAR Installer
=========================
Installing the PEAR Installer.

You should install PEAR on a local development machine first.  Installing
PEAR on a remote production machine should only be done after you are
familiar with PEAR and have tested code using PEAR on your development
machine.

There are two methods of installing PEAR
 - PEAR bundled in PHP
 - go-pear

We will first examine how to install PEAR that is bundled with PHP.

Microsoft Windows
=================
If you are running PHP 5.2.0 or newer, simply download and
run the windows installer (.msi) and PEAR can be automatically
installed.

Otherwise, for older PHP versions, download the .zip of windows,
there is a script included with your PHP distribution that is called
"go-pear".  You must open a command box in order to run it.  Click
"start" then click "Run..." and type "cmd.exe" to open a command box.
Use "cd" to change directory to the location of PHP where you unzipped it,
and run the go-pear command.

Unix
====
When compiling PHP from source, you simply need to include the
--with-pear directive on the "./configure" command.  This is "on"
by default in most PHP versions, but it doesn't hurt to list it
explicitly.  You should also consider enabling the zlib extension via
--enable-zlib, so that the PEAR installer will be able to handle gzipped
files (i.e. smaller package files for faster downloads).  Later, when you
run "make install" to install PHP itself, part of the process will be
prompts that ask you where you want PEAR to be installed.

go-pear
=======
For users who cannot perform the above steps, or who wish to obtain the
latest PEAR with a slightly higher risk of failure, use go-pear.  go-pear
is obtained by downloading http://pear.php.net/go-pear and saving it as go-pear.php.
After downloading, simply run "php go-pear.php" or open it in a web browser
(windows only) to download and install PEAR.

You can always ask general installation questions on pear-general@lists.php.net,
a public mailing list devoted to support for PEAR packages and installation-
related issues.

Happy PHPing, we hope PEAR will be a great tool for your development work!
PK�*�\DG�e��pear/Structures_Graph/LICENSEnu�[���		   GNU LESSER GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.


  This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.

  0. Additional Definitions.

  As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.

  "The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.

  An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.

  A "Combined Work" is a work produced by combining or linking an
Application with the Library.  The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".

  The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.

  The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.

  1. Exception to Section 3 of the GNU GPL.

  You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.

  2. Conveying Modified Versions.

  If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:

   a) under this License, provided that you make a good faith effort to
   ensure that, in the event an Application does not supply the
   function or data, the facility still operates, and performs
   whatever part of its purpose remains meaningful, or

   b) under the GNU GPL, with none of the additional permissions of
   this License applicable to that copy.

  3. Object Code Incorporating Material from Library Header Files.

  The object code form of an Application may incorporate material from
a header file that is part of the Library.  You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:

   a) Give prominent notice with each copy of the object code that the
   Library is used in it and that the Library and its use are
   covered by this License.

   b) Accompany the object code with a copy of the GNU GPL and this license
   document.

  4. Combined Works.

  You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:

   a) Give prominent notice with each copy of the Combined Work that
   the Library is used in it and that the Library and its use are
   covered by this License.

   b) Accompany the Combined Work with a copy of the GNU GPL and this license
   document.

   c) For a Combined Work that displays copyright notices during
   execution, include the copyright notice for the Library among
   these notices, as well as a reference directing the user to the
   copies of the GNU GPL and this license document.

   d) Do one of the following:

       0) Convey the Minimal Corresponding Source under the terms of this
       License, and the Corresponding Application Code in a form
       suitable for, and under terms that permit, the user to
       recombine or relink the Application with a modified version of
       the Linked Version to produce a modified Combined Work, in the
       manner specified by section 6 of the GNU GPL for conveying
       Corresponding Source.

       1) Use a suitable shared library mechanism for linking with the
       Library.  A suitable mechanism is one that (a) uses at run time
       a copy of the Library already present on the user's computer
       system, and (b) will operate properly with a modified version
       of the Library that is interface-compatible with the Linked
       Version.

   e) Provide Installation Information, but only if you would otherwise
   be required to provide such information under section 6 of the
   GNU GPL, and only to the extent that such information is
   necessary to install and execute a modified version of the
   Combined Work produced by recombining or relinking the
   Application with a modified version of the Linked Version. (If
   you use option 4d0, the Installation Information must accompany
   the Minimal Corresponding Source and Corresponding Application
   Code. If you use option 4d1, you must provide the Installation
   Information in the manner specified by section 6 of the GNU GPL
   for conveying Corresponding Source.)

  5. Combined Libraries.

  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 that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:

   a) Accompany the combined library with a copy of the same work based
   on the Library, uncombined with any other library facilities,
   conveyed under the terms of this License.

   b) Give prominent notice with the combined library that part of it
   is a work based on the Library, and explaining where to find the
   accompanying uncombined form of the same work.

  6. Revised Versions of the GNU Lesser General Public License.

  The Free Software Foundation may publish revised and/or new versions
of the GNU 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 as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.

  If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
PK�*�\x� ���Jpear/Structures_Graph/docs/tutorials/Structures_Graph/Structures_Graph.pkgnu�[���<refentry id="{@id package.database.structures_graph.tutorial}">
 <refnamediv>
  <refname><classname>Structures_Graph</classname> Tutorial</refname>
  <refpurpose>A first tour of graph datastructure manipulation</refpurpose>
 </refnamediv>
 <refsect1 id="{@id package.database.structures_graph.tutorial.intro}">
  <title>Introduction</title>
  <para>
  Structures_Graph is a package for creating and manipulating graph datastructures. A graph is a set of objects, called nodes, connected by arcs. When used as a datastructure, usually nodes contain data, and arcs represent relationships between nodes. When arcs have a direction, and can be travelled only one way, graphs are said to be directed. When arcs have no direction, and can always be travelled both ways, graphs are said to be non directed.
  </para>
  <para>
  Structures_Graph provides an object oriented API to create and directly query a graph, as well as a set of Manipulator classes to extract information from the graph.
  </para>
 </refsect1>
 <refsect1 id="{@id package.database.structures_graph.tutorial.creation}">
  <title>Creating a Graph</title>
  <para>
   Creating a graph is done using the simple constructor:
   <programlisting>
    <![CDATA[
require_once 'Structures/Graph.php';

$directedGraph =& new Structures_Graph(true);
$nonDirectedGraph =& new Structures_Graph(false);
    ]]>
   </programlisting>
   and passing the constructor a flag telling it whether the graph should be directed. A directed graph will always be directed during its lifetime. It's a permanent characteristic.
  </para>
  <para>
  To fill out the graph, we'll need to create some nodes, and then call Graph::addNode.
   <programlisting>
    <![CDATA[
require_once 'Structures/Graph/Node.php';

$nodeOne =& new Structures_Graph_Node();
$nodeTwo =& new Structures_Graph_Node();
$nodeThree =& new Structures_Graph_Node();

$directedGraph->addNode(&$nodeOne);
$directedGraph->addNode(&$nodeTwo);
$directedGraph->addNode(&$nodeThree);
    ]]>
   </programlisting>
   and then setup the arcs:
   <programlisting>
    <![CDATA[
$nodeOne->connectTo($nodeTwo);
$nodeOne->connectTo($nodeThree);
    ]]>
   </programlisting>
   Note that arcs can only be created after the nodes have been inserted into the graph. 
  </para>
 </refsect1>
 <refsect1 id="{@id package.database.structures_graph.tutorial.nodesanddata}">
  <title>Associating Data</title>
  <para>
  Graphs are only useful as datastructures if they can hold data. Structure_Graph stores data in nodes. Each node contains a setter and a getter for its data.
   <programlisting>
    <![CDATA[
$nodeOne->setData("Node One's Data is a String");
$nodeTwo->setData(1976);
$nodeThree->setData('Some other string');

print("NodeTwo's Data is an integer: " . $nodeTwo->getData());
    ]]>
   </programlisting>
  </para>
  <para>
  Structure_Graph nodes can also store metadata, alongside with the main data. Metadata differs from regular data just because it is stored under a key, making it possible to store more than one data reference per node. The metadata getter and setter need the key to perform the operation:
   <programlisting>
    <![CDATA[
$nodeOne->setMetadata('example key', "Node One's Sample Metadata");
print("Metadata stored under key 'example key' in node one: " . $nodeOne->getMetadata('example key'));
$nodeOne->unsetMetadata('example key');
    ]]>
   </programlisting>
  </para>
 </refsect1>
 <refsect1 id="{@id package.database.structures_graph.tutorial.querying}">
  <title>Querying a Graph</title>
  <para>
  Structures_Graph provides for basic querying of the graph:
   <programlisting>
    <![CDATA[
// Nodes are able to calculate their indegree and outdegree
print("NodeOne's inDegree: " . $nodeOne->inDegree());
print("NodeOne's outDegree: " . $nodeOne->outDegree());

// and naturally, nodes can report on their arcs
$arcs = $nodeOne->getNeighbours();
for ($i=0;$i<sizeof($arcs);$i++) {
    print("NodeOne has an arc to " . $arcs[$i]->getData());
}
    ]]>
   </programlisting>
  </para>
 </refsect1>
</refentry>
PK�*�\�r¦J�J%pear/Archive_Tar/docs/Archive_Tar.txtnu�[���Documentation for class Archive_Tar
===================================
Last update : 2001-08-15



Overview :
----------

  The Archive_Tar class helps in creating and managing GNU TAR format
  files compressed by GNU ZIP or not. 
  The class offers basic functions like creating an archive, adding
  files in the archive, extracting files from the archive and listing
  the archive content. 
  It also provide advanced functions that allow the adding and
  extraction of files with path manipulation. 


Sample :
--------

  // ----- Creating the object (uncompressed archive)
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);

  // ----- Creating the archive
  $v_list[0]="file.txt";
  $v_list[1]="data/";
  $v_list[2]="file.log";
  $tar_object->create($v_list);

  // ----- Adding files
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/";
  $v_list[2]="log/file.log";
  $tar_object->add($v_list);

  // ----- Adding more files
  $tar_object->add("release/newfile.log release/readme.txt");

  // ----- Listing the content
  if (($v_list  =  $tar_object->listContent()) != 0)
    for ($i=0; $i<sizeof($v_list); $i++)
    {
      echo "Filename :'".$v_list[$i][filename]."'<br>";
      echo " .size :'".$v_list[$i][size]."'<br>";
      echo " .mtime :'".$v_list[$i][mtime]."' (".date("l dS of F Y h:i:s A", $v_list[$i][mtime]).")<br>";
      echo " .mode :'".$v_list[$i][mode]."'<br>";
      echo " .uid :'".$v_list[$i][uid]."'<br>";
      echo " .gid :'".$v_list[$i][gid]."'<br>";
      echo " .typeflag :'".$v_list[$i][typeflag]."'<br>";
    }

  // ----- Extracting the archive in directory "install"
  $tar_object->extract("install");


Public arguments :
------------------

None


Public Methods :
----------------

Method : Archive_Tar($p_tarname, $compress = null)
Description :
  Archive_Tar Class constructor. This flavour of the constructor only
  declare a new Archive_Tar object, identifying it by the name of the
  tar file.
  If the compress argument is set the tar will be read or created as a
  gzip or bz2 compressed TAR file. 
Arguments :
  $p_tarname : A valid filename for the tar archive file.
  $p_compress : can be null, 'gz' or 'bz2'. For
                compatibility reason it can also be true. This
                parameter indicates if gzip or bz2 compression
                is required. 
Return value :
  The Archive_Tar object.
Sample :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object_compressed = new Archive_Tar("tarname.tgz", true);
How it works :
  Initialize the object.

Method : create($p_filelist)
Description :
  This method creates the archive file and add the files / directories
  that are listed in $p_filelist. 
  If the file already exists and is writable, it is replaced by the
  new tar. It is a create and not an add. If the file exists and is
  read-only or is a directory it is not replaced. The method return
  false and a PEAR error text. 
  The $p_filelist parameter can be an array of string, each string
  representing a filename or a directory name with their path if
  needed. It can also be a single string with names separated by a
  single blank. 
  See also createModify() method for more details.
Arguments :
  $p_filelist : An array of filenames and directory names, or a single
  string with names separated by a single blank space. 
Return value :
  true on success, false on error.
Sample 1 :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
  $v_list[0]="file.txt";
  $v_list[1]="data/"; (Optional '/' at the end)
  $v_list[2]="file.log";
  $tar_object->create($v_list);
Sample 2 :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
  $tar_object->create("file.txt data/ file.log");
How it works :
  Just calling the createModify() method with the right parameters.

Method : createModify($p_filelist, $p_add_dir, $p_remove_dir = "")
Description :
  This method creates the archive file and add the files / directories
  that are listed in $p_filelist. 
  If the file already exists and is writable, it is replaced by the
  new tar. It is a create and not an add. If the file exists and is
  read-only or is a directory it is not replaced. The method return
  false and a PEAR error text. 
  The $p_filelist parameter can be an array of string, each string
  representing a filename or a directory name with their path if
  needed. It can also be a single string with names separated by a
  single blank. 
  The path indicated in $p_remove_dir will be removed from the
  memorized path of each file / directory listed when this path
  exists. By default nothing is removed (empty path "") 
  The path indicated in $p_add_dir will be added at the beginning of
  the memorized path of each file / directory listed. However it can
  be set to empty "". The adding of a path is done after the removing
  of path. 
  The path add/remove ability enables the user to prepare an archive
  for extraction in a different path than the origin files are. 
  See also addModify() method for file adding properties.
Arguments :
  $p_filelist : An array of filenames and directory names, or a single
                string with names separated by a single blank space.
  $p_add_dir : A string which contains a path to be added to the
               memorized path of each element in the list. 
  $p_remove_dir : A string which contains a path to be removed from
                  the memorized path of each element in the list, when
		  relevant.
Return value :
  true on success, false on error.
Sample 1 :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
  $v_list[0]="file.txt";
  $v_list[1]="data/"; (Optional '/' at the end)
  $v_list[2]="file.log";
  $tar_object->createModify($v_list, "install");
  // files are stored in the archive as :
  //   install/file.txt
  //   install/data
  //   install/data/file1.txt
  //   install/data/... all the files and sub-dirs of data/
  //   install/file.log
Sample 2 :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->setErrorHandling(PEAR_ERROR_PRINT);  // Optional error handling
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/"; (Optional '/' at the end)
  $v_list[2]="log/file.log";
  $tar_object->createModify($v_list, "install", "dev");
  // files are stored in the archive as :
  //   install/file.txt
  //   install/data
  //   install/data/file1.txt
  //   install/data/... all the files and sub-dirs of data/
  //   install/log/file.log
How it works :
  Open the file in write mode (erasing the existing one if one),
  call the _addList() method for adding the files in an empty archive,
  add the tar footer (512 bytes block), close the tar file.


Method : addModify($p_filelist, $p_add_dir, $p_remove_dir="")
Description :
  This method add the files / directories listed in $p_filelist at the
  end of the existing archive. If the archive does not yet exists it
  is created.
  The $p_filelist parameter can be an array of string, each string
  representing a filename or a directory name with their path if
  needed. It can also be a single string with names separated by a
  single blank. 
  The path indicated in $p_remove_dir will be removed from the
  memorized path of each file / directory listed when this path
  exists. By default nothing is removed (empty path "") 
  The path indicated in $p_add_dir will be added at the beginning of
  the memorized path of each file / directory listed. However it can
  be set to empty "". The adding of a path is done after the removing
  of path. 
  The path add/remove ability enables the user to prepare an archive
  for extraction in a different path than the origin files are. 
  If a file/dir is already in the archive it will only be added at the
  end of the archive. There is no update of the existing archived
  file/dir. However while extracting the archive, the last file will
  replace the first one. This results in a none optimization of the
  archive size. 
  If a file/dir does not exist the file/dir is ignored. However an
  error text is send to PEAR error. 
  If a file/dir is not readable the file/dir is ignored. However an
  error text is send to PEAR error. 
  If the resulting filename/dirname (after the add/remove option or
  not) string is greater than 99 char, the file/dir is
  ignored. However an error text is send to PEAR error. 
Arguments :
  $p_filelist : An array of filenames and directory names, or a single
                string with names separated by a single blank space. 
  $p_add_dir : A string which contains a path to be added to the
               memorized path of each element in the list. 
  $p_remove_dir : A string which contains a path to be removed from
                  the memorized path of each element in the list, when
		  relevant.
Return value :
  true on success, false on error.
Sample 1 :
  $tar_object = new Archive_Tar("tarname.tar");
  [...]
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/"; (Optional '/' at the end)
  $v_list[2]="log/file.log";
  $tar_object->addModify($v_list, "install");
  // files are stored in the archive as :
  //   install/file.txt
  //   install/data
  //   install/data/file1.txt
  //   install/data/... all the files and sub-dirs of data/
  //   install/file.log
Sample 2 :
  $tar_object = new Archive_Tar("tarname.tar");
  [...]
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/"; (Optional '/' at the end)
  $v_list[2]="log/file.log";
  $tar_object->addModify($v_list, "install", "dev");
  // files are stored in the archive as :
  //   install/file.txt
  //   install/data
  //   install/data/file1.txt
  //   install/data/... all the files and sub-dirs of data/
  //   install/log/file.log
How it works :
  If the archive does not exists it create it and add the files.
  If the archive does exists and is not compressed, it open it, jump
  before the last empty 512 bytes block (tar footer) and add the files
  at this point.
  If the archive does exists and is compressed, a temporary copy file
  is created. This temporary file is then 'gzip' read block by block
  until the last empty block. The new files are then added in the
  compressed file.
  The adding of files is done by going through the file/dir list,
  adding files per files, in a recursive way through the
  directory. Each time a path need to be added/removed it is done
  before writing the file header in the archive.

Method : add($p_filelist)
Description :
  This method add the files / directories listed in $p_filelist at the
  end of the existing archive. If the archive does not yet exists it
  is created. 
  The $p_filelist parameter can be an array of string, each string
  representing a filename or a directory name with their path if
  needed. It can also be a single string with names separated by a
  single blank. 
  See addModify() method for details and limitations.
Arguments :
  $p_filelist : An array of filenames and directory names, or a single
  string with names separated by a single blank space. 
Return value :
  true on success, false on error.
Sample 1 :
  $tar_object = new Archive_Tar("tarname.tar");
  [...]
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/"; (Optional '/' at the end)
  $v_list[2]="log/file.log";
  $tar_object->add($v_list);
Sample 2 :
  $tar_object = new Archive_Tar("tarname.tgz", true);
  [...]
  $v_list[0]="dev/file.txt";
  $v_list[1]="dev/data/"; (Optional '/' at the end)
  $v_list[2]="log/file.log";
  $tar_object->add($v_list);
How it works :
  Simply call the addModify() method with the right parameters.

Method : addString($p_filename, $p_string, $p_datetime, $p_params)
Description :
  This method add a single string as a file at the
  end of the existing archive. If the archive does not yet exists it
  is created.
Arguments :
  $p_filename : A string which contains the full filename path
                that will be associated with the string.
  $p_string :   The content of the file added in the archive.
  $p_datetime : (Optional) Timestamp of the file (default = now)
  $p_params :   (Optional) Various file metadata:
                    stamp - As above, timestamp of the file
                    mode - UNIX-style permissions (default 0600)
                    type - Is this a regular file or link (see TAR
                           format spec for how to create a hard/symlink)
                    uid - UNIX-style user ID (default 0 = root)
                    gid - UNIX-style group ID (default 0 = root)
Return value :
  true on success, false on error.
Sample 1 :
  $v_archive = & new Archive_Tar($p_filename);
  $v_archive->setErrorHandling(PEAR_ERROR_PRINT);
  $v_result = $v_archive->addString('data/test.txt', 'This is the text of the string');
  $v_result = $v_archive->addString(
                  'data/test.sh',
                  "#!/bin/sh\necho 'Hello'",
                  time(),
                  array( "mode" => 0755, "uid" => 34 )
              );


Method : extract($p_path = "")
Description :
  This method extract all the content of the archive in the directory
  indicated by $p_path.If $p_path is optional, if not set the archive
  is extracted in the current directory. 
  While extracting a file, if the directory path does not exists it is
  created. 
  See extractModify() for details and limitations.
Arguments :
  $p_path : Optional path where the files/dir need to by extracted.
Return value :
  true on success, false on error.
Sample :
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->extract();
How it works :
  Simply call the extractModify() method with appropriate parameters.

Method : extractModify($p_path, $p_remove_path)
Description :
  This method extract all the content of the archive in the directory
  indicated by $p_path. When relevant the memorized path of the
  files/dir can be modified by removing the $p_remove_path path at the
  beginning of the file/dir path. 
  While extracting a file, if the directory path does not exists it is
  created. 
  While extracting a file, if the file already exists it is replaced
  without looking for last modification date. 
  While extracting a file, if the file already exists and is write
  protected, the extraction is aborted. 
  While extracting a file, if a directory with the same name already
  exists, the extraction is aborted. 
  While extracting a directory, if a file with the same name already
  exists, the extraction is aborted. 
  While extracting a file/directory if the destination directory exist
  and is write protected, or does not exist but can not be created,
  the extraction is aborted. 
  If after extraction an extracted file does not show the correct
  stored file size, the extraction is aborted. 
  When the extraction is aborted, a PEAR error text is set and false
  is returned. However the result can be a partial extraction that may
  need to be manually cleaned. 
Arguments :
  $p_path : The path of the directory where the files/dir need to by
            extracted. 
  $p_remove_path : Part of the memorized path that can be removed if
                   present at the beginning of the file/dir path. 
Return value :
  true on success, false on error.
Sample :
  // Imagine tarname.tar with files :
  //   dev/data/file.txt
  //   dev/data/log.txt
  //   readme.txt
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->extractModify("install", "dev");
  // Files will be extracted there :
  //   install/data/file.txt
  //   install/data/log.txt
  //   install/readme.txt
How it works :
  Open the archive and call a more generic function that can extract
  only a part of the archive or all the archive. 
  See extractList() method for more details.

Method : extractInString($p_filename)
Description :
  This method extract from the archive one file identified by $p_filename.
  The return value is a string with the file content, or NULL on error. 
Arguments :
  $p_filename : The path of the file to extract in a string. 
Return value :
  a string with the file content or NULL.
Sample :
  // Imagine tarname.tar with files :
  //   dev/data/file.txt
  //   dev/data/log.txt
  //   dev/readme.txt
  $v_archive = & new Archive_Tar('tarname.tar');
  $v_archive->setErrorHandling(PEAR_ERROR_PRINT);
  $v_string = $v_archive->extractInString('dev/readme.txt');
  echo $v_string;

Method : listContent()
Description :
  This method returns an array of arrays that describe each
  file/directory present in the archive. 
  The array is not sorted, so it show the position of the file in the
  archive. 
  The file informations are :
    $file[filename] : Name and path of the file/dir.
    $file[mode] : File permissions (result of fileperms())
    $file[uid] : user id
    $file[gid] : group id
    $file[size] : filesize
    $file[mtime] : Last modification time (result of filemtime())
    $file[typeflag] : "" for file, "5" for directory
Arguments :
Return value :
  An array of arrays or 0 on error.
Sample :
  $tar_object = new Archive_Tar("tarname.tar");
  if (($v_list  =  $tar_object->listContent()) != 0)
    for ($i=0; $i<sizeof($v_list); $i++)
    {
      echo "Filename :'".$v_list[$i][filename]."'<br>";
      echo " .size :'".$v_list[$i][size]."'<br>";
      echo " .mtime :'".$v_list[$i][mtime]."' (".
           date("l dS of F Y h:i:s A", $v_list[$i][mtime]).")<br>";
      echo " .mode :'".$v_list[$i][mode]."'<br>";
      echo " .uid :'".$v_list[$i][uid]."'<br>";
      echo " .gid :'".$v_list[$i][gid]."'<br>";
      echo " .typeflag :'".$v_list[$i][typeflag]."'<br>";
    }
How it works :
  Call the same function as an extract however with a flag to only go
  through the archive without extracting the files. 

Method : extractList($p_filelist, $p_path = "", $p_remove_path = "")
Description :
  This method extract from the archive only the files indicated in the
  $p_filelist. These files are extracted in the current directory or
  in the directory indicated by the optional $p_path parameter. 
  If indicated the $p_remove_path can be used in the same way as it is
  used in extractModify() method. 
Arguments :
  $p_filelist : An array of filenames and directory names, or a single
                string with names separated by a single blank space. 
  $p_path : The path of the directory where the files/dir need to by
            extracted. 
  $p_remove_path : Part of the memorized path that can be removed if
                   present at the beginning of the file/dir path. 
Return value :
  true on success, false on error.
Sample :
  // Imagine tarname.tar with files :
  //   dev/data/file.txt
  //   dev/data/log.txt
  //   readme.txt
  $tar_object = new Archive_Tar("tarname.tar");
  $tar_object->extractList("dev/data/file.txt readme.txt", "install",
                           "dev");
  // Files will be extracted there :
  //   install/data/file.txt
  //   install/readme.txt
How it works :
  Go through the archive and extract only the files present in the
  list. 

PK�*�\$�u���!alt-php84-snuffleupagus/README.mdnu�[���<h1 align="center">
  <br>
  <a href="https://snuffleupagus.readthedocs.io/">
    <img src="https://github.com/jvoisin/snuffleupagus/raw/master/doc/source/_static/sp.png" alt="Snuffleupagus' logo" width="200"></a>
  <br>
  Snuffleupagus
  <br>
</h1>

<h4 align="center">Security module for php7 and php8 - Killing bugclasses and virtual-patching the rest!</h4>

<p align="center">
  <a href="https://github.com/jvoisin/snuffleupagus/actions/workflows/distributions_php7.yml">
    <img src="https://github.com/jvoisin/snuffleupagus/actions/workflows/distributions_php7.yml/badge.svg"
         alt="Testing PHP7 on various Linux distributions" />
  </a>
  <a href="https://github.com/jvoisin/snuffleupagus/actions/workflows/distributions_php8.yml">
    <img src="https://github.com/jvoisin/snuffleupagus/actions/workflows/distributions_php8.yml/badge.svg"
         alt="Testing PHP8 on various Linux distributions" />
  </a>
  <a href="https://scan.coverity.com/projects/jvoisin-snuffleupagus">
    <img src="https://scan.coverity.com/projects/13821/badge.svg?flat=1"
         alt="Coverity">
  </a>
  <a href="https://bestpractices.coreinfrastructure.org/projects/1267">
      <img src="https://bestpractices.coreinfrastructure.org/projects/1267/badge"
           alt="CII Best Practises">
  </a>
  <a href="http://snuffleupagus.readthedocs.io/?badge=latest">
    <img src="https://readthedocs.org/projects/snuffleupagus/badge/?version=latest"
         alt="readthedocs.org">
  </a>
  <a href="https://coveralls.io/github/jvoisin/snuffleupagus?branch=master">
    <img src="https://coveralls.io/repos/github/jvoisin/snuffleupagus/badge.svg?branch=master"
         alt="coveralls">
  </a>
  <a href="https://twitter.com/dustriorg">
    <img src="https://img.shields.io/badge/twitter-follow-blue.svg"
         alt="twitter">
  </a>
  <a href="https://repology.org/project/php:snuffleupagus/versions">
    <img src="https://repology.org/badge/tiny-repos/php:snuffleupagus.svg"
         alt="Packaging status">
  </a>
  <a href="https://github.com/jvoisin/snuffleupagus">
    <img src="https://github.com/jvoisin/snuffleupagus/actions/workflows/codeql-analysis.yml/badge.svg"
         alt="CodeQL">
  </a>
</p>

<p align="center">
  <a href="#key-features">Key Features</a> •
  <a href="#download">Download</a> •
  <a href="#examples">Examples</a> •
  <a href="https://snuffleupagus.readthedocs.io/">Documentation</a> •
  <a href="https://github.com/jvoisin/snuffleupagus/blob/master/LICENSE">License</a> •
  <a href="#thanks">Thanks</a>
</p>

Snuffleupagus is a [PHP 7+ and 8+](https://secure.php.net/) module designed to
drastically raise the cost of attacks against websites, by killing entire bug
classes. It also provides a powerful virtual-patching system, allowing
administrator to fix specific vulnerabilities and audit suspicious behaviours
without having to touch the PHP code.

## Key Features

* No [noticeable performance impact](https://dustri.org/b/snuffleupagus-030-dentalium-elephantinum.html)
* Powerful yet simple to write virtual-patching rules
* Killing several classes of vulnerabilities
  * [Unserialize-based](https://www.owasp.org/images/9/9e/Utilizing-Code-Reuse-Or-Return-Oriented-Programming-In-PHP-Application-Exploits.pdf) code execution
  * [`mail`-based]( https://blog.ripstech.com/2016/roundcube-command-execution-via-email/ ) code execution
  * Cookie-stealing [XSS]( https://en.wikipedia.org/wiki/Cross-site_scripting )
  * File-upload based code execution
  * Weak PRNG
  * [XXE]( https://en.wikipedia.org/wiki/XML_external_entity_attack )
  * Filter based remote code execution and assorted shenanigans
* Several hardening features
  * Automatic `secure` and `samesite` flag for cookies
  * Bundled set of rules to detect post-compromissions behaviours
  * Global [strict mode]( https://secure.php.net/manual/en/migration70.new-features.php#migration70.new-features.scalar-type-declarations) and type-juggling prevention
  * Whitelisting of [stream wrappers](https://secure.php.net/manual/en/intro.stream.php)
  * Preventing writeable files execution
  * Whitelist/blacklist for `eval`
  * Enforcing TLS certificate validation when using [curl](https://secure.php.net/manual/en/book.curl.php)
  * Request dumping capability
* A relatively sane code base:
  * A [comprehensive](https://coveralls.io/github/jvoisin/snuffleupagus?branch=master) test suite close to 100% coverage
  * Every commit is tested on [several distributions](https://gitlab.com/jvoisin/snuffleupagus/pipelines)
  * An `clang-format`-enforced code style
  * A [comprehensive documentation](https://snuffleupagus.rtfd.io)
  * Usage of [coverity](https://scan.coverity.com/projects/jvoisin-snuffleupagus), codeql, [scan-build](https://clang-analyzer.llvm.org/scan-build.html), …

## Download

We've got a [download
page](https://snuffleupagus.readthedocs.io/download.html), where you can find
packages for your distribution, but you can of course just `git clone` this
repo, or check the releases on [github](https://github.com/jvoisin/snuffleupagus/releases).

## Examples

We're providing [various example rules](https://github.com/jvoisin/snuffleupagus/tree/master/config),
that are looking like this:

```python
# Harden the `chmod` function
sp.disable_function.function("chmod").param("mode").value_r("^[0-9]{2}[67]$").drop();

# Mitigate command injection in `system`
sp.disable_function.function("system").param("command").value_r("[$|;&`\\n]").drop();
```

Upon violation of a rule, you should see lines like this in your logs:

```python
[snuffleupagus][0.0.0.0][disabled_function][drop] The execution has been aborted in /var/www/index.php:2, because the return value (0) of the function 'strpos' matched a rule.
```

## Documentation

We've got a [comprehensive website](https://snuffleupagus.readthedocs.io/) with
all the documentation that you could possibly wish for. You can of course
[build it yourself](https://github.com/jvoisin/snuffleupagus/tree/master/doc).

## Thanks

Many thanks to:

- The [Suhosin project](https://suhosin.org) for being a __huge__ source of inspiration
- [NBS System](https://www.nbs-system.com) for initially sponsoring the development
- [Suhosin-ng](https://github.com/sektioneins/suhosin-ng) for their
  [experimentations](https://github.com/sektioneins/suhosin-ng/wiki/News)
  and [contributions](https://github.com/jvoisin/snuffleupagus/commits?author=bef),
  as well as [NLNet](https://nlnet.nl/project/Suhosin-NG/) for sponsoring it
- All [our contributors](https://github.com/jvoisin/snuffleupagus/graphs/contributors)

PK�*�\�E#/&&'alt-php84-snuffleupagus/CONTRIBUTING.mdnu�[���## Contributing

First off, thank you for considering contributing to snuffleupagus.

### 1. Where do I go from here?

If you've noticed a bug or have a question,
look at the [faq](https://snuffleupagus.readthedocs.io/faq.html) and
[search the issue tracker](https://github.com/jvoisin/snuffleupagus/issues)
to see if someone else has already created a ticket. If not, go ahead and
[make one](https://github.com/jvoisin/snuffleupagus/issues/new)!

### 2. Fork & create a branch

If this is something you think you can fix,
then [fork snuffleupagus](https://help.github.com/articles/fork-a-repo) and
create a branch with a descriptive name.

A good branch name would be (where issue #325 is the ticket you're working on):

```sh
git checkout -b 325-kill-sql-injections
```

### 3. Get the test suite running

Just type `make coverage` or `make debug`, the testsuite should be run
automatically.

Please add tests if you're fixing a bug or adding a new feature: we do have a
[high coverage](https://coveralls.io/github/jvoisin/snuffleupagus?branch=master)
(functions, lines and branches), and intend to keep it that way.

#### 3.3 Debugging failures in the test suite

If your changes have introduced run-time failures in the test-suite, you can
easily troubleshoot them by inspecting the files that
[php has generated](https://qa.php.net/write-test.php#analyzing-failing-tests)
for this purpose.

A nice trick is to edit the `.sh` file to prepend `gdb --args` to it before
launching it, in order to run the failing test inside GDB.


### 4. Did you find a bug?

* **Ensure the bug was not already reported** by
  [searching all issues](https://github.com/jvoisin/snuffleupagus/issues?q=).
* If you're unable to find an open issue addressing the problem,
  [open a new one](https://github.com/jvoisin/snuffleupagus/issues/new).
  Be sure to include a **title and clear description**,
  as much relevant information as possible, and a **code sample**
  or an **executable test case** demonstrating the expected behavior that is not
  occurring.


### 5. Get the style right

Your patch should follow the same conventions & pass the same code quality
checks as the rest of the project. We're using [clang-format](http://clang.llvm.org/docs/ClangFormat.html) to
ensure a consistent code-style. Please run it with `clang-format --style="{BasedOnStyle: google, SortIncludes: false}"`
before committing, or even better, use a [pre-commit hook](https://github.com/andrewseidl/githook-clang-format).

### 6. Make a Pull Request

At this point, you should switch back to your master branch and make sure it's
up to date with our upstream master branch:

```sh
git remote add upstream git@github.com:jvoisin/snuffleupagus.git
git checkout master
git pull upstream master
```

Then update your feature branch from your local copy of master, and push it!

```sh
git checkout 325-kill-sql-injections
git rebase master
git push --set-upstream origin 325-kill-sql-injections
```

Finally, go to GitHub and [make a Pull Request](https://help.github.com/articles/creating-a-pull-request) :D

Travis CI will [run our test suite](https://travis-ci.org/jvoisin/snuffleupagus)
against all supported PHP versions. We care about quality, so your PR won't be
merged until all tests pass. It's unlikely, but it's possible that your changes
pass tests in one PHP version but fail in another. In that case, you'll have to
setup your development environment to use the problematic PHP version, and
investigate what's going on!

### 7. Keeping your Pull Request updated

If a maintainer asks you to "rebase" your PR, they're saying that a lot of code
has changed, and that you need to update your branch so it's easier to merge.

To learn more about rebasing in Git, there are a lot of [good](http://git-scm.com/book/en/Git-Branching-Rebasing)
[resources](https://help.github.com/articles/interactive-rebase) but here's the suggested workflow:

```sh
git checkout 325-kill-sql-injections
git pull --rebase upstream master
git push --force-with-lease 325-kill-sql-injections
```

### 8. Merging a PR (maintainers only)

A PR can only be merged into master by a maintainer if:

1. It is passing CI.
2. It has been approved by at least one maintainer. If it was a maintainer who
   opened the PR, only one extra approval is needed.
3. It has no requested changes.
4. It is up to date with current master.

Any maintainer is allowed to merge a PR if all of these conditions are met.

### 9. Shipping a release (maintainers only)

Maintainers need to do the following to push out a release:

1. Make sure that all pending and mergeable pull requests are in
2. Close the corresponding
	 [milestone](https://github.com/jvoisin/snuffleupagus/milestones)
2. Run `valgrind` (by adding a `-m` after the `-q` in the Makefile) and check that everything is ok.
   Don't mind the python-related issues.
2. Run `cd src; phpize; ./configure --enable-snuffleupagus --enable-debug; scan-build make`
   and fix the possible issues.
3. Update the `src/php_snuffleupagus.h` according to [semantic versioning](https://semver.org/)
4. Update the changelog page in the documentation
5. Update the Debian changelog in `./debian/changelog` with `cd debian; dch`
6. Commit the result
7. Clean up the folder `make clean; git clean -xdf`
8. Create a tag for the release:

  ```sh
  git tag -s v$MAJOR.$MINOR.$PATCH -m "v$MAJOR.$MINOR.$PATCH"
  git push --tags
	git push origin master
  ```

9. Wait for the CI on the new tag branch to finish
10. Create the [release on github](https://github.com/jvoisin/snuffleupagus/releases)
11. Add the freshly built Debian packages from the CI to the release
12. Do the *secret release dance*
PK��\�Yď%alt-python39-setuptools-wheel/LICENSEnu�[���Copyright Jason R. Coombs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
PK��\ԉ�y�'�'alt-python39/README.rstnu�[���This is Python version 3.9.23
=============================

.. image:: https://travis-ci.org/python/cpython.svg?branch=3.9
   :alt: CPython build status on Travis CI
   :target: https://travis-ci.org/python/cpython

.. image:: https://github.com/python/cpython/workflows/Tests/badge.svg
   :alt: CPython build status on GitHub Actions
   :target: https://github.com/python/cpython/actions

.. image:: https://dev.azure.com/python/cpython/_apis/build/status/Azure%20Pipelines%20CI?branchName=3.9
   :alt: CPython build status on Azure DevOps
   :target: https://dev.azure.com/python/cpython/_build/latest?definitionId=4&branchName=3.9

.. image:: https://codecov.io/gh/python/cpython/branch/3.9/graph/badge.svg
   :alt: CPython code coverage on Codecov
   :target: https://codecov.io/gh/python/cpython

.. image:: https://img.shields.io/badge/discourse-join_chat-brightgreen.svg
   :alt: Python Discourse chat
   :target: https://discuss.python.org/


Copyright © 2001-2023 Python Software Foundation.  All rights reserved.

See the end of this file for further copyright and license information.

.. contents::

General Information
-------------------

- Website: https://www.python.org
- Source code: https://github.com/python/cpython
- Issue tracker: https://bugs.python.org
- Documentation: https://docs.python.org
- Developer's Guide: https://devguide.python.org/

Contributing to CPython
-----------------------

For more complete instructions on contributing to CPython development,
see the `Developer Guide`_.

.. _Developer Guide: https://devguide.python.org/

Using Python
------------

Installable Python kits, and information about using Python, are available at
`python.org`_.

.. _python.org: https://www.python.org/

Build Instructions
------------------

On Unix, Linux, BSD, macOS, and Cygwin::

    ./configure
    make
    make test
    sudo make install

This will install Python as ``python3``.

You can pass many options to the configure script; run ``./configure --help``
to find out more.  On macOS case-insensitive file systems and on Cygwin,
the executable is called ``python.exe``; elsewhere it's just ``python``.

Building a complete Python installation requires the use of various
additional third-party libraries, depending on your build platform and
configure options.  Not all standard library modules are buildable or
useable on all platforms.  Refer to the
`Install dependencies <https://devguide.python.org/setup/#install-dependencies>`_
section of the `Developer Guide`_ for current detailed information on
dependencies for various Linux distributions and macOS.

On macOS, there are additional configure and build options related
to macOS framework and universal builds.  Refer to `Mac/README.rst
<https://github.com/python/cpython/blob/3.9/Mac/README.rst>`_.

On Windows, see `PCbuild/readme.txt
<https://github.com/python/cpython/blob/3.9/PCbuild/readme.txt>`_.

If you wish, you can create a subdirectory and invoke configure from there.
For example::

    mkdir debug
    cd debug
    ../configure --with-pydebug
    make
    make test

(This will fail if you *also* built at the top-level directory.  You should do
a ``make clean`` at the top-level first.)

To get an optimized build of Python, ``configure --enable-optimizations``
before you run ``make``.  This sets the default make targets up to enable
Profile Guided Optimization (PGO) and may be used to auto-enable Link Time
Optimization (LTO) on some platforms.  For more details, see the sections
below.

Profile Guided Optimization
^^^^^^^^^^^^^^^^^^^^^^^^^^^

PGO takes advantage of recent versions of the GCC or Clang compilers.  If used,
either via ``configure --enable-optimizations`` or by manually running
``make profile-opt`` regardless of configure flags, the optimized build
process will perform the following steps:

The entire Python directory is cleaned of temporary files that may have
resulted from a previous compilation.

An instrumented version of the interpreter is built, using suitable compiler
flags for each flavor. Note that this is just an intermediary step.  The
binary resulting from this step is not good for real-life workloads as it has
profiling instructions embedded inside.

After the instrumented interpreter is built, the Makefile will run a training
workload.  This is necessary in order to profile the interpreter's execution.
Note also that any output, both stdout and stderr, that may appear at this step
is suppressed.

The final step is to build the actual interpreter, using the information
collected from the instrumented one.  The end result will be a Python binary
that is optimized; suitable for distribution or production installation.


Link Time Optimization
^^^^^^^^^^^^^^^^^^^^^^

Enabled via configure's ``--with-lto`` flag.  LTO takes advantage of the
ability of recent compiler toolchains to optimize across the otherwise
arbitrary ``.o`` file boundary when building final executables or shared
libraries for additional performance gains.


What's New
----------

We have a comprehensive overview of the changes in the `What's New in Python
3.9 <https://docs.python.org/3.9/whatsnew/3.9.html>`_ document.  For a more
detailed change log, read `Misc/NEWS
<https://github.com/python/cpython/blob/3.9/Misc/NEWS.d>`_, but a full
accounting of changes can only be gleaned from the `commit history
<https://github.com/python/cpython/commits/3.9>`_.

If you want to install multiple versions of Python, see the section below
entitled "Installing multiple versions".


Documentation
-------------

`Documentation for Python 3.9 <https://docs.python.org/3.9/>`_ is online,
updated daily.

It can also be downloaded in many formats for faster access.  The documentation
is downloadable in HTML, PDF, and reStructuredText formats; the latter version
is primarily for documentation authors, translators, and people with special
formatting requirements.

For information about building Python's documentation, refer to `Doc/README.rst
<https://github.com/python/cpython/blob/3.9/Doc/README.rst>`_.


Converting From Python 2.x to 3.x
---------------------------------

Significant backward incompatible changes were made for the release of Python
3.0, which may cause programs written for Python 2 to fail when run with Python
3.  For more information about porting your code from Python 2 to Python 3, see
the `Porting HOWTO <https://docs.python.org/3/howto/pyporting.html>`_.


Testing
-------

To test the interpreter, type ``make test`` in the top-level directory.  The
test set produces some output.  You can generally ignore the messages about
skipped tests due to optional features which can't be imported.  If a message
is printed about a failed test or a traceback or core dump is produced,
something is wrong.

By default, tests are prevented from overusing resources like disk space and
memory.  To enable these tests, run ``make testall``.

If any tests fail, you can re-run the failing test(s) in verbose mode.  For
example, if ``test_os`` and ``test_gdb`` failed, you can run::

    make test TESTOPTS="-v test_os test_gdb"

If the failure persists and appears to be a problem with Python rather than
your environment, you can `file a bug report <https://bugs.python.org>`_ and
include relevant output from that command to show the issue.

See `Running & Writing Tests <https://devguide.python.org/runtests/>`_
for more on running tests.

Installing multiple versions
----------------------------

On Unix and Mac systems if you intend to install multiple versions of Python
using the same installation prefix (``--prefix`` argument to the configure
script) you must take care that your primary python executable is not
overwritten by the installation of a different version.  All files and
directories installed using ``make altinstall`` contain the major and minor
version and can thus live side-by-side.  ``make install`` also creates
``${prefix}/bin/python3`` which refers to ``${prefix}/bin/pythonX.Y``.  If you
intend to install multiple versions using the same prefix you must decide which
version (if any) is your "primary" version.  Install that version using ``make
install``.  Install all other versions using ``make altinstall``.

For example, if you want to install Python 2.7, 3.6, and 3.9 with 3.9 being the
primary version, you would execute ``make install`` in your 3.9 build directory
and ``make altinstall`` in the others.


Issue Tracker and Mailing List
------------------------------

Bug reports are welcome!  You can use the `issue tracker
<https://bugs.python.org>`_ to report bugs, and/or submit pull requests `on
GitHub <https://github.com/python/cpython>`_.

You can also follow development discussion on the `python-dev mailing list
<https://mail.python.org/mailman/listinfo/python-dev/>`_.


Proposals for enhancement
-------------------------

If you have a proposal to change Python, you may want to send an email to the
`comp.lang.python`_ or `python-ideas`_ mailing lists for initial feedback.  A
Python Enhancement Proposal (PEP) may be submitted if your idea gains ground.
All current PEPs, as well as guidelines for submitting a new PEP, are listed at
`python.org/dev/peps/ <https://www.python.org/dev/peps/>`_.

.. _python-ideas: https://mail.python.org/mailman/listinfo/python-ideas/
.. _comp.lang.python: https://mail.python.org/mailman/listinfo/python-list


Release Schedule
----------------

See :pep:`596` for Python 3.9 release details.


Copyright and License Information
---------------------------------

Copyright © 2001-2023 Python Software Foundation.  All rights reserved.

Copyright © 2000 BeOpen.com.  All rights reserved.

Copyright © 1995-2001 Corporation for National Research Initiatives.  All
rights reserved.

Copyright © 1991-1995 Stichting Mathematisch Centrum.  All rights reserved.

See the file "LICENSE" for information on the history of this software, terms &
conditions for usage, and a DISCLAIMER OF ALL WARRANTIES.

This Python distribution contains *no* GNU General Public License (GPL) code,
so it may be used in proprietary projects.  There are interfaces to some GNU
code but these are entirely optional.

All trademarks referenced herein are property of their respective holders.PK��\����alt-python39-pip/README.rstnu�[���pip - The Python Package Installer
==================================

.. image:: https://img.shields.io/pypi/v/pip.svg
   :target: https://pypi.org/project/pip/

.. image:: https://readthedocs.org/projects/pip/badge/?version=latest
   :target: https://pip.pypa.io/en/latest

pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes.

Please take a look at our documentation for how to install and use pip:

* `Installation`_
* `Usage`_

We release updates regularly, with a new version every 3 months. Find more details in our documentation:

* `Release notes`_
* `Release process`_

In pip 20.3, we've `made a big improvement to the heart of pip`_; `learn more`_. We want your input, so `sign up for our user experience research studies`_ to help us do it right.

**Note**: pip 21.0, in January 2021, removed Python 2 support, per pip's `Python 2 support policy`_. Please migrate to Python 3.

If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms:

* `Issue tracking`_
* `Discourse channel`_
* `User IRC`_

If you want to get involved head over to GitHub to get the source code, look at our development documentation and feel free to jump on the developer mailing lists and chat rooms:

* `GitHub page`_
* `Development documentation`_
* `Development mailing list`_
* `Development IRC`_

Code of Conduct
---------------

Everyone interacting in the pip project's codebases, issue trackers, chat
rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.

.. _package installer: https://packaging.python.org/guides/tool-recommendations/
.. _Python Package Index: https://pypi.org
.. _Installation: https://pip.pypa.io/en/stable/installation/
.. _Usage: https://pip.pypa.io/en/stable/
.. _Release notes: https://pip.pypa.io/en/stable/news.html
.. _Release process: https://pip.pypa.io/en/latest/development/release-process/
.. _GitHub page: https://github.com/pypa/pip
.. _Development documentation: https://pip.pypa.io/en/latest/development
.. _made a big improvement to the heart of pip: https://pyfound.blogspot.com/2020/11/pip-20-3-new-resolver.html
.. _learn more: https://pip.pypa.io/en/latest/user_guide/#changes-to-the-pip-dependency-resolver-in-20-3-2020
.. _sign up for our user experience research studies: https://pyfound.blogspot.com/2020/03/new-pip-resolver-to-roll-out-this-year.html
.. _Python 2 support policy: https://pip.pypa.io/en/latest/development/release-process/#python-2-support
.. _Issue tracking: https://github.com/pypa/pip/issues
.. _Discourse channel: https://discuss.python.org/c/packaging
.. _Development mailing list: https://mail.python.org/mailman3/lists/distutils-sig.python.org/
.. _User IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa
.. _Development IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa-dev
.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
PK��\���3{"{"'alt-python39-devel/valgrind-python.suppnu�[���#
# This is a valgrind suppression file that should be used when using valgrind.
#
#  Here's an example of running valgrind:
#
#	cd python/dist/src
#	valgrind --tool=memcheck --suppressions=Misc/valgrind-python.supp \
#		./python -E ./Lib/test/regrtest.py -u gui,network
#
# You must edit Objects/obmalloc.c and uncomment Py_USING_MEMORY_DEBUGGER
# to use the preferred suppressions with address_in_range.
#
# If you do not want to recompile Python, you can uncomment
# suppressions for _PyObject_Free and _PyObject_Realloc.
#
# See Misc/README.valgrind for more information.

# all tool names: Addrcheck,Memcheck,cachegrind,helgrind,massif
{
   ADDRESS_IN_RANGE/Invalid read of size 4
   Memcheck:Addr4
   fun:address_in_range
}

{
   ADDRESS_IN_RANGE/Invalid read of size 4
   Memcheck:Value4
   fun:address_in_range
}

{
   ADDRESS_IN_RANGE/Invalid read of size 8 (x86_64 aka amd64)
   Memcheck:Value8
   fun:address_in_range
}

{
   ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
   Memcheck:Cond
   fun:address_in_range
}

#
# Leaks (including possible leaks)
#    Hmmm, I wonder if this masks some real leaks.  I think it does.
#    Will need to fix that.
#

{
   Suppress leaking the GIL.  Happens once per process, see comment in ceval.c.
   Memcheck:Leak
   fun:malloc
   fun:PyThread_allocate_lock
   fun:PyEval_InitThreads
}

{
   Suppress leaking the GIL after a fork.
   Memcheck:Leak
   fun:malloc
   fun:PyThread_allocate_lock
   fun:PyEval_ReInitThreads
}

{
   Suppress leaking the autoTLSkey.  This looks like it shouldn't leak though.
   Memcheck:Leak
   fun:malloc
   fun:PyThread_create_key
   fun:_PyGILState_Init
   fun:Py_InitializeEx
   fun:Py_Main
}

{
   Hmmm, is this a real leak or like the GIL?
   Memcheck:Leak
   fun:malloc
   fun:PyThread_ReInitTLS
}

{
   Handle PyMalloc confusing valgrind (possibly leaked)
   Memcheck:Leak
   fun:realloc
   fun:_PyObject_GC_Resize
   fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING
}

{
   Handle PyMalloc confusing valgrind (possibly leaked)
   Memcheck:Leak
   fun:malloc
   fun:_PyObject_GC_New
   fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING
}

{
   Handle PyMalloc confusing valgrind (possibly leaked)
   Memcheck:Leak
   fun:malloc
   fun:_PyObject_GC_NewVar
   fun:COMMENT_THIS_LINE_TO_DISABLE_LEAK_WARNING
}

#
# Non-python specific leaks
#

{
   Handle pthread issue (possibly leaked)
   Memcheck:Leak
   fun:calloc
   fun:allocate_dtv
   fun:_dl_allocate_tls_storage
   fun:_dl_allocate_tls
}

{
   Handle pthread issue (possibly leaked)
   Memcheck:Leak
   fun:memalign
   fun:_dl_allocate_tls_storage
   fun:_dl_allocate_tls
}

###{
###   ADDRESS_IN_RANGE/Invalid read of size 4
###   Memcheck:Addr4
###   fun:_PyObject_Free
###}
###
###{
###   ADDRESS_IN_RANGE/Invalid read of size 4
###   Memcheck:Value4
###   fun:_PyObject_Free
###}
###
###{
###   ADDRESS_IN_RANGE/Use of uninitialised value of size 8
###   Memcheck:Addr8
###   fun:_PyObject_Free
###}
###
###{
###   ADDRESS_IN_RANGE/Use of uninitialised value of size 8
###   Memcheck:Value8
###   fun:_PyObject_Free
###}
###
###{
###   ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
###   Memcheck:Cond
###   fun:_PyObject_Free
###}

###{
###   ADDRESS_IN_RANGE/Invalid read of size 4
###   Memcheck:Addr4
###   fun:_PyObject_Realloc
###}
###
###{
###   ADDRESS_IN_RANGE/Invalid read of size 4
###   Memcheck:Value4
###   fun:_PyObject_Realloc
###}
###
###{
###   ADDRESS_IN_RANGE/Use of uninitialised value of size 8
###   Memcheck:Addr8
###   fun:_PyObject_Realloc
###}
###
###{
###   ADDRESS_IN_RANGE/Use of uninitialised value of size 8
###   Memcheck:Value8
###   fun:_PyObject_Realloc
###}
###
###{
###   ADDRESS_IN_RANGE/Conditional jump or move depends on uninitialised value
###   Memcheck:Cond
###   fun:_PyObject_Realloc
###}

###
### All the suppressions below are for errors that occur within libraries
### that Python uses.  The problems to not appear to be related to Python's
### use of the libraries.
###

{
   Generic ubuntu ld problems
   Memcheck:Addr8
   obj:/lib/ld-2.4.so
   obj:/lib/ld-2.4.so
   obj:/lib/ld-2.4.so
   obj:/lib/ld-2.4.so
}

{
   Generic gentoo ld problems
   Memcheck:Cond
   obj:/lib/ld-2.3.4.so
   obj:/lib/ld-2.3.4.so
   obj:/lib/ld-2.3.4.so
   obj:/lib/ld-2.3.4.so
}

{
   DBM problems, see test_dbm
   Memcheck:Param
   write(buf)
   fun:write
   obj:/usr/lib/libdb1.so.2
   obj:/usr/lib/libdb1.so.2
   obj:/usr/lib/libdb1.so.2
   obj:/usr/lib/libdb1.so.2
   fun:dbm_close
}

{
   DBM problems, see test_dbm
   Memcheck:Value8
   fun:memmove
   obj:/usr/lib/libdb1.so.2
   obj:/usr/lib/libdb1.so.2
   obj:/usr/lib/libdb1.so.2
   obj:/usr/lib/libdb1.so.2
   fun:dbm_store
   fun:dbm_ass_sub
}

{
   DBM problems, see test_dbm
   Memcheck:Cond
   obj:/usr/lib/libdb1.so.2
   obj:/usr/lib/libdb1.so.2
   obj:/usr/lib/libdb1.so.2
   fun:dbm_store
   fun:dbm_ass_sub
}

{
   DBM problems, see test_dbm
   Memcheck:Cond
   fun:memmove
   obj:/usr/lib/libdb1.so.2
   obj:/usr/lib/libdb1.so.2
   obj:/usr/lib/libdb1.so.2
   obj:/usr/lib/libdb1.so.2
   fun:dbm_store
   fun:dbm_ass_sub
}

{
   GDBM problems, see test_gdbm
   Memcheck:Param
   write(buf)
   fun:write
   fun:gdbm_open

}

{
   Uninitialised byte(s) false alarm, see bpo-35561
   Memcheck:Param
   epoll_ctl(event)
   fun:epoll_ctl
   fun:pyepoll_internal_ctl
}

{
   ZLIB problems, see test_gzip
   Memcheck:Cond
   obj:/lib/libz.so.1.2.3
   obj:/lib/libz.so.1.2.3
   fun:deflate
}

{
   Avoid problems w/readline doing a putenv and leaking on exit
   Memcheck:Leak
   fun:malloc
   fun:xmalloc
   fun:sh_set_lines_and_columns
   fun:_rl_get_screen_size
   fun:_rl_init_terminal_io
   obj:/lib/libreadline.so.4.3
   fun:rl_initialize
}

# Valgrind emits "Conditional jump or move depends on uninitialised value(s)"
# false alarms on GCC builtin strcmp() function. The GCC code is correct.
#
# Valgrind bug: https://bugs.kde.org/show_bug.cgi?id=264936
{
   bpo-38118: Valgrind emits false alarm on GCC builtin strcmp()
   Memcheck:Cond
   fun:PyUnicode_Decode
}


###
### These occur from somewhere within the SSL, when running
###  test_socket_sll.  They are too general to leave on by default.
###
###{
###   somewhere in SSL stuff
###   Memcheck:Cond
###   fun:memset
###}
###{
###   somewhere in SSL stuff
###   Memcheck:Value4
###   fun:memset
###}
###
###{
###   somewhere in SSL stuff
###   Memcheck:Cond
###   fun:MD5_Update
###}
###
###{
###   somewhere in SSL stuff
###   Memcheck:Value4
###   fun:MD5_Update
###}

# Fedora's package "openssl-1.0.1-0.1.beta2.fc17.x86_64" on x86_64
# See http://bugs.python.org/issue14171
{
   openssl 1.0.1 prng 1
   Memcheck:Cond
   fun:bcmp
   fun:fips_get_entropy
   fun:FIPS_drbg_instantiate
   fun:RAND_init_fips
   fun:OPENSSL_init_library
   fun:SSL_library_init
   fun:init_hashlib
}

{
   openssl 1.0.1 prng 2
   Memcheck:Cond
   fun:fips_get_entropy
   fun:FIPS_drbg_instantiate
   fun:RAND_init_fips
   fun:OPENSSL_init_library
   fun:SSL_library_init
   fun:init_hashlib
}

{
   openssl 1.0.1 prng 3
   Memcheck:Value8
   fun:_x86_64_AES_encrypt_compact
   fun:AES_encrypt
}

#
# All of these problems come from using test_socket_ssl
#
{
   from test_socket_ssl
   Memcheck:Cond
   fun:BN_bin2bn
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:BN_num_bits_word
}

{
   from test_socket_ssl
   Memcheck:Value4
   fun:BN_num_bits_word
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:BN_mod_exp_mont_word
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:BN_mod_exp_mont
}

{
   from test_socket_ssl
   Memcheck:Param
   write(buf)
   fun:write
   obj:/usr/lib/libcrypto.so.0.9.7
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:RSA_verify
}

{
   from test_socket_ssl
   Memcheck:Value4
   fun:RSA_verify
}

{
   from test_socket_ssl
   Memcheck:Value4
   fun:DES_set_key_unchecked
}

{
   from test_socket_ssl
   Memcheck:Value4
   fun:DES_encrypt2
}

{
   from test_socket_ssl
   Memcheck:Cond
   obj:/usr/lib/libssl.so.0.9.7
}

{
   from test_socket_ssl
   Memcheck:Value4
   obj:/usr/lib/libssl.so.0.9.7
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:BUF_MEM_grow_clean
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:memcpy
   fun:ssl3_read_bytes
}

{
   from test_socket_ssl
   Memcheck:Cond
   fun:SHA1_Update
}

{
   from test_socket_ssl
   Memcheck:Value4
   fun:SHA1_Update
}

{
   test_buffer_non_debug
   Memcheck:Addr4
   fun:PyUnicodeUCS2_FSConverter
}

{
   test_buffer_non_debug
   Memcheck:Addr4
   fun:PyUnicode_FSConverter
}

{
   wcscmp_false_positive
   Memcheck:Addr8
   fun:wcscmp
   fun:_PyOS_GetOpt
   fun:Py_Main
   fun:main
}

# Additional suppressions for the unified decimal tests:
{
   test_decimal
   Memcheck:Addr4
   fun:PyUnicodeUCS2_FSConverter
}

{
   test_decimal2
   Memcheck:Addr4
   fun:PyUnicode_FSConverter
}

PK��\蘪zccalt-python39-devel/gdbinitnu�[���# If you use the GNU debugger gdb to debug the Python C runtime, you
# might find some of the following commands useful.  Copy this to your
# ~/.gdbinit file and it'll get loaded into gdb automatically when you
# start it up.  Then, at the gdb prompt you can do things like:
#
#    (gdb) pyo apyobjectptr
#    <module 'foobar' (built-in)>
#    refcounts: 1
#    address    : 84a7a2c
#    $1 = void
#    (gdb)
#
# NOTE: If you have gdb 7 or later, it supports debugging of Python directly
# with embedded macros that you may find superior to what is in here.
# See Tools/gdb/libpython.py and http://bugs.python.org/issue8032.

define pyo
    # side effect of calling _PyObject_Dump is to dump the object's
    # info - assigning just prevents gdb from printing the
    # NULL return value
    set $_unused_void = _PyObject_Dump($arg0)
end
document pyo
  Prints a representation of the object to stderr, along with the
  number of reference counts it currently has and the hex address the
  object is allocated at.  The argument must be a PyObject*
end

define pyg
    print _PyGC_Dump($arg0)
end
document pyg
  Prints a representation of the object to stderr, along with the
  number of reference counts it currently has and the hex address the
  object is allocated at.  The argument must be a PyGC_Head*
end

define pylocals
    set $_i = 0
    while $_i < f->f_code->co_nlocals
	if f->f_localsplus + $_i != 0
	    set $_names = f->f_code->co_varnames
	    set $_name = PyUnicode_AsUTF8(PyTuple_GetItem($_names, $_i))
	    printf "%s:\n", $_name
            pyo f->f_localsplus[$_i]
	end
        set $_i = $_i + 1
    end
end
document pylocals
  Print the local variables of the current frame.
end

# A rewrite of the Python interpreter's line number calculator in GDB's
# command language
define lineno
    set $__continue = 1
    set $__co = f->f_code
    set $__lasti = f->f_lasti
    set $__sz = ((PyVarObject *)$__co->co_lnotab)->ob_size/2
    set $__p = (unsigned char *)((PyBytesObject *)$__co->co_lnotab)->ob_sval
    set $__li = $__co->co_firstlineno
    set $__ad = 0
    while ($__sz-1 >= 0 && $__continue)
      set $__sz = $__sz - 1
      set $__ad = $__ad + *$__p
      set $__p = $__p + 1
      if ($__ad > $__lasti)
	set $__continue = 0
      else
        set $__li = $__li + *$__p
        set $__p = $__p + 1
      end
    end
    printf "%d", $__li
end

define pyframev
    pyframe
    pylocals
end
document pyframev
  Print the current frame - verbose
end

define pyframe
    set $__fn = PyUnicode_AsUTF8(f->f_code->co_filename)
    set $__n = PyUnicode_AsUTF8(f->f_code->co_name)
    printf "%s (", $__fn
    lineno
    printf "): %s\n", $__n
### Uncomment these lines when using from within Emacs/XEmacs so it will
### automatically track/display the current Python source line
#    printf "%c%c%s:", 032, 032, $__fn
#    lineno
#    printf ":1\n"
end

### Use these at your own risk.  It appears that a bug in gdb causes it
### to crash in certain circumstances.

#define up
#    up-silently 1
#    printframe
#end

#define down
#    down-silently 1
#    printframe
#end

define printframe
    if $pc > PyEval_EvalFrameEx && $pc < _PyEval_EvalFrameDefault
	pyframe
    else
        frame
    end
end

# Here's a somewhat fragile way to print the entire Python stack from gdb.
# It's fragile because the tests for the value of $pc depend on the layout
# of specific functions in the C source code.

# Explanation of while and if tests: We want to pop up the stack until we
# land in Py_Main (this is probably an incorrect assumption in an embedded
# interpreter, but the test can be extended by an interested party).  If
# Py_Main <= $pc <= Py_GetArgcArv is true, $pc is in Py_Main(), so the while
# tests succeeds as long as it's not true.  In a similar fashion the if
# statement tests to see if we are in PyEval_EvalFrameEx().

# Note: The name of the main interpreter function and the function which
# follow it has changed over time.  This version of pystack works with this
# version of Python.  If you try using it with older or newer versions of
# the interpreter you may will have to change the functions you compare with
# $pc.

define pystack
    while $pc < Py_Main || $pc > Py_GetArgcArgv
        if $pc > PyEval_EvalFrameEx && $pc < _PyEval_EvalFrameDefault
	    pyframe
        end
        up-silently 1
    end
    select-frame 0
end
document pystack
  Print the entire Python call stack
end

define pystackv
    while $pc < Py_Main || $pc > Py_GetArgcArgv
        if $pc > PyEval_EvalFrameEx && $pc < _PyEval_EvalFrameDefault
	    pyframev
        end
        up-silently 1
    end
    select-frame 0
end
document pystackv
  Print the entire Python call stack - verbose mode
end

define pu
  set $uni = $arg0
  set $i = 0
  while (*$uni && $i++<100)
    if (*$uni < 0x80)
      print *(char*)$uni++
    else
      print /x *(short*)$uni++
    end
  end
end
document pu
  Generally useful macro to print a Unicode string
end
PK��\�̈��"alt-python39-devel/README.valgrindnu�[���This document describes some caveats about the use of Valgrind with
Python.  Valgrind is used periodically by Python developers to try
to ensure there are no memory leaks or invalid memory reads/writes.

If you want to enable valgrind support in Python, you will need to
configure Python --with-valgrind option or an older option
--without-pymalloc.

UPDATE: Python 3.6 now supports PYTHONMALLOC=malloc environment variable which
can be used to force the usage of the malloc() allocator of the C library.

If you don't want to read about the details of using Valgrind, there
are still two things you must do to suppress the warnings.  First,
you must use a suppressions file.  One is supplied in
Misc/valgrind-python.supp.  Second, you must uncomment the lines in 
Misc/valgrind-python.supp that suppress the warnings for PyObject_Free and
PyObject_Realloc.

If you want to use Valgrind more effectively and catch even more
memory leaks, you will need to configure python --without-pymalloc.
PyMalloc allocates a few blocks in big chunks and most object
allocations don't call malloc, they use chunks doled about by PyMalloc
from the big blocks.  This means Valgrind can't detect
many allocations (and frees), except for those that are forwarded
to the system malloc.  Note: configuring python --without-pymalloc
makes Python run much slower, especially when running under Valgrind.
You may need to run the tests in batches under Valgrind to keep
the memory usage down to allow the tests to complete.  It seems to take
about 5 times longer to run --without-pymalloc.

Apr 15, 2006:
  test_ctypes causes Valgrind 3.1.1 to fail (crash).
  test_socket_ssl should be skipped when running valgrind.
	The reason is that it purposely uses uninitialized memory.
	This causes many spurious warnings, so it's easier to just skip it.


Details:
--------
Python uses its own small-object allocation scheme on top of malloc,
called PyMalloc.

Valgrind may show some unexpected results when PyMalloc is used.
Starting with Python 2.3, PyMalloc is used by default.  You can disable
PyMalloc when configuring python by adding the --without-pymalloc option.
If you disable PyMalloc, most of the information in this document and
the supplied suppressions file will not be useful.  As discussed above,
disabling PyMalloc can catch more problems.

PyMalloc uses 256KB chunks of memory, so it can't detect anything
wrong within these blocks.  For that reason, compiling Python
--without-pymalloc usually increases the usefulness of other tools.

If you use valgrind on a default build of Python,  you will see
many errors like:

        ==6399== Use of uninitialised value of size 4
        ==6399== at 0x4A9BDE7E: PyObject_Free (obmalloc.c:711)
        ==6399== by 0x4A9B8198: dictresize (dictobject.c:477)

These are expected and not a problem.  Tim Peters explains
the situation:

        PyMalloc needs to know whether an arbitrary address is one
	that's managed by it, or is managed by the system malloc.
	The current scheme allows this to be determined in constant
	time, regardless of how many memory areas are under pymalloc's
	control.

        The memory pymalloc manages itself is in one or more "arenas",
	each a large contiguous memory area obtained from malloc.
	The base address of each arena is saved by pymalloc
	in a vector.  Each arena is carved into "pools", and a field at
	the start of each pool contains the index of that pool's arena's
	base address in that vector.

        Given an arbitrary address, pymalloc computes the pool base
	address corresponding to it, then looks at "the index" stored
	near there.  If the index read up is out of bounds for the
	vector of arena base addresses pymalloc maintains, then
	pymalloc knows for certain that this address is not under
	pymalloc's control.  Otherwise the index is in bounds, and
	pymalloc compares

            the arena base address stored at that index in the vector

        to

            the arbitrary address pymalloc is investigating

        pymalloc controls this arbitrary address if and only if it lies
        in the arena the address's pool's index claims it lies in.

        It doesn't matter whether the memory pymalloc reads up ("the
	index") is initialized.  If it's not initialized, then
	whatever trash gets read up will lead pymalloc to conclude
	(correctly) that the address isn't controlled by it, either
	because the index is out of bounds, or the index is in bounds
	but the arena it represents doesn't contain the address.

        This determination has to be made on every call to one of
	pymalloc's free/realloc entry points, so its speed is critical
	(Python allocates and frees dynamic memory at a ferocious rate
	-- everything in Python, from integers to "stack frames",
	lives in the heap).
PK��\ԉ�y�'�'alt-python39-libs/README.rstnu�[���This is Python version 3.9.23
=============================

.. image:: https://travis-ci.org/python/cpython.svg?branch=3.9
   :alt: CPython build status on Travis CI
   :target: https://travis-ci.org/python/cpython

.. image:: https://github.com/python/cpython/workflows/Tests/badge.svg
   :alt: CPython build status on GitHub Actions
   :target: https://github.com/python/cpython/actions

.. image:: https://dev.azure.com/python/cpython/_apis/build/status/Azure%20Pipelines%20CI?branchName=3.9
   :alt: CPython build status on Azure DevOps
   :target: https://dev.azure.com/python/cpython/_build/latest?definitionId=4&branchName=3.9

.. image:: https://codecov.io/gh/python/cpython/branch/3.9/graph/badge.svg
   :alt: CPython code coverage on Codecov
   :target: https://codecov.io/gh/python/cpython

.. image:: https://img.shields.io/badge/discourse-join_chat-brightgreen.svg
   :alt: Python Discourse chat
   :target: https://discuss.python.org/


Copyright © 2001-2023 Python Software Foundation.  All rights reserved.

See the end of this file for further copyright and license information.

.. contents::

General Information
-------------------

- Website: https://www.python.org
- Source code: https://github.com/python/cpython
- Issue tracker: https://bugs.python.org
- Documentation: https://docs.python.org
- Developer's Guide: https://devguide.python.org/

Contributing to CPython
-----------------------

For more complete instructions on contributing to CPython development,
see the `Developer Guide`_.

.. _Developer Guide: https://devguide.python.org/

Using Python
------------

Installable Python kits, and information about using Python, are available at
`python.org`_.

.. _python.org: https://www.python.org/

Build Instructions
------------------

On Unix, Linux, BSD, macOS, and Cygwin::

    ./configure
    make
    make test
    sudo make install

This will install Python as ``python3``.

You can pass many options to the configure script; run ``./configure --help``
to find out more.  On macOS case-insensitive file systems and on Cygwin,
the executable is called ``python.exe``; elsewhere it's just ``python``.

Building a complete Python installation requires the use of various
additional third-party libraries, depending on your build platform and
configure options.  Not all standard library modules are buildable or
useable on all platforms.  Refer to the
`Install dependencies <https://devguide.python.org/setup/#install-dependencies>`_
section of the `Developer Guide`_ for current detailed information on
dependencies for various Linux distributions and macOS.

On macOS, there are additional configure and build options related
to macOS framework and universal builds.  Refer to `Mac/README.rst
<https://github.com/python/cpython/blob/3.9/Mac/README.rst>`_.

On Windows, see `PCbuild/readme.txt
<https://github.com/python/cpython/blob/3.9/PCbuild/readme.txt>`_.

If you wish, you can create a subdirectory and invoke configure from there.
For example::

    mkdir debug
    cd debug
    ../configure --with-pydebug
    make
    make test

(This will fail if you *also* built at the top-level directory.  You should do
a ``make clean`` at the top-level first.)

To get an optimized build of Python, ``configure --enable-optimizations``
before you run ``make``.  This sets the default make targets up to enable
Profile Guided Optimization (PGO) and may be used to auto-enable Link Time
Optimization (LTO) on some platforms.  For more details, see the sections
below.

Profile Guided Optimization
^^^^^^^^^^^^^^^^^^^^^^^^^^^

PGO takes advantage of recent versions of the GCC or Clang compilers.  If used,
either via ``configure --enable-optimizations`` or by manually running
``make profile-opt`` regardless of configure flags, the optimized build
process will perform the following steps:

The entire Python directory is cleaned of temporary files that may have
resulted from a previous compilation.

An instrumented version of the interpreter is built, using suitable compiler
flags for each flavor. Note that this is just an intermediary step.  The
binary resulting from this step is not good for real-life workloads as it has
profiling instructions embedded inside.

After the instrumented interpreter is built, the Makefile will run a training
workload.  This is necessary in order to profile the interpreter's execution.
Note also that any output, both stdout and stderr, that may appear at this step
is suppressed.

The final step is to build the actual interpreter, using the information
collected from the instrumented one.  The end result will be a Python binary
that is optimized; suitable for distribution or production installation.


Link Time Optimization
^^^^^^^^^^^^^^^^^^^^^^

Enabled via configure's ``--with-lto`` flag.  LTO takes advantage of the
ability of recent compiler toolchains to optimize across the otherwise
arbitrary ``.o`` file boundary when building final executables or shared
libraries for additional performance gains.


What's New
----------

We have a comprehensive overview of the changes in the `What's New in Python
3.9 <https://docs.python.org/3.9/whatsnew/3.9.html>`_ document.  For a more
detailed change log, read `Misc/NEWS
<https://github.com/python/cpython/blob/3.9/Misc/NEWS.d>`_, but a full
accounting of changes can only be gleaned from the `commit history
<https://github.com/python/cpython/commits/3.9>`_.

If you want to install multiple versions of Python, see the section below
entitled "Installing multiple versions".


Documentation
-------------

`Documentation for Python 3.9 <https://docs.python.org/3.9/>`_ is online,
updated daily.

It can also be downloaded in many formats for faster access.  The documentation
is downloadable in HTML, PDF, and reStructuredText formats; the latter version
is primarily for documentation authors, translators, and people with special
formatting requirements.

For information about building Python's documentation, refer to `Doc/README.rst
<https://github.com/python/cpython/blob/3.9/Doc/README.rst>`_.


Converting From Python 2.x to 3.x
---------------------------------

Significant backward incompatible changes were made for the release of Python
3.0, which may cause programs written for Python 2 to fail when run with Python
3.  For more information about porting your code from Python 2 to Python 3, see
the `Porting HOWTO <https://docs.python.org/3/howto/pyporting.html>`_.


Testing
-------

To test the interpreter, type ``make test`` in the top-level directory.  The
test set produces some output.  You can generally ignore the messages about
skipped tests due to optional features which can't be imported.  If a message
is printed about a failed test or a traceback or core dump is produced,
something is wrong.

By default, tests are prevented from overusing resources like disk space and
memory.  To enable these tests, run ``make testall``.

If any tests fail, you can re-run the failing test(s) in verbose mode.  For
example, if ``test_os`` and ``test_gdb`` failed, you can run::

    make test TESTOPTS="-v test_os test_gdb"

If the failure persists and appears to be a problem with Python rather than
your environment, you can `file a bug report <https://bugs.python.org>`_ and
include relevant output from that command to show the issue.

See `Running & Writing Tests <https://devguide.python.org/runtests/>`_
for more on running tests.

Installing multiple versions
----------------------------

On Unix and Mac systems if you intend to install multiple versions of Python
using the same installation prefix (``--prefix`` argument to the configure
script) you must take care that your primary python executable is not
overwritten by the installation of a different version.  All files and
directories installed using ``make altinstall`` contain the major and minor
version and can thus live side-by-side.  ``make install`` also creates
``${prefix}/bin/python3`` which refers to ``${prefix}/bin/pythonX.Y``.  If you
intend to install multiple versions using the same prefix you must decide which
version (if any) is your "primary" version.  Install that version using ``make
install``.  Install all other versions using ``make altinstall``.

For example, if you want to install Python 2.7, 3.6, and 3.9 with 3.9 being the
primary version, you would execute ``make install`` in your 3.9 build directory
and ``make altinstall`` in the others.


Issue Tracker and Mailing List
------------------------------

Bug reports are welcome!  You can use the `issue tracker
<https://bugs.python.org>`_ to report bugs, and/or submit pull requests `on
GitHub <https://github.com/python/cpython>`_.

You can also follow development discussion on the `python-dev mailing list
<https://mail.python.org/mailman/listinfo/python-dev/>`_.


Proposals for enhancement
-------------------------

If you have a proposal to change Python, you may want to send an email to the
`comp.lang.python`_ or `python-ideas`_ mailing lists for initial feedback.  A
Python Enhancement Proposal (PEP) may be submitted if your idea gains ground.
All current PEPs, as well as guidelines for submitting a new PEP, are listed at
`python.org/dev/peps/ <https://www.python.org/dev/peps/>`_.

.. _python-ideas: https://mail.python.org/mailman/listinfo/python-ideas/
.. _comp.lang.python: https://mail.python.org/mailman/listinfo/python-list


Release Schedule
----------------

See :pep:`596` for Python 3.9 release details.


Copyright and License Information
---------------------------------

Copyright © 2001-2023 Python Software Foundation.  All rights reserved.

Copyright © 2000 BeOpen.com.  All rights reserved.

Copyright © 1995-2001 Corporation for National Research Initiatives.  All
rights reserved.

Copyright © 1991-1995 Stichting Mathematisch Centrum.  All rights reserved.

See the file "LICENSE" for information on the history of this software, terms &
conditions for usage, and a DISCLAIMER OF ALL WARRANTIES.

This Python distribution contains *no* GNU General Public License (GPL) code,
so it may be used in proprietary projects.  There are interfaces to some GNU
code but these are entirely optional.

All trademarks referenced herein are property of their respective holders.PK��\�Yďalt-python39-setuptools/LICENSEnu�[���Copyright Jason R. Coombs

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
PK��\_��alt-python39-setuptools/zpl.txtnu�[���Zope Public License (ZPL) Version 2.1

A copyright notice accompanies this license document that identifies the
copyright holders.

This license has been certified as open source. It has also been designated as
GPL compatible by the Free Software Foundation (FSF).

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions in source code must retain the accompanying copyright
notice, this list of conditions, and the following disclaimer.

2. Redistributions in binary form must reproduce the accompanying copyright
notice, this list of conditions, and the following disclaimer in the
documentation and/or other materials provided with the distribution.

3. Names of the copyright holders must not be used to endorse or promote
products derived from this software without prior written permission from the
copyright holders.

4. The right to distribute this software or to use it for any purpose does not
give you the right to use Servicemarks (sm) or Trademarks (tm) of the
copyright
holders. Use of them is covered by separate agreement with the copyright
holders.

5. If any files are modified, you must cause the modified files to carry
prominent notices stating that you changed the files and the date of any
change.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
PK��\SoD �1�1 alt-python39-setuptools/psfl.txtnu�[���A. HISTORY OF THE SOFTWARE
==========================

Python was created in the early 1990s by Guido van Rossum at Stichting
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
as a successor of a language called ABC.  Guido remains Python's
principal author, although it includes many contributions from others.

In 1995, Guido continued his work on Python at the Corporation for
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
in Reston, Virginia where he released several versions of the
software.

In May 2000, Guido and the Python core development team moved to
BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
year, the PythonLabs team moved to Digital Creations (now Zope
Corporation, see http://www.zope.com).  In 2001, the Python Software
Foundation (PSF, see http://www.python.org/psf/) was formed, a
non-profit organization created specifically to own Python-related
Intellectual Property.  Zope Corporation is a sponsoring member of
the PSF.

All Python releases are Open Source (see http://www.opensource.org for
the Open Source Definition).  Historically, most, but not all, Python
releases have also been GPL-compatible; the table below summarizes
the various releases.

    Release         Derived     Year        Owner       GPL-
                    from                                compatible? (1)

    0.9.0 thru 1.2              1991-1995   CWI         yes
    1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
    1.6             1.5.2       2000        CNRI        no
    2.0             1.6         2000        BeOpen.com  no
    1.6.1           1.6         2001        CNRI        yes (2)
    2.1             2.0+1.6.1   2001        PSF         no
    2.0.1           2.0+1.6.1   2001        PSF         yes
    2.1.1           2.1+2.0.1   2001        PSF         yes
    2.1.2           2.1.1       2002        PSF         yes
    2.1.3           2.1.2       2002        PSF         yes
    2.2 and above   2.1.1       2001-now    PSF         yes

Footnotes:

(1) GPL-compatible doesn't mean that we're distributing Python under
    the GPL.  All Python licenses, unlike the GPL, let you distribute
    a modified version without making your changes open source.  The
    GPL-compatible licenses make it possible to combine Python with
    other software that is released under the GPL; the others don't.

(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
    because its license has a choice of law clause.  According to
    CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
    is "not incompatible" with the GPL.

Thanks to the many outside volunteers who have worked under Guido's
direction to make these releases possible.


B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
===============================================================

PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
--------------------------------------------

1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using this software ("Python") in source or binary form and
its associated documentation.

2. Subject to the terms and conditions of this License Agreement, PSF hereby
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python alone or in any derivative version,
provided, however, that PSF's License Agreement and PSF's notice of copyright,
i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2011, 2012, 2013, 2014, 2015, 2016 Python Software Foundation; All Rights
Reserved" are retained in Python alone or in any derivative version prepared by
Licensee.

3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python.

4. PSF is making Python available to Licensee on an "AS IS"
basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.

5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.

6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.

7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee.  This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.

8. By copying, installing or otherwise using Python, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.


BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
-------------------------------------------

BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1

1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
Individual or Organization ("Licensee") accessing and otherwise using
this software in source or binary form and its associated
documentation ("the Software").

2. Subject to the terms and conditions of this BeOpen Python License
Agreement, BeOpen hereby grants Licensee a non-exclusive,
royalty-free, world-wide license to reproduce, analyze, test, perform
and/or display publicly, prepare derivative works, distribute, and
otherwise use the Software alone or in any derivative version,
provided, however, that the BeOpen Python License is retained in the
Software, alone or in any derivative version prepared by Licensee.

3. BeOpen is making the Software available to Licensee on an "AS IS"
basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.

4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.

5. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.

6. This License Agreement shall be governed by and interpreted in all
respects by the law of the State of California, excluding conflict of
law provisions.  Nothing in this License Agreement shall be deemed to
create any relationship of agency, partnership, or joint venture
between BeOpen and Licensee.  This License Agreement does not grant
permission to use BeOpen trademarks or trade names in a trademark
sense to endorse or promote products or services of Licensee, or any
third party.  As an exception, the "BeOpen Python" logos available at
http://www.pythonlabs.com/logos.html may be used according to the
permissions granted on that web page.

7. By copying, installing or otherwise using the software, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.


CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
---------------------------------------

1. This LICENSE AGREEMENT is between the Corporation for National
Research Initiatives, having an office at 1895 Preston White Drive,
Reston, VA 20191 ("CNRI"), and the Individual or Organization
("Licensee") accessing and otherwise using Python 1.6.1 software in
source or binary form and its associated documentation.

2. Subject to the terms and conditions of this License Agreement, CNRI
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python 1.6.1
alone or in any derivative version, provided, however, that CNRI's
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
1995-2001 Corporation for National Research Initiatives; All Rights
Reserved" are retained in Python 1.6.1 alone or in any derivative
version prepared by Licensee.  Alternately, in lieu of CNRI's License
Agreement, Licensee may substitute the following text (omitting the
quotes): "Python 1.6.1 is made available subject to the terms and
conditions in CNRI's License Agreement.  This Agreement together with
Python 1.6.1 may be located on the Internet using the following
unique, persistent identifier (known as a handle): 1895.22/1013.  This
Agreement may also be obtained from a proxy server on the Internet
using the following URL: http://hdl.handle.net/1895.22/1013".

3. In the event Licensee prepares a derivative work that is based on
or incorporates Python 1.6.1 or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python 1.6.1.

4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.

5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.

6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.

7. This License Agreement shall be governed by the federal
intellectual property law of the United States, including without
limitation the federal copyright law, and, to the extent such
U.S. federal law does not apply, by the law of the Commonwealth of
Virginia, excluding Virginia's conflict of law provisions.
Notwithstanding the foregoing, with regard to derivative works based
on Python 1.6.1 that incorporate non-separable material that was
previously distributed under the GNU General Public License (GPL), the
law of the Commonwealth of Virginia shall govern this License
Agreement only as to issues arising under or with respect to
Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
License Agreement shall be deemed to create any relationship of
agency, partnership, or joint venture between CNRI and Licensee.  This
License Agreement does not grant permission to use CNRI trademarks or
trade name in a trademark sense to endorse or promote products or
services of Licensee, or any third party.

8. By clicking on the "ACCEPT" button where indicated, or by copying,
installing or otherwise using Python 1.6.1, Licensee agrees to be
bound by the terms and conditions of this License Agreement.

        ACCEPT


CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
--------------------------------------------------

Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
The Netherlands.  All rights reserved.

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.

STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
PK��\���&alt-python39-setuptools/docs/index.rstnu�[���setuptools
==========

Setuptools is a fully-featured, actively-maintained, and stable library
designed to facilitate packaging Python projects.

.. toctree::
   :maxdepth: 1
   :hidden:

   User guide <userguide/index>
   build_meta
   pkg_resources
   references/keywords
   roadmap
   setuptools
   Development guide <development/index>
   Backward compatibility & deprecated practice <deprecated/index>
   Changelog <history>

.. tidelift-referral-banner::
PK��\��U��8alt-python39-setuptools/docs/deprecated/easy_install.rstnu�[���============
Easy Install
============

.. warning::
    Easy Install is deprecated. Do not use it. Instead use pip. If
    you think you need Easy Install, please reach out to the PyPA
    team (a ticket to pip or setuptools is fine), describing your
    use-case.

Easy Install is a python module (``easy_install``) bundled with ``setuptools``
that lets you automatically download, build, install, and manage Python
packages.

Please share your experiences with us! If you encounter difficulty installing
a package, please contact us via the `distutils mailing list
<http://mail.python.org/pipermail/distutils-sig/>`_.  (Note: please DO NOT send
private email directly to the author of setuptools; it will be discarded.  The
mailing list is a searchable archive of previously-asked and answered
questions; you should begin your research there before reporting something as a
bug -- and then do so via list discussion first.)

(Also, if you'd like to learn about how you can use ``setuptools`` to make your
own packages work better with EasyInstall, or provide EasyInstall-like features
without requiring your users to use EasyInstall directly, you'll probably want
to check out the full documentation as well.)

Using "Easy Install"
====================


.. _installation instructions:

Installing "Easy Install"
-------------------------

Please see the `setuptools PyPI page <https://pypi.org/project/setuptools/>`_
for download links and basic installation instructions for each of the
supported platforms.

You will need at least Python 3.5 or 2.7.  An ``easy_install`` script will be
installed in the normal location for Python scripts on your platform.

Note that the instructions on the setuptools PyPI page assume that you are
are installing to Python's primary ``site-packages`` directory.  If this is
not the case, you should consult the section below on `Custom Installation
Locations`_ before installing.  (And, on Windows, you should not use the
``.exe`` installer when installing to an alternate location.)

Note that ``easy_install`` normally works by downloading files from the
internet.  If you are behind an NTLM-based firewall that prevents Python
programs from accessing the net directly, you may wish to first install and use
the `APS proxy server <http://ntlmaps.sf.net/>`_, which lets you get past such
firewalls in the same way that your web browser(s) do.

(Alternately, if you do not wish easy_install to actually download anything, you
can restrict it from doing so with the ``--allow-hosts`` option; see the
sections on `restricting downloads with --allow-hosts`_ and `command-line
options`_ for more details.)


Troubleshooting
~~~~~~~~~~~~~~~

If EasyInstall/setuptools appears to install correctly, and you can run the
``easy_install`` command but it fails with an ``ImportError``, the most likely
cause is that you installed to a location other than ``site-packages``,
without taking any of the steps described in the `Custom Installation
Locations`_ section below.  Please see that section and follow the steps to
make sure that your custom location will work correctly.  Then re-install.

Similarly, if you can run ``easy_install``, and it appears to be installing
packages, but then you can't import them, the most likely issue is that you
installed EasyInstall correctly but are using it to install packages to a
non-standard location that hasn't been properly prepared.  Again, see the
section on `Custom Installation Locations`_ for more details.


Windows Notes
~~~~~~~~~~~~~

Installing setuptools will provide an ``easy_install`` command according to
the techniques described in `Executables and Launchers`_. If the
``easy_install`` command is not available after installation, that section
provides details on how to configure Windows to make the commands available.


Downloading and Installing a Package
------------------------------------

For basic use of ``easy_install``, you need only supply the filename or URL of
a source distribution or .egg file (`Python Egg`__).

__ http://peak.telecommunity.com/DevCenter/PythonEggs

**Example 1**. Install a package by name, searching PyPI for the latest
version, and automatically downloading, building, and installing it::

    easy_install SQLObject

**Example 2**. Install or upgrade a package by name and version by finding
links on a given "download page"::

    easy_install -f http://pythonpaste.org/package_index.html SQLObject

**Example 3**. Download a source distribution from a specified URL,
automatically building and installing it::

    easy_install http://example.com/path/to/MyPackage-1.2.3.tgz

**Example 4**. Install an already-downloaded .egg file::

    easy_install /my_downloads/OtherPackage-3.2.1-py2.3.egg

**Example 5**.  Upgrade an already-installed package to the latest version
listed on PyPI::

    easy_install --upgrade PyProtocols

**Example 6**.  Install a source distribution that's already downloaded and
extracted in the current directory (New in 0.5a9)::

    easy_install .

**Example 7**.  (New in 0.6a1) Find a source distribution or Subversion
checkout URL for a package, and extract it or check it out to
``~/projects/sqlobject`` (the name will always be in all-lowercase), where it
can be examined or edited.  (The package will not be installed, but it can
easily be installed with ``easy_install ~/projects/sqlobject``.  See `Editing
and Viewing Source Packages`_ below for more info.)::

    easy_install --editable --build-directory ~/projects SQLObject

**Example 7**. (New in 0.6.11) Install a distribution within your home dir::

    easy_install --user SQLAlchemy

Easy Install accepts URLs, filenames, PyPI package names (i.e., ``distutils``
"distribution" names), and package+version specifiers.  In each case, it will
attempt to locate the latest available version that meets your criteria.

When downloading or processing downloaded files, Easy Install recognizes
distutils source distribution files with extensions of .tgz, .tar, .tar.gz,
.tar.bz2, or .zip.  And of course it handles already-built .egg
distributions as well as ``.win32.exe`` installers built using distutils.

By default, packages are installed to the running Python installation's
``site-packages`` directory, unless you provide the ``-d`` or ``--install-dir``
option to specify an alternative directory, or specify an alternate location
using distutils configuration files.  (See `Configuration Files`_, below.)

By default, any scripts included with the package are installed to the running
Python installation's standard script installation location.  However, if you
specify an installation directory via the command line or a config file, then
the default directory for installing scripts will be the same as the package
installation directory, to ensure that the script will have access to the
installed package.  You can override this using the ``-s`` or ``--script-dir``
option.

Installed packages are added to an ``easy-install.pth`` file in the install
directory, so that Python will always use the most-recently-installed version
of the package.  If you would like to be able to select which version to use at
runtime, you should use the ``-m`` or ``--multi-version`` option.


Upgrading a Package
-------------------

You don't need to do anything special to upgrade a package: just install the
new version, either by requesting a specific version, e.g.::

    easy_install "SomePackage==2.0"

a version greater than the one you have now::

    easy_install "SomePackage>2.0"

using the upgrade flag, to find the latest available version on PyPI::

    easy_install --upgrade SomePackage

or by using a download page, direct download URL, or package filename::

    easy_install -f http://example.com/downloads ExamplePackage

    easy_install http://example.com/downloads/ExamplePackage-2.0-py2.4.egg

    easy_install my_downloads/ExamplePackage-2.0.tgz

If you're using ``-m`` or ``--multi-version`` , using the ``require()``
function at runtime automatically selects the newest installed version of a
package that meets your version criteria.  So, installing a newer version is
the only step needed to upgrade such packages.

If you're installing to a directory on PYTHONPATH, or a configured "site"
directory (and not using ``-m``), installing a package automatically replaces
any previous version in the ``easy-install.pth`` file, so that Python will
import the most-recently installed version by default.  So, again, installing
the newer version is the only upgrade step needed.

If you haven't suppressed script installation (using ``--exclude-scripts`` or
``-x``), then the upgraded version's scripts will be installed, and they will
be automatically patched to ``require()`` the corresponding version of the
package, so that you can use them even if they are installed in multi-version
mode.

``easy_install`` never actually deletes packages (unless you're installing a
package with the same name and version number as an existing package), so if
you want to get rid of older versions of a package, please see `Uninstalling
Packages`_, below.


Changing the Active Version
---------------------------

If you've upgraded a package, but need to revert to a previously-installed
version, you can do so like this::

    easy_install PackageName==1.2.3

Where ``1.2.3`` is replaced by the exact version number you wish to switch to.
If a package matching the requested name and version is not already installed
in a directory on ``sys.path``, it will be located via PyPI and installed.

If you'd like to switch to the latest installed version of ``PackageName``, you
can do so like this::

    easy_install PackageName

This will activate the latest installed version.  (Note: if you have set any
``find_links`` via distutils configuration files, those download pages will be
checked for the latest available version of the package, and it will be
downloaded and installed if it is newer than your current version.)

Note that changing the active version of a package will install the newly
active version's scripts, unless the ``--exclude-scripts`` or ``-x`` option is
specified.


Uninstalling Packages
---------------------

If you have replaced a package with another version, then you can just delete
the package(s) you don't need by deleting the PackageName-versioninfo.egg file
or directory (found in the installation directory).

If you want to delete the currently installed version of a package (or all
versions of a package), you should first run::

    easy_install -m PackageName

This will ensure that Python doesn't continue to search for a package you're
planning to remove. After you've done this, you can safely delete the .egg
files or directories, along with any scripts you wish to remove.


Managing Scripts
----------------

Whenever you install, upgrade, or change versions of a package, EasyInstall
automatically installs the scripts for the selected package version, unless
you tell it not to with ``-x`` or ``--exclude-scripts``.  If any scripts in
the script directory have the same name, they are overwritten.

Thus, you do not normally need to manually delete scripts for older versions of
a package, unless the newer version of the package does not include a script
of the same name.  However, if you are completely uninstalling a package, you
may wish to manually delete its scripts.

EasyInstall's default behavior means that you can normally only run scripts
from one version of a package at a time.  If you want to keep multiple versions
of a script available, however, you can simply use the ``--multi-version`` or
``-m`` option, and rename the scripts that EasyInstall creates.  This works
because EasyInstall installs scripts as short code stubs that ``require()`` the
matching version of the package the script came from, so renaming the script
has no effect on what it executes.

For example, suppose you want to use two versions of the ``rst2html`` tool
provided by the `docutils <http://docutils.sf.net/>`_ package.  You might
first install one version::

    easy_install -m docutils==0.3.9

then rename the ``rst2html.py`` to ``r2h_039``, and install another version::

    easy_install -m docutils==0.3.10

This will create another ``rst2html.py`` script, this one using docutils
version 0.3.10 instead of 0.3.9.  You now have two scripts, each using a
different version of the package.  (Notice that we used ``-m`` for both
installations, so that Python won't lock us out of using anything but the most
recently-installed version of the package.)


Executables and Launchers
-------------------------

On Unix systems, scripts are installed with as natural files with a "#!"
header and no extension and they launch under the Python version indicated in
the header.

On Windows, there is no mechanism to "execute" files without extensions, so
EasyInstall provides two techniques to mirror the Unix behavior. The behavior
is indicated by the SETUPTOOLS_LAUNCHER environment variable, which may be
"executable" (default) or "natural".

Regardless of the technique used, the script(s) will be installed to a Scripts
directory (by default in the Python installation directory). It is recommended
for EasyInstall that you ensure this directory is in the PATH environment
variable. The easiest way to ensure the Scripts directory is in the PATH is
to run ``Tools\Scripts\win_add2path.py`` from the Python directory.

Note that instead of changing your ``PATH`` to include the Python scripts
directory, you can also retarget the installation location for scripts so they
go on a directory that's already on the ``PATH``.  For more information see
`Command-Line Options`_ and `Configuration Files`_.  During installation,
pass command line options (such as ``--script-dir``) to control where
scripts will be installed.


Windows Executable Launcher
~~~~~~~~~~~~~~~~~~~~~~~~~~~

If the "executable" launcher is used, EasyInstall will create a '.exe'
launcher of the same name beside each installed script (including
``easy_install`` itself). These small .exe files launch the script of the
same name using the Python version indicated in the '#!' header.

This behavior is currently default. To force
the use of executable launchers, set ``SETUPTOOLS_LAUNCHER`` to "executable".

Natural Script Launcher
~~~~~~~~~~~~~~~~~~~~~~~

EasyInstall also supports deferring to an external launcher such as
`pylauncher <https://bitbucket.org/pypa/pylauncher>`_ for launching scripts.
Enable this experimental functionality by setting the
``SETUPTOOLS_LAUNCHER`` environment variable to "natural". EasyInstall will
then install scripts as simple
scripts with a .pya (or .pyw) extension appended. If these extensions are
associated with the pylauncher and listed in the PATHEXT environment variable,
these scripts can then be invoked simply and directly just like any other
executable. This behavior may become default in a future version.

EasyInstall uses the .pya extension instead of simply
the typical '.py' extension. This distinct extension is necessary to prevent
Python
from treating the scripts as importable modules (where name conflicts exist).
Current releases of pylauncher do not yet associate with .pya files by
default, but future versions should do so.


Tips & Techniques
-----------------

Multiple Python Versions
~~~~~~~~~~~~~~~~~~~~~~~~

EasyInstall installs itself under two names:
``easy_install`` and ``easy_install-N.N``, where ``N.N`` is the Python version
used to install it.  Thus, if you install EasyInstall for both Python 3.2 and
2.7, you can use the ``easy_install-3.2`` or ``easy_install-2.7`` scripts to
install packages for the respective Python version.

Setuptools also supplies easy_install as a runnable module which may be
invoked using ``python -m easy_install`` for any Python with Setuptools
installed.

Restricting Downloads with ``--allow-hosts``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can use the ``--allow-hosts`` (``-H``) option to restrict what domains
EasyInstall will look for links and downloads on.  ``--allow-hosts=None``
prevents downloading altogether.  You can also use wildcards, for example
to restrict downloading to hosts in your own intranet.  See the section below
on `Command-Line Options`_ for more details on the ``--allow-hosts`` option.

By default, there are no host restrictions in effect, but you can change this
default by editing the appropriate `configuration files`_ and adding:

.. code-block:: ini

    [easy_install]
    allow_hosts = *.myintranet.example.com,*.python.org

The above example would then allow downloads only from hosts in the
``python.org`` and ``myintranet.example.com`` domains, unless overridden on the
command line.


Installing on Un-networked Machines
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Just copy the eggs or source packages you need to a directory on the target
machine, then use the ``-f`` or ``--find-links`` option to specify that
directory's location.  For example::

    easy_install -H None -f somedir SomePackage

will attempt to install SomePackage using only eggs and source packages found
in ``somedir`` and disallowing all remote access.  You should of course make
sure you have all of SomePackage's dependencies available in somedir.

If you have another machine of the same operating system and library versions
(or if the packages aren't platform-specific), you can create the directory of
eggs using a command like this::

    easy_install -zmaxd somedir SomePackage

This will tell EasyInstall to put zipped eggs or source packages for
SomePackage and all its dependencies into ``somedir``, without creating any
scripts or .pth files.  You can then copy the contents of ``somedir`` to the
target machine.  (``-z`` means zipped eggs, ``-m`` means multi-version, which
prevents .pth files from being used, ``-a`` means to copy all the eggs needed,
even if they're installed elsewhere on the machine, and ``-d`` indicates the
directory to place the eggs in.)

You can also build the eggs from local development packages that were installed
with the ``setup.py develop`` command, by including the ``-l`` option, e.g.::

    easy_install -zmaxld somedir SomePackage

This will use locally-available source distributions to build the eggs.


Packaging Others' Projects As Eggs
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Need to distribute a package that isn't published in egg form?  You can use
EasyInstall to build eggs for a project.  You'll want to use the ``--zip-ok``,
``--exclude-scripts``, and possibly ``--no-deps`` options (``-z``, ``-x`` and
``-N``, respectively).  Use ``-d`` or ``--install-dir`` to specify the location
where you'd like the eggs placed.  By placing them in a directory that is
published to the web, you can then make the eggs available for download, either
in an intranet or to the internet at large.

If someone distributes a package in the form of a single ``.py`` file, you can
wrap it in an egg by tacking an ``#egg=name-version`` suffix on the file's URL.
So, something like this::

    easy_install -f "http://some.example.com/downloads/foo.py#egg=foo-1.0" foo

will install the package as an egg, and this::

    easy_install -zmaxd. \
        -f "http://some.example.com/downloads/foo.py#egg=foo-1.0" foo

will create a ``.egg`` file in the current directory.


Creating your own Package Index
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In addition to local directories and the Python Package Index, EasyInstall can
find download links on most any web page whose URL is given to the ``-f``
(``--find-links``) option.  In the simplest case, you can simply have a web
page with links to eggs or Python source packages, even an automatically
generated directory listing (such as the Apache web server provides).

If you are setting up an intranet site for package downloads, you may want to
configure the target machines to use your download site by default, adding
something like this to their `configuration files`_:

.. code-block:: ini

    [easy_install]
    find_links = http://mypackages.example.com/somedir/
                 http://turbogears.org/download/
                 http://peak.telecommunity.com/dist/

As you can see, you can list multiple URLs separated by whitespace, continuing
on multiple lines if necessary (as long as the subsequent lines are indented.

If you are more ambitious, you can also create an entirely custom package index
or PyPI mirror.  See the ``--index-url`` option under `Command-Line Options`_,
below, and also the section on `Package Index "API"`_.


Password-Protected Sites
------------------------

If a site you want to download from is password-protected using HTTP "Basic"
authentication, you can specify your credentials in the URL, like so::

    http://some_userid:some_password@some.example.com/some_path/

You can do this with both index page URLs and direct download URLs.  As long
as any HTML pages read by easy_install use *relative* links to point to the
downloads, the same user ID and password will be used to do the downloading.

Using .pypirc Credentials
-------------------------

In additional to supplying credentials in the URL, ``easy_install`` will also
honor credentials if present in the .pypirc file. Teams maintaining a private
repository of packages may already have defined access credentials for
uploading packages according to the distutils documentation. ``easy_install``
will attempt to honor those if present. Refer to the distutils documentation
for Python 2.5 or later for details on the syntax.

Controlling Build Options
~~~~~~~~~~~~~~~~~~~~~~~~~

EasyInstall respects standard distutils `Configuration Files`_, so you can use
them to configure build options for packages that it installs from source.  For
example, if you are on Windows using the MinGW compiler, you can configure the
default compiler by putting something like this:

.. code-block:: ini

    [build]
    compiler = mingw32

into the appropriate distutils configuration file.  In fact, since this is just
normal distutils configuration, it will affect any builds using that config
file, not just ones done by EasyInstall.  For example, if you add those lines
to ``distutils.cfg`` in the ``distutils`` package directory, it will be the
default compiler for *all* packages you build.  See `Configuration Files`_
below for a list of the standard configuration file locations, and links to
more documentation on using distutils configuration files.


Editing and Viewing Source Packages
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Sometimes a package's source distribution  contains additional documentation,
examples, configuration files, etc., that are not part of its actual code.  If
you want to be able to examine these files, you can use the ``--editable``
option to EasyInstall, and EasyInstall will look for a source distribution
or Subversion URL for the package, then download and extract it or check it out
as a subdirectory of the ``--build-directory`` you specify.  If you then wish
to install the package after editing or configuring it, you can do so by
rerunning EasyInstall with that directory as the target.

Note that using ``--editable`` stops EasyInstall from actually building or
installing the package; it just finds, obtains, and possibly unpacks it for
you.  This allows you to make changes to the package if necessary, and to
either install it in development mode using ``setup.py develop`` (if the
package uses setuptools, that is), or by running ``easy_install projectdir``
(where ``projectdir`` is the subdirectory EasyInstall created for the
downloaded package.

In order to use ``--editable`` (``-e`` for short), you *must* also supply a
``--build-directory`` (``-b`` for short).  The project will be placed in a
subdirectory of the build directory.  The subdirectory will have the same
name as the project itself, but in all-lowercase.  If a file or directory of
that name already exists, EasyInstall will print an error message and exit.

Also, when using ``--editable``, you cannot use URLs or filenames as arguments.
You *must* specify project names (and optional version requirements) so that
EasyInstall knows what directory name(s) to create.  If you need to force
EasyInstall to use a particular URL or filename, you should specify it as a
``--find-links`` item (``-f`` for short), and then also specify
the project name, e.g.::

    easy_install -eb ~/projects \
     -fhttp://prdownloads.sourceforge.net/ctypes/ctypes-0.9.6.tar.gz?download \
     ctypes==0.9.6


Dealing with Installation Conflicts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

(NOTE: As of 0.6a11, this section is obsolete; it is retained here only so that
people using older versions of EasyInstall can consult it.  As of version
0.6a11, installation conflicts are handled automatically without deleting the
old or system-installed packages, and without ignoring the issue.  Instead,
eggs are automatically shifted to the front of ``sys.path`` using special
code added to the ``easy-install.pth`` file.  So, if you are using version
0.6a11 or better of setuptools, you do not need to worry about conflicts,
and the following issues do not apply to you.)

EasyInstall installs distributions in a "managed" way, such that each
distribution can be independently activated or deactivated on ``sys.path``.
However, packages that were not installed by EasyInstall are "unmanaged",
in that they usually live all in one directory and cannot be independently
activated or deactivated.

As a result, if you are using EasyInstall to upgrade an existing package, or
to install a package with the same name as an existing package, EasyInstall
will warn you of the conflict.  (This is an improvement over ``setup.py
install``, because the ``distutils`` just install new packages on top of old
ones, possibly combining two unrelated packages or leaving behind modules that
have been deleted in the newer version of the package.)

EasyInstall will stop the installation if it detects a conflict
between an existing, "unmanaged" package, and a module or package in any of
the distributions you're installing.  It will display a list of all of the
existing files and directories that would need to be deleted for the new
package to be able to function correctly.  To proceed, you must manually
delete these conflicting files and directories and re-run EasyInstall.

Of course, once you've replaced all of your existing "unmanaged" packages with
versions managed by EasyInstall, you won't have any more conflicts to worry
about!


Compressed Installation
~~~~~~~~~~~~~~~~~~~~~~~

EasyInstall tries to install packages in zipped form, if it can.  Zipping
packages can improve Python's overall import performance if you're not using
the ``--multi-version`` option, because Python processes zipfile entries on
``sys.path`` much faster than it does directories.

As of version 0.5a9, EasyInstall analyzes packages to determine whether they
can be safely installed as a zipfile, and then acts on its analysis.  (Previous
versions would not install a package as a zipfile unless you used the
``--zip-ok`` option.)

The current analysis approach is fairly conservative; it currently looks for:

 * Any use of the ``__file__`` or ``__path__`` variables (which should be
   replaced with ``pkg_resources`` API calls)

 * Possible use of ``inspect`` functions that expect to manipulate source files
   (e.g. ``inspect.getsource()``)

 * Top-level modules that might be scripts used with ``python -m`` (Python 2.4)

If any of the above are found in the package being installed, EasyInstall will
assume that the package cannot be safely run from a zipfile, and unzip it to
a directory instead.  You can override this analysis with the ``-zip-ok`` flag,
which will tell EasyInstall to install the package as a zipfile anyway.  Or,
you can use the ``--always-unzip`` flag, in which case EasyInstall will always
unzip, even if its analysis says the package is safe to run as a zipfile.

Normally, however, it is simplest to let EasyInstall handle the determination
of whether to zip or unzip, and only specify overrides when needed to work
around a problem.  If you find you need to override EasyInstall's guesses, you
may want to contact the package author and the EasyInstall maintainers, so that
they can make appropriate changes in future versions.

(Note: If a package uses ``setuptools`` in its setup script, the package author
has the option to declare the package safe or unsafe for zipped usage via the
``zip_safe`` argument to ``setup()``.  If the package author makes such a
declaration, EasyInstall believes the package's author and does not perform its
own analysis.  However, your command-line option, if any, will still override
the package author's choice.)


Reference Manual
================

Configuration Files
-------------------

(New in 0.4a2)

You may specify default options for EasyInstall using the standard
distutils configuration files, under the command heading ``easy_install``.
EasyInstall will look first for a ``setup.cfg`` file in the current directory,
then a ``~/.pydistutils.cfg`` or ``$HOME\\pydistutils.cfg`` (on Unix-like OSes
and Windows, respectively), and finally a ``distutils.cfg`` file in the
``distutils`` package directory.  Here's a simple example:

.. code-block:: ini

    [easy_install]

    # set the default location to install packages
    install_dir = /home/me/lib/python

    # Notice that indentation can be used to continue an option
    # value; this is especially useful for the "--find-links"
    # option, which tells easy_install to use download links on
    # these pages before consulting PyPI:
    #
    find_links = http://sqlobject.org/
                 http://peak.telecommunity.com/dist/

In addition to accepting configuration for its own options under
``[easy_install]``, EasyInstall also respects defaults specified for other
distutils commands.  For example, if you don't set an ``install_dir`` for
``[easy_install]``, but *have* set an ``install_lib`` for the ``[install]``
command, this will become EasyInstall's default installation directory.  Thus,
if you are already using distutils configuration files to set default install
locations, build options, etc., EasyInstall will respect your existing settings
until and unless you override them explicitly in an ``[easy_install]`` section.

For more information, see also the current Python documentation on the `use and
location of distutils configuration files <https://docs.python.org/install/index.html#inst-config-files>`_.

Notice that ``easy_install`` will use the ``setup.cfg`` from the current
working directory only if it was triggered from ``setup.py`` through the
``install_requires`` option. The standalone command will not use that file.

Command-Line Options
--------------------

``--zip-ok, -z``
    Install all packages as zip files, even if they are marked as unsafe for
    running as a zipfile.  This can be useful when EasyInstall's analysis
    of a non-setuptools package is too conservative, but keep in mind that
    the package may not work correctly.  (Changed in 0.5a9; previously this
    option was required in order for zipped installation to happen at all.)

``--always-unzip, -Z``
    Don't install any packages as zip files, even if the packages are marked
    as safe for running as a zipfile.  This can be useful if a package does
    something unsafe, but not in a way that EasyInstall can easily detect.
    EasyInstall's default analysis is currently very conservative, however, so
    you should only use this option if you've had problems with a particular
    package, and *after* reporting the problem to the package's maintainer and
    to the EasyInstall maintainers.

    (Note: the ``-z/-Z`` options only affect the installation of newly-built
    or downloaded packages that are not already installed in the target
    directory; if you want to convert an existing installed version from
    zipped to unzipped or vice versa, you'll need to delete the existing
    version first, and re-run EasyInstall.)

``--multi-version, -m``
    "Multi-version" mode. Specifying this option prevents ``easy_install`` from
    adding an ``easy-install.pth`` entry for the package being installed, and
    if an entry for any version the package already exists, it will be removed
    upon successful installation. In multi-version mode, no specific version of
    the package is available for importing, unless you use
    ``pkg_resources.require()`` to put it on ``sys.path``. This can be as
    simple as::

        from pkg_resources import require
        require("SomePackage", "OtherPackage", "MyPackage")

    which will put the latest installed version of the specified packages on
    ``sys.path`` for you. (For more advanced uses, like selecting specific
    versions and enabling optional dependencies, see the ``pkg_resources`` API
    doc.)

    Changed in 0.6a10: this option is no longer silently enabled when
    installing to a non-PYTHONPATH, non-"site" directory.  You must always
    explicitly use this option if you want it to be active.

``--upgrade, -U``   (New in 0.5a4)
    By default, EasyInstall only searches online if a project/version
    requirement can't be met by distributions already installed
    on sys.path or the installation directory.  However, if you supply the
    ``--upgrade`` or ``-U`` flag, EasyInstall will always check the package
    index and ``--find-links`` URLs before selecting a version to install.  In
    this way, you can force EasyInstall to use the latest available version of
    any package it installs (subject to any version requirements that might
    exclude such later versions).

``--install-dir=DIR, -d DIR``
    Set the installation directory. It is up to you to ensure that this
    directory is on ``sys.path`` at runtime, and to use
    ``pkg_resources.require()`` to enable the installed package(s) that you
    need.

    (New in 0.4a2) If this option is not directly specified on the command line
    or in a distutils configuration file, the distutils default installation
    location is used.  Normally, this would be the ``site-packages`` directory,
    but if you are using distutils configuration files, setting things like
    ``prefix`` or ``install_lib``, then those settings are taken into
    account when computing the default installation directory, as is the
    ``--prefix`` option.

``--script-dir=DIR, -s DIR``
    Set the script installation directory.  If you don't supply this option
    (via the command line or a configuration file), but you *have* supplied
    an ``--install-dir`` (via command line or config file), then this option
    defaults to the same directory, so that the scripts will be able to find
    their associated package installation.  Otherwise, this setting defaults
    to the location where the distutils would normally install scripts, taking
    any distutils configuration file settings into account.

``--exclude-scripts, -x``
    Don't install scripts.  This is useful if you need to install multiple
    versions of a package, but do not want to reset the version that will be
    run by scripts that are already installed.

``--user`` (New in 0.6.11)
    Use the user-site-packages as specified in :pep:`370`
    instead of the global site-packages.

``--always-copy, -a``   (New in 0.5a4)
    Copy all needed distributions to the installation directory, even if they
    are already present in a directory on sys.path.  In older versions of
    EasyInstall, this was the default behavior, but now you must explicitly
    request it.  By default, EasyInstall will no longer copy such distributions
    from other sys.path directories to the installation directory, unless you
    explicitly gave the distribution's filename on the command line.

    Note that as of 0.6a10, using this option excludes "system" and
    "development" eggs from consideration because they can't be reliably
    copied.  This may cause EasyInstall to choose an older version of a package
    than what you expected, or it may cause downloading and installation of a
    fresh copy of something that's already installed.  You will see warning
    messages for any eggs that EasyInstall skips, before it falls back to an
    older version or attempts to download a fresh copy.

``--find-links=URLS_OR_FILENAMES, -f URLS_OR_FILENAMES``
    Scan the specified "download pages" or directories for direct links to eggs
    or other distributions.  Any existing file or directory names or direct
    download URLs are immediately added to EasyInstall's search cache, and any
    indirect URLs (ones that don't point to eggs or other recognized archive
    formats) are added to a list of additional places to search for download
    links.  As soon as EasyInstall has to go online to find a package (either
    because it doesn't exist locally, or because ``--upgrade`` or ``-U`` was
    used), the specified URLs will be downloaded and scanned for additional
    direct links.

    Eggs and archives found by way of ``--find-links`` are only downloaded if
    they are needed to meet a requirement specified on the command line; links
    to unneeded packages are ignored.

    If all requested packages can be found using links on the specified
    download pages, the Python Package Index will not be consulted unless you
    also specified the ``--upgrade`` or ``-U`` option.

    (Note: if you want to refer to a local HTML file containing links, you must
    use a ``file:`` URL, as filenames that do not refer to a directory, egg, or
    archive are ignored.)

    You may specify multiple URLs or file/directory names with this option,
    separated by whitespace.  Note that on the command line, you will probably
    have to surround the URL list with quotes, so that it is recognized as a
    single option value.  You can also specify URLs in a configuration file;
    see `Configuration Files`_, above.

    Changed in 0.6a10: previously all URLs and directories passed to this
    option were scanned as early as possible, but from 0.6a10 on, only
    directories and direct archive links are scanned immediately; URLs are not
    retrieved unless a package search was already going to go online due to a
    package not being available locally, or due to the use of the ``--update``
    or ``-U`` option.

``--no-find-links`` Blocks the addition of any link.
    This parameter is useful if you want to avoid adding links defined in a
    project easy_install is installing (whether it's a requested project or a
    dependency). When used, ``--find-links`` is ignored.

    Added in Distribute 0.6.11 and Setuptools 0.7.

``--index-url=URL, -i URL`` (New in 0.4a1; default changed in 0.6c7)
    Specifies the base URL of the Python Package Index.  The default is
    https://pypi.org/simple/ if not specified.  When a package is requested
    that is not locally available or linked from a ``--find-links`` download
    page, the package index will be searched for download pages for the needed
    package, and those download pages will be searched for links to download
    an egg or source distribution.

``--editable, -e`` (New in 0.6a1)
    Only find and download source distributions for the specified projects,
    unpacking them to subdirectories of the specified ``--build-directory``.
    EasyInstall will not actually build or install the requested projects or
    their dependencies; it will just find and extract them for you.  See
    `Editing and Viewing Source Packages`_ above for more details.

``--build-directory=DIR, -b DIR`` (UPDATED in 0.6a1)
    Set the directory used to build source packages.  If a package is built
    from a source distribution or checkout, it will be extracted to a
    subdirectory of the specified directory.  The subdirectory will have the
    same name as the extracted distribution's project, but in all-lowercase.
    If a file or directory of that name already exists in the given directory,
    a warning will be printed to the console, and the build will take place in
    a temporary directory instead.

    This option is most useful in combination with the ``--editable`` option,
    which forces EasyInstall to *only* find and extract (but not build and
    install) source distributions.  See `Editing and Viewing Source Packages`_,
    above, for more information.

``--verbose, -v, --quiet, -q`` (New in 0.4a4)
    Control the level of detail of EasyInstall's progress messages.  The
    default detail level is "info", which prints information only about
    relatively time-consuming operations like running a setup script, unpacking
    an archive, or retrieving a URL.  Using ``-q`` or ``--quiet`` drops the
    detail level to "warn", which will only display installation reports,
    warnings, and errors.  Using ``-v`` or ``--verbose`` increases the detail
    level to include individual file-level operations, link analysis messages,
    and distutils messages from any setup scripts that get run.  If you include
    the ``-v`` option more than once, the second and subsequent uses are passed
    down to any setup scripts, increasing the verbosity of their reporting as
    well.

``--dry-run, -n`` (New in 0.4a4)
    Don't actually install the package or scripts.  This option is passed down
    to any setup scripts run, so packages should not actually build either.
    This does *not* skip downloading, nor does it skip extracting source
    distributions to a temporary/build directory.

``--optimize=LEVEL``, ``-O LEVEL`` (New in 0.4a4)
    If you are installing from a source distribution, and are *not* using the
    ``--zip-ok`` option, this option controls the optimization level for
    compiling installed ``.py`` files to ``.pyo`` files.  It does not affect
    the compilation of modules contained in ``.egg`` files, only those in
    ``.egg`` directories.  The optimization level can be set to 0, 1, or 2;
    the default is 0 (unless it's set under ``install`` or ``install_lib`` in
    one of your distutils configuration files).

``--record=FILENAME``  (New in 0.5a4)
    Write a record of all installed files to FILENAME.  This is basically the
    same as the same option for the standard distutils "install" command, and
    is included for compatibility with tools that expect to pass this option
    to "setup.py install".

``--site-dirs=DIRLIST, -S DIRLIST``   (New in 0.6a1)
    Specify one or more custom "site" directories (separated by commas).
    "Site" directories are directories where ``.pth`` files are processed, such
    as the main Python ``site-packages`` directory.  As of 0.6a10, EasyInstall
    automatically detects whether a given directory processes ``.pth`` files
    (or can be made to do so), so you should not normally need to use this
    option.  It is is now only necessary if you want to override EasyInstall's
    judgment and force an installation directory to be treated as if it
    supported ``.pth`` files.

``--no-deps, -N``  (New in 0.6a6)
    Don't install any dependencies.  This is intended as a convenience for
    tools that wrap eggs in a platform-specific packaging system.  (We don't
    recommend that you use it for anything else.)

``--allow-hosts=PATTERNS, -H PATTERNS``   (New in 0.6a6)
    Restrict downloading and spidering to hosts matching the specified glob
    patterns.  E.g. ``-H *.python.org`` restricts web access so that only
    packages listed and downloadable from machines in the ``python.org``
    domain.  The glob patterns must match the *entire* user/host/port section of
    the target URL(s).  For example, ``*.python.org`` will NOT accept a URL
    like ``http://python.org/foo`` or ``http://www.python.org:8080/``.
    Multiple patterns can be specified by separating them with commas.  The
    default pattern is ``*``, which matches anything.

    In general, this option is mainly useful for blocking EasyInstall's web
    access altogether (e.g. ``-Hlocalhost``), or to restrict it to an intranet
    or other trusted site.  EasyInstall will do the best it can to satisfy
    dependencies given your host restrictions, but of course can fail if it
    can't find suitable packages.  EasyInstall displays all blocked URLs, so
    that you can adjust your ``--allow-hosts`` setting if it is more strict
    than you intended.  Some sites may wish to define a restrictive default
    setting for this option in their `configuration files`_, and then manually
    override the setting on the command line as needed.

``--prefix=DIR`` (New in 0.6a10)
    Use the specified directory as a base for computing the default
    installation and script directories.  On Windows, the resulting default
    directories will be ``prefix\\Lib\\site-packages`` and ``prefix\\Scripts``,
    while on other platforms the defaults will be
    ``prefix/lib/python2.X/site-packages`` (with the appropriate version
    substituted) for libraries and ``prefix/bin`` for scripts.

    Note that the ``--prefix`` option only sets the *default* installation and
    script directories, and does not override the ones set on the command line
    or in a configuration file.

``--local-snapshots-ok, -l`` (New in 0.6c6)
    Normally, EasyInstall prefers to only install *released* versions of
    projects, not in-development ones, because such projects may not
    have a currently-valid version number.  So, it usually only installs them
    when their ``setup.py`` directory is explicitly passed on the command line.

    However, if this option is used, then any in-development projects that were
    installed using the ``setup.py develop`` command, will be used to build
    eggs, effectively upgrading the "in-development" project to a snapshot
    release.  Normally, this option is used only in conjunction with the
    ``--always-copy`` option to create a distributable snapshot of every egg
    needed to run an application.

    Note that if you use this option, you must make sure that there is a valid
    version number (such as an SVN revision number tag) for any in-development
    projects that may be used, as otherwise EasyInstall may not be able to tell
    what version of the project is "newer" when future installations or
    upgrades are attempted.


.. _non-root installation:

Custom Installation Locations
-----------------------------

By default, EasyInstall installs python packages into Python's main ``site-packages`` directory,
and manages them using a custom ``.pth`` file in that same directory.

Very often though, a user or developer wants ``easy_install`` to install and manage python packages
in an alternative location, usually for one of 3 reasons:

1. They don't have access to write to the main Python site-packages directory.

2. They want a user-specific stash of packages, that is not visible to other users.

3. They want to isolate a set of packages to a specific python application, usually to minimize
   the possibility of version conflicts.

Historically, there have been many approaches to achieve custom installation.
The following section lists only the easiest and most relevant approaches [1]_.

`Use the "--user" option`_

`Use the "--user" option and customize "PYTHONUSERBASE"`_

`Use "virtualenv"`_

.. [1] There are older ways to achieve custom installation using various ``easy_install`` and ``setup.py install`` options, combined with ``PYTHONPATH`` and/or ``PYTHONUSERBASE`` alterations, but all of these are effectively deprecated by the User scheme brought in by `PEP-370`_.

.. _PEP-370: http://www.python.org/dev/peps/pep-0370/


Use the "--user" option
~~~~~~~~~~~~~~~~~~~~~~~
Python provides a User scheme for installation, which means that all
python distributions support an alternative install location that is specific to a user [3]_.
The Default location for each OS is explained in the python documentation
for the ``site.USER_BASE`` variable.  This mode of installation can be turned on by
specifying the ``--user`` option to ``setup.py install`` or ``easy_install``.
This approach serves the need to have a user-specific stash of packages.

.. [3] Prior to the User scheme, there was the Home scheme, which is still available, but requires more effort than the User scheme to get packages recognized.

Use the "--user" option and customize "PYTHONUSERBASE"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The User scheme install location can be customized by setting the ``PYTHONUSERBASE`` environment
variable, which updates the value of ``site.USER_BASE``.  To isolate packages to a specific
application, simply set the OS environment of that application to a specific value of
``PYTHONUSERBASE``, that contains just those packages.

Use "virtualenv"
~~~~~~~~~~~~~~~~
"virtualenv" is a 3rd-party python package that effectively "clones" a python installation, thereby
creating an isolated location to install packages.  The evolution of "virtualenv" started before the existence
of the User installation scheme.  "virtualenv" provides a version of ``easy_install`` that is
scoped to the cloned python install and is used in the normal way. "virtualenv" does offer various features
that the User installation scheme alone does not provide, e.g. the ability to hide the main python site-packages.

Please refer to the `virtualenv`_ documentation for more details.

.. _virtualenv: https://pypi.org/project/virtualenv/



Package Index "API"
-------------------

Custom package indexes (and PyPI) must follow the following rules for
EasyInstall to be able to look up and download packages:

1. Except where stated otherwise, "pages" are HTML or XHTML, and "links"
   refer to ``href`` attributes.

2. Individual project version pages' URLs must be of the form
   ``base/projectname/version``, where ``base`` is the package index's base URL.

3. Omitting the ``/version`` part of a project page's URL (but keeping the
   trailing ``/``) should result in a page that is either:

   a) The single active version of that project, as though the version had been
      explicitly included, OR

   b) A page with links to all of the active version pages for that project.

4. Individual project version pages should contain direct links to downloadable
   distributions where possible.  It is explicitly permitted for a project's
   "long_description" to include URLs, and these should be formatted as HTML
   links by the package index, as EasyInstall does no special processing to
   identify what parts of a page are index-specific and which are part of the
   project's supplied description.

5. Where available, MD5 information should be added to download URLs by
   appending a fragment identifier of the form ``#md5=...``, where ``...`` is
   the 32-character hex MD5 digest.  EasyInstall will verify that the
   downloaded file's MD5 digest matches the given value.

6. Individual project version pages should identify any "homepage" or
   "download" URLs using ``rel="homepage"`` and ``rel="download"`` attributes
   on the HTML elements linking to those URLs. Use of these attributes will
   cause EasyInstall to always follow the provided links, unless it can be
   determined by inspection that they are downloadable distributions. If the
   links are not to downloadable distributions, they are retrieved, and if they
   are HTML, they are scanned for download links. They are *not* scanned for
   additional "homepage" or "download" links, as these are only processed for
   pages that are part of a package index site.

7. The root URL of the index, if retrieved with a trailing ``/``, must result
   in a page containing links to *all* projects' active version pages.

   (Note: This requirement is a workaround for the absence of case-insensitive
   ``safe_name()`` matching of project names in URL paths. If project names are
   matched in this fashion (e.g. via the PyPI server, mod_rewrite, or a similar
   mechanism), then it is not necessary to include this all-packages listing
   page.)

8. If a package index is accessed via a ``file://`` URL, then EasyInstall will
   automatically use ``index.html`` files, if present, when trying to read a
   directory with a trailing ``/`` on the URL.
PK��\�Ӹ�1alt-python39-setuptools/docs/deprecated/index.rstnu�[���======================================================
Guides on backward compatibility & deprecated practice
======================================================

``Setuptools`` has undergone tremendous changes since its first debut. As its
development continues to roll forward, many of the practice and mechanisms it
had established are now considered deprecated. But they still remain relevant
as a plethora of libraries continue to depend on them. Many people also find
it necessary to equip themselves with the knowledge to better support backward
compatibility. This guide aims to provide the essential information for such
objectives.

.. toctree::
    :maxdepth: 1

    python3
    python_eggs
    easy_install
    distutils/index
    distutils-legacy
    functionalities
PK��\@�G���;alt-python39-setuptools/docs/deprecated/distutils/index.rstnu�[���.. _distutils-index:

##############################################
  Distributing Python Modules (Legacy version)
##############################################

:Authors: Greg Ward, Anthony Baxter
:Email: distutils-sig@python.org

.. seealso::

   :ref:`distributing-index`
      The up to date module distribution documentations

.. include:: ./_setuptools_disclaimer.rst

.. note::

   This guide only covers the basic tools for building and distributing
   extensions that are provided as part of this version of Python. Third party
   tools offer easier to use and more secure alternatives. Refer to the `quick
   recommendations section <https://packaging.python.org/guides/tool-recommendations/>`__
   in the Python Packaging User Guide for more information.

This document describes the Python Distribution Utilities ("Distutils") from
the module developer's point of view, describing the underlying capabilities
that ``setuptools`` builds on to allow Python developers to make Python modules
and extensions readily available to a wider audience.

.. toctree::
   :maxdepth: 2
   :numbered:

   introduction.rst
   setupscript.rst
   configfile.rst
   sourcedist.rst
   builtdist.rst
   examples.rst
   extending.rst
   commandref.rst
   apiref.rst
PK��\R����?alt-python39-setuptools/docs/deprecated/distutils/extending.rstnu�[���.. _extending-distutils:

*******************
Extending Distutils
*******************

.. include:: ./_setuptools_disclaimer.rst

Distutils can be extended in various ways.  Most extensions take the form of new
commands or replacements for existing commands.  New commands may be written to
support new types of platform-specific packaging, for example, while
replacements for existing commands may be made to modify details of how the
command operates on a package.

Most extensions of the distutils are made within :file:`setup.py` scripts that
want to modify existing commands; many simply add a few file extensions that
should be copied into packages in addition to :file:`.py` files as a
convenience.

Most distutils command implementations are subclasses of the
:class:`distutils.cmd.Command` class.  New commands may directly inherit from
:class:`~distutils.cmd.Command`, while replacements often derive from :class:`~distutils.cmd.Command`
indirectly, directly subclassing the command they are replacing.  Commands are
required to derive from :class:`~distutils.cmd.Command`.

.. % \section{Extending existing commands}
.. % \label{extend-existing}

.. % \section{Writing new commands}
.. % \label{new-commands}
.. % \XXX{Would an uninstall command be a good example here?}


Integrating new commands
========================

There are different ways to integrate new command implementations into
distutils.  The most difficult is to lobby for the inclusion of the new features
in distutils itself, and wait for (and require) a version of Python that
provides that support.  This is really hard for many reasons.

The most common, and possibly the most reasonable for most needs, is to include
the new implementations with your :file:`setup.py` script, and cause the
:func:`distutils.core.setup` function use them::

   from distutils.command.build_py import build_py as _build_py
   from distutils.core import setup

   class build_py(_build_py):
       """Specialized Python source builder."""

       # implement whatever needs to be different...

   setup(cmdclass={'build_py': build_py},
         ...)

This approach is most valuable if the new implementations must be used to use a
particular package, as everyone interested in the package will need to have the
new command implementation.

Beginning with Python 2.4, a third option is available, intended to allow new
commands to be added which can support existing :file:`setup.py` scripts without
requiring modifications to the Python installation.  This is expected to allow
third-party extensions to provide support for additional packaging systems, but
the commands can be used for anything distutils commands can be used for.  A new
configuration option, ``command_packages`` (command-line option
:option:`!--command-packages`), can be used to specify additional packages to be
searched for modules implementing commands.  Like all distutils options, this
can be specified on the command line or in a configuration file.  This option
can only be set in the ``[global]`` section of a configuration file, or before
any commands on the command line.  If set in a configuration file, it can be
overridden from the command line; setting it to an empty string on the command
line causes the default to be used.  This should never be set in a configuration
file provided with a package.

This new option can be used to add any number of packages to the list of
packages searched for command implementations; multiple package names should be
separated by commas.  When not specified, the search is only performed in the
:mod:`distutils.command` package.  When :file:`setup.py` is run with the option
``--command-packages distcmds,buildcmds``, however, the packages
:mod:`distutils.command`, ``distcmds``, and ``buildcmds`` will be searched
in that order.  New commands are expected to be implemented in modules of the
same name as the command by classes sharing the same name.  Given the example
command line option above, the command :command:`bdist_openpkg` could be
implemented by the class ``distcmds.bdist_openpkg.bdist_openpkg`` or
``buildcmds.bdist_openpkg.bdist_openpkg``.


Adding new distribution types
=============================

Commands that create distributions (files in the :file:`dist/` directory) need
to add ``(command, filename)`` pairs to ``self.distribution.dist_files`` so that
:command:`upload` can upload it to PyPI.  The *filename* in the pair contains no
path information, only the name of the file itself.  In dry-run mode, pairs
should still be added to represent what would have been created.


PK��\�Y�
&
&@alt-python39-setuptools/docs/deprecated/distutils/sourcedist.rstnu�[���.. _source-dist:

******************************
Creating a Source Distribution
******************************

.. include:: ./_setuptools_disclaimer.rst

As shown in section :ref:`distutils-simple-example`, you use the :command:`sdist` command
to create a source distribution.  In the simplest case, ::

   python setup.py sdist

(assuming you haven't specified any :command:`sdist` options in the setup script
or config file), :command:`sdist` creates the archive of the default format for
the current platform.  The default format is a gzip'ed tar file
(:file:`.tar.gz`) on Unix, and ZIP file on Windows.

You can specify as many formats as you like using the :option:`!--formats`
option, for example::

   python setup.py sdist --formats=gztar,zip

to create a gzipped tarball and a zip file.  The available formats are:

+-----------+-------------------------+---------+
| Format    | Description             | Notes   |
+===========+=========================+=========+
| ``zip``   | zip file (:file:`.zip`) | (1),(3) |
+-----------+-------------------------+---------+
| ``gztar`` | gzip'ed tar file        | \(2)    |
|           | (:file:`.tar.gz`)       |         |
+-----------+-------------------------+---------+
| ``bztar`` | bzip2'ed tar file       |         |
|           | (:file:`.tar.bz2`)      |         |
+-----------+-------------------------+---------+
| ``xztar`` | xz'ed tar file          |         |
|           | (:file:`.tar.xz`)       |         |
+-----------+-------------------------+---------+
| ``ztar``  | compressed tar file     | \(4)    |
|           | (:file:`.tar.Z`)        |         |
+-----------+-------------------------+---------+
| ``tar``   | tar file (:file:`.tar`) |         |
+-----------+-------------------------+---------+

.. versionchanged:: 3.5
   Added support for the ``xztar`` format.

Notes:

(1)
   default on Windows

(2)
   default on Unix

(3)
   requires either external :program:`zip` utility or :mod:`zipfile` module (part
   of the standard Python library since Python 1.6)

(4)
   requires the :program:`compress` program. Notice that this format is now
   pending for deprecation and will be removed in the future versions of Python.

When using any ``tar`` format (``gztar``, ``bztar``, ``xztar``, ``ztar`` or
``tar``), under Unix you can specify the ``owner`` and ``group`` names
that will be set for each member of the archive.

For example, if you want all files of the archive to be owned by root::

    python setup.py sdist --owner=root --group=root


.. _manifest:

Specifying the files to distribute
==================================

If you don't supply an explicit list of files (or instructions on how to
generate one), the :command:`sdist` command puts a minimal default set into the
source distribution:

* all Python source files implied by the ``py_modules`` and
  ``packages`` options

* all C source files mentioned in the ``ext_modules`` or
  ``libraries`` options

  .. XXX getting C library sources currently broken---no
         :meth:`get_source_files` method in :file:`build_clib.py`!

* scripts identified by the ``scripts`` option
  See :ref:`distutils-installing-scripts`.

* anything that looks like a test script: :file:`test/test\*.py` (currently, the
  Distutils don't do anything with test scripts except include them in source
  distributions, but in the future there will be a standard for testing Python
  module distributions)

* Any of the standard README files (:file:`README`, :file:`README.txt`,
  or :file:`README.rst`), :file:`setup.py` (or whatever you called your setup
  script), and :file:`setup.cfg`.

* all files that matches the ``package_data`` metadata.
  See :ref:`distutils-installing-package-data`.

* all files that matches the ``data_files`` metadata.
  See :ref:`distutils-additional-files`.

Sometimes this is enough, but usually you will want to specify additional files
to distribute.  The typical way to do this is to write a *manifest template*,
called :file:`MANIFEST.in` by default.  The manifest template is just a list of
instructions for how to generate your manifest file, :file:`MANIFEST`, which is
the exact list of files to include in your source distribution.  The
:command:`sdist` command processes this template and generates a manifest based
on its instructions and what it finds in the filesystem.

If you prefer to roll your own manifest file, the format is simple: one filename
per line, regular files (or symlinks to them) only.  If you do supply your own
:file:`MANIFEST`, you must specify everything: the default set of files
described above does not apply in this case.

.. versionchanged:: 3.1
   An existing generated :file:`MANIFEST` will be regenerated without
   :command:`sdist` comparing its modification time to the one of
   :file:`MANIFEST.in` or :file:`setup.py`.

.. versionchanged:: 3.1.3
   :file:`MANIFEST` files start with a comment indicating they are generated.
   Files without this comment are not overwritten or removed.

.. versionchanged:: 3.2.2
   :command:`sdist` will read a :file:`MANIFEST` file if no :file:`MANIFEST.in`
   exists, like it used to do.

.. versionchanged:: 3.7
   :file:`README.rst` is now included in the list of distutils standard READMEs.


The manifest template has one command per line, where each command specifies a
set of files to include or exclude from the source distribution.  For an
example, again we turn to the Distutils' own manifest template:

.. code-block:: none

   include *.txt
   recursive-include examples *.txt *.py
   prune examples/sample?/build

The meanings should be fairly clear: include all files in the distribution root
matching :file:`\*.txt`, all files anywhere under the :file:`examples` directory
matching :file:`\*.txt` or :file:`\*.py`, and exclude all directories matching
:file:`examples/sample?/build`.  All of this is done *after* the standard
include set, so you can exclude files from the standard set with explicit
instructions in the manifest template.  (Or, you can use the
:option:`!--no-defaults` option to disable the standard set entirely.)  There are
several other commands available in the manifest template mini-language; see
section :ref:`sdist-cmd`.

The order of commands in the manifest template matters: initially, we have the
list of default files as described above, and each command in the template adds
to or removes from that list of files.  Once we have fully processed the
manifest template, we remove files that should not be included in the source
distribution:

* all files in the Distutils "build" tree (default :file:`build/`)

* all files in directories named :file:`RCS`, :file:`CVS`, :file:`.svn`,
  :file:`.hg`, :file:`.git`, :file:`.bzr` or :file:`_darcs`

Now we have our complete list of files, which is written to the manifest for
future reference, and then used to build the source distribution archive(s).

You can disable the default set of included files with the
:option:`!--no-defaults` option, and you can disable the standard exclude set
with :option:`!--no-prune`.

Following the Distutils' own manifest template, let's trace how the
:command:`sdist` command builds the list of files to include in the Distutils
source distribution:

#. include all Python source files in the :file:`distutils` and
   :file:`distutils/command` subdirectories (because packages corresponding to
   those two directories were mentioned in the ``packages`` option in the
   setup script---see section :ref:`setup-script`)

#. include :file:`README.txt`, :file:`setup.py`, and :file:`setup.cfg` (standard
   files)

#. include :file:`test/test\*.py` (standard files)

#. include :file:`\*.txt` in the distribution root (this will find
   :file:`README.txt` a second time, but such redundancies are weeded out later)

#. include anything matching :file:`\*.txt` or :file:`\*.py` in the sub-tree
   under :file:`examples`,

#. exclude all files in the sub-trees starting at directories matching
   :file:`examples/sample?/build`\ ---this may exclude files included by the
   previous two steps, so it's important that the ``prune`` command in the manifest
   template comes after the ``recursive-include`` command

#. exclude the entire :file:`build` tree, and any :file:`RCS`, :file:`CVS`,
   :file:`.svn`, :file:`.hg`, :file:`.git`, :file:`.bzr` and :file:`_darcs`
   directories

Just like in the setup script, file and directory names in the manifest template
should always be slash-separated; the Distutils will take care of converting
them to the standard representation on your platform. That way, the manifest
template is portable across operating systems.


.. _manifest-options:

Manifest-related options
========================

The normal course of operations for the :command:`sdist` command is as follows:

* if the manifest file (:file:`MANIFEST` by default) exists and the first line
  does not have a comment indicating it is generated from :file:`MANIFEST.in`,
  then it is used as is, unaltered

* if the manifest file doesn't exist or has been previously automatically
  generated, read :file:`MANIFEST.in` and create the manifest

* if neither :file:`MANIFEST` nor :file:`MANIFEST.in` exist, create a manifest
  with just the default file set

* use the list of files now in :file:`MANIFEST` (either just generated or read
  in) to create the source distribution archive(s)

There are a couple of options that modify this behaviour.  First, use the
:option:`!--no-defaults` and :option:`!--no-prune` to disable the standard
"include" and "exclude" sets.

Second, you might just want to (re)generate the manifest, but not create a source
distribution::

   python setup.py sdist --manifest-only

:option:`!-o` is a shortcut for :option:`!--manifest-only`.
PK��\f�m�(�(>alt-python39-setuptools/docs/deprecated/distutils/examples.rstnu�[���.. _distutils_examples:

******************
Distutils Examples
******************

.. include:: ./_setuptools_disclaimer.rst

This chapter provides a number of basic examples to help get started with
distutils.  Additional information about using distutils can be found in the
Distutils Cookbook.


.. seealso::

   `Distutils Cookbook <https://wiki.python.org/moin/Distutils/Cookbook>`_
      Collection of recipes showing how to achieve more control over distutils.


.. _pure-mod:

Pure Python distribution (by module)
====================================

If you're just distributing a couple of modules, especially if they don't live
in a particular package, you can specify them individually using the
``py_modules`` option in the setup script.

In the simplest case, you'll have two files to worry about: a setup script and
the single module you're distributing, :file:`foo.py` in this example::

   <root>/
           setup.py
           foo.py

(In all diagrams in this section, *<root>* will refer to the distribution root
directory.)  A minimal setup script to describe this situation would be::

   from distutils.core import setup
   setup(name='foo',
         version='1.0',
         py_modules=['foo'],
         )

Note that the name of the distribution is specified independently with the
``name`` option, and there's no rule that says it has to be the same as
the name of the sole module in the distribution (although that's probably a good
convention to follow).  However, the distribution name is used to generate
filenames, so you should stick to letters, digits, underscores, and hyphens.

Since ``py_modules`` is a list, you can of course specify multiple
modules, eg. if you're distributing modules ``foo`` and ``bar``, your
setup might look like this::

   <root>/
           setup.py
           foo.py
           bar.py

and the setup script might be  ::

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         py_modules=['foo', 'bar'],
         )

You can put module source files into another directory, but if you have enough
modules to do that, it's probably easier to specify modules by package rather
than listing them individually.


.. _pure-pkg:

Pure Python distribution (by package)
=====================================

If you have more than a couple of modules to distribute, especially if they are
in multiple packages, it's probably easier to specify whole packages rather than
individual modules.  This works even if your modules are not in a package; you
can just tell the Distutils to process modules from the root package, and that
works the same as any other package (except that you don't have to have an
:file:`__init__.py` file).

The setup script from the last example could also be written as  ::

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         packages=[''],
         )

(The empty string stands for the root package.)

If those two files are moved into a subdirectory, but remain in the root
package, e.g.::

   <root>/
           setup.py
           src/      foo.py
                     bar.py

then you would still specify the root package, but you have to tell the
Distutils where source files in the root package live::

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         package_dir={'': 'src'},
         packages=[''],
         )

More typically, though, you will want to distribute multiple modules in the same
package (or in sub-packages).  For example, if the ``foo``  and ``bar``
modules belong in package ``foobar``, one way to layout your source tree is
::

   <root>/
           setup.py
           foobar/
                    __init__.py
                    foo.py
                    bar.py

This is in fact the default layout expected by the Distutils, and the one that
requires the least work to describe in your setup script::

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         packages=['foobar'],
         )

If you want to put modules in directories not named for their package, then you
need to use the ``package_dir`` option again.  For example, if the
:file:`src` directory holds modules in the ``foobar`` package::

   <root>/
           setup.py
           src/
                    __init__.py
                    foo.py
                    bar.py

an appropriate setup script would be  ::

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         package_dir={'foobar': 'src'},
         packages=['foobar'],
         )

Or, you might put modules from your main package right in the distribution
root::

   <root>/
           setup.py
           __init__.py
           foo.py
           bar.py

in which case your setup script would be  ::

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         package_dir={'foobar': ''},
         packages=['foobar'],
         )

(The empty string also stands for the current directory.)

If you have sub-packages, they must be explicitly listed in ``packages``,
but any entries in ``package_dir`` automatically extend to sub-packages.
(In other words, the Distutils does *not* scan your source tree, trying to
figure out which directories correspond to Python packages by looking for
:file:`__init__.py` files.)  Thus, if the default layout grows a sub-package::

   <root>/
           setup.py
           foobar/
                    __init__.py
                    foo.py
                    bar.py
                    subfoo/
                              __init__.py
                              blah.py

then the corresponding setup script would be  ::

   from distutils.core import setup
   setup(name='foobar',
         version='1.0',
         packages=['foobar', 'foobar.subfoo'],
         )


.. _single-ext:

Single extension module
=======================

Extension modules are specified using the ``ext_modules`` option.
``package_dir`` has no effect on where extension source files are found;
it only affects the source for pure Python modules.  The simplest  case, a
single extension module in a single C source file, is::

   <root>/
           setup.py
           foo.c

If the ``foo`` extension belongs in the root package, the setup script for
this could be  ::

   from distutils.core import setup
   from distutils.extension import Extension
   setup(name='foobar',
         version='1.0',
         ext_modules=[Extension('foo', ['foo.c'])],
         )

If the extension actually belongs in a package, say ``foopkg``, then

With exactly the same source tree layout, this extension can be put in the
``foopkg`` package simply by changing the name of the extension::

   from distutils.core import setup
   from distutils.extension import Extension
   setup(name='foobar',
         version='1.0',
         ext_modules=[Extension('foopkg.foo', ['foo.c'])],
         )

Checking a package
==================

The ``check`` command allows you to verify if your package meta-data
meet the minimum requirements to build a distribution.

To run it, just call it using your :file:`setup.py` script. If something is
missing, ``check`` will display a warning.

Let's take an example with a simple script::

    from distutils.core import setup

    setup(name='foobar')

Running the ``check`` command will display some warnings:

.. code-block:: shell-session

    $ python setup.py check
    running check
    warning: check: missing required meta-data: version, url
    warning: check: missing meta-data: either (author and author_email) or
             (maintainer and maintainer_email) should be supplied


If you use the reStructuredText syntax in the ``long_description`` field and
`docutils`_  is installed you can check if the syntax is fine with the
``check`` command, using the ``restructuredtext`` option.

For example, if the :file:`setup.py` script is changed like this::

    from distutils.core import setup

    desc = """\
    My description
    ==============

    This is the description of the ``foobar`` package.
    """

    setup(name='foobar', version='1', author='tarek',
        author_email='tarek@ziade.org',
        url='http://example.com', long_description=desc)

Where the long description is broken, ``check`` will be able to detect it
by using the :mod:`docutils` parser:

.. code-block:: shell-session

    $ python setup.py check --restructuredtext
    running check
    warning: check: Title underline too short. (line 2)
    warning: check: Could not finish the parsing.

Reading the metadata
=====================

The :func:`distutils.core.setup` function provides a command-line interface
that allows you to query the metadata fields of a project through the
``setup.py`` script of a given project:

.. code-block:: shell-session

    $ python setup.py --name
    distribute

This call reads the ``name`` metadata by running the
:func:`distutils.core.setup`  function. Although, when a source or binary
distribution is created with Distutils, the metadata fields are written
in a static file called :file:`PKG-INFO`. When a Distutils-based project is
installed in Python, the :file:`PKG-INFO` file is copied alongside the modules
and packages of the distribution under :file:`NAME-VERSION-pyX.X.egg-info`,
where ``NAME`` is the name of the project, ``VERSION`` its version as defined
in the Metadata, and ``pyX.X`` the major and minor version of Python like
``2.7`` or ``3.2``.

You can read back this static file, by using the
:class:`distutils.dist.DistributionMetadata` class and its
:func:`~distutils.dist.DistributionMetadata.read_pkg_file` method::

    >>> from distutils.dist import DistributionMetadata
    >>> metadata = DistributionMetadata()
    >>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info'))
    >>> metadata.name
    'distribute'
    >>> metadata.version
    '0.6.8'
    >>> metadata.description
    'Easily download, build, install, upgrade, and uninstall Python packages'

Notice that the class can also be instantiated with a metadata file path to
loads its values::

    >>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info'
    >>> DistributionMetadata(pkg_info_path).name
    'distribute'


.. % \section{Multiple extension modules}
.. % \label{multiple-ext}

.. % \section{Putting it all together}


.. _docutils: http://docutils.sourceforge.net
PK��\U��)�z�zAalt-python39-setuptools/docs/deprecated/distutils/setupscript.rstnu�[���.. _setup-script:

************************
Writing the Setup Script
************************

.. include:: ./_setuptools_disclaimer.rst

The setup script is the centre of all activity in building, distributing, and
installing modules using the Distutils.  The main purpose of the setup script is
to describe your module distribution to the Distutils, so that the various
commands that operate on your modules do the right thing.  As we saw in section
:ref:`distutils-simple-example` above, the setup script consists mainly of a call to :func:`~distutils.core.setup`, and most information
supplied to the Distutils by the module developer is supplied as keyword
arguments to :func:`~distutils.core.setup`.

Here's a slightly more involved example, which we'll follow for the next couple
of sections: the Distutils' own setup script.  (Keep in mind that although the
Distutils are included with Python 1.6 and later, they also have an independent
existence so that Python 1.5.2 users can use them to install other module
distributions.  The Distutils' own setup script, shown here, is used to install
the package into Python 1.5.2.) ::

    #!/usr/bin/env python

    from distutils.core import setup

    setup(name='Distutils',
          version='1.0',
          description='Python Distribution Utilities',
          author='Greg Ward',
          author_email='gward@python.net',
          url='https://www.python.org/sigs/distutils-sig/',
          packages=['distutils', 'distutils.command'],
         )

There are only two differences between this and the trivial one-file
distribution presented in section :ref:`distutils-simple-example`: more metadata, and the
specification of pure Python modules by package, rather than by module.  This is
important since the Distutils consist of a couple of dozen modules split into
(so far) two packages; an explicit list of every module would be tedious to
generate and difficult to maintain.  For more information on the additional
meta-data, see section :ref:`meta-data`.

Note that any pathnames (files or directories) supplied in the setup script
should be written using the Unix convention, i.e. slash-separated.  The
Distutils will take care of converting this platform-neutral representation into
whatever is appropriate on your current platform before actually using the
pathname.  This makes your setup script portable across operating systems, which
of course is one of the major goals of the Distutils.  In this spirit, all
pathnames in this document are slash-separated.

This, of course, only applies to pathnames given to Distutils functions.  If
you, for example, use standard Python functions such as :func:`glob.glob` or
:func:`os.listdir` to specify files, you should be careful to write portable
code instead of hardcoding path separators::

    glob.glob(os.path.join('mydir', 'subdir', '*.html'))
    os.listdir(os.path.join('mydir', 'subdir'))


.. _listing-packages:

Listing whole packages
======================

The ``packages`` option tells the Distutils to process (build, distribute,
install, etc.) all pure Python modules found in each package mentioned in the
``packages`` list.  In order to do this, of course, there has to be a
correspondence between package names and directories in the filesystem.  The
default correspondence is the most obvious one, i.e. package :mod:`distutils` is
found in the directory :file:`distutils` relative to the distribution root.
Thus, when you say ``packages = ['foo']`` in your setup script, you are
promising that the Distutils will find a file :file:`foo/__init__.py` (which
might be spelled differently on your system, but you get the idea) relative to
the directory where your setup script lives.  If you break this promise, the
Distutils will issue a warning but still process the broken package anyway.

If you use a different convention to lay out your source directory, that's no
problem: you just have to supply the ``package_dir`` option to tell the
Distutils about your convention.  For example, say you keep all Python source
under :file:`lib`, so that modules in the "root package" (i.e., not in any
package at all) are in :file:`lib`, modules in the ``foo`` package are in
:file:`lib/foo`, and so forth.  Then you would put ::

    package_dir = {'': 'lib'}

in your setup script.  The keys to this dictionary are package names, and an
empty package name stands for the root package.  The values are directory names
relative to your distribution root.  In this case, when you say ``packages =
['foo']``, you are promising that the file :file:`lib/foo/__init__.py` exists.

Another possible convention is to put the ``foo`` package right in
:file:`lib`, the ``foo.bar`` package in :file:`lib/bar`, etc.  This would be
written in the setup script as ::

    package_dir = {'foo': 'lib'}

A ``package: dir`` entry in the ``package_dir`` dictionary implicitly
applies to all packages below *package*, so the ``foo.bar`` case is
automatically handled here.  In this example, having ``packages = ['foo',
'foo.bar']`` tells the Distutils to look for :file:`lib/__init__.py` and
:file:`lib/bar/__init__.py`.  (Keep in mind that although ``package_dir``
applies recursively, you must explicitly list all packages in
``packages``: the Distutils will *not* recursively scan your source tree
looking for any directory with an :file:`__init__.py` file.)


.. _listing-modules:

Listing individual modules
==========================

For a small module distribution, you might prefer to list all modules rather
than listing packages---especially the case of a single module that goes in the
"root package" (i.e., no package at all).  This simplest case was shown in
section :ref:`distutils-simple-example`; here is a slightly more involved example::

    py_modules = ['mod1', 'pkg.mod2']

This describes two modules, one of them in the "root" package, the other in the
``pkg`` package.  Again, the default package/directory layout implies that
these two modules can be found in :file:`mod1.py` and :file:`pkg/mod2.py`, and
that :file:`pkg/__init__.py` exists as well. And again, you can override the
package/directory correspondence using the ``package_dir`` option.


.. _describing-extensions:

Describing extension modules
============================

Just as writing Python extension modules is a bit more complicated than writing
pure Python modules, describing them to the Distutils is a bit more complicated.
Unlike pure modules, it's not enough just to list modules or packages and expect
the Distutils to go out and find the right files; you have to specify the
extension name, source file(s), and any compile/link requirements (include
directories, libraries to link with, etc.).

.. XXX read over this section

All of this is done through another keyword argument to
:func:`~distutils.core.setup`, the
``ext_modules`` option.  ``ext_modules`` is just a list of
:class:`~distutils.core.Extension` instances, each of which describes a
single extension module.
Suppose your distribution includes a single extension, called ``foo`` and
implemented by :file:`foo.c`.  If no additional instructions to the
compiler/linker are needed, describing this extension is quite simple::

    Extension('foo', ['foo.c'])

The :class:`~distutils.extension.Extension` class can be imported from :mod:`distutils.core` along
with :func:`~distutils.core.setup`.  Thus, the setup script for a module distribution that
contains only this one extension and nothing else might be::

    from distutils.core import setup, Extension
    setup(name='foo',
          version='1.0',
          ext_modules=[Extension('foo', ['foo.c'])],
          )

The :class:`~distutils.extension.Extension` class (actually, the underlying extension-building
machinery implemented by the :command:`build_ext` command) supports a great deal
of flexibility in describing Python extensions, which is explained in the
following sections.


Extension names and packages
----------------------------

The first argument to the :class:`~distutils.core.Extension` constructor is
always the name of the extension, including any package names.  For example, ::

    Extension('foo', ['src/foo1.c', 'src/foo2.c'])

describes an extension that lives in the root package, while ::

    Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c'])

describes the same extension in the ``pkg`` package.  The source files and
resulting object code are identical in both cases; the only difference is where
in the filesystem (and therefore where in Python's namespace hierarchy) the
resulting extension lives.

If you have a number of extensions all in the same package (or all under the
same base package), use the ``ext_package`` keyword argument to
:func:`~distutils.core.setup`.  For example, ::

    setup(...,
          ext_package='pkg',
          ext_modules=[Extension('foo', ['foo.c']),
                       Extension('subpkg.bar', ['bar.c'])],
         )

will compile :file:`foo.c` to the extension ``pkg.foo``, and
:file:`bar.c` to ``pkg.subpkg.bar``.


Extension source files
----------------------

The second argument to the :class:`~distutils.core.Extension` constructor is
a list of source
files.  Since the Distutils currently only support C, C++, and Objective-C
extensions, these are normally C/C++/Objective-C source files.  (Be sure to use
appropriate extensions to distinguish C++ source files: :file:`.cc` and
:file:`.cpp` seem to be recognized by both Unix and Windows compilers.)

However, you can also include SWIG interface (:file:`.i`) files in the list; the
:command:`build_ext` command knows how to deal with SWIG extensions: it will run
SWIG on the interface file and compile the resulting C/C++ file into your
extension.

.. XXX SWIG support is rough around the edges and largely untested!

This warning notwithstanding, options to SWIG can be currently passed like
this::

    setup(...,
          ext_modules=[Extension('_foo', ['foo.i'],
                                 swig_opts=['-modern', '-I../include'])],
          py_modules=['foo'],
         )

Or on the commandline like this::

    > python setup.py build_ext --swig-opts="-modern -I../include"

On some platforms, you can include non-source files that are processed by the
compiler and included in your extension.  Currently, this just means Windows
message text (:file:`.mc`) files and resource definition (:file:`.rc`) files for
Visual C++. These will be compiled to binary resource (:file:`.res`) files and
linked into the executable.


Preprocessor options
--------------------

Three optional arguments to :class:`~distutils.core.Extension` will help if
you need to specify include directories to search or preprocessor macros to
define/undefine: ``include_dirs``, ``define_macros``, and ``undef_macros``.

For example, if your extension requires header files in the :file:`include`
directory under your distribution root, use the ``include_dirs`` option::

    Extension('foo', ['foo.c'], include_dirs=['include'])

You can specify absolute directories there; if you know that your extension will
only be built on Unix systems with X11R6 installed to :file:`/usr`, you can get
away with ::

    Extension('foo', ['foo.c'], include_dirs=['/usr/include/X11'])

You should avoid this sort of non-portable usage if you plan to distribute your
code: it's probably better to write C code like  ::

    #include <X11/Xlib.h>

If you need to include header files from some other Python extension, you can
take advantage of the fact that header files are installed in a consistent way
by the Distutils :command:`install_headers` command.  For example, the Numerical
Python header files are installed (on a standard Unix installation) to
:file:`/usr/local/include/python1.5/Numerical`. (The exact location will differ
according to your platform and Python installation.)  Since the Python include
directory---\ :file:`/usr/local/include/python1.5` in this case---is always
included in the search path when building Python extensions, the best approach
is to write C code like  ::

    #include <Numerical/arrayobject.h>

If you must put the :file:`Numerical` include directory right into your header
search path, though, you can find that directory using the Distutils
:mod:`distutils.sysconfig` module::

    from distutils.sysconfig import get_python_inc
    incdir = os.path.join(get_python_inc(plat_specific=1), 'Numerical')
    setup(...,
          Extension(..., include_dirs=[incdir]),
          )

Even though this is quite portable---it will work on any Python installation,
regardless of platform---it's probably easier to just write your C code in the
sensible way.

You can define and undefine pre-processor macros with the ``define_macros`` and
``undef_macros`` options. ``define_macros`` takes a list of ``(name, value)``
tuples, where ``name`` is the name of the macro to define (a string) and
``value`` is its value: either a string or ``None``.  (Defining a macro ``FOO``
to ``None`` is the equivalent of a bare ``#define FOO`` in your C source: with
most compilers, this sets ``FOO`` to the string ``1``.)  ``undef_macros`` is
just a list of macros to undefine.

For example::

    Extension(...,
              define_macros=[('NDEBUG', '1'),
                             ('HAVE_STRFTIME', None)],
              undef_macros=['HAVE_FOO', 'HAVE_BAR'])

is the equivalent of having this at the top of every C source file::

    #define NDEBUG 1
    #define HAVE_STRFTIME
    #undef HAVE_FOO
    #undef HAVE_BAR


Library options
---------------

You can also specify the libraries to link against when building your extension,
and the directories to search for those libraries.  The ``libraries`` option is
a list of libraries to link against, ``library_dirs`` is a list of directories
to search for libraries at  link-time, and ``runtime_library_dirs`` is a list of
directories to  search for shared (dynamically loaded) libraries at run-time.

For example, if you need to link against libraries known to be in the standard
library search path on target systems ::

    Extension(...,
              libraries=['gdbm', 'readline'])

If you need to link with libraries in a non-standard location, you'll have to
include the location in ``library_dirs``::

    Extension(...,
              library_dirs=['/usr/X11R6/lib'],
              libraries=['X11', 'Xt'])

(Again, this sort of non-portable construct should be avoided if you intend to
distribute your code.)

.. XXX Should mention clib libraries here or somewhere else!


Other options
-------------

There are still some other options which can be used to handle special cases.

The ``optional`` option is a boolean; if it is true,
a build failure in the extension will not abort the build process, but
instead simply not install the failing extension.

The ``extra_objects`` option is a list of object files to be passed to the
linker. These files must not have extensions, as the default extension for the
compiler is used.

``extra_compile_args`` and ``extra_link_args`` can be used to
specify additional command line options for the respective compiler and linker
command lines.

``export_symbols`` is only useful on Windows.  It can contain a list of
symbols (functions or variables) to be exported. This option is not needed when
building compiled extensions: Distutils  will automatically add ``initmodule``
to the list of exported symbols.

The ``depends`` option is a list of files that the extension depends on
(for example header files). The build command will call the compiler on the
sources to rebuild extension if any on this files has been modified since the
previous build.

Relationships between Distributions and Packages
================================================

A distribution may relate to packages in three specific ways:

#. It can require packages or modules.

#. It can provide packages or modules.

#. It can obsolete packages or modules.

These relationships can be specified using keyword arguments to the
:func:`distutils.core.setup` function.

Dependencies on other Python modules and packages can be specified by supplying
the *requires* keyword argument to :func:`~distutils.core.setup`. The
value must be a list of
strings.  Each string specifies a package that is required, and optionally what
versions are sufficient.

To specify that any version of a module or package is required, the string
should consist entirely of the module or package name. Examples include
``'mymodule'`` and ``'xml.parsers.expat'``.

If specific versions are required, a sequence of qualifiers can be supplied in
parentheses.  Each qualifier may consist of a comparison operator and a version
number.  The accepted comparison operators are::

    <    >    ==
    <=   >=   !=

These can be combined by using multiple qualifiers separated by commas (and
optional whitespace).  In this case, all of the qualifiers must be matched; a
logical AND is used to combine the evaluations.

Let's look at a bunch of examples:

+-------------------------+----------------------------------------------+
| Requires Expression     | Explanation                                  |
+=========================+==============================================+
| ``==1.0``               | Only version ``1.0`` is compatible           |
+-------------------------+----------------------------------------------+
| ``>1.0, !=1.5.1, <2.0`` | Any version after ``1.0`` and before ``2.0`` |
|                         | is compatible, except ``1.5.1``              |
+-------------------------+----------------------------------------------+

Now that we can specify dependencies, we also need to be able to specify what we
provide that other distributions can require.  This is done using the *provides*
keyword argument to :func:`~distutils.core.setup`. The value for this keyword is a list of
strings, each of which names a Python module or package, and optionally
identifies the version.  If the version is not specified, it is assumed to match
that of the distribution.

Some examples:

+---------------------+----------------------------------------------+
| Provides Expression | Explanation                                  |
+=====================+==============================================+
| ``mypkg``           | Provide ``mypkg``, using the distribution    |
|                     | version                                      |
+---------------------+----------------------------------------------+
| ``mypkg (1.1)``     | Provide ``mypkg`` version 1.1, regardless of |
|                     | the distribution version                     |
+---------------------+----------------------------------------------+

A package can declare that it obsoletes other packages using the *obsoletes*
keyword argument.  The value for this is similar to that of the *requires*
keyword: a list of strings giving module or package specifiers.  Each specifier
consists of a module or package name optionally followed by one or more version
qualifiers.  Version qualifiers are given in parentheses after the module or
package name.

The versions identified by the qualifiers are those that are obsoleted by the
distribution being described.  If no qualifiers are given, all versions of the
named module or package are understood to be obsoleted.

.. _distutils-installing-scripts:

Installing Scripts
==================

So far we have been dealing with pure and non-pure Python modules, which are
usually not run by themselves but imported by scripts.

Scripts are files containing Python source code, intended to be started from the
command line.  Scripts don't require Distutils to do anything very complicated.
The only clever feature is that if the first line of the script starts with
``#!`` and contains the word "python", the Distutils will adjust the first line
to refer to the current interpreter location. By default, it is replaced with
the current interpreter location.  The :option:`!--executable` (or :option:`!-e`)
option will allow the interpreter path to be explicitly overridden.

The ``scripts`` option simply is a list of files to be handled in this
way.  From the PyXML setup script::

    setup(...,
          scripts=['scripts/xmlproc_parse', 'scripts/xmlproc_val']
          )

.. versionchanged:: 3.1
   All the scripts will also be added to the ``MANIFEST`` file if no template is
   provided.  See :ref:`manifest`.


.. _distutils-installing-package-data:

Installing Package Data
=======================

Often, additional files need to be installed into a package.  These files are
often data that's closely related to the package's implementation, or text files
containing documentation that might be of interest to programmers using the
package.  These files are called :dfn:`package data`.

Package data can be added to packages using the ``package_data`` keyword
argument to the :func:`~distutils.core.setup` function.  The value must be a mapping from
package name to a list of relative path names that should be copied into the
package.  The paths are interpreted as relative to the directory containing the
package (information from the ``package_dir`` mapping is used if appropriate);
that is, the files are expected to be part of the package in the source
directories. They may contain glob patterns as well.

The path names may contain directory portions; any necessary directories will be
created in the installation.

For example, if a package should contain a subdirectory with several data files,
the files can be arranged like this in the source tree::

    setup.py
    src/
        mypkg/
            __init__.py
            module.py
            data/
                tables.dat
                spoons.dat
                forks.dat

The corresponding call to :func:`~distutils.core.setup` might be::

    setup(...,
          packages=['mypkg'],
          package_dir={'mypkg': 'src/mypkg'},
          package_data={'mypkg': ['data/*.dat']},
          )


.. versionchanged:: 3.1
   All the files that match ``package_data`` will be added to the ``MANIFEST``
   file if no template is provided.  See :ref:`manifest`.


.. _distutils-additional-files:

Installing Additional Files
===========================

The ``data_files`` option can be used to specify additional files needed
by the module distribution: configuration files, message catalogs, data files,
anything which doesn't fit in the previous categories.

``data_files`` specifies a sequence of (*directory*, *files*) pairs in the
following way::

    setup(...,
          data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
                      ('config', ['cfg/data.cfg'])],
         )

Each (*directory*, *files*) pair in the sequence specifies the installation
directory and the files to install there.

Each file name in *files* is interpreted relative to the :file:`setup.py`
script at the top of the package source distribution. Note that you can
specify the directory where the data files will be installed, but you cannot
rename the data files themselves.

The *directory* should be a relative path. It is interpreted relative to the
installation prefix (Python's ``sys.prefix`` for system installations;
``site.USER_BASE`` for user installations). Distutils allows *directory* to be
an absolute installation path, but this is discouraged since it is
incompatible with the wheel packaging format. No directory information from
*files* is used to determine the final location of the installed file; only
the name of the file is used.

You can specify the ``data_files`` options as a simple sequence of files
without specifying a target directory, but this is not recommended, and the
:command:`install` command will print a warning in this case. To install data
files directly in the target directory, an empty string should be given as the
directory.

.. versionchanged:: 3.1
   All the files that match ``data_files`` will be added to the ``MANIFEST``
   file if no template is provided.  See :ref:`manifest`.


.. _meta-data:

Additional meta-data
====================

The setup script may include additional meta-data beyond the name and version.
This information includes:

+----------------------+---------------------------+-----------------+--------+
| Meta-Data            | Description               | Value           | Notes  |
+======================+===========================+=================+========+
| ``name``             | name of the package       | short string    | \(1)   |
+----------------------+---------------------------+-----------------+--------+
| ``version``          | version of this release   | short string    | (1)(2) |
+----------------------+---------------------------+-----------------+--------+
| ``author``           | package author's name     | short string    | \(3)   |
+----------------------+---------------------------+-----------------+--------+
| ``author_email``     | email address of the      | email address   | \(3)   |
|                      | package author            |                 |        |
+----------------------+---------------------------+-----------------+--------+
| ``maintainer``       | package maintainer's name | short string    | \(3)   |
+----------------------+---------------------------+-----------------+--------+
| ``maintainer_email`` | email address of the      | email address   | \(3)   |
|                      | package maintainer        |                 |        |
+----------------------+---------------------------+-----------------+--------+
| ``url``              | home page for the package | URL             | \(1)   |
+----------------------+---------------------------+-----------------+--------+
| ``description``      | short, summary            | short string    |        |
|                      | description of the        |                 |        |
|                      | package                   |                 |        |
+----------------------+---------------------------+-----------------+--------+
| ``long_description`` | longer description of the | long string     | \(4)   |
|                      | package                   |                 |        |
+----------------------+---------------------------+-----------------+--------+
| ``download_url``     | location where the        | URL             |        |
|                      | package may be downloaded |                 |        |
+----------------------+---------------------------+-----------------+--------+
| ``classifiers``      | a list of classifiers     | list of strings | (6)(7) |
+----------------------+---------------------------+-----------------+--------+
| ``platforms``        | a list of platforms       | list of strings | (6)(8) |
+----------------------+---------------------------+-----------------+--------+
| ``keywords``         | a list of keywords        | list of strings | (6)(8) |
+----------------------+---------------------------+-----------------+--------+
| ``license``          | license for the package   | short string    | \(5)   |
+----------------------+---------------------------+-----------------+--------+

Notes:

(1)
    These fields are required.

(2)
    It is recommended that versions take the form *major.minor[.patch[.sub]]*.

(3)
    Either the author or the maintainer must be identified. If maintainer is
    provided, distutils lists it as the author in :file:`PKG-INFO`.

(4)
    The ``long_description`` field is used by PyPI when you publish a package,
    to build its project page.

(5)
    The ``license`` field is a text indicating the license covering the
    package where the license is not a selection from the "License" Trove
    classifiers. See the ``Classifier`` field. Notice that
    there's a ``licence`` distribution option which is deprecated but still
    acts as an alias for ``license``.

(6)
    This field must be a list.

(7)
    The valid classifiers are listed on
    `PyPI <https://pypi.org/classifiers>`_.

(8)
    To preserve backward compatibility, this field also accepts a string. If
    you pass a comma-separated string ``'foo, bar'``, it will be converted to
    ``['foo', 'bar']``, Otherwise, it will be converted to a list of one
    string.

'short string'
    A single line of text, not more than 200 characters.

'long string'
    Multiple lines of plain text in reStructuredText format (see
    http://docutils.sourceforge.net/).

'list of strings'
    See below.

Encoding the version information is an art in itself. Python packages generally
adhere to the version format *major.minor[.patch][sub]*. The major number is 0
for initial, experimental releases of software. It is incremented for releases
that represent major milestones in a package. The minor number is incremented
when important new features are added to the package. The patch number
increments when bug-fix releases are made. Additional trailing version
information is sometimes used to indicate sub-releases.  These are
"a1,a2,...,aN" (for alpha releases, where functionality and API may change),
"b1,b2,...,bN" (for beta releases, which only fix bugs) and "pr1,pr2,...,prN"
(for final pre-release release testing). Some examples:

0.1.0
    the first, experimental release of a package

1.0.1a2
    the second alpha release of the first patch version of 1.0

``classifiers`` must be specified in a list::

    setup(...,
          classifiers=[
              'Development Status :: 4 - Beta',
              'Environment :: Console',
              'Environment :: Web Environment',
              'Intended Audience :: End Users/Desktop',
              'Intended Audience :: Developers',
              'Intended Audience :: System Administrators',
              'License :: OSI Approved :: Python Software Foundation License',
              'Operating System :: MacOS :: MacOS X',
              'Operating System :: Microsoft :: Windows',
              'Operating System :: POSIX',
              'Programming Language :: Python',
              'Topic :: Communications :: Email',
              'Topic :: Office/Business',
              'Topic :: Software Development :: Bug Tracking',
              ],
          )

.. versionchanged:: 3.7
   :class:`~distutils.core.setup` now warns when ``classifiers``, ``keywords``
   or ``platforms`` fields are not specified as a list or a string.

.. _debug-setup-script:

Debugging the setup script
==========================

Sometimes things go wrong, and the setup script doesn't do what the developer
wants.

Distutils catches any exceptions when running the setup script, and print a
simple error message before the script is terminated.  The motivation for this
behaviour is to not confuse administrators who don't know much about Python and
are trying to install a package.  If they get a big long traceback from deep
inside the guts of Distutils, they may think the package or the Python
installation is broken because they don't read all the way down to the bottom
and see that it's a permission problem.

On the other hand, this doesn't help the developer to find the cause of the
failure. For this purpose, the :envvar:`DISTUTILS_DEBUG` environment variable can be set
to anything except an empty string, and distutils will now print detailed
information about what it is doing, dump the full traceback when an exception
occurs, and print the whole command line when an external program (like a C
compiler) fails.
PK��\�W��@alt-python39-setuptools/docs/deprecated/distutils/commandref.rstnu�[���.. _reference:

*****************
Command Reference
*****************

.. include:: ./_setuptools_disclaimer.rst

.. % \section{Building modules: the \protect\command{build} command family}
.. % \label{build-cmds}
.. % \subsubsection{\protect\command{build}}
.. % \label{build-cmd}
.. % \subsubsection{\protect\command{build\_py}}
.. % \label{build-py-cmd}
.. % \subsubsection{\protect\command{build\_ext}}
.. % \label{build-ext-cmd}
.. % \subsubsection{\protect\command{build\_clib}}
.. % \label{build-clib-cmd}


.. _install-cmd:

Installing modules: the :command:`install` command family
=========================================================

The install command ensures that the build commands have been run and then runs
the subcommands :command:`install_lib`, :command:`install_data` and
:command:`install_scripts`.

.. % \subsubsection{\protect\command{install\_lib}}
.. % \label{install-lib-cmd}


.. _install-data-cmd:

:command:`install_data`
-----------------------

This command installs all data files provided with the distribution.


.. _install-scripts-cmd:

:command:`install_scripts`
--------------------------

This command installs all (Python) scripts in the distribution.

.. % \subsection{Cleaning up: the \protect\command{clean} command}
.. % \label{clean-cmd}


.. _sdist-cmd:

Creating a source distribution: the :command:`sdist` command
============================================================

.. XXX fragment moved down from above: needs context!

The manifest template commands are:

+-------------------------------------------+-----------------------------------------------+
| Command                                   | Description                                   |
+===========================================+===============================================+
| :command:`include pat1 pat2 ...`          | include all files matching any of the listed  |
|                                           | patterns                                      |
+-------------------------------------------+-----------------------------------------------+
| :command:`exclude pat1 pat2 ...`          | exclude all files matching any of the listed  |
|                                           | patterns                                      |
+-------------------------------------------+-----------------------------------------------+
| :command:`recursive-include dir pat1 pat2 | include all files under *dir* matching any of |
| ...`                                      | the listed patterns                           |
+-------------------------------------------+-----------------------------------------------+
| :command:`recursive-exclude dir pat1 pat2 | exclude all files under *dir* matching any of |
| ...`                                      | the listed patterns                           |
+-------------------------------------------+-----------------------------------------------+
| :command:`global-include pat1 pat2 ...`   | include all files anywhere in the source tree |
|                                           | matching --- & any of the listed patterns     |
+-------------------------------------------+-----------------------------------------------+
| :command:`global-exclude pat1 pat2 ...`   | exclude all files anywhere in the source tree |
|                                           | matching --- & any of the listed patterns     |
+-------------------------------------------+-----------------------------------------------+
| :command:`prune dir`                      | exclude all files under *dir*                 |
+-------------------------------------------+-----------------------------------------------+
| :command:`graft dir`                      | include all files under *dir*                 |
+-------------------------------------------+-----------------------------------------------+

The patterns here are Unix-style "glob" patterns: ``*`` matches any sequence of
regular filename characters, ``?`` matches any single regular filename
character, and ``[range]`` matches any of the characters in *range* (e.g.,
``a-z``, ``a-zA-Z``, ``a-f0-9_.``).  The definition of "regular filename
character" is platform-specific: on Unix it is anything except slash; on Windows
anything except backslash or colon.

.. XXX Windows support not there yet

.. % \section{Creating a built distribution: the
.. % \protect\command{bdist} command family}
.. % \label{bdist-cmds}

.. % \subsection{\protect\command{bdist}}
.. % \subsection{\protect\command{bdist\_dumb}}
.. % \subsection{\protect\command{bdist\_rpm}}
.. % \subsection{\protect\command{bdist\_wininst}}


PK��\.<��ZZ?alt-python39-setuptools/docs/deprecated/distutils/builtdist.rstnu�[���.. _built-dist:

****************************
Creating Built Distributions
****************************

.. include:: ./_setuptools_disclaimer.rst

A "built distribution" is what you're probably used to thinking of either as a
"binary package" or an "installer" (depending on your background).  It's not
necessarily binary, though, because it might contain only Python source code
and/or byte-code; and we don't call it a package, because that word is already
spoken for in Python.  (And "installer" is a term specific to the world of
mainstream desktop systems.)

A built distribution is how you make life as easy as possible for installers of
your module distribution: for users of RPM-based Linux systems, it's a binary
RPM; for Windows users, it's an executable installer; for Debian-based Linux
users, it's a Debian package; and so forth.  Obviously, no one person will be
able to create built distributions for every platform under the sun, so the
Distutils are designed to enable module developers to concentrate on their
specialty---writing code and creating source distributions---while an
intermediary species called *packagers* springs up to turn source distributions
into built distributions for as many platforms as there are packagers.

Of course, the module developer could be their own packager; or the packager could
be a volunteer "out there" somewhere who has access to a platform which the
original developer does not; or it could be software periodically grabbing new
source distributions and turning them into built distributions for as many
platforms as the software has access to.  Regardless of who they are, a packager
uses the setup script and the :command:`bdist` command family to generate built
distributions.

As a simple example, if I run the following command in the Distutils source
tree::

   python setup.py bdist

then the Distutils builds my module distribution (the Distutils itself in this
case), does a "fake" installation (also in the :file:`build` directory), and
creates the default type of built distribution for my platform.  The default
format for built distributions is a "dumb" tar file on Unix, and a simple
executable installer on Windows.  (That tar file is considered "dumb" because it
has to be unpacked in a specific location to work.)

Thus, the above command on a Unix system creates
:file:`Distutils-1.0.{plat}.tar.gz`; unpacking this tarball from the right place
installs the Distutils just as though you had downloaded the source distribution
and run ``python setup.py install``.  (The "right place" is either the root of
the filesystem or  Python's :file:`{prefix}` directory, depending on the options
given to the :command:`bdist_dumb` command; the default is to make dumb
distributions relative to :file:`{prefix}`.)

Obviously, for pure Python distributions, this isn't any simpler than just
running ``python setup.py install``\ ---but for non-pure distributions, which
include extensions that would need to be compiled, it can mean the difference
between someone being able to use your extensions or not.  And creating "smart"
built distributions, such as an RPM package or an executable installer for
Windows, is far more convenient for users even if your distribution doesn't
include any extensions.

The :command:`bdist` command has a :option:`!--formats` option, similar to the
:command:`sdist` command, which you can use to select the types of built
distribution to generate: for example, ::

   python setup.py bdist --format=zip

would, when run on a Unix system, create
:file:`Distutils-1.0.{plat}.zip`\ ---again, this archive would be unpacked
from the root directory to install the Distutils.

The available formats for built distributions are:

+-------------+------------------------------+---------+
| Format      | Description                  | Notes   |
+=============+==============================+=========+
| ``gztar``   | gzipped tar file             | \(1)    |
|             | (:file:`.tar.gz`)            |         |
+-------------+------------------------------+---------+
| ``bztar``   | bzipped tar file             |         |
|             | (:file:`.tar.bz2`)           |         |
+-------------+------------------------------+---------+
| ``xztar``   | xzipped tar file             |         |
|             | (:file:`.tar.xz`)            |         |
+-------------+------------------------------+---------+
| ``ztar``    | compressed tar file          | \(3)    |
|             | (:file:`.tar.Z`)             |         |
+-------------+------------------------------+---------+
| ``tar``     | tar file (:file:`.tar`)      |         |
+-------------+------------------------------+---------+
| ``zip``     | zip file (:file:`.zip`)      | (2),(4) |
+-------------+------------------------------+---------+
| ``rpm``     | RPM                          | \(5)    |
+-------------+------------------------------+---------+
| ``pkgtool`` | Solaris :program:`pkgtool`   |         |
+-------------+------------------------------+---------+
| ``sdux``    | HP-UX :program:`swinstall`   |         |
+-------------+------------------------------+---------+
| ``wininst`` | self-extracting ZIP file for | \(4)    |
|             | Windows                      |         |
+-------------+------------------------------+---------+
| ``msi``     | Microsoft Installer.         |         |
+-------------+------------------------------+---------+

.. versionchanged:: 3.5
   Added support for the ``xztar`` format.


Notes:

(1)
   default on Unix

(2)
   default on Windows

(3)
   requires external :program:`compress` utility.

(4)
   requires either external :program:`zip` utility or :mod:`zipfile` module (part
   of the standard Python library since Python 1.6)

(5)
   requires external :program:`rpm` utility, version 3.0.4 or better (use ``rpm
   --version`` to find out which version you have)

You don't have to use the :command:`bdist` command with the :option:`!--formats`
option; you can also use the command that directly implements the format you're
interested in.  Some of these :command:`bdist` "sub-commands" actually generate
several similar formats; for instance, the :command:`bdist_dumb` command
generates all the "dumb" archive formats (``tar``, ``gztar``, ``bztar``,
``xztar``, ``ztar``, and ``zip``), and :command:`bdist_rpm` generates both
binary and source RPMs.  The :command:`bdist` sub-commands, and the formats
generated by each, are:

+--------------------------+-------------------------------------+
| Command                  | Formats                             |
+==========================+=====================================+
| :command:`bdist_dumb`    | tar, gztar, bztar, xztar, ztar, zip |
+--------------------------+-------------------------------------+
| :command:`bdist_rpm`     | rpm, srpm                           |
+--------------------------+-------------------------------------+
| :command:`bdist_wininst` | wininst                             |
+--------------------------+-------------------------------------+
| :command:`bdist_msi`     | msi                                 |
+--------------------------+-------------------------------------+

.. note::
   bdist_wininst is deprecated since Python 3.8.

.. note::
   bdist_msi is deprecated since Python 3.9.

The following sections give details on the individual :command:`bdist_\*`
commands.


.. .. _creating-dumb:

.. Creating dumb built distributions
.. =================================

.. XXX Need to document absolute vs. prefix-relative packages here, but first
   I have to implement it!


.. _creating-rpms:

Creating RPM packages
=====================

The RPM format is used by many popular Linux distributions, including Red Hat,
SuSE, and Mandrake.  If one of these (or any of the other RPM-based Linux
distributions) is your usual environment, creating RPM packages for other users
of that same distribution is trivial. Depending on the complexity of your module
distribution and differences between Linux distributions, you may also be able
to create RPMs that work on different RPM-based distributions.

The usual way to create an RPM of your module distribution is to run the
:command:`bdist_rpm` command::

   python setup.py bdist_rpm

or the :command:`bdist` command with the :option:`!--format` option::

   python setup.py bdist --formats=rpm

The former allows you to specify RPM-specific options; the latter allows  you to
easily specify multiple formats in one run.  If you need to do both, you can
explicitly specify multiple :command:`bdist_\*` commands and their options::

   python setup.py bdist_rpm --packager="John Doe <jdoe@example.org>" \
                   bdist_wininst --target-version="2.0"

Creating RPM packages is driven by a :file:`.spec` file, much as using the
Distutils is driven by the setup script.  To make your life easier, the
:command:`bdist_rpm` command normally creates a :file:`.spec` file based on the
information you supply in the setup script, on the command line, and in any
Distutils configuration files.  Various options and sections in the
:file:`.spec` file are derived from options in the setup script as follows:

+------------------------------------------+----------------------------------------------+
| RPM :file:`.spec` file option or section | Distutils setup script option                |
+==========================================+==============================================+
| Name                                     | ``name``                                     |
+------------------------------------------+----------------------------------------------+
| Summary (in preamble)                    | ``description``                              |
+------------------------------------------+----------------------------------------------+
| Version                                  | ``version``                                  |
+------------------------------------------+----------------------------------------------+
| Vendor                                   | ``author`` and ``author_email``,             |
|                                          | or  --- & ``maintainer`` and                 |
|                                          | ``maintainer_email``                         |
+------------------------------------------+----------------------------------------------+
| Copyright                                | ``license``                                  |
+------------------------------------------+----------------------------------------------+
| Url                                      | ``url``                                      |
+------------------------------------------+----------------------------------------------+
| %description (section)                   | ``long_description``                         |
+------------------------------------------+----------------------------------------------+

Additionally, there are many options in :file:`.spec` files that don't have
corresponding options in the setup script.  Most of these are handled through
options to the :command:`bdist_rpm` command as follows:

+-------------------------------+-----------------------------+-------------------------+
| RPM :file:`.spec` file option | :command:`bdist_rpm` option | default value           |
| or section                    |                             |                         |
+===============================+=============================+=========================+
| Release                       | ``release``                 | "1"                     |
+-------------------------------+-----------------------------+-------------------------+
| Group                         | ``group``                   | "Development/Libraries" |
+-------------------------------+-----------------------------+-------------------------+
| Vendor                        | ``vendor``                  | (see above)             |
+-------------------------------+-----------------------------+-------------------------+
| Packager                      | ``packager``                | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Provides                      | ``provides``                | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Requires                      | ``requires``                | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Conflicts                     | ``conflicts``               | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Obsoletes                     | ``obsoletes``               | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Distribution                  | ``distribution_name``       | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| BuildRequires                 | ``build_requires``          | (none)                  |
+-------------------------------+-----------------------------+-------------------------+
| Icon                          | ``icon``                    | (none)                  |
+-------------------------------+-----------------------------+-------------------------+

Obviously, supplying even a few of these options on the command-line would be
tedious and error-prone, so it's usually best to put them in the setup
configuration file, :file:`setup.cfg`\ ---see section :ref:`setup-config`.  If
you distribute or package many Python module distributions, you might want to
put options that apply to all of them in your personal Distutils configuration
file (:file:`~/.pydistutils.cfg`).  If you want to temporarily disable
this file, you can pass the :option:`!--no-user-cfg` option to :file:`setup.py`.

There are three steps to building a binary RPM package, all of which are
handled automatically by the Distutils:

#. create a :file:`.spec` file, which describes the package (analogous  to the
   Distutils setup script; in fact, much of the information in the  setup script
   winds up in the :file:`.spec` file)

#. create the source RPM

#. create the "binary" RPM (which may or may not contain binary code, depending
   on whether your module distribution contains Python extensions)

Normally, RPM bundles the last two steps together; when you use the Distutils,
all three steps are typically bundled together.

If you wish, you can separate these three steps.  You can use the
:option:`!--spec-only` option to make :command:`bdist_rpm` just create the
:file:`.spec` file and exit; in this case, the :file:`.spec` file will be
written to the "distribution directory"---normally :file:`dist/`, but
customizable with the :option:`!--dist-dir` option.  (Normally, the :file:`.spec`
file winds up deep in the "build tree," in a temporary directory created by
:command:`bdist_rpm`.)

.. % \XXX{this isn't implemented yet---is it needed?!}
.. % You can also specify a custom \file{.spec} file with the
.. % \longprogramopt{spec-file} option; used in conjunction with
.. % \longprogramopt{spec-only}, this gives you an opportunity to customize
.. % the \file{.spec} file manually:
.. %
.. % \ begin{verbatim}
.. % > python setup.py bdist_rpm --spec-only
.. % # ...edit dist/FooBar-1.0.spec
.. % > python setup.py bdist_rpm --spec-file=dist/FooBar-1.0.spec
.. % \ end{verbatim}
.. %
.. % (Although a better way to do this is probably to override the standard
.. % \command{bdist\_rpm} command with one that writes whatever else you want
.. % to the \file{.spec} file.)


.. _creating-wininst:

Creating Windows Installers
===========================

.. warning::
   bdist_wininst is deprecated since Python 3.8.

.. warning::
   bdist_msi is deprecated since Python 3.9.

Executable installers are the natural format for binary distributions on
Windows.  They display a nice graphical user interface, display some information
about the module distribution to be installed taken from the metadata in the
setup script, let the user select a few options, and start or cancel the
installation.

Since the metadata is taken from the setup script, creating Windows installers
is usually as easy as running::

   python setup.py bdist_wininst

or the :command:`bdist` command with the :option:`!--formats` option::

   python setup.py bdist --formats=wininst

If you have a pure module distribution (only containing pure Python modules and
packages), the resulting installer will be version independent and have a name
like :file:`foo-1.0.win32.exe`. Note that creating ``wininst`` binary
distributions in only supported on Windows systems.

If you have a non-pure distribution, the extensions can only be created on a
Windows platform, and will be Python version dependent. The installer filename
will reflect this and now has the form :file:`foo-1.0.win32-py2.0.exe`.  You
have to create a separate installer for every Python version you want to
support.

The installer will try to compile pure modules into :term:`bytecode` after installation
on the target system in normal and optimizing mode.  If you don't want this to
happen for some reason, you can run the :command:`bdist_wininst` command with
the :option:`!--no-target-compile` and/or the :option:`!--no-target-optimize`
option.

By default the installer will display the cool "Python Powered" logo when it is
run, but you can also supply your own 152x261 bitmap which must be a Windows
:file:`.bmp` file with the :option:`!--bitmap` option.

The installer will also display a large title on the desktop background window
when it is run, which is constructed from the name of your distribution and the
version number.  This can be changed to another text by using the
:option:`!--title` option.

The installer file will be written to the "distribution directory" --- normally
:file:`dist/`, but customizable with the :option:`!--dist-dir` option.

.. _cross-compile-windows:

Cross-compiling on Windows
==========================

Starting with Python 2.6, distutils is capable of cross-compiling between
Windows platforms.  In practice, this means that with the correct tools
installed, you can use a 32bit version of Windows to create 64bit extensions
and vice-versa.

To build for an alternate platform, specify the :option:`!--plat-name` option
to the build command.  Valid values are currently 'win32', and  'win-amd64'.
For example, on a 32bit version of Windows, you could execute::

   python setup.py build --plat-name=win-amd64

to build a 64bit version of your extension.  The Windows Installers also
support this option, so the command::

   python setup.py build --plat-name=win-amd64 bdist_wininst

would create a 64bit installation executable on your 32bit version of Windows.

To cross-compile, you must download the Python source code and cross-compile
Python itself for the platform you are targeting - it is not possible from a
binary installation of Python (as the .lib etc file for other platforms are
not included.)  In practice, this means the user of a 32 bit operating
system will need to use Visual Studio 2008 to open the
:file:`PCbuild/PCbuild.sln` solution in the Python source tree and build the
"x64" configuration of the 'pythoncore' project before cross-compiling
extensions is possible.

Note that by default, Visual Studio 2008 does not install 64bit compilers or
tools.  You may need to reexecute the Visual Studio setup process and select
these tools (using Control Panel->[Add/Remove] Programs is a convenient way to
check or modify your existing install.)

.. _postinstallation-script:

The Postinstallation script
---------------------------

Starting with Python 2.3, a postinstallation script can be specified with the
:option:`!--install-script` option.  The basename of the script must be
specified, and the script filename must also be listed in the scripts argument
to the setup function.

This script will be run at installation time on the target system after all the
files have been copied, with ``argv[1]`` set to :option:`!-install`, and again at
uninstallation time before the files are removed with ``argv[1]`` set to
:option:`!-remove`.

The installation script runs embedded in the windows installer, every output
(``sys.stdout``, ``sys.stderr``) is redirected into a buffer and will be
displayed in the GUI after the script has finished.

Some functions especially useful in this context are available as additional
built-in functions in the installation script.


.. function:: directory_created(path)
              file_created(path)

   These functions should be called when a directory or file is created by the
   postinstall script at installation time.  It will register *path* with the
   uninstaller, so that it will be removed when the distribution is uninstalled.
   To be safe, directories are only removed if they are empty.


.. function:: get_special_folder_path(csidl_string)

   This function can be used to retrieve special folder locations on Windows like
   the Start Menu or the Desktop.  It returns the full path to the folder.
   *csidl_string* must be one of the following strings::

      "CSIDL_APPDATA"

      "CSIDL_COMMON_STARTMENU"
      "CSIDL_STARTMENU"

      "CSIDL_COMMON_DESKTOPDIRECTORY"
      "CSIDL_DESKTOPDIRECTORY"

      "CSIDL_COMMON_STARTUP"
      "CSIDL_STARTUP"

      "CSIDL_COMMON_PROGRAMS"
      "CSIDL_PROGRAMS"

      "CSIDL_FONTS"

   If the folder cannot be retrieved, :exc:`OSError` is raised.

   Which folders are available depends on the exact Windows version, and probably
   also the configuration.  For details refer to Microsoft's documentation of the
   :c:func:`SHGetSpecialFolderPath` function.


.. function:: create_shortcut(target, description, filename[, arguments[, workdir[, iconpath[, iconindex]]]])

   This function creates a shortcut. *target* is the path to the program to be
   started by the shortcut. *description* is the description of the shortcut.
   *filename* is the title of the shortcut that the user will see. *arguments*
   specifies the command line arguments, if any. *workdir* is the working directory
   for the program. *iconpath* is the file containing the icon for the shortcut,
   and *iconindex* is the index of the icon in the file *iconpath*.  Again, for
   details consult the Microsoft documentation for the :class:`IShellLink`
   interface.


Vista User Access Control (UAC)
===============================

Starting with Python 2.6, bdist_wininst supports a :option:`!--user-access-control`
option.  The default is 'none' (meaning no UAC handling is done), and other
valid values are 'auto' (meaning prompt for UAC elevation if Python was
installed for all users) and 'force' (meaning always prompt for elevation).

.. note::
   bdist_wininst is deprecated since Python 3.8.

.. note::
   bdist_msi is deprecated since Python 3.9.
PK��\:X���?alt-python39-setuptools/docs/deprecated/distutils/uploading.rstnu�[���:orphan:

***************************************
Uploading Packages to the Package Index
***************************************

References to up to date PyPI documentation can be found at
:ref:`publishing-python-packages`.
PK��\n�O���Lalt-python39-setuptools/docs/deprecated/distutils/_setuptools_disclaimer.rstnu�[���.. note::

   This document is being retained solely until the ``setuptools`` documentation
   at https://setuptools.readthedocs.io/en/latest/setuptools.html
   independently covers all of the relevant information currently included here.
PK��\>�yq�!�!Balt-python39-setuptools/docs/deprecated/distutils/introduction.rstnu�[���.. _distutils-intro:

****************************
An Introduction to Distutils
****************************

.. include:: ./_setuptools_disclaimer.rst

This document covers using the Distutils to distribute your Python modules,
concentrating on the role of developer/distributor: if you're looking for
information on installing Python modules, you should refer to the
:ref:`install-index` chapter.


.. _distutils-concepts:

Concepts & Terminology
======================

Using the Distutils is quite simple, both for module developers and for
users/administrators installing third-party modules.  As a developer, your
responsibilities (apart from writing solid, well-documented and well-tested
code, of course!) are:

* write a setup script (:file:`setup.py` by convention)

* (optional) write a setup configuration file

* create a source distribution

* (optional) create one or more built (binary) distributions

Each of these tasks is covered in this document.

Not all module developers have access to a multitude of platforms, so it's not
always feasible to expect them to create a multitude of built distributions.  It
is hoped that a class of intermediaries, called *packagers*, will arise to
address this need.  Packagers will take source distributions released by module
developers, build them on one or more platforms, and release the resulting built
distributions.  Thus, users on the most popular platforms will be able to
install most popular Python module distributions in the most natural way for
their platform, without having to run a single setup script or compile a line of
code.


.. _distutils-simple-example:

A Simple Example
================

The setup script is usually quite simple, although since it's written in Python,
there are no arbitrary limits to what you can do with it, though you should be
careful about putting arbitrarily expensive operations in your setup script.
Unlike, say, Autoconf-style configure scripts, the setup script may be run
multiple times in the course of building and installing your module
distribution.

If all you want to do is distribute a module called ``foo``, contained in a
file :file:`foo.py`, then your setup script can be as simple as this::

   from distutils.core import setup
   setup(name='foo',
         version='1.0',
         py_modules=['foo'],
         )

Some observations:

* most information that you supply to the Distutils is supplied as keyword
  arguments to the :func:`~distutils.core.setup` function

* those keyword arguments fall into two categories: package metadata (name,
  version number) and information about what's in the package (a list of pure
  Python modules, in this case)

* modules are specified by module name, not filename (the same will hold true
  for packages and extensions)

* it's recommended that you supply a little more metadata, in particular your
  name, email address and a URL for the project (see section :ref:`setup-script`
  for an example)

To create a source distribution for this module, you would create a setup
script, :file:`setup.py`, containing the above code, and run this command from a
terminal::

   python setup.py sdist

For Windows, open a command prompt window (:menuselection:`Start -->
Accessories`) and change the command to::

   setup.py sdist

:command:`sdist` will create an archive file (e.g., tarball on Unix, ZIP file on Windows)
containing your setup script :file:`setup.py`, and your module :file:`foo.py`.
The archive file will be named :file:`foo-1.0.tar.gz` (or :file:`.zip`), and
will unpack into a directory :file:`foo-1.0`.

If an end-user wishes to install your ``foo`` module, all they have to do is
download :file:`foo-1.0.tar.gz` (or :file:`.zip`), unpack it, and---from the
:file:`foo-1.0` directory---run ::

   python setup.py install

which will ultimately copy :file:`foo.py` to the appropriate directory for
third-party modules in their Python installation.

This simple example demonstrates some fundamental concepts of the Distutils.
First, both developers and installers have the same basic user interface, i.e.
the setup script.  The difference is which Distutils *commands* they use: the
:command:`sdist` command is almost exclusively for module developers, while
:command:`install` is more often for installers (although most developers will
want to install their own code occasionally).

If you want to make things really easy for your users, you can create one or
more built distributions for them.  For instance, if you are running on a
Windows machine, and want to make things easy for other Windows users, you can
create an executable installer (the most appropriate type of built distribution
for this platform) with the :command:`bdist_wininst` command.  For example::

   python setup.py bdist_wininst

will create an executable installer, :file:`foo-1.0.win32.exe`, in the current
directory.

Other useful built distribution formats are RPM, implemented by the
:command:`bdist_rpm` command, Solaris :program:`pkgtool`
(:command:`bdist_pkgtool`), and HP-UX :program:`swinstall`
(:command:`bdist_sdux`).  For example, the following command will create an RPM
file called :file:`foo-1.0.noarch.rpm`::

   python setup.py bdist_rpm

(The :command:`bdist_rpm` command uses the :command:`rpm` executable, therefore
this has to be run on an RPM-based system such as Red Hat Linux, SuSE Linux, or
Mandrake Linux.)

You can find out what distribution formats are available at any time by running
::

   python setup.py bdist --help-formats


.. _python-terms:

General Python terminology
==========================

If you're reading this document, you probably have a good idea of what modules,
extensions, and so forth are.  Nevertheless, just to be sure that everyone is
operating from a common starting point, we offer the following glossary of
common Python terms:

module
   the basic unit of code reusability in Python: a block of code imported by some
   other code.  Three types of modules concern us here: pure Python modules,
   extension modules, and packages.

pure Python module
   a module written in Python and contained in a single :file:`.py` file (and
   possibly associated :file:`.pyc` files).  Sometimes referred to as a
   "pure module."

extension module
   a module written in the low-level language of the Python implementation: C/C++
   for Python, Java for Jython. Typically contained in a single dynamically
   loadable pre-compiled file, e.g. a shared object (:file:`.so`) file for Python
   extensions on Unix, a DLL (given the :file:`.pyd` extension) for Python
   extensions on Windows, or a Java class file for Jython extensions.  (Note that
   currently, the Distutils only handles C/C++ extensions for Python.)

package
   a module that contains other modules; typically contained in a directory in the
   filesystem and distinguished from other directories by the presence of a file
   :file:`__init__.py`.

root package
   the root of the hierarchy of packages.  (This isn't really a package, since it
   doesn't have an :file:`__init__.py` file.  But we have to call it something.)
   The vast majority of the standard library is in the root package, as are many
   small, standalone third-party modules that don't belong to a larger module
   collection. Unlike regular packages, modules in the root package can be found in
   many directories: in fact, every directory listed in ``sys.path`` contributes
   modules to the root package.


.. _distutils-term:

Distutils-specific terminology
==============================

The following terms apply more specifically to the domain of distributing Python
modules using the Distutils:

module distribution
   a collection of Python modules distributed together as a single downloadable
   resource and meant to be installed *en masse*.  Examples of some well-known
   module distributions are NumPy, SciPy, Pillow,
   or mxBase.  (This would be called a *package*, except that term is
   already taken in the Python context: a single module distribution may contain
   zero, one, or many Python packages.)

pure module distribution
   a module distribution that contains only pure Python modules and packages.
   Sometimes referred to as a "pure distribution."

non-pure module distribution
   a module distribution that contains at least one extension module.  Sometimes
   referred to as a "non-pure distribution."

distribution root
   the top-level directory of your source tree (or  source distribution); the
   directory where :file:`setup.py` exists.  Generally  :file:`setup.py` will be
   run from this directory.
PK��\I���Balt-python39-setuptools/docs/deprecated/distutils/packageindex.rstnu�[���:orphan:

.. _package-index:

*******************************
The Python Package Index (PyPI)
*******************************

The `Python Package Index (PyPI)`_ stores metadata describing distributions
packaged with distutils and other publishing tools, as well the distribution
archives themselves.

References to up to date PyPI documentation can be found at
:ref:`publishing-python-packages`.

.. _Python Package Index (PyPI): https://pypi.org
PK��\�ׁ���@alt-python39-setuptools/docs/deprecated/distutils/configfile.rstnu�[���.. _setup-config:

************************************
Writing the Setup Configuration File
************************************

.. include:: ./_setuptools_disclaimer.rst

Often, it's not possible to write down everything needed to build a distribution
*a priori*: you may need to get some information from the user, or from the
user's system, in order to proceed.  As long as that information is fairly
simple---a list of directories to search for C header files or libraries, for
example---then providing a configuration file, :file:`setup.cfg`, for users to
edit is a cheap and easy way to solicit it.  Configuration files also let you
provide default values for any command option, which the installer can then
override either on the command-line or by editing the config file.

The setup configuration file is a useful middle-ground between the setup
script---which, ideally, would be opaque to installers [#]_---and the command-line to
the setup script, which is outside of your control and entirely up to the
installer.  In fact, :file:`setup.cfg` (and any other Distutils configuration
files present on the target system) are processed after the contents of the
setup script, but before the command-line.  This has  several useful
consequences:

.. % (If you have more advanced needs, such as determining which extensions
.. % to build based on what capabilities are present on the target system,
.. % then you need the Distutils ``auto-configuration'' facility.  This
.. % started to appear in Distutils 0.9 but, as of this writing, isn't mature
.. % or stable enough yet for real-world use.)

* installers can override some of what you put in :file:`setup.py` by editing
  :file:`setup.cfg`

* you can provide non-standard defaults for options that are not easily set in
  :file:`setup.py`

* installers can override anything in :file:`setup.cfg` using the command-line
  options to :file:`setup.py`

The basic syntax of the configuration file is simple:

.. code-block:: ini

   [command]
   option=value
   ...

where *command* is one of the Distutils commands (e.g. :command:`build_py`,
:command:`install`), and *option* is one of the options that command supports.
Any number of options can be supplied for each command, and any number of
command sections can be included in the file.  Blank lines are ignored, as are
comments, which run from a ``'#'`` character until the end of the line.  Long
option values can be split across multiple lines simply by indenting the
continuation lines.

You can find out the list of options supported by a particular command with the
universal :option:`!--help` option, e.g.

.. code-block:: shell-session

   $ python setup.py --help build_ext
   [...]
   Options for 'build_ext' command:
     --build-lib (-b)     directory for compiled extension modules
     --build-temp (-t)    directory for temporary files (build by-products)
     --inplace (-i)       ignore build-lib and put compiled extensions into the
                          source directory alongside your pure Python modules
     --include-dirs (-I)  list of directories to search for header files
     --define (-D)        C preprocessor macros to define
     --undef (-U)         C preprocessor macros to undefine
     --swig-opts          list of SWIG command line options
   [...]

Note that an option spelled :option:`!--foo-bar` on the command-line  is spelled
``foo_bar`` in configuration files.

.. _distutils-build-ext-inplace:

For example, say you want your extensions to be built "in-place"---that is, you
have an extension ``pkg.ext``, and you want the compiled extension file
(:file:`ext.so` on Unix, say) to be put in the same source directory as your
pure Python modules ``pkg.mod1`` and ``pkg.mod2``.  You can always use the
:option:`!--inplace` option on the command-line to ensure this:

.. code-block:: sh

   python setup.py build_ext --inplace

But this requires that you always specify the :command:`build_ext` command
explicitly, and remember to provide :option:`!--inplace`. An easier way is to
"set and forget" this option, by encoding it in :file:`setup.cfg`, the
configuration file for this distribution:

.. code-block:: ini

   [build_ext]
   inplace=1

This will affect all builds of this module distribution, whether or not you
explicitly specify :command:`build_ext`.  If you include :file:`setup.cfg` in
your source distribution, it will also affect end-user builds---which is
probably a bad idea for this option, since always building extensions in-place
would break installation of the module distribution.  In certain peculiar cases,
though, modules are built right in their installation directory, so this is
conceivably a useful ability.  (Distributing extensions that expect to be built
in their installation directory is almost always a bad idea, though.)

Another example: certain commands take a lot of options that don't change from
run to run; for example, :command:`bdist_rpm` needs to know everything required
to generate a "spec" file for creating an RPM distribution.  Some of this
information comes from the setup script, and some is automatically generated by
the Distutils (such as the list of files installed).  But some of it has to be
supplied as options to :command:`bdist_rpm`, which would be very tedious to do
on the command-line for every run.  Hence, here is a snippet from the Distutils'
own :file:`setup.cfg`:

.. code-block:: ini

   [bdist_rpm]
   release = 1
   packager = Greg Ward <gward@python.net>
   doc_files = CHANGES.txt
               README.txt
               USAGE.txt
               doc/
               examples/

Note that the ``doc_files`` option is simply a whitespace-separated string
split across multiple lines for readability.


.. seealso::

   :ref:`inst-config-syntax` in "Installing Python Modules"
      More information on the configuration files is available in the manual for
      system administrators.


.. rubric:: Footnotes

.. [#] This ideal probably won't be achieved until auto-configuration is fully
   supported by the Distutils.

PK��\I��xzxz<alt-python39-setuptools/docs/deprecated/distutils/apiref.rstnu�[���.. _api-reference:

*************
API Reference
*************

.. seealso::

   `New and changed setup.py arguments in setuptools`_
      The ``setuptools`` project adds new capabilities to the ``setup`` function
      and other APIs, makes the API consistent across different Python versions,
      and is hence recommended over using ``distutils`` directly.

.. _New and changed setup.py arguments in setuptools: https://setuptools.readthedocs.io/en/latest/setuptools.html#new-and-changed-setup-keywords

.. include:: ./_setuptools_disclaimer.rst

:mod:`distutils.core` --- Core Distutils functionality
======================================================

.. module:: distutils.core
   :synopsis: The core Distutils functionality


The :mod:`distutils.core` module is the only module that needs to be installed
to use the Distutils. It provides the :func:`setup` (which is called from the
setup script). Indirectly provides the  :class:`distutils.dist.Distribution` and
:class:`distutils.cmd.Command` class.


.. function:: setup(arguments)

   The basic do-everything function that does most everything you could ever ask
   for from a Distutils method.

   The setup function takes a large number of arguments. These are laid out in the
   following table.

   .. tabularcolumns:: |l|L|L|

   +--------------------+--------------------------------+-------------------------------------------------------------+
   | argument name      | value                          | type                                                        |
   +====================+================================+=============================================================+
   | *name*             | The name of the package        | a string                                                    |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *version*          | The version number of the      | a string                                                    |
   |                    | package; see                   |                                                             |
   |                    | :mod:`distutils.version`       |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *description*      | A single line describing the   | a string                                                    |
   |                    | package                        |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *long_description* | Longer description of the      | a string                                                    |
   |                    | package                        |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *author*           | The name of the package author | a string                                                    |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *author_email*     | The email address of the       | a string                                                    |
   |                    | package author                 |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *maintainer*       | The name of the current        | a string                                                    |
   |                    | maintainer, if different from  |                                                             |
   |                    | the author. Note that if       |                                                             |
   |                    | the maintainer is provided,    |                                                             |
   |                    | distutils will use it as the   |                                                             |
   |                    | author in :file:`PKG-INFO`     |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *maintainer_email* | The email address of the       | a string                                                    |
   |                    | current maintainer, if         |                                                             |
   |                    | different from the author      |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *url*              | A URL for the package          | a string                                                    |
   |                    | (homepage)                     |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *download_url*     | A URL to download the package  | a string                                                    |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *packages*         | A list of Python packages that | a list of strings                                           |
   |                    | distutils will manipulate      |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *py_modules*       | A list of Python modules that  | a list of strings                                           |
   |                    | distutils will manipulate      |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *scripts*          | A list of standalone script    | a list of strings                                           |
   |                    | files to be built and          |                                                             |
   |                    | installed                      |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *ext_modules*      | A list of Python extensions to | a list of instances of                                      |
   |                    | be built                       | :class:`distutils.core.Extension`                           |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *classifiers*      | A list of categories for the   | a list of strings; valid classifiers are listed on `PyPI    |
   |                    | package                        | <https://pypi.org/classifiers>`_.                           |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *distclass*        | the :class:`Distribution`      | a subclass of                                               |
   |                    | class to use                   | :class:`distutils.core.Distribution`                        |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *script_name*      | The name of the setup.py       | a string                                                    |
   |                    | script - defaults to           |                                                             |
   |                    | ``sys.argv[0]``                |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *script_args*      | Arguments to supply to the     | a list of strings                                           |
   |                    | setup script                   |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *options*          | default options for the setup  | a dictionary                                                |
   |                    | script                         |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *license*          | The license for the package    | a string                                                    |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *keywords*         | Descriptive meta-data, see     | a list of strings or a comma-separated string               |
   |                    | :pep:`314`                     |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *platforms*        |                                | a list of strings or a comma-separated string               |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *cmdclass*         | A mapping of command names to  | a dictionary                                                |
   |                    | :class:`Command` subclasses    |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *data_files*       | A list of data files to        | a list                                                      |
   |                    | install                        |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+
   | *package_dir*      | A mapping of package to        | a dictionary                                                |
   |                    | directory names                |                                                             |
   +--------------------+--------------------------------+-------------------------------------------------------------+



.. function:: run_setup(script_name[, script_args=None, stop_after='run'])

   Run a setup script in a somewhat controlled environment, and return  the
   :class:`distutils.dist.Distribution` instance that drives things.   This is
   useful if you need to find out the distribution meta-data  (passed as keyword
   args from *script* to :func:`setup`), or  the contents of the config files or
   command-line.

   *script_name* is a file that will be read and run with :func:`exec`.  ``sys.argv[0]``
   will be replaced with *script* for the duration of the call.  *script_args* is a
   list of strings; if supplied, ``sys.argv[1:]`` will be replaced by *script_args*
   for the duration  of the call.

   *stop_after* tells :func:`setup` when to stop processing; possible  values:

   .. tabularcolumns:: |l|L|

   +---------------+---------------------------------------------+
   | value         | description                                 |
   +===============+=============================================+
   | *init*        | Stop after the :class:`Distribution`        |
   |               | instance has been created  and populated    |
   |               | with the keyword arguments to :func:`setup` |
   +---------------+---------------------------------------------+
   | *config*      | Stop after config files have been parsed    |
   |               | (and their data stored in the               |
   |               | :class:`Distribution` instance)             |
   +---------------+---------------------------------------------+
   | *commandline* | Stop after the command-line                 |
   |               | (``sys.argv[1:]`` or  *script_args*) have   |
   |               | been parsed (and the data stored in the     |
   |               | :class:`Distribution` instance.)            |
   +---------------+---------------------------------------------+
   | *run*         | Stop after all commands have been run (the  |
   |               | same as  if :func:`setup` had been called   |
   |               | in the usual way). This is the default      |
   |               | value.                                      |
   +---------------+---------------------------------------------+

In addition, the :mod:`distutils.core` module exposed a number of  classes that
live elsewhere.

* :class:`~distutils.extension.Extension` from :mod:`distutils.extension`

* :class:`~distutils.cmd.Command` from :mod:`distutils.cmd`

* :class:`~distutils.dist.Distribution` from :mod:`distutils.dist`

A short description of each of these follows, but see the relevant module for
the full reference.


.. class:: Extension

   The Extension class describes a single C or C++ extension module in a setup
   script. It accepts the following keyword arguments in its constructor:

   .. tabularcolumns:: |l|L|l|

   +------------------------+--------------------------------+---------------------------+
   | argument name          | value                          | type                      |
   +========================+================================+===========================+
   | *name*                 | the full name of the           | a string                  |
   |                        | extension, including any       |                           |
   |                        | packages --- ie. *not* a       |                           |
   |                        | filename or pathname, but      |                           |
   |                        | Python dotted name             |                           |
   +------------------------+--------------------------------+---------------------------+
   | *sources*              | list of source filenames,      | a list of strings         |
   |                        | relative to the distribution   |                           |
   |                        | root (where the setup script   |                           |
   |                        | lives), in Unix form           |                           |
   |                        | (slash-separated) for          |                           |
   |                        | portability.                   |                           |
   |                        | Source files may be C, C++,    |                           |
   |                        | SWIG (.i), platform-specific   |                           |
   |                        | resource files, or whatever    |                           |
   |                        | else is recognized by the      |                           |
   |                        | :command:`build_ext` command   |                           |
   |                        | as source for a Python         |                           |
   |                        | extension.                     |                           |
   +------------------------+--------------------------------+---------------------------+
   | *include_dirs*         | list of directories to search  | a list of strings         |
   |                        | for C/C++ header files (in     |                           |
   |                        | Unix form for portability)     |                           |
   +------------------------+--------------------------------+---------------------------+
   | *define_macros*        | list of macros to define; each | a list of tuples          |
   |                        | macro is defined using a       |                           |
   |                        | 2-tuple ``(name, value)``,     |                           |
   |                        | where *value* is               |                           |
   |                        | either the string to define it |                           |
   |                        | to or ``None`` to define it    |                           |
   |                        | without a particular value     |                           |
   |                        | (equivalent of ``#define FOO`` |                           |
   |                        | in source or :option:`!-DFOO`  |                           |
   |                        | on Unix C compiler command     |                           |
   |                        | line)                          |                           |
   +------------------------+--------------------------------+---------------------------+
   | *undef_macros*         | list of macros to undefine     | a list of strings         |
   |                        | explicitly                     |                           |
   +------------------------+--------------------------------+---------------------------+
   | *library_dirs*         | list of directories to search  | a list of strings         |
   |                        | for C/C++ libraries at link    |                           |
   |                        | time                           |                           |
   +------------------------+--------------------------------+---------------------------+
   | *libraries*            | list of library names (not     | a list of strings         |
   |                        | filenames or paths) to link    |                           |
   |                        | against                        |                           |
   +------------------------+--------------------------------+---------------------------+
   | *runtime_library_dirs* | list of directories to search  | a list of strings         |
   |                        | for C/C++ libraries at run     |                           |
   |                        | time (for shared extensions,   |                           |
   |                        | this is when the extension is  |                           |
   |                        | loaded)                        |                           |
   +------------------------+--------------------------------+---------------------------+
   | *extra_objects*        | list of extra files to link    | a list of strings         |
   |                        | with (eg. object files not     |                           |
   |                        | implied by 'sources', static   |                           |
   |                        | library that must be           |                           |
   |                        | explicitly specified, binary   |                           |
   |                        | resource files, etc.)          |                           |
   +------------------------+--------------------------------+---------------------------+
   | *extra_compile_args*   | any extra platform- and        | a list of strings         |
   |                        | compiler-specific information  |                           |
   |                        | to use when compiling the      |                           |
   |                        | source files in 'sources'. For |                           |
   |                        | platforms and compilers where  |                           |
   |                        | a command line makes sense,    |                           |
   |                        | this is typically a list of    |                           |
   |                        | command-line arguments, but    |                           |
   |                        | for other platforms it could   |                           |
   |                        | be anything.                   |                           |
   +------------------------+--------------------------------+---------------------------+
   | *extra_link_args*      | any extra platform- and        | a list of strings         |
   |                        | compiler-specific information  |                           |
   |                        | to use when linking object     |                           |
   |                        | files together to create the   |                           |
   |                        | extension (or to create a new  |                           |
   |                        | static Python interpreter).    |                           |
   |                        | Similar interpretation as for  |                           |
   |                        | 'extra_compile_args'.          |                           |
   +------------------------+--------------------------------+---------------------------+
   | *export_symbols*       | list of symbols to be exported | a list of strings         |
   |                        | from a shared extension. Not   |                           |
   |                        | used on all platforms, and not |                           |
   |                        | generally necessary for Python |                           |
   |                        | extensions, which typically    |                           |
   |                        | export exactly one symbol:     |                           |
   |                        | ``init`` + extension_name.     |                           |
   +------------------------+--------------------------------+---------------------------+
   | *depends*              | list of files that the         | a list of strings         |
   |                        | extension depends on           |                           |
   +------------------------+--------------------------------+---------------------------+
   | *language*             | extension language (i.e.       | a string                  |
   |                        | ``'c'``, ``'c++'``,            |                           |
   |                        | ``'objc'``). Will be detected  |                           |
   |                        | from the source extensions if  |                           |
   |                        | not provided.                  |                           |
   +------------------------+--------------------------------+---------------------------+
   | *optional*             | specifies that a build failure | a boolean                 |
   |                        | in the extension should not    |                           |
   |                        | abort the build process, but   |                           |
   |                        | simply skip the extension.     |                           |
   +------------------------+--------------------------------+---------------------------+

   .. versionchanged:: 3.8

      On Unix, C extensions are no longer linked to libpython except on
      Android and Cygwin.


.. class:: Distribution

   A :class:`Distribution` describes how to build, install and package up a Python
   software package.

   See the :func:`setup` function for a list of keyword arguments accepted  by the
   Distribution constructor. :func:`setup` creates a Distribution instance.

   .. versionchanged:: 3.7
      :class:`~distutils.core.Distribution` now warns if ``classifiers``,
      ``keywords`` and ``platforms`` fields are not specified as a list or
      a string.

.. class:: Command

   A :class:`Command` class (or rather, an instance of one of its subclasses)
   implement a single distutils command.


:mod:`distutils.ccompiler` --- CCompiler base class
===================================================

.. module:: distutils.ccompiler
   :synopsis: Abstract CCompiler class


This module provides the abstract base class for the :class:`CCompiler`
classes.  A :class:`CCompiler` instance can be used for all the compile  and
link steps needed to build a single project. Methods are provided to  set
options for the compiler --- macro definitions, include directories,  link path,
libraries and the like.

This module provides the following functions.


.. function:: gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries)

   Generate linker options for searching library directories and linking with
   specific libraries.  *libraries* and *library_dirs* are, respectively, lists of
   library names (not filenames!) and search directories.  Returns a list of
   command-line options suitable for use with some compiler (depending on the two
   format strings passed in).


.. function:: gen_preprocess_options(macros, include_dirs)

   Generate C pre-processor options (:option:`!-D`, :option:`!-U`, :option:`!-I`) as
   used by at least two types of compilers: the typical Unix compiler and Visual
   C++. *macros* is the usual thing, a list of 1- or 2-tuples, where ``(name,)``
   means undefine (:option:`!-U`) macro *name*, and ``(name, value)`` means define
   (:option:`!-D`) macro *name* to *value*.  *include_dirs* is just a list of
   directory names to be added to the header file search path (:option:`!-I`).
   Returns a list of command-line options suitable for either Unix compilers or
   Visual C++.


.. function:: get_default_compiler(osname, platform)

   Determine the default compiler to use for the given platform.

   *osname* should be one of the standard Python OS names (i.e. the ones returned
   by ``os.name``) and *platform* the common value returned by ``sys.platform`` for
   the platform in question.

   The default values are ``os.name`` and ``sys.platform`` in case the parameters
   are not given.


.. function:: new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0)

   Factory function to generate an instance of some CCompiler subclass for the
   supplied platform/compiler combination. *plat* defaults to ``os.name`` (eg.
   ``'posix'``, ``'nt'``), and *compiler*  defaults to the default compiler for
   that platform. Currently only ``'posix'`` and ``'nt'`` are supported, and the
   default compilers are "traditional Unix interface" (:class:`UnixCCompiler`
   class) and Visual C++ (:class:`MSVCCompiler` class).  Note that it's perfectly
   possible to ask for a Unix compiler object under Windows, and a Microsoft
   compiler object under Unix---if you supply a value for *compiler*, *plat* is
   ignored.

   .. % Is the posix/nt only thing still true? Mac OS X seems to work, and
   .. % returns a UnixCCompiler instance. How to document this... hmm.


.. function:: show_compilers()

   Print list of available compilers (used by the :option:`!--help-compiler` options
   to :command:`build`, :command:`build_ext`, :command:`build_clib`).


.. class:: CCompiler([verbose=0, dry_run=0, force=0])

   The abstract base class :class:`CCompiler` defines the interface that  must be
   implemented by real compiler classes.  The class also has  some utility methods
   used by several compiler classes.

   The basic idea behind a compiler abstraction class is that each instance can be
   used for all the compile/link steps in building a single project.  Thus,
   attributes common to all of those compile and link steps --- include
   directories, macros to define, libraries to link against, etc. --- are
   attributes of the compiler instance.  To allow for variability in how individual
   files are treated, most of those attributes may be varied on a per-compilation
   or per-link basis.

   The constructor for each subclass creates an instance of the Compiler object.
   Flags are *verbose* (show verbose output), *dry_run* (don't actually execute the
   steps) and *force* (rebuild everything, regardless of dependencies). All of
   these flags default to ``0`` (off). Note that you probably don't want to
   instantiate :class:`CCompiler` or one of its subclasses directly - use the
   :func:`distutils.CCompiler.new_compiler` factory function instead.

   The following methods allow you to manually alter compiler options for  the
   instance of the Compiler class.


   .. method:: CCompiler.add_include_dir(dir)

      Add *dir* to the list of directories that will be searched for header files.
      The compiler is instructed to search directories in the order in which they are
      supplied by successive calls to :meth:`add_include_dir`.


   .. method:: CCompiler.set_include_dirs(dirs)

      Set the list of directories that will be searched to *dirs* (a list of strings).
      Overrides any preceding calls to :meth:`add_include_dir`; subsequent calls to
      :meth:`add_include_dir` add to the list passed to :meth:`set_include_dirs`.
      This does not affect any list of standard include directories that the compiler
      may search by default.


   .. method:: CCompiler.add_library(libname)

      Add *libname* to the list of libraries that will be included in all links driven
      by this compiler object.  Note that *libname* should \*not\* be the name of a
      file containing a library, but the name of the library itself: the actual
      filename will be inferred by the linker, the compiler, or the compiler class
      (depending on the platform).

      The linker will be instructed to link against libraries in the order they were
      supplied to :meth:`add_library` and/or :meth:`set_libraries`.  It is perfectly
      valid to duplicate library names; the linker will be instructed to link against
      libraries as many times as they are mentioned.


   .. method:: CCompiler.set_libraries(libnames)

      Set the list of libraries to be included in all links driven by this compiler
      object to *libnames* (a list of strings).  This does not affect any standard
      system libraries that the linker may include by default.


   .. method:: CCompiler.add_library_dir(dir)

      Add *dir* to the list of directories that will be searched for libraries
      specified to :meth:`add_library` and :meth:`set_libraries`.  The linker will be
      instructed to search for libraries in the order they are supplied to
      :meth:`add_library_dir` and/or :meth:`set_library_dirs`.


   .. method:: CCompiler.set_library_dirs(dirs)

      Set the list of library search directories to *dirs* (a list of strings).  This
      does not affect any standard library search path that the linker may search by
      default.


   .. method:: CCompiler.add_runtime_library_dir(dir)

      Add *dir* to the list of directories that will be searched for shared libraries
      at runtime.


   .. method:: CCompiler.set_runtime_library_dirs(dirs)

      Set the list of directories to search for shared libraries at runtime to *dirs*
      (a list of strings).  This does not affect any standard search path that the
      runtime linker may search by default.


   .. method:: CCompiler.define_macro(name[, value=None])

      Define a preprocessor macro for all compilations driven by this compiler object.
      The optional parameter *value* should be a string; if it is not supplied, then
      the macro will be defined without an explicit value and the exact outcome
      depends on the compiler used.

      .. XXX true? does ANSI say anything about this?


   .. method:: CCompiler.undefine_macro(name)

      Undefine a preprocessor macro for all compilations driven by this compiler
      object.  If the same macro is defined by :meth:`define_macro` and
      undefined by :meth:`undefine_macro` the last call takes precedence
      (including multiple redefinitions or undefinitions).  If the macro is
      redefined/undefined on a per-compilation basis (ie. in the call to
      :meth:`compile`), then that takes precedence.


   .. method:: CCompiler.add_link_object(object)

      Add *object* to the list of object files (or analogues, such as explicitly named
      library files or the output of "resource compilers") to be included in every
      link driven by this compiler object.


   .. method:: CCompiler.set_link_objects(objects)

      Set the list of object files (or analogues) to be included in every link to
      *objects*.  This does not affect any standard object files that the linker may
      include by default (such as system libraries).

   The following methods implement methods for autodetection of compiler  options,
   providing some functionality similar to GNU :program:`autoconf`.


   .. method:: CCompiler.detect_language(sources)

      Detect the language of a given file, or list of files. Uses the  instance
      attributes :attr:`~CCompiler.language_map` (a dictionary), and  :attr:`~CCompiler.language_order` (a
      list) to do the job.


   .. method:: CCompiler.find_library_file(dirs, lib[, debug=0])

      Search the specified list of directories for a static or shared library file
      *lib* and return the full path to that file.  If *debug* is true, look for a
      debugging version (if that makes sense on the current platform).  Return
      ``None`` if *lib* wasn't found in any of the specified directories.


   .. method:: CCompiler.has_function(funcname [, includes=None, include_dirs=None, libraries=None, library_dirs=None])

      Return a boolean indicating whether *funcname* is supported on the current
      platform.  The optional arguments can be used to augment the compilation
      environment by providing additional include files and paths and libraries and
      paths.


   .. method:: CCompiler.library_dir_option(dir)

      Return the compiler option to add *dir* to the list of directories searched for
      libraries.


   .. method:: CCompiler.library_option(lib)

      Return the compiler option to add *lib* to the list of libraries linked into the
      shared library or executable.


   .. method:: CCompiler.runtime_library_dir_option(dir)

      Return the compiler option to add *dir* to the list of directories searched for
      runtime libraries.


   .. method:: CCompiler.set_executables(**args)

      Define the executables (and options for them) that will be run to perform the
      various stages of compilation.  The exact set of executables that may be
      specified here depends on the compiler class (via the 'executables' class
      attribute), but most will have:

      +--------------+------------------------------------------+
      | attribute    | description                              |
      +==============+==========================================+
      | *compiler*   | the C/C++ compiler                       |
      +--------------+------------------------------------------+
      | *linker_so*  | linker used to create shared objects and |
      |              | libraries                                |
      +--------------+------------------------------------------+
      | *linker_exe* | linker used to create binary executables |
      +--------------+------------------------------------------+
      | *archiver*   | static library creator                   |
      +--------------+------------------------------------------+

      On platforms with a command-line (Unix, DOS/Windows), each of these is a string
      that will be split into executable name and (optional) list of arguments.
      (Splitting the string is done similarly to how Unix shells operate: words are
      delimited by spaces, but quotes and backslashes can override this.  See
      :func:`distutils.util.split_quoted`.)

   The following methods invoke stages in the build process.


   .. method:: CCompiler.compile(sources[, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None])

      Compile one or more source files. Generates object files (e.g.  transforms a
      :file:`.c` file to a :file:`.o` file.)

      *sources* must be a list of filenames, most likely C/C++ files, but in reality
      anything that can be handled by a particular compiler and compiler class (eg.
      :class:`MSVCCompiler` can handle resource files in *sources*).  Return a list of
      object filenames, one per source filename in *sources*.  Depending on the
      implementation, not all source files will necessarily be compiled, but all
      corresponding object filenames will be returned.

      If *output_dir* is given, object files will be put under it, while retaining
      their original path component.  That is, :file:`foo/bar.c` normally compiles to
      :file:`foo/bar.o` (for a Unix implementation); if *output_dir* is *build*, then
      it would compile to :file:`build/foo/bar.o`.

      *macros*, if given, must be a list of macro definitions.  A macro definition is
      either a ``(name, value)`` 2-tuple or a ``(name,)`` 1-tuple. The former defines
      a macro; if the value is ``None``, the macro is defined without an explicit
      value.  The 1-tuple case undefines a macro.  Later
      definitions/redefinitions/undefinitions take precedence.

      *include_dirs*, if given, must be a list of strings, the directories to add to
      the default include file search path for this compilation only.

      *debug* is a boolean; if true, the compiler will be instructed to output debug
      symbols in (or alongside) the object file(s).

      *extra_preargs* and *extra_postargs* are implementation-dependent. On platforms
      that have the notion of a command-line (e.g. Unix, DOS/Windows), they are most
      likely lists of strings: extra command-line arguments to prepend/append to the
      compiler command line.  On other platforms, consult the implementation class
      documentation.  In any event, they are intended as an escape hatch for those
      occasions when the abstract compiler framework doesn't cut the mustard.

      *depends*, if given, is a list of filenames that all targets depend on.  If a
      source file is older than any file in depends, then the source file will be
      recompiled.  This supports dependency tracking, but only at a coarse
      granularity.

      Raises :exc:`CompileError` on failure.


   .. method:: CCompiler.create_static_lib(objects, output_libname[, output_dir=None, debug=0, target_lang=None])

      Link a bunch of stuff together to create a static library file. The "bunch of
      stuff" consists of the list of object files supplied as *objects*, the extra
      object files supplied to :meth:`add_link_object` and/or
      :meth:`set_link_objects`, the libraries supplied to :meth:`add_library` and/or
      :meth:`set_libraries`, and the libraries supplied as *libraries* (if any).

      *output_libname* should be a library name, not a filename; the filename will be
      inferred from the library name.  *output_dir* is the directory where the library
      file will be put.

      .. XXX defaults to what?

      *debug* is a boolean; if true, debugging information will be included in the
      library (note that on most platforms, it is the compile step where this matters:
      the *debug* flag is included here just for consistency).

      *target_lang* is the target language for which the given objects are being
      compiled. This allows specific linkage time treatment of certain languages.

      Raises :exc:`LibError` on failure.


   .. method:: CCompiler.link(target_desc, objects, output_filename[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, target_lang=None])

      Link a bunch of stuff together to create an executable or shared library file.

      The "bunch of stuff" consists of the list of object files supplied as *objects*.
      *output_filename* should be a filename.  If *output_dir* is supplied,
      *output_filename* is relative to it (i.e. *output_filename* can provide
      directory components if needed).

      *libraries* is a list of libraries to link against.  These are library names,
      not filenames, since they're translated into filenames in a platform-specific
      way (eg. *foo* becomes :file:`libfoo.a` on Unix and :file:`foo.lib` on
      DOS/Windows).  However, they can include a directory component, which means the
      linker will look in that specific directory rather than searching all the normal
      locations.

      *library_dirs*, if supplied, should be a list of directories to search for
      libraries that were specified as bare library names (ie. no directory
      component).  These are on top of the system default and those supplied to
      :meth:`add_library_dir` and/or :meth:`set_library_dirs`.  *runtime_library_dirs*
      is a list of directories that will be embedded into the shared library and used
      to search for other shared libraries that \*it\* depends on at run-time.  (This
      may only be relevant on Unix.)

      *export_symbols* is a list of symbols that the shared library will export.
      (This appears to be relevant only on Windows.)

      *debug* is as for :meth:`compile` and :meth:`create_static_lib`,  with the
      slight distinction that it actually matters on most platforms (as opposed to
      :meth:`create_static_lib`, which includes a *debug* flag mostly for form's
      sake).

      *extra_preargs* and *extra_postargs* are as for :meth:`compile`  (except of
      course that they supply command-line arguments for the particular linker being
      used).

      *target_lang* is the target language for which the given objects are being
      compiled. This allows specific linkage time treatment of certain languages.

      Raises :exc:`LinkError` on failure.


   .. method:: CCompiler.link_executable(objects, output_progname[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, target_lang=None])

      Link an executable.  *output_progname* is the name of the file executable, while
      *objects* are a list of object filenames to link in. Other arguments  are as for
      the :meth:`link` method.


   .. method:: CCompiler.link_shared_lib(objects, output_libname[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, target_lang=None])

      Link a shared library. *output_libname* is the name of the output  library,
      while *objects* is a list of object filenames to link in.  Other arguments are
      as for the :meth:`link` method.


   .. method:: CCompiler.link_shared_object(objects, output_filename[, output_dir=None, libraries=None, library_dirs=None, runtime_library_dirs=None, export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None, build_temp=None, target_lang=None])

      Link a shared object. *output_filename* is the name of the shared object that
      will be created, while *objects* is a list of object filenames  to link in.
      Other arguments are as for the :meth:`link` method.


   .. method:: CCompiler.preprocess(source[, output_file=None, macros=None, include_dirs=None, extra_preargs=None, extra_postargs=None])

      Preprocess a single C/C++ source file, named in *source*. Output will be written
      to file named *output_file*, or *stdout* if *output_file* not supplied.
      *macros* is a list of macro definitions as for :meth:`compile`, which will
      augment the macros set with :meth:`define_macro` and :meth:`undefine_macro`.
      *include_dirs* is a list of directory names that will be added to the  default
      list, in the same way as :meth:`add_include_dir`.

      Raises :exc:`PreprocessError` on failure.

   The following utility methods are defined by the :class:`CCompiler` class, for
   use by the various concrete subclasses.


   .. method:: CCompiler.executable_filename(basename[, strip_dir=0, output_dir=''])

      Returns the filename of the executable for the given *basename*.  Typically for
      non-Windows platforms this is the same as the basename,  while Windows will get
      a :file:`.exe` added.


   .. method:: CCompiler.library_filename(libname[, lib_type='static', strip_dir=0, output_dir=''])

      Returns the filename for the given library name on the current platform. On Unix
      a library with *lib_type* of ``'static'`` will typically  be of the form
      :file:`liblibname.a`, while a *lib_type* of ``'dynamic'``  will be of the form
      :file:`liblibname.so`.


   .. method:: CCompiler.object_filenames(source_filenames[, strip_dir=0, output_dir=''])

      Returns the name of the object files for the given source files.
      *source_filenames* should be a list of filenames.


   .. method:: CCompiler.shared_object_filename(basename[, strip_dir=0, output_dir=''])

      Returns the name of a shared object file for the given file name *basename*.


   .. method:: CCompiler.execute(func, args[, msg=None, level=1])

      Invokes :func:`distutils.util.execute`. This method invokes a  Python function
      *func* with the given arguments *args*, after  logging and taking into account
      the *dry_run* flag.


   .. method:: CCompiler.spawn(cmd)

      Invokes :func:`distutils.spawn.spawn`. This invokes an external  process to run
      the given command.


   .. method:: CCompiler.mkpath(name[, mode=511])

      Invokes :func:`distutils.dir_util.mkpath`. This creates a directory  and any
      missing ancestor directories.


   .. method:: CCompiler.move_file(src, dst)

      Invokes :meth:`distutils.file_util.move_file`. Renames *src* to  *dst*.


   .. method:: CCompiler.announce(msg[, level=1])

      Write a message using :func:`distutils.log.debug`.


   .. method:: CCompiler.warn(msg)

      Write a warning message *msg* to standard error.


   .. method:: CCompiler.debug_print(msg)

      If the *debug* flag is set on this :class:`CCompiler` instance, print  *msg* to
      standard output, otherwise do nothing.

.. % \subsection{Compiler-specific modules}
.. %
.. % The following modules implement concrete subclasses of the abstract
.. % \class{CCompiler} class. They should not be instantiated directly, but should
.. % be created using \function{distutils.ccompiler.new_compiler()} factory
.. % function.


:mod:`distutils.unixccompiler` --- Unix C Compiler
==================================================

.. module:: distutils.unixccompiler
   :synopsis: UNIX C Compiler


This module provides the :class:`UnixCCompiler` class, a subclass of
:class:`CCompiler` that handles the typical Unix-style command-line  C compiler:

* macros defined with :option:`!-Dname[=value]`

* macros undefined with :option:`!-Uname`

* include search directories specified with :option:`!-Idir`

* libraries specified with :option:`!-llib`

* library search directories specified with :option:`!-Ldir`

* compile handled by :program:`cc` (or similar) executable with :option:`!-c`
  option: compiles :file:`.c` to :file:`.o`

* link static library handled by :program:`ar` command (possibly with
  :program:`ranlib`)

* link shared library handled by :program:`cc` :option:`!-shared`


:mod:`distutils.msvccompiler` --- Microsoft Compiler
====================================================

.. module:: distutils.msvccompiler
   :synopsis: Microsoft Compiler

.. XXX: This is *waaaaay* out of date!

This module provides :class:`MSVCCompiler`, an implementation of the abstract
:class:`CCompiler` class for Microsoft Visual Studio. Typically, extension
modules need to be compiled with the same compiler that was used to compile
Python. For Python 2.3 and earlier, the compiler was Visual Studio 6. For Python
2.4 and 2.5, the compiler is Visual Studio .NET 2003.

:class:`MSVCCompiler` will normally choose the right compiler, linker etc. on
its own. To override this choice, the environment variables *DISTUTILS_USE_SDK*
and *MSSdk* must be both set. *MSSdk* indicates that the current environment has
been setup by the SDK's ``SetEnv.Cmd`` script, or that the environment variables
had been registered when the SDK was installed; *DISTUTILS_USE_SDK* indicates
that the distutils user has made an explicit choice to override the compiler
selection by :class:`MSVCCompiler`.


:mod:`distutils.bcppcompiler` --- Borland Compiler
==================================================

.. module:: distutils.bcppcompiler


This module provides :class:`BorlandCCompiler`, a subclass of the abstract
:class:`CCompiler` class for the Borland C++ compiler.


:mod:`distutils.cygwinccompiler` --- Cygwin Compiler
====================================================

.. module:: distutils.cygwinccompiler


This module provides the :class:`CygwinCCompiler` class, a subclass of
:class:`UnixCCompiler` that handles the Cygwin port of the GNU C compiler to
Windows.  It also contains the Mingw32CCompiler class which handles the mingw32
port of GCC (same as cygwin in no-cygwin mode).


:mod:`distutils.archive_util` ---  Archiving utilities
======================================================

.. module:: distutils.archive_util
   :synopsis: Utility functions for creating archive files (tarballs, zip files, ...)


This module provides a few functions for creating archive files, such as
tarballs or zipfiles.


.. function:: make_archive(base_name, format[, root_dir=None, base_dir=None, verbose=0, dry_run=0])

   Create an archive file (eg. ``zip`` or ``tar``).  *base_name*  is the name of
   the file to create, minus any format-specific extension;  *format* is the
   archive format: one of ``zip``, ``tar``, ``gztar``, ``bztar``, ``xztar``, or
   ``ztar``. *root_dir* is a directory that will be the root directory of the
   archive; ie. we typically ``chdir`` into *root_dir* before  creating the
   archive.  *base_dir* is the directory where we start  archiving from; ie.
   *base_dir* will be the common prefix of all files and directories in the
   archive.  *root_dir* and *base_dir* both default to the current directory.
   Returns the name of the archive file.

   .. versionchanged:: 3.5
      Added support for the ``xztar`` format.


.. function:: make_tarball(base_name, base_dir[, compress='gzip', verbose=0, dry_run=0])

   'Create an (optional compressed) archive as a tar file from all files in and
   under *base_dir*. *compress* must be ``'gzip'`` (the default),
   ``'bzip2'``, ``'xz'``, ``'compress'``, or ``None``.  For the ``'compress'``
   method the compression utility named by :program:`compress` must be on the
   default program search path, so this is probably Unix-specific.  The output
   tar file will be named :file:`base_dir.tar`, possibly plus the appropriate
   compression extension (``.gz``, ``.bz2``, ``.xz`` or ``.Z``).  Return the
   output filename.

   .. versionchanged:: 3.5
      Added support for the ``xz`` compression.


.. function:: make_zipfile(base_name, base_dir[, verbose=0, dry_run=0])

   Create a zip file from all files in and under *base_dir*.  The output zip file
   will be named *base_name* + :file:`.zip`.  Uses either the  :mod:`zipfile` Python
   module (if available) or the InfoZIP :file:`zip`  utility (if installed and
   found on the default search path).  If neither  tool is available, raises
   :exc:`DistutilsExecError`.   Returns the name of the output zip file.


:mod:`distutils.dep_util` --- Dependency checking
=================================================

.. module:: distutils.dep_util
   :synopsis: Utility functions for simple dependency checking


This module provides functions for performing simple, timestamp-based
dependency of files and groups of files; also, functions based entirely  on such
timestamp dependency analysis.


.. function:: newer(source, target)

   Return true if *source* exists and is more recently modified than *target*, or
   if *source* exists and *target* doesn't. Return false if both exist and *target*
   is the same age or newer  than *source*. Raise :exc:`DistutilsFileError` if
   *source* does not exist.


.. function:: newer_pairwise(sources, targets)

   Walk two filename lists in parallel, testing if each source is newer than its
   corresponding target.  Return a pair of lists (*sources*, *targets*) where
   source is newer than target, according to the semantics of :func:`newer`.

   .. % % equivalent to a listcomp...


.. function:: newer_group(sources, target[, missing='error'])

   Return true if *target* is out-of-date with respect to any file listed in
   *sources*.  In other words, if *target* exists and is newer than every file in
   *sources*, return false; otherwise return true. *missing* controls what we do
   when a source file is missing; the default (``'error'``) is to blow up with an
   :exc:`OSError` from  inside :func:`os.stat`; if it is ``'ignore'``, we silently
   drop any missing source files; if it is ``'newer'``, any missing source files
   make us assume that *target* is out-of-date (this is handy in "dry-run" mode:
   it'll make you pretend to carry out commands that wouldn't work because inputs
   are missing, but that doesn't matter because you're not actually going to run
   the commands).


:mod:`distutils.dir_util` --- Directory tree operations
=======================================================

.. module:: distutils.dir_util
   :synopsis: Utility functions for operating on directories and directory trees


This module provides functions for operating on directories and trees of
directories.


.. function:: mkpath(name[, mode=0o777, verbose=0, dry_run=0])

   Create a directory and any missing ancestor directories.  If the directory
   already exists (or if *name* is the empty string, which means the current
   directory, which of course exists), then do nothing.  Raise
   :exc:`DistutilsFileError` if unable to create some directory along the way (eg.
   some sub-path exists, but is a file rather than a directory).  If *verbose* is
   true, print a one-line summary of each mkdir to stdout.  Return the list of
   directories actually created.


.. function:: create_tree(base_dir, files[, mode=0o777, verbose=0, dry_run=0])

   Create all the empty directories under *base_dir* needed to put *files* there.
   *base_dir* is just the name of a directory which doesn't necessarily exist
   yet; *files* is a list of filenames to be interpreted relative to *base_dir*.
   *base_dir* + the directory portion of every file in *files* will be created if
   it doesn't already exist.  *mode*, *verbose* and *dry_run* flags  are as for
   :func:`mkpath`.


.. function:: copy_tree(src, dst[, preserve_mode=1, preserve_times=1, preserve_symlinks=0, update=0, verbose=0, dry_run=0])

   Copy an entire directory tree *src* to a new location *dst*.  Both *src* and
   *dst* must be directory names.  If *src* is not a directory, raise
   :exc:`DistutilsFileError`.  If *dst* does  not exist, it is created with
   :func:`mkpath`.  The end result of the  copy is that every file in *src* is
   copied to *dst*, and  directories under *src* are recursively copied to *dst*.
   Return the list of files that were copied or might have been copied, using their
   output name. The return value is unaffected by *update* or *dry_run*: it is
   simply the list of all files under *src*, with the names changed to be under
   *dst*.

   *preserve_mode* and *preserve_times* are the same as for
   :func:`distutils.file_util.copy_file`; note that they only apply to
   regular files, not to
   directories.  If *preserve_symlinks* is true, symlinks will be copied as
   symlinks (on platforms that support them!); otherwise (the default), the
   destination of the symlink will be copied.  *update* and *verbose* are the same
   as for :func:`~distutils.file_util.copy_file`.

   Files in *src* that begin with :file:`.nfs` are skipped (more information on
   these files is available in answer D2 of the `NFS FAQ page
   <http://nfs.sourceforge.net/#section_d>`_).

   .. versionchanged:: 3.3.1
      NFS files are ignored.

.. function:: remove_tree(directory[, verbose=0, dry_run=0])

   Recursively remove *directory* and all files and directories underneath it. Any
   errors are ignored (apart from being reported to ``sys.stdout`` if *verbose* is
   true).


:mod:`distutils.file_util` --- Single file operations
=====================================================

.. module:: distutils.file_util
   :synopsis: Utility functions for operating on single files


This module contains some utility functions for operating on individual files.


.. function:: copy_file(src, dst[, preserve_mode=1, preserve_times=1, update=0, link=None, verbose=0, dry_run=0])

   Copy file *src* to *dst*. If *dst* is a directory, then *src* is copied there
   with the same name; otherwise, it must be a filename. (If the file exists, it
   will be ruthlessly clobbered.) If *preserve_mode* is true (the default), the
   file's mode (type and permission bits, or whatever is analogous on the
   current platform) is copied. If *preserve_times* is true (the default), the
   last-modified and last-access times are copied as well. If *update* is true,
   *src* will only be copied if *dst* does not exist, or if *dst* does exist but
   is older than *src*.

   *link* allows you to make hard links (using :func:`os.link`) or symbolic links
   (using :func:`os.symlink`) instead of copying: set it to ``'hard'`` or
   ``'sym'``; if it is ``None`` (the default), files are copied. Don't set *link*
   on systems that don't support it: :func:`copy_file` doesn't check if hard or
   symbolic linking is available.  It uses :func:`~distutils.file_util._copy_file_contents` to copy file
   contents.

   Return a tuple ``(dest_name, copied)``: *dest_name* is the actual  name of the
   output file, and *copied* is true if the file was copied  (or would have been
   copied, if *dry_run* true).

   .. % XXX if the destination file already exists, we clobber it if
   .. % copying, but blow up if linking.  Hmmm.  And I don't know what
   .. % macostools.copyfile() does.  Should definitely be consistent, and
   .. % should probably blow up if destination exists and we would be
   .. % changing it (ie. it's not already a hard/soft link to src OR
   .. % (not update) and (src newer than dst)).


.. function:: move_file(src, dst[, verbose, dry_run])

   Move file *src* to *dst*. If *dst* is a directory, the file will be moved into
   it with the same name; otherwise, *src* is just renamed to *dst*.  Returns the
   new full name of the file.

   .. warning::

      Handles cross-device moves on Unix using :func:`copy_file`.  What about
      other systems?


.. function:: write_file(filename, contents)

   Create a file called *filename* and write *contents* (a sequence of strings
   without line terminators) to it.


:mod:`distutils.util` --- Miscellaneous other utility functions
===============================================================

.. module:: distutils.util
   :synopsis: Miscellaneous other utility functions


This module contains other assorted bits and pieces that don't fit into  any
other utility module.


.. function:: get_platform()

   Return a string that identifies the current platform.  This is used mainly to
   distinguish platform-specific build directories and platform-specific built
   distributions.  Typically includes the OS name and version and the
   architecture (as supplied by 'os.uname()'), although the exact information
   included depends on the OS; e.g., on Linux, the kernel version isn't
   particularly important.

   Examples of returned values:

   * ``linux-i586``
   * ``linux-alpha``
   * ``solaris-2.6-sun4u``

   For non-POSIX platforms, currently just returns ``sys.platform``.

   For Mac OS X systems the OS version reflects the minimal version on which
   binaries will run (that is, the value of ``MACOSX_DEPLOYMENT_TARGET``
   during the build of Python), not the OS version of the current system.

   For universal binary builds on Mac OS X the architecture value reflects
   the universal binary status instead of the architecture of the current
   processor. For 32-bit universal binaries the architecture is ``fat``,
   for 64-bit universal binaries the architecture is ``fat64``, and
   for 4-way universal binaries the architecture is ``universal``. Starting
   from Python 2.7 and Python 3.2 the architecture ``fat3`` is used for
   a 3-way universal build (ppc, i386, x86_64) and ``intel`` is used for
   a universal build with the i386 and x86_64 architectures

   Examples of returned values on Mac OS X:

   * ``macosx-10.3-ppc``

   * ``macosx-10.3-fat``

   * ``macosx-10.5-universal``

   * ``macosx-10.6-intel``

   For AIX, Python 3.9 and later return a string starting with "aix", followed
   by additional fields (separated by ``'-'``) that represent the combined
   values of AIX Version, Release and Technology Level (first field), Build Date
   (second field), and bit-size (third field). Python 3.8 and earlier returned
   only a single additional field with the AIX Version and Release.

   Examples of returned values on AIX:

   * ``aix-5307-0747-32`` # 32-bit build on AIX ``oslevel -s``: 5300-07-00-0000

   * ``aix-7105-1731-64`` # 64-bit build on AIX ``oslevel -s``: 7100-05-01-1731

   * ``aix-7.2``          # Legacy form reported in Python 3.8 and earlier

   .. versionchanged:: 3.9
      The AIX platform string format now also includes the technology level,
      build date, and ABI bit-size.


.. function:: convert_path(pathname)

   Return 'pathname' as a name that will work on the native filesystem, i.e. split
   it on '/' and put it back together again using the current directory separator.
   Needed because filenames in the setup script are always supplied in Unix style,
   and have to be converted to the local convention before we can actually use them
   in the filesystem.  Raises :exc:`ValueError` on non-Unix-ish systems if
   *pathname* either  starts or ends with a slash.


.. function:: change_root(new_root, pathname)

   Return *pathname* with *new_root* prepended.  If *pathname* is relative, this is
   equivalent to ``os.path.join(new_root,pathname)`` Otherwise, it requires making
   *pathname* relative and then joining the two, which is tricky on DOS/Windows.


.. function:: check_environ()

   Ensure that 'os.environ' has all the environment variables we guarantee that
   users can use in config files, command-line options, etc.  Currently this
   includes:

   * :envvar:`HOME` - user's home directory (Unix only)
   * :envvar:`PLAT` - description of the current platform, including hardware and
     OS (see :func:`get_platform`)


.. function:: subst_vars(s, local_vars)

   Perform shell/Perl-style variable substitution on *s*.  Every occurrence of
   ``$`` followed by a name is considered a variable, and variable is substituted
   by the value found in the *local_vars* dictionary, or in ``os.environ`` if it's
   not in *local_vars*. *os.environ* is first checked/augmented to guarantee that
   it contains certain values: see :func:`check_environ`.  Raise :exc:`ValueError`
   for any variables not found in either *local_vars* or ``os.environ``.

   Note that this is not a fully-fledged string interpolation function. A valid
   ``$variable`` can consist only of upper and lower case letters, numbers and an
   underscore. No { } or ( ) style quoting is available.


.. function:: split_quoted(s)

   Split a string up according to Unix shell-like rules for quotes and backslashes.
   In short: words are delimited by spaces, as long as those spaces are not escaped
   by a backslash, or inside a quoted string. Single and double quotes are
   equivalent, and the quote characters can be backslash-escaped.  The backslash is
   stripped from any two-character escape sequence, leaving only the escaped
   character.  The quote characters are stripped from any quoted string.  Returns a
   list of words.

   .. % Should probably be moved into the standard library.


.. function:: execute(func, args[, msg=None, verbose=0, dry_run=0])

   Perform some action that affects the outside world (for instance, writing to the
   filesystem).  Such actions are special because they are disabled by the
   *dry_run* flag.  This method takes  care of all that bureaucracy for you; all
   you have to do is supply the function to call and an argument tuple for it (to
   embody the "external action" being performed), and an optional message to print.


.. function:: strtobool(val)

   Convert a string representation of truth to true (1) or false (0).

   True values are ``y``, ``yes``, ``t``, ``true``, ``on``  and ``1``; false values
   are ``n``, ``no``, ``f``, ``false``,  ``off`` and ``0``.  Raises
   :exc:`ValueError` if *val*  is anything else.


.. function:: byte_compile(py_files[, optimize=0, force=0, prefix=None, base_dir=None, verbose=1, dry_run=0, direct=None])

   Byte-compile a collection of Python source files to :file:`.pyc` files in a
   :file:`__pycache__` subdirectory (see :pep:`3147` and :pep:`488`).
   *py_files* is a list of files to compile; any files that don't end in
   :file:`.py` are silently skipped.  *optimize* must be one of the following:

   * ``0`` - don't optimize
   * ``1`` - normal optimization (like ``python -O``)
   * ``2`` - extra optimization (like ``python -OO``)

   If *force* is true, all files are recompiled regardless of timestamps.

   The source filename encoded in each :term:`bytecode` file defaults to the filenames
   listed in *py_files*; you can modify these with *prefix* and *basedir*.
   *prefix* is a string that will be stripped off of each source filename, and
   *base_dir* is a directory name that will be prepended (after *prefix* is
   stripped).  You can supply either or both (or neither) of *prefix* and
   *base_dir*, as you wish.

   If *dry_run* is true, doesn't actually do anything that would affect the
   filesystem.

   Byte-compilation is either done directly in this interpreter process with the
   standard :mod:`py_compile` module, or indirectly by writing a temporary script
   and executing it.  Normally, you should let :func:`byte_compile` figure out to
   use direct compilation or not (see the source for details).  The *direct* flag
   is used by the script generated in indirect mode; unless you know what you're
   doing, leave it set to ``None``.

   .. versionchanged:: 3.2.3
      Create ``.pyc`` files with an :func:`import magic tag
      <imp.get_tag>` in their name, in a :file:`__pycache__` subdirectory
      instead of files without tag in the current directory.

   .. versionchanged:: 3.5
      Create ``.pyc`` files according to :pep:`488`.


.. function:: rfc822_escape(header)

   Return a version of *header* escaped for inclusion in an :rfc:`822` header, by
   ensuring there are 8 spaces space after each newline. Note that it does no other
   modification of the string.

   .. % this _can_ be replaced

.. % \subsection{Distutils objects}


:mod:`distutils.dist` --- The Distribution class
================================================

.. module:: distutils.dist
   :synopsis: Provides the Distribution class, which represents the module distribution being
              built/installed/distributed


This module provides the :class:`~distutils.core.Distribution` class, which
represents the module distribution being built/installed/distributed.


:mod:`distutils.extension` --- The Extension class
==================================================

.. module:: distutils.extension
   :synopsis: Provides the Extension class, used to describe C/C++ extension modules in setup
              scripts


This module provides the :class:`~distutils.extension.Extension` class,
used to describe C/C++ extension modules in setup scripts.

.. % \subsection{Ungrouped modules}
.. % The following haven't been moved into a more appropriate section yet.


:mod:`distutils.debug` --- Distutils debug mode
===============================================

.. module:: distutils.debug
   :synopsis: Provides the debug flag for distutils


This module provides the DEBUG flag.


:mod:`distutils.errors` --- Distutils exceptions
================================================

.. module:: distutils.errors
   :synopsis: Provides standard distutils exceptions


Provides exceptions used by the Distutils modules.  Note that Distutils modules
may raise standard exceptions; in particular, SystemExit is usually raised for
errors that are obviously the end-user's fault (eg. bad command-line arguments).

This module is safe to use in ``from ... import *`` mode; it only exports
symbols whose names start with ``Distutils`` and end with ``Error``.


:mod:`distutils.fancy_getopt` --- Wrapper around the standard getopt module
===========================================================================

.. module:: distutils.fancy_getopt
   :synopsis: Additional getopt functionality


This module provides a wrapper around the standard :mod:`getopt`  module that
provides the following additional features:

* short and long options are tied together

* options have help strings, so :func:`fancy_getopt` could potentially  create a
  complete usage summary

* options set attributes of a passed-in object

* boolean options can have "negative aliases" --- eg. if :option:`!--quiet` is
  the "negative alias" of :option:`!--verbose`, then :option:`!--quiet` on the
  command line sets *verbose* to false.

.. function:: fancy_getopt(options, negative_opt, object, args)

   Wrapper function. *options* is a list of ``(long_option, short_option,
   help_string)`` 3-tuples as described in the constructor for
   :class:`FancyGetopt`. *negative_opt* should be a dictionary mapping option names
   to option names, both the key and value should be in the *options* list.
   *object* is an object which will be used to store values (see the :meth:`~FancyGetopt.getopt`
   method of the :class:`FancyGetopt` class). *args* is the argument list. Will use
   ``sys.argv[1:]`` if you  pass ``None`` as *args*.


.. function:: wrap_text(text, width)

   Wraps *text* to less than *width* wide.


.. class:: FancyGetopt([option_table=None])

   The option_table is a list of 3-tuples: ``(long_option, short_option,
   help_string)``

   If an option takes an argument, its *long_option* should have ``'='`` appended;
   *short_option* should just be a single character, no ``':'`` in any case.
   *short_option* should be ``None`` if a *long_option*  doesn't have a
   corresponding *short_option*. All option tuples must have long options.

The :class:`FancyGetopt` class provides the following methods:


.. method:: FancyGetopt.getopt([args=None, object=None])

   Parse command-line options in args. Store as attributes on *object*.

   If *args* is ``None`` or not supplied, uses ``sys.argv[1:]``.  If *object* is
   ``None`` or not supplied, creates a new :class:`OptionDummy` instance, stores
   option values there, and returns a tuple ``(args, object)``.  If *object* is
   supplied, it is modified in place and :func:`getopt` just returns *args*; in
   both cases, the returned *args* is a modified copy of the passed-in *args* list,
   which is left untouched.

   .. % and args returned are?


.. method:: FancyGetopt.get_option_order()

   Returns the list of ``(option, value)`` tuples processed by the previous run of
   :meth:`getopt`  Raises :exc:`RuntimeError` if :meth:`getopt` hasn't been called
   yet.


.. method:: FancyGetopt.generate_help([header=None])

   Generate help text (a list of strings, one per suggested line of output) from
   the option table for this :class:`FancyGetopt` object.

   If supplied, prints the supplied *header* at the top of the help.


:mod:`distutils.filelist` --- The FileList class
================================================

.. module:: distutils.filelist
   :synopsis: The FileList class, used for poking about the file system and
              building lists of files.


This module provides the :class:`FileList` class, used for poking about the
filesystem and building lists of files.


:mod:`distutils.log` --- Simple :pep:`282`-style logging
========================================================

.. module:: distutils.log
   :synopsis: A simple logging mechanism, :pep:`282`-style


:mod:`distutils.spawn` --- Spawn a sub-process
==============================================

.. module:: distutils.spawn
   :synopsis: Provides the spawn() function


This module provides the :func:`~distutils.spawn.spawn` function, a
front-end to  various platform-specific functions for launching another
program in a  sub-process.
Also provides :func:`~distutils.spawn.find_executable` to search the path for a given executable
name.


:mod:`distutils.sysconfig` --- System configuration information
===============================================================

.. module:: distutils.sysconfig
   :synopsis: Low-level access to configuration information of the Python interpreter.
.. moduleauthor:: Fred L. Drake, Jr. <fdrake@acm.org>
.. moduleauthor:: Greg Ward <gward@python.net>
.. sectionauthor:: Fred L. Drake, Jr. <fdrake@acm.org>


The :mod:`distutils.sysconfig` module provides access to Python's low-level
configuration information.  The specific configuration variables available
depend heavily on the platform and configuration. The specific variables depend
on the build process for the specific version of Python being run; the variables
are those found in the :file:`Makefile` and configuration header that are
installed with Python on Unix systems.  The configuration header is called
:file:`pyconfig.h` for Python versions starting with 2.2, and :file:`config.h`
for earlier versions of Python.

Some additional functions are provided which perform some useful manipulations
for other parts of the :mod:`distutils` package.


.. data:: PREFIX

   The result of ``os.path.normpath(sys.prefix)``.


.. data:: EXEC_PREFIX

   The result of ``os.path.normpath(sys.exec_prefix)``.


.. function:: get_config_var(name)

   Return the value of a single variable.  This is equivalent to
   ``get_config_vars().get(name)``.


.. function:: get_config_vars(...)

   Return a set of variable definitions.  If there are no arguments, this returns a
   dictionary mapping names of configuration variables to values.  If arguments are
   provided, they should be strings, and the return value will be a sequence giving
   the associated values. If a given name does not have a corresponding value,
   ``None`` will be included for that variable.


.. function:: get_config_h_filename()

   Return the full path name of the configuration header.  For Unix, this will be
   the header generated by the :program:`configure` script; for other platforms the
   header will have been supplied directly by the Python source distribution.  The
   file is a platform-specific text file.


.. function:: get_makefile_filename()

   Return the full path name of the :file:`Makefile` used to build Python.  For
   Unix, this will be a file generated by the :program:`configure` script; the
   meaning for other platforms will vary.  The file is a platform-specific text
   file, if it exists. This function is only useful on POSIX platforms.


.. function:: get_python_inc([plat_specific[, prefix]])

   Return the directory for either the general or platform-dependent C include
   files.  If *plat_specific* is true, the platform-dependent include directory is
   returned; if false or omitted, the platform-independent directory is returned.
   If *prefix* is given, it is used as either the prefix instead of
   :const:`PREFIX`, or as the exec-prefix instead of :const:`EXEC_PREFIX` if
   *plat_specific* is true.


.. function:: get_python_lib([plat_specific[, standard_lib[, prefix]]])

   Return the directory for either the general or platform-dependent library
   installation.  If *plat_specific* is true, the platform-dependent include
   directory is returned; if false or omitted, the platform-independent directory
   is returned.  If *prefix* is given, it is used as either the prefix instead of
   :const:`PREFIX`, or as the exec-prefix instead of :const:`EXEC_PREFIX` if
   *plat_specific* is true.  If *standard_lib* is true, the directory for the
   standard library is returned rather than the directory for the installation of
   third-party extensions.

The following function is only intended for use within the :mod:`distutils`
package.


.. function:: customize_compiler(compiler)

   Do any platform-specific customization of a
   :class:`distutils.ccompiler.CCompiler` instance.

   This function is only needed on Unix at this time, but should be called
   consistently to support forward-compatibility.  It inserts the information that
   varies across Unix flavors and is stored in Python's :file:`Makefile`.  This
   information includes the selected compiler, compiler and linker options, and the
   extension used by the linker for shared objects.

This function is even more special-purpose, and should only be used from
Python's own build procedures.


.. function:: set_python_build()

   Inform the :mod:`distutils.sysconfig` module that it is being used as part of
   the build process for Python.  This changes a lot of relative locations for
   files, allowing them to be located in the build area rather than in an installed
   Python.


:mod:`distutils.text_file` --- The TextFile class
=================================================

.. module:: distutils.text_file
   :synopsis: Provides the TextFile class, a simple interface to text files


This module provides the :class:`TextFile` class, which gives an interface  to
text files that (optionally) takes care of stripping comments, ignoring  blank
lines, and joining lines with backslashes.


.. class:: TextFile([filename=None, file=None, **options])

   This class provides a file-like object that takes care of all  the things you
   commonly want to do when processing a text file  that has some line-by-line
   syntax: strip comments (as long as ``#``  is your comment character), skip blank
   lines, join adjacent lines by escaping the newline (ie. backslash at end of
   line), strip leading and/or trailing whitespace.  All of these are optional and
   independently controllable.

   The class provides a :meth:`warn` method so you can generate  warning messages
   that report physical line number, even if the  logical line in question spans
   multiple physical lines.  Also  provides :meth:`unreadline` for implementing
   line-at-a-time lookahead.

   :class:`TextFile` instances are create with either *filename*, *file*, or both.
   :exc:`RuntimeError` is raised if both are ``None``. *filename* should be a
   string, and *file* a file object (or something that provides :meth:`readline`
   and :meth:`close`  methods).  It is recommended that you supply at least
   *filename*,  so that :class:`TextFile` can include it in warning messages.  If
   *file* is not supplied, :class:`TextFile` creates its own using the
   :func:`open` built-in function.

   The options are all boolean, and affect the values returned by :meth:`readline`

   .. tabularcolumns:: |l|L|l|

   +------------------+--------------------------------+---------+
   | option name      | description                    | default |
   +==================+================================+=========+
   | *strip_comments* | strip from ``'#'`` to          | true    |
   |                  | end-of-line, as well as any    |         |
   |                  | whitespace leading up to the   |         |
   |                  | ``'#'``\ ---unless it is       |         |
   |                  | escaped by a backslash         |         |
   +------------------+--------------------------------+---------+
   | *lstrip_ws*      | strip leading whitespace from  | false   |
   |                  | each line before returning it  |         |
   +------------------+--------------------------------+---------+
   | *rstrip_ws*      | strip trailing whitespace      | true    |
   |                  | (including line terminator!)   |         |
   |                  | from each line before          |         |
   |                  | returning it.                  |         |
   +------------------+--------------------------------+---------+
   | *skip_blanks*    | skip lines that are empty      | true    |
   |                  | \*after\* stripping comments   |         |
   |                  | and whitespace.  (If both      |         |
   |                  | lstrip_ws and rstrip_ws are    |         |
   |                  | false, then some lines may     |         |
   |                  | consist of solely whitespace:  |         |
   |                  | these will \*not\* be skipped, |         |
   |                  | even if *skip_blanks* is       |         |
   |                  | true.)                         |         |
   +------------------+--------------------------------+---------+
   | *join_lines*     | if a backslash is the last     | false   |
   |                  | non-newline character on a     |         |
   |                  | line after stripping comments  |         |
   |                  | and whitespace, join the       |         |
   |                  | following line to it to form   |         |
   |                  | one logical line; if N         |         |
   |                  | consecutive lines end with a   |         |
   |                  | backslash, then N+1 physical   |         |
   |                  | lines will be joined to form   |         |
   |                  | one logical line.              |         |
   +------------------+--------------------------------+---------+
   | *collapse_join*  | strip leading whitespace from  | false   |
   |                  | lines that are joined to their |         |
   |                  | predecessor; only matters if   |         |
   |                  | ``(join_lines and not          |         |
   |                  | lstrip_ws)``                   |         |
   +------------------+--------------------------------+---------+

   Note that since *rstrip_ws* can strip the trailing newline, the semantics of
   :meth:`readline` must differ from those of the built-in file object's
   :meth:`readline` method!  In particular, :meth:`readline`  returns ``None`` for
   end-of-file: an empty string might just be a  blank line (or an all-whitespace
   line), if *rstrip_ws* is true  but *skip_blanks* is not.


   .. method:: TextFile.open(filename)

      Open a new file *filename*.  This overrides any *file* or *filename*
      constructor arguments.


   .. method:: TextFile.close()

      Close the current file and forget everything we know about it (including the
      filename and the current line number).


   .. method:: TextFile.warn(msg[,line=None])

      Print (to stderr) a warning message tied to the current logical line in the
      current file.  If the current logical line in the file spans multiple physical
      lines, the warning refers to the whole range, such as ``"lines 3-5"``.  If
      *line* is supplied,  it overrides the current line number; it may be a list or
      tuple  to indicate a range of physical lines, or an integer for a  single
      physical line.


   .. method:: TextFile.readline()

      Read and return a single logical line from the current file (or from an internal
      buffer if lines have previously been "unread" with :meth:`unreadline`).  If the
      *join_lines* option  is true, this may involve reading multiple physical lines
      concatenated into a single string.  Updates the current line number,  so calling
      :meth:`warn` after :meth:`readline` emits a warning  about the physical line(s)
      just read.  Returns ``None`` on end-of-file,  since the empty string can occur
      if *rstrip_ws* is true but  *strip_blanks* is not.


   .. method:: TextFile.readlines()

      Read and return the list of all logical lines remaining in the current file.
      This updates the current line number to the last line of the file.


   .. method:: TextFile.unreadline(line)

      Push *line* (a string) onto an internal buffer that will be checked by future
      :meth:`readline` calls.  Handy for implementing a parser with line-at-a-time
      lookahead. Note that lines that are "unread" with :meth:`unreadline` are not
      subsequently re-cleansed (whitespace  stripped, or whatever) when read with
      :meth:`readline`. If multiple calls are made to :meth:`unreadline` before a call
      to :meth:`readline`, the lines will be returned most in most recent first order.


:mod:`distutils.version` --- Version number classes
===================================================

.. module:: distutils.version
   :synopsis: Implements classes that represent module version numbers.


.. % todo
.. % \section{Distutils Commands}
.. %
.. % This part of Distutils implements the various Distutils commands, such
.. % as \code{build}, \code{install} \&c. Each command is implemented as a
.. % separate module, with the command name as the name of the module.


:mod:`distutils.cmd` --- Abstract base class for Distutils commands
===================================================================

.. module:: distutils.cmd
   :synopsis: Provides the abstract base class :class:`~distutils.cmd.Command`. This class
              is subclassed by the modules in the distutils.command subpackage.


This module supplies the abstract base class :class:`Command`.


.. class:: Command(dist)

   Abstract base class for defining command classes, the "worker bees" of the
   Distutils.  A useful analogy for command classes is to think of them as
   subroutines with local variables called *options*.  The options are declared
   in :meth:`initialize_options` and defined (given their final values) in
   :meth:`finalize_options`, both of which must be defined by every command
   class.  The distinction between the two is necessary because option values
   might come from the outside world (command line, config file, ...), and any
   options dependent on other options must be computed after these outside
   influences have been processed --- hence :meth:`finalize_options`.  The body
   of the subroutine, where it does all its work based on the values of its
   options, is the :meth:`run` method, which must also be implemented by every
   command class.

   The class constructor takes a single argument *dist*, a
   :class:`~distutils.core.Distribution` instance.


Creating a new Distutils command
================================

This section outlines the steps to create a new Distutils command.

A new command lives in a module in the :mod:`distutils.command` package. There
is a sample template in that directory called :file:`command_template`.  Copy
this file to a new module with the same name as the new command you're
implementing.  This module should implement a class with the same name as the
module (and the command).  So, for instance, to create the command
``peel_banana`` (so that users can run ``setup.py peel_banana``), you'd copy
:file:`command_template` to :file:`distutils/command/peel_banana.py`, then edit
it so that it's implementing the class ``peel_banana``, a subclass of
:class:`distutils.cmd.Command`.

Subclasses of :class:`Command` must define the following methods.

.. method:: Command.initialize_options()

   Set default values for all the options that this command supports.  Note that
   these defaults may be overridden by other commands, by the setup script, by
   config files, or by the command-line.  Thus, this is not the place to code
   dependencies between options; generally, :meth:`initialize_options`
   implementations are just a bunch of ``self.foo = None`` assignments.


.. method:: Command.finalize_options()

   Set final values for all the options that this command supports. This is
   always called as late as possible, ie.  after any option assignments from the
   command-line or from other commands have been done.  Thus, this is the place
   to code option dependencies: if *foo* depends on *bar*, then it is safe to
   set *foo* from *bar* as long as *foo* still has the same value it was
   assigned in :meth:`initialize_options`.


.. method:: Command.run()

   A command's raison d'etre: carry out the action it exists to perform, controlled
   by the options initialized in :meth:`initialize_options`, customized by other
   commands, the setup script, the command-line, and config files, and finalized in
   :meth:`finalize_options`.  All terminal output and filesystem interaction should
   be done by :meth:`run`.


.. attribute:: Command.sub_commands

   *sub_commands* formalizes the notion of a "family" of commands,
   e.g. ``install`` as the parent with sub-commands ``install_lib``,
   ``install_headers``, etc.  The parent of a family of commands defines
   *sub_commands* as a class attribute; it's a list of 2-tuples ``(command_name,
   predicate)``, with *command_name* a string and *predicate* a function, a
   string or ``None``.  *predicate* is a method of the parent command that
   determines whether the corresponding command is applicable in the current
   situation.  (E.g. ``install_headers`` is only applicable if we have any C
   header files to install.)  If *predicate* is ``None``, that command is always
   applicable.

   *sub_commands* is usually defined at the *end* of a class, because
   predicates can be methods of the class, so they must already have been
   defined.  The canonical example is the :command:`install` command.


:mod:`distutils.command` --- Individual Distutils commands
==========================================================

.. module:: distutils.command
   :synopsis: Contains one module for each standard Distutils command.


.. % \subsubsection{Individual Distutils commands}
.. % todo


:mod:`distutils.command.bdist` --- Build a binary installer
===========================================================

.. module:: distutils.command.bdist
   :synopsis: Build a binary installer for a package


.. % todo


:mod:`distutils.command.bdist_packager` --- Abstract base class for packagers
=============================================================================

.. module:: distutils.command.bdist_packager
   :synopsis: Abstract base class for packagers


.. % todo


:mod:`distutils.command.bdist_dumb` --- Build a "dumb" installer
================================================================

.. module:: distutils.command.bdist_dumb
   :synopsis: Build a "dumb" installer - a simple archive of files


.. % todo


:mod:`distutils.command.bdist_msi` --- Build a Microsoft Installer binary package
=================================================================================

.. module:: distutils.command.bdist_msi
   :synopsis: Build a binary distribution as a Windows MSI file

.. class:: bdist_msi

.. deprecated:: 3.9
   Use bdist_wheel (wheel packages) instead.

   Builds a `Windows Installer`_ (.msi) binary package.

   .. _Windows Installer: https://msdn.microsoft.com/en-us/library/cc185688(VS.85).aspx

   In most cases, the ``bdist_msi`` installer is a better choice than the
   ``bdist_wininst`` installer, because it provides better support for
   Win64 platforms, allows administrators to perform non-interactive
   installations, and allows installation through group policies.


:mod:`distutils.command.bdist_rpm` --- Build a binary distribution as a Redhat RPM and SRPM
===========================================================================================

.. module:: distutils.command.bdist_rpm
   :synopsis: Build a binary distribution as a Redhat RPM and SRPM


.. % todo


:mod:`distutils.command.bdist_wininst` --- Build a Windows installer
====================================================================

.. module:: distutils.command.bdist_wininst
   :synopsis: Build a Windows installer

.. deprecated:: 3.8
   Use bdist_wheel (wheel packages) instead.


.. % todo


:mod:`distutils.command.sdist` --- Build a source distribution
==============================================================

.. module:: distutils.command.sdist
   :synopsis: Build a source distribution


.. % todo


:mod:`distutils.command.build` --- Build all files of a package
===============================================================

.. module:: distutils.command.build
   :synopsis: Build all files of a package


.. % todo


:mod:`distutils.command.build_clib` --- Build any C libraries in a package
==========================================================================

.. module:: distutils.command.build_clib
   :synopsis: Build any C libraries in a package


.. % todo


:mod:`distutils.command.build_ext` --- Build any extensions in a package
========================================================================

.. module:: distutils.command.build_ext
   :synopsis: Build any extensions in a package


.. % todo


:mod:`distutils.command.build_py` --- Build the .py/.pyc files of a package
===========================================================================

.. module:: distutils.command.build_py
   :synopsis: Build the .py/.pyc files of a package


.. class:: build_py


:mod:`distutils.command.build_scripts` --- Build the scripts of a package
=========================================================================

.. module:: distutils.command.build_scripts
   :synopsis: Build the scripts of a package


.. % todo


:mod:`distutils.command.clean` --- Clean a package build area
=============================================================

.. module:: distutils.command.clean
   :synopsis: Clean a package build area

This command removes the temporary files created by :command:`build`
and its subcommands, like intermediary compiled object files.  With
the ``--all`` option, the complete build directory will be removed.

Extension modules built :ref:`in place <distutils-build-ext-inplace>`
will not be cleaned, as they are not in the build directory.


:mod:`distutils.command.config` --- Perform package configuration
=================================================================

.. module:: distutils.command.config
   :synopsis: Perform package configuration


.. % todo


:mod:`distutils.command.install` --- Install a package
======================================================

.. module:: distutils.command.install
   :synopsis: Install a package


.. % todo


:mod:`distutils.command.install_data` --- Install data files from a package
===========================================================================

.. module:: distutils.command.install_data
   :synopsis: Install data files from a package


.. % todo


:mod:`distutils.command.install_headers` --- Install C/C++ header files from a package
======================================================================================

.. module:: distutils.command.install_headers
   :synopsis: Install C/C++ header files from a package


.. % todo


:mod:`distutils.command.install_lib` --- Install library files from a package
=============================================================================

.. module:: distutils.command.install_lib
   :synopsis: Install library files from a package


.. % todo


:mod:`distutils.command.install_scripts` --- Install script files from a package
================================================================================

.. module:: distutils.command.install_scripts
   :synopsis: Install script files from a package


.. % todo


:mod:`distutils.command.register` --- Register a module with the Python Package Index
=====================================================================================

.. module:: distutils.command.register
   :synopsis: Register a module with the Python Package Index


The ``register`` command registers the package with the Python Package  Index.
This is described in more detail in :pep:`301`.

.. % todo


:mod:`distutils.command.check` --- Check the meta-data of a package
===================================================================

.. module:: distutils.command.check
   :synopsis: Check the meta-data of a package


The ``check`` command performs some tests on the meta-data of a package.
For example, it verifies that all required meta-data are provided as
the arguments passed to the :func:`~distutils.core.setup` function.

.. % todo
PK��\2�����<alt-python39-setuptools/docs/deprecated/distutils-legacy.rstnu�[���Porting from Distutils
======================

Setuptools and the PyPA have a `stated goal <https://github.com/pypa/packaging-problems/issues/127>`_ to make Setuptools the reference API for distutils.

Since the 49.1.2 release, Setuptools includes a local, vendored copy of distutils (from late copies of CPython) that is disabled by default. To enable the use of this copy of distutils when invoking setuptools, set the enviroment variable:

	SETUPTOOLS_USE_DISTUTILS=local

This behavior is planned to become the default.

Prefer Setuptools
-----------------

As Distutils is deprecated, any usage of functions or objects from distutils is similarly discouraged, and Setuptools aims to replace or deprecate all such uses. This section describes the recommended replacements.

``distutils.core.setup`` → ``setuptools.setup``

``distutils.cmd.Command`` → ``setuptools.Command``

``distutils.log`` → (no replacement yet)

``distutils.version.*`` → ``packaging.version.*``

If a project relies on uses of ``distutils`` that do not have a suitable replacement above, please search the `Setuptools issue tracker <https://github.com/pypa/setuptools/issues/>`_ and file a request, describing the use-case so that Setuptools' maintainers can investigate. Please provide enough detail to help the maintainers understand how distutils is used, what value it provides, and why that behavior should be supported.
PK��\�j'��;alt-python39-setuptools/docs/deprecated/functionalities.rstnu�[���"Eggsecutable" Scripts
----------------------

.. deprecated:: 45.3.0

Occasionally, there are situations where it's desirable to make an ``.egg``
file directly executable.  You can do this by including an entry point such
as the following::

    setup(
        # other arguments here...
        entry_points={
            "setuptools.installation": [
                "eggsecutable = my_package.some_module:main_func",
            ]
        }
    )

Any eggs built from the above setup script will include a short executable
prelude that imports and calls ``main_func()`` from ``my_package.some_module``.
The prelude can be run on Unix-like platforms (including Mac and Linux) by
invoking the egg with ``/bin/sh``, or by enabling execute permissions on the
``.egg`` file.  For the executable prelude to run, the appropriate version of
Python must be available via the ``PATH`` environment variable, under its
"long" name.  That is, if the egg is built for Python 2.3, there must be a
``python2.3`` executable present in a directory on ``PATH``.

IMPORTANT NOTE: Eggs with an "eggsecutable" header cannot be renamed, or
invoked via symlinks.  They *must* be invoked using their original filename, in
order to ensure that, once running, ``pkg_resources`` will know what project
and version is in use.  The header script will check this and exit with an
error if the ``.egg`` file has been renamed or is invoked via a symlink that
changes its base name.PK��\eO<�x�x7alt-python39-setuptools/docs/deprecated/python_eggs.rstnu�[���=====================================
The Internal Structure of Python Eggs
=====================================

STOP! This is not the first document you should read!



----------------------
Eggs and their Formats
----------------------

A "Python egg" is a logical structure embodying the release of a
specific version of a Python project, comprising its code, resources,
and metadata. There are multiple formats that can be used to physically
encode a Python egg, and others can be developed. However, a key
principle of Python eggs is that they should be discoverable and
importable. That is, it should be possible for a Python application to
easily and efficiently find out what eggs are present on a system, and
to ensure that the desired eggs' contents are importable.

There are two basic formats currently implemented for Python eggs:

1. ``.egg`` format: a directory or zipfile *containing* the project's
   code and resources, along with an ``EGG-INFO`` subdirectory that
   contains the project's metadata

2. ``.egg-info`` format: a file or directory placed *adjacent* to the
   project's code and resources, that directly contains the project's
   metadata.

Both formats can include arbitrary Python code and resources, including
static data files, package and non-package directories, Python
modules, C extension modules, and so on.  But each format is optimized
for different purposes.

The ``.egg`` format is well-suited to distribution and the easy
uninstallation or upgrades of code, since the project is essentially
self-contained within a single directory or file, unmingled with any
other projects' code or resources.  It also makes it possible to have
multiple versions of a project simultaneously installed, such that
individual programs can select the versions they wish to use.

The ``.egg-info`` format, on the other hand, was created to support
backward-compatibility, performance, and ease of installation for system
packaging tools that expect to install all projects' code and resources
to a single directory (e.g. ``site-packages``).  Placing the metadata
in that same directory simplifies the installation process, since it
isn't necessary to create ``.pth`` files or otherwise modify
``sys.path`` to include each installed egg.

Its disadvantage, however, is that it provides no support for clean
uninstallation or upgrades, and of course only a single version of a
project can be installed to a given directory. Thus, support from a
package management tool is required. (This is why setuptools' "install"
command refers to this type of egg installation as "single-version,
externally managed".)  Also, they lack sufficient data to allow them to
be copied from their installation source.  easy_install can "ship" an
application by copying ``.egg`` files or directories to a target
location, but it cannot do this for ``.egg-info`` installs, because
there is no way to tell what code and resources belong to a particular
egg -- there may be several eggs "scrambled" together in a single
installation location, and the ``.egg-info`` format does not currently
include a way to list the files that were installed.  (This may change
in a future version.)


Code and Resources
==================

The layout of the code and resources is dictated by Python's normal
import layout, relative to the egg's "base location".

For the ``.egg`` format, the base location is the ``.egg`` itself. That
is, adding the ``.egg`` filename or directory name to ``sys.path``
makes its contents importable.

For the ``.egg-info`` format, however, the base location is the
directory that *contains* the ``.egg-info``, and thus it is the
directory that must be added to ``sys.path`` to make the egg importable.
(Note that this means that the "normal" installation of a package to a
``sys.path`` directory is sufficient to make it an "egg" if it has an
``.egg-info`` file or directory installed alongside of it.)


Project Metadata
=================

If eggs contained only code and resources, there would of course be
no difference between them and any other directory or zip file on
``sys.path``.  Thus, metadata must also be included, using a metadata
file or directory.

For the ``.egg`` format, the metadata is placed in an ``EGG-INFO``
subdirectory, directly within the ``.egg`` file or directory.  For the
``.egg-info`` format, metadata is stored directly within the
``.egg-info`` directory itself.

The minimum project metadata that all eggs must have is a standard
Python ``PKG-INFO`` file, named ``PKG-INFO`` and placed within the
metadata directory appropriate to the format.  Because it's possible for
this to be the only metadata file included, ``.egg-info`` format eggs
are not required to be a directory; they can just be a ``.egg-info``
file that directly contains the ``PKG-INFO`` metadata.  This eliminates
the need to create a directory just to store one file.  This option is
*not* available for ``.egg`` formats, since setuptools always includes
other metadata.  (In fact, setuptools itself never generates
``.egg-info`` files, either; the support for using files was added so
that the requirement could easily be satisfied by other tools, such
as distutils).

In addition to the ``PKG-INFO`` file, an egg's metadata directory may
also include files and directories representing various forms of
optional standard metadata (see the section on `Standard Metadata`_,
below) or user-defined metadata required by the project.  For example,
some projects may define a metadata format to describe their application
plugins, and metadata in this format would then be included by plugin
creators in their projects' metadata directories.


Filename-Embedded Metadata
==========================

To allow introspection of installed projects and runtime resolution of
inter-project dependencies, a certain amount of information is embedded
in egg filenames.  At a minimum, this includes the project name, and
ideally will also include the project version number.  Optionally, it
can also include the target Python version and required runtime
platform if platform-specific C code is included.  The syntax of an
egg filename is as follows::

    name ["-" version ["-py" pyver ["-" required_platform]]] "." ext

The "name" and "version" should be escaped using the ``to_filename()``
function provided by ``pkg_resources``, after first processing them with
``safe_name()`` and ``safe_version()`` respectively.  These latter two
functions can also be used to later "unescape" these parts of the
filename.  (For a detailed description of these transformations, please
see the "Parsing Utilities" section of the ``pkg_resources`` manual.)

The "pyver" string is the Python major version, as found in the first
3 characters of ``sys.version``.  "required_platform" is essentially
a distutils ``get_platform()`` string, but with enhancements to properly
distinguish Mac OS versions.  (See the ``get_build_platform()``
documentation in the "Platform Utilities" section of the
``pkg_resources`` manual for more details.)

Finally, the "ext" is either ``.egg`` or ``.egg-info``, as appropriate
for the egg's format.

Normally, an egg's filename should include at least the project name and
version, as this allows the runtime system to find desired project
versions without having to read the egg's PKG-INFO to determine its
version number.

Setuptools, however, only includes the version number in the filename
when an ``.egg`` file is built using the ``bdist_egg`` command, or when
an ``.egg-info`` directory is being installed by the
``install_egg_info`` command. When generating metadata for use with the
original source tree, it only includes the project name, so that the
directory will not have to be renamed each time the project's version
changes.

This is especially important when version numbers change frequently, and
the source metadata directory is kept under version control with the
rest of the project.  (As would be the case when the project's source
includes project-defined metadata that is not generated from by
setuptools from data in the setup script.)


Egg Links
=========

In addition to the ``.egg`` and ``.egg-info`` formats, there is a third
egg-related extension that you may encounter on occasion: ``.egg-link``
files.

These files are not eggs, strictly speaking. They simply provide a way
to reference an egg that is not physically installed in the desired
location. They exist primarily as a cross-platform alternative to
symbolic links, to support "installing" code that is being developed in
a different location than the desired installation location. For
example, if a user is developing an application plugin in their home
directory, but the plugin needs to be "installed" in an application
plugin directory, running "setup.py develop -md /path/to/app/plugins"
will install an ``.egg-link`` file in ``/path/to/app/plugins``, that
tells the egg runtime system where to find the actual egg (the user's
project source directory and its ``.egg-info`` subdirectory).

``.egg-link`` files are named following the format for ``.egg`` and
``.egg-info`` names, but only the project name is included; no version,
Python version, or platform information is included.  When the runtime
searches for available eggs, ``.egg-link`` files are opened and the
actual egg file/directory name is read from them.

Each ``.egg-link`` file should contain a single file or directory name,
with no newlines.  This filename should be the base location of one or
more eggs.  That is, the name must either end in ``.egg``, or else it
should be the parent directory of one or more ``.egg-info`` format eggs.

As of setuptools 0.6c6, the path may be specified as a platform-independent
(i.e. ``/``-separated) relative path from the directory containing the
``.egg-link`` file, and a second line may appear in the file, specifying a
platform-independent relative path from the egg's base directory to its
setup script directory.  This allows installation tools such as EasyInstall
to find the project's setup directory and build eggs or perform other setup
commands on it.


-----------------
Standard Metadata
-----------------

In addition to the minimum required ``PKG-INFO`` metadata, projects can
include a variety of standard metadata files or directories, as
described below.  Except as otherwise noted, these files and directories
are automatically generated by setuptools, based on information supplied
in the setup script or through analysis of the project's code and
resources.

Most of these files and directories are generated via "egg-info
writers" during execution of the setuptools ``egg_info`` command, and
are listed in the ``egg_info.writers`` entry point group defined by
setuptools' own ``setup.py`` file.

Project authors can register their own metadata writers as entry points
in this group (as described in the setuptools manual under "Adding new
EGG-INFO Files") to cause setuptools to generate project-specific
metadata files or directories during execution of the ``egg_info``
command.  It is up to project authors to document these new metadata
formats, if they create any.


``.txt`` File Formats
=====================

Files described in this section that have ``.txt`` extensions have a
simple lexical format consisting of a sequence of text lines, each line
terminated by a linefeed character (regardless of platform).  Leading
and trailing whitespace on each line is ignored, as are blank lines and
lines whose first nonblank character is a ``#`` (comment symbol).  (This
is the parsing format defined by the ``yield_lines()`` function of
the ``pkg_resources`` module.)

All ``.txt`` files defined by this section follow this format, but some
are also "sectioned" files, meaning that their contents are divided into
sections, using square-bracketed section headers akin to Windows
``.ini`` format.  Note that this does *not* imply that the lines within
the sections follow an ``.ini`` format, however.  Please see an
individual metadata file's documentation for a description of what the
lines and section names mean in that particular file.

Sectioned files can be parsed using the ``split_sections()`` function;
see the "Parsing Utilities" section of the ``pkg_resources`` manual for
for details.


Dependency Metadata
===================


``requires.txt``
----------------

This is a "sectioned" text file.  Each section is a sequence of
"requirements", as parsed by the ``parse_requirements()`` function;
please see the ``pkg_resources`` manual for the complete requirement
parsing syntax.

The first, unnamed section (i.e., before the first section header) in
this file is the project's core requirements, which must be installed
for the project to function.  (Specified using the ``install_requires``
keyword to ``setup()``).

The remaining (named) sections describe the project's "extra"
requirements, as specified using the ``extras_require`` keyword to
``setup()``.  The section name is the name of the optional feature, and
the section body lists that feature's dependencies.

Note that it is not normally necessary to inspect this file directly;
``pkg_resources.Distribution`` objects have a ``requires()`` method
that can be used to obtain ``Requirement`` objects describing the
project's core and optional dependencies.


``setup_requires.txt``
----------------------

Much like ``requires.txt`` except represents the requirements
specified by the ``setup_requires`` parameter to the Distribution.


``dependency_links.txt``
------------------------

A list of dependency URLs, one per line, as specified using the
``dependency_links`` keyword to ``setup()``.  These may be direct
download URLs, or the URLs of web pages containing direct download
links. Please see the setuptools manual for more information on
specifying this option.


``depends.txt`` -- Obsolete, do not create!
-------------------------------------------

This file follows an identical format to ``requires.txt``, but is
obsolete and should not be used.  The earliest versions of setuptools
required users to manually create and maintain this file, so the runtime
still supports reading it, if it exists.  The new filename was created
so that it could be automatically generated from ``setup()`` information
without overwriting an existing hand-created ``depends.txt``, if one
was already present in the project's source ``.egg-info`` directory.


``namespace_packages.txt`` -- Namespace Package Metadata
========================================================

A list of namespace package names, one per line, as supplied to the
``namespace_packages`` keyword to ``setup()``.  Please see the manuals
for setuptools and ``pkg_resources`` for more information about
namespace packages.


``entry_points.txt`` -- "Entry Point"/Plugin Metadata
=====================================================

This is a "sectioned" text file, whose contents encode the
``entry_points`` keyword supplied to ``setup()``.  All sections are
named, as the section names specify the entry point groups in which the
corresponding section's entry points are registered.

Each section is a sequence of "entry point" lines, each parseable using
the ``EntryPoint.parse`` classmethod; please see the ``pkg_resources``
manual for the complete entry point parsing syntax.

Note that it is not necessary to parse this file directly; the
``pkg_resources`` module provides a variety of APIs to locate and load
entry points automatically.  Please see the setuptools and
``pkg_resources`` manuals for details on the nature and uses of entry
points.


The ``scripts`` Subdirectory
============================

This directory is currently only created for ``.egg`` files built by
the setuptools ``bdist_egg`` command.  It will contain copies of all
of the project's "traditional" scripts (i.e., those specified using the
``scripts`` keyword to ``setup()``).  This is so that they can be
reconstituted when an ``.egg`` file is installed.

The scripts are placed here using the distutils' standard
``install_scripts`` command, so any ``#!`` lines reflect the Python
installation where the egg was built.  But instead of copying the
scripts to the local script installation directory, EasyInstall writes
short wrapper scripts that invoke the original scripts from inside the
egg, after ensuring that sys.path includes the egg and any eggs it
depends on.  For more about `script wrappers`_, see the section below on
`Installation and Path Management Issues`_.


Zip Support Metadata
====================


``native_libs.txt``
-------------------

A list of C extensions and other dynamic link libraries contained in
the egg, one per line.  Paths are ``/``-separated and relative to the
egg's base location.

This file is generated as part of ``bdist_egg`` processing, and as such
only appears in ``.egg`` files (and ``.egg`` directories created by
unpacking them).  It is used to ensure that all libraries are extracted
from a zipped egg at the same time, in case there is any direct linkage
between them.  Please see the `Zip File Issues`_ section below for more
information on library and resource extraction from ``.egg`` files.


``eager_resources.txt``
-----------------------

A list of resource files and/or directories, one per line, as specified
via the ``eager_resources`` keyword to ``setup()``.  Paths are
``/``-separated and relative to the egg's base location.

Resource files or directories listed here will be extracted
simultaneously, if any of the named resources are extracted, or if any
native libraries listed in ``native_libs.txt`` are extracted.  Please
see the setuptools manual for details on what this feature is used for
and how it works, as well as the `Zip File Issues`_ section below.


``zip-safe`` and ``not-zip-safe``
---------------------------------

These are zero-length files, and either one or the other should exist.
If ``zip-safe`` exists, it means that the project will work properly
when installed as an ``.egg`` zipfile, and conversely the existence of
``not-zip-safe`` means the project should not be installed as an
``.egg`` file.  The ``zip_safe`` option to setuptools' ``setup()``
determines which file will be written. If the option isn't provided,
setuptools attempts to make its own assessment of whether the package
can work, based on code and content analysis.

If neither file is present at installation time, EasyInstall defaults
to assuming that the project should be unzipped.  (Command-line options
to EasyInstall, however, take precedence even over an existing
``zip-safe`` or ``not-zip-safe`` file.)

Note that these flag files appear only in ``.egg`` files generated by
``bdist_egg``, and in ``.egg`` directories created by unpacking such an
``.egg`` file.



``top_level.txt`` -- Conflict Management Metadata
=================================================

This file is a list of the top-level module or package names provided
by the project, one Python identifier per line.

Subpackages are not included; a project containing both a ``foo.bar``
and a ``foo.baz`` would include only one line, ``foo``, in its
``top_level.txt``.

This data is used by ``pkg_resources`` at runtime to issue a warning if
an egg is added to ``sys.path`` when its contained packages may have
already been imported.

(It was also once used to detect conflicts with non-egg packages at
installation time, but in more recent versions, setuptools installs eggs
in such a way that they always override non-egg packages, thus
preventing a problem from arising.)


``SOURCES.txt`` -- Source Files Manifest
========================================

This file is roughly equivalent to the distutils' ``MANIFEST`` file.
The differences are as follows:

* The filenames always use ``/`` as a path separator, which must be
  converted back to a platform-specific path whenever they are read.

* The file is automatically generated by setuptools whenever the
  ``egg_info`` or ``sdist`` commands are run, and it is *not*
  user-editable.

Although this metadata is included with distributed eggs, it is not
actually used at runtime for any purpose.  Its function is to ensure
that setuptools-built *source* distributions can correctly discover
what files are part of the project's source, even if the list had been
generated using revision control metadata on the original author's
system.

In other words, ``SOURCES.txt`` has little or no runtime value for being
included in distributed eggs, and it is possible that future versions of
the ``bdist_egg`` and ``install_egg_info`` commands will strip it before
installation or distribution.  Therefore, do not rely on its being
available outside of an original source directory or source
distribution.


------------------------------
Other Technical Considerations
------------------------------


Zip File Issues
===============

Although zip files resemble directories, they are not fully
substitutable for them.  Most platforms do not support loading dynamic
link libraries contained in zipfiles, so it is not possible to directly
import C extensions from ``.egg`` zipfiles.  Similarly, there are many
existing libraries -- whether in Python or C -- that require actual
operating system filenames, and do not work with arbitrary "file-like"
objects or in-memory strings, and thus cannot operate directly on the
contents of zip files.

To address these issues, the ``pkg_resources`` module provides a
"resource API" to support obtaining either the contents of a resource,
or a true operating system filename for the resource.  If the egg
containing the resource is a directory, the resource's real filename
is simply returned.  However, if the egg is a zipfile, then the
resource is first extracted to a cache directory, and the filename
within the cache is returned.

The cache directory is determined by the ``pkg_resources`` API; please
see the ``set_cache_path()`` and ``get_default_cache()`` documentation
for details.


The Extraction Process
----------------------

Resources are extracted to a cache subdirectory whose name is based
on the enclosing ``.egg`` filename and the path to the resource.  If
there is already a file of the correct name, size, and timestamp, its
filename is returned to the requester.  Otherwise, the desired file is
extracted first to a temporary name generated using
``mkstemp(".$extract",target_dir)``, and then its timestamp is set to
match the one in the zip file, before renaming it to its final name.
(Some collision detection and resolution code is used to handle the
fact that Windows doesn't overwrite files when renaming.)

If a resource directory is requested, all of its contents are
recursively extracted in this fashion, to ensure that the directory
name can be used as if it were valid all along.

If the resource requested for extraction is listed in the
``native_libs.txt`` or ``eager_resources.txt`` metadata files, then
*all* resources listed in *either* file will be extracted before the
requested resource's filename is returned, thus ensuring that all
C extensions and data used by them will be simultaneously available.


Extension Import Wrappers
-------------------------

Since Python's built-in zip import feature does not support loading
C extension modules from zipfiles, the setuptools ``bdist_egg`` command
generates special import wrappers to make it work.

The wrappers are ``.py`` files (along with corresponding ``.pyc``
and/or ``.pyo`` files) that have the same module name as the
corresponding C extension.  These wrappers are located in the same
package directory (or top-level directory) within the zipfile, so that
say, ``foomodule.so`` will get a corresponding ``foo.py``, while
``bar/baz.pyd`` will get a corresponding ``bar/baz.py``.

These wrapper files contain a short stanza of Python code that asks
``pkg_resources`` for the filename of the corresponding C extension,
then reloads the module using the obtained filename.  This will cause
``pkg_resources`` to first ensure that all of the egg's C extensions
(and any accompanying "eager resources") are extracted to the cache
before attempting to link to the C library.

Note, by the way, that ``.egg`` directories will also contain these
wrapper files.  However, Python's default import priority is such that
C extensions take precedence over same-named Python modules, so the
import wrappers are ignored unless the egg is a zipfile.


Installation and Path Management Issues
=======================================

Python's initial setup of ``sys.path`` is very dependent on the Python
version and installation platform, as well as how Python was started
(i.e., script vs. ``-c`` vs. ``-m`` vs. interactive interpreter).
In fact, Python also provides only two relatively robust ways to affect
``sys.path`` outside of direct manipulation in code: the ``PYTHONPATH``
environment variable, and ``.pth`` files.

However, with no cross-platform way to safely and persistently change
environment variables, this leaves ``.pth`` files as EasyInstall's only
real option for persistent configuration of ``sys.path``.

But ``.pth`` files are rather strictly limited in what they are allowed
to do normally.  They add directories only to the *end* of ``sys.path``,
after any locally-installed ``site-packages`` directory, and they are
only processed *in* the ``site-packages`` directory to start with.

This is a double whammy for users who lack write access to that
directory, because they can't create a ``.pth`` file that Python will
read, and even if a sympathetic system administrator adds one for them
that calls ``site.addsitedir()`` to allow some other directory to
contain ``.pth`` files, they won't be able to install newer versions of
anything that's installed in the systemwide ``site-packages``, because
their paths will still be added *after* ``site-packages``.

So EasyInstall applies two workarounds to solve these problems.

The first is that EasyInstall leverages ``.pth`` files' "import" feature
to manipulate ``sys.path`` and ensure that anything EasyInstall adds
to a ``.pth`` file will always appear before both the standard library
and the local ``site-packages`` directories.  Thus, it is always
possible for a user who can write a Python-read ``.pth`` file to ensure
that their packages come first in their own environment.

Second, when installing to a ``PYTHONPATH`` directory (as opposed to
a "site" directory like ``site-packages``) EasyInstall will also install
a special version of the ``site`` module.  Because it's in a
``PYTHONPATH`` directory, this module will get control before the
standard library version of ``site`` does.  It will record the state of
``sys.path`` before invoking the "real" ``site`` module, and then
afterwards it processes any ``.pth`` files found in ``PYTHONPATH``
directories, including all the fixups needed to ensure that eggs always
appear before the standard library in sys.path, but are in a relative
order to one another that is defined by their ``PYTHONPATH`` and
``.pth``-prescribed sequence.

The net result of these changes is that ``sys.path`` order will be
as follows at runtime:

1. The ``sys.argv[0]`` directory, or an empty string if no script
   is being executed.

2. All eggs installed by EasyInstall in any ``.pth`` file in each
   ``PYTHONPATH`` directory, in order first by ``PYTHONPATH`` order,
   then normal ``.pth`` processing order (which is to say alphabetical
   by ``.pth`` filename, then by the order of listing within each
   ``.pth`` file).

3. All eggs installed by EasyInstall in any ``.pth`` file in each "site"
   directory (such as ``site-packages``), following the same ordering
   rules as for the ones on ``PYTHONPATH``.

4. The ``PYTHONPATH`` directories themselves, in their original order

5. Any paths from ``.pth`` files found on ``PYTHONPATH`` that were *not*
   eggs installed by EasyInstall, again following the same relative
   ordering rules.

6. The standard library and "site" directories, along with the contents
   of any ``.pth`` files found in the "site" directories.

Notice that sections 1, 4, and 6 comprise the "normal" Python setup for
``sys.path``.  Sections 2 and 3 are inserted to support eggs, and
section 5 emulates what the "normal" semantics of ``.pth`` files on
``PYTHONPATH`` would be if Python natively supported them.

For further discussion of the tradeoffs that went into this design, as
well as notes on the actual magic inserted into ``.pth`` files to make
them do these things, please see also the following messages to the
distutils-SIG mailing list:

* http://mail.python.org/pipermail/distutils-sig/2006-February/006026.html
* http://mail.python.org/pipermail/distutils-sig/2006-March/006123.html


Script Wrappers
---------------

EasyInstall never directly installs a project's original scripts to
a script installation directory.  Instead, it writes short wrapper
scripts that first ensure that the project's dependencies are active
on sys.path, before invoking the original script.  These wrappers
have a #! line that points to the version of Python that was used to
install them, and their second line is always a comment that indicates
the type of script wrapper, the project version required for the script
to run, and information identifying the script to be invoked.

The format of this marker line is::

    "# EASY-INSTALL-" script_type ": " tuple_of_strings "\n"

The ``script_type`` is one of ``SCRIPT``, ``DEV-SCRIPT``, or
``ENTRY-SCRIPT``.  The ``tuple_of_strings`` is a comma-separated
sequence of Python string constants.  For ``SCRIPT`` and ``DEV-SCRIPT``
wrappers, there are two strings: the project version requirement, and
the script name (as a filename within the ``scripts`` metadata
directory).  For ``ENTRY-SCRIPT`` wrappers, there are three:
the project version requirement, the entry point group name, and the
entry point name.  (See the "Automatic Script Creation" section in the
setuptools manual for more information about entry point scripts.)

In each case, the project version requirement string will be a string
parseable with the ``pkg_resources`` modules' ``Requirement.parse()``
classmethod.  The only difference between a ``SCRIPT`` wrapper and a
``DEV-SCRIPT`` is that a ``DEV-SCRIPT`` actually executes the original
source script in the project's source tree, and is created when the
"setup.py develop" command is run.  A ``SCRIPT`` wrapper, on the other
hand, uses the "installed" script written to the ``EGG-INFO/scripts``
subdirectory of the corresponding ``.egg`` zipfile or directory.
(``.egg-info`` eggs do not have script wrappers associated with them,
except in the "setup.py develop" case.)

The purpose of including the marker line in generated script wrappers is
to facilitate introspection of installed scripts, and their relationship
to installed eggs.  For example, an uninstallation tool could use this
data to identify what scripts can safely be removed, and/or identify
what scripts would stop working if a particular egg is uninstalled.
PK��\4W�pppp.alt-python39-setuptools/docs/pkg_resources.rstnu�[���=============================================================
Package Discovery and Resource Access using ``pkg_resources``
=============================================================

The ``pkg_resources`` module distributed with ``setuptools`` provides an API
for Python libraries to access their resource files, and for extensible
applications and frameworks to automatically discover plugins.  It also
provides runtime support for using C extensions that are inside zipfile-format
eggs, support for merging packages that have separately-distributed modules or
subpackages, and APIs for managing Python's current "working set" of active
packages.

Use of ``pkg_resources`` is discouraged in favor of
`importlib.resources <https://docs.python.org/3/library/importlib.html#module-importlib.resources>`_,
`importlib.metadata <https://docs.python.org/3/library/importlib.metadata.html>`_,
and their backports (`resources <https://pypi.org/project/importlib_resources>`_,
`metadata <https://pypi.org/project/importlib_metadata>`_).
Please consider using those libraries instead of pkg_resources.


--------
Overview
--------

The ``pkg_resources`` module provides runtime facilities for finding,
introspecting, activating and using installed Python distributions. Some
of the more advanced features (notably the support for parallel installation
of multiple versions) rely specifically on the "egg" format (either as a
zip archive or subdirectory), while others (such as plugin discovery) will
work correctly so long as "egg-info" metadata directories are available for
relevant distributions.

Eggs are a distribution format for Python modules, similar in concept to
Java's "jars" or Ruby's "gems", or the "wheel" format defined in PEP 427.
However, unlike a pure distribution format, eggs can also be installed and
added directly to ``sys.path`` as an import location. When installed in
this way, eggs are *discoverable*, meaning that they carry metadata that
unambiguously identifies their contents and dependencies. This means that
an installed egg can be *automatically* found and added to ``sys.path`` in
response to simple requests of the form, "get me everything I need to use
docutils' PDF support". This feature allows mutually conflicting versions of
a distribution to co-exist in the same Python installation, with individual
applications activating the desired version at runtime by manipulating the
contents of ``sys.path`` (this differs from the virtual environment
approach, which involves creating isolated environments for each
application).

The following terms are needed in order to explain the capabilities offered
by this module:

project
    A library, framework, script, plugin, application, or collection of data
    or other resources, or some combination thereof.  Projects are assumed to
    have "relatively unique" names, e.g. names registered with PyPI.

release
    A snapshot of a project at a particular point in time, denoted by a version
    identifier.

distribution
    A file or files that represent a particular release.

importable distribution
    A file or directory that, if placed on ``sys.path``, allows Python to
    import any modules contained within it.

pluggable distribution
    An importable distribution whose filename unambiguously identifies its
    release (i.e. project and version), and whose contents unambiguously
    specify what releases of other projects will satisfy its runtime
    requirements.

extra
    An "extra" is an optional feature of a release, that may impose additional
    runtime requirements.  For example, if docutils PDF support required a
    PDF support library to be present, docutils could define its PDF support as
    an "extra", and list what other project releases need to be available in
    order to provide it.

environment
    A collection of distributions potentially available for importing, but not
    necessarily active.  More than one distribution (i.e. release version) for
    a given project may be present in an environment.

working set
    A collection of distributions actually available for importing, as on
    ``sys.path``.  At most one distribution (release version) of a given
    project may be present in a working set, as otherwise there would be
    ambiguity as to what to import.

eggs
    Eggs are pluggable distributions in one of the three formats currently
    supported by ``pkg_resources``.  There are built eggs, development eggs,
    and egg links.  Built eggs are directories or zipfiles whose name ends
    with ``.egg`` and follows the egg naming conventions, and contain an
    ``EGG-INFO`` subdirectory (zipped or otherwise).  Development eggs are
    normal directories of Python code with one or more ``ProjectName.egg-info``
    subdirectories. The development egg format is also used to provide a
    default version of a distribution that is available to software that
    doesn't use ``pkg_resources`` to request specific versions. Egg links
    are ``*.egg-link`` files that contain the name of a built or
    development egg, to support symbolic linking on platforms that do not
    have native symbolic links (or where the symbolic link support is
    limited).

(For more information about these terms and concepts, see also this
`architectural overview`_ of ``pkg_resources`` and Python Eggs in general.)

.. _architectural overview: http://mail.python.org/pipermail/distutils-sig/2005-June/004652.html


.. -----------------
.. Developer's Guide
.. -----------------

.. This section isn't written yet.  Currently planned topics include
    Accessing Resources
    Finding and Activating Package Distributions
        get_provider()
        require()
        WorkingSet
        iter_distributions
    Running Scripts
    Configuration
    Namespace Packages
    Extensible Applications and Frameworks
        Locating entry points
        Activation listeners
        Metadata access
        Extended Discovery and Installation
    Supporting Custom PEP 302 Implementations
.. For now, please check out the extensive `API Reference`_ below.


-------------
API Reference
-------------

Namespace Package Support
=========================

A namespace package is a package that only contains other packages and modules,
with no direct contents of its own.  Such packages can be split across
multiple, separately-packaged distributions.  They are normally used to split
up large packages produced by a single organization, such as in the ``zope``
namespace package for Zope Corporation packages, and the ``peak`` namespace
package for the Python Enterprise Application Kit.

To create a namespace package, you list it in the ``namespace_packages``
argument to ``setup()``, in your project's ``setup.py``.  (See the
:ref:`setuptools documentation on namespace packages <Namespace Packages>` for
more information on this.)  Also, you must add a ``declare_namespace()`` call
in the package's ``__init__.py`` file(s):

``declare_namespace(name)``
    Declare that the dotted package name ``name`` is a "namespace package" whose
    contained packages and modules may be spread across multiple distributions.
    The named package's ``__path__`` will be extended to include the
    corresponding package in all distributions on ``sys.path`` that contain a
    package of that name.  (More precisely, if an importer's
    ``find_module(name)`` returns a loader, then it will also be searched for
    the package's contents.)  Whenever a Distribution's ``activate()`` method
    is invoked, it checks for the presence of namespace packages and updates
    their ``__path__`` contents accordingly.

Applications that manipulate namespace packages or directly alter ``sys.path``
at runtime may also need to use this API function:

``fixup_namespace_packages(path_item)``
    Declare that ``path_item`` is a newly added item on ``sys.path`` that may
    need to be used to update existing namespace packages.  Ordinarily, this is
    called for you when an egg is automatically added to ``sys.path``, but if
    your application modifies ``sys.path`` to include locations that may
    contain portions of a namespace package, you will need to call this
    function to ensure they are added to the existing namespace packages.

Although by default ``pkg_resources`` only supports namespace packages for
filesystem and zip importers, you can extend its support to other "importers"
compatible with PEP 302 using the ``register_namespace_handler()`` function.
See the section below on `Supporting Custom Importers`_ for details.


``WorkingSet`` Objects
======================

The ``WorkingSet`` class provides access to a collection of "active"
distributions.  In general, there is only one meaningful ``WorkingSet``
instance: the one that represents the distributions that are currently active
on ``sys.path``.  This global instance is available under the name
``working_set`` in the ``pkg_resources`` module.  However, specialized
tools may wish to manipulate working sets that don't correspond to
``sys.path``, and therefore may wish to create other ``WorkingSet`` instances.

It's important to note that the global ``working_set`` object is initialized
from ``sys.path`` when ``pkg_resources`` is first imported, but is only updated
if you do all future ``sys.path`` manipulation via ``pkg_resources`` APIs.  If
you manually modify ``sys.path``, you must invoke the appropriate methods on
the ``working_set`` instance to keep it in sync.  Unfortunately, Python does
not provide any way to detect arbitrary changes to a list object like
``sys.path``, so ``pkg_resources`` cannot automatically update the
``working_set`` based on changes to ``sys.path``.

``WorkingSet(entries=None)``
    Create a ``WorkingSet`` from an iterable of path entries.  If ``entries``
    is not supplied, it defaults to the value of ``sys.path`` at the time
    the constructor is called.

    Note that you will not normally construct ``WorkingSet`` instances
    yourself, but instead you will implicitly or explicitly use the global
    ``working_set`` instance.  For the most part, the ``pkg_resources`` API
    is designed so that the ``working_set`` is used by default, such that you
    don't have to explicitly refer to it most of the time.

All distributions available directly on ``sys.path`` will be activated
automatically when ``pkg_resources`` is imported. This behaviour can cause
version conflicts for applications which require non-default versions of
those distributions. To handle this situation, ``pkg_resources`` checks for a
``__requires__`` attribute in the ``__main__`` module when initializing the
default working set, and uses this to ensure a suitable version of each
affected distribution is activated. For example::

    __requires__ = ["CherryPy < 3"] # Must be set before pkg_resources import
    import pkg_resources


Basic ``WorkingSet`` Methods
----------------------------

The following methods of ``WorkingSet`` objects are also available as module-
level functions in ``pkg_resources`` that apply to the default ``working_set``
instance.  Thus, you can use e.g. ``pkg_resources.require()`` as an
abbreviation for ``pkg_resources.working_set.require()``:


``require(*requirements)``
    Ensure that distributions matching ``requirements`` are activated

    ``requirements`` must be a string or a (possibly-nested) sequence
    thereof, specifying the distributions and versions required.  The
    return value is a sequence of the distributions that needed to be
    activated to fulfill the requirements; all relevant distributions are
    included, even if they were already activated in this working set.

    For the syntax of requirement specifiers, see the section below on
    `Requirements Parsing`_.

    In general, it should not be necessary for you to call this method
    directly.  It's intended more for use in quick-and-dirty scripting and
    interactive interpreter hacking than for production use. If you're creating
    an actual library or application, it's strongly recommended that you create
    a "setup.py" script using ``setuptools``, and declare all your requirements
    there.  That way, tools like pip can automatically detect what requirements
    your package has, and deal with them accordingly.

    Note that calling ``require('SomePackage')`` will not install
    ``SomePackage`` if it isn't already present.  If you need to do this, you
    should use the ``resolve()`` method instead, which allows you to pass an
    ``installer`` callback that will be invoked when a needed distribution
    can't be found on the local machine.  You can then have this callback
    display a dialog, automatically download the needed distribution, or
    whatever else is appropriate for your application. See the documentation
    below on the ``resolve()`` method for more information, and also on the
    ``obtain()`` method of ``Environment`` objects.

``run_script(requires, script_name)``
    Locate distribution specified by ``requires`` and run its ``script_name``
    script.  ``requires`` must be a string containing a requirement specifier.
    (See `Requirements Parsing`_ below for the syntax.)

    The script, if found, will be executed in *the caller's globals*.  That's
    because this method is intended to be called from wrapper scripts that
    act as a proxy for the "real" scripts in a distribution.  A wrapper script
    usually doesn't need to do anything but invoke this function with the
    correct arguments.

    If you need more control over the script execution environment, you
    probably want to use the ``run_script()`` method of a ``Distribution``
    object's `Metadata API`_ instead.

``iter_entry_points(group, name=None)``
    Yield entry point objects from ``group`` matching ``name``

    If ``name`` is None, yields all entry points in ``group`` from all
    distributions in the working set, otherwise only ones matching both
    ``group`` and ``name`` are yielded.  Entry points are yielded from the active
    distributions in the order that the distributions appear in the working
    set.  (For the global ``working_set``, this should be the same as the order
    that they are listed in ``sys.path``.)  Note that within the entry points
    advertised by an individual distribution, there is no particular ordering.

    Please see the section below on `Entry Points`_ for more information.


``WorkingSet`` Methods and Attributes
-------------------------------------

These methods are used to query or manipulate the contents of a specific
working set, so they must be explicitly invoked on a particular ``WorkingSet``
instance:

``add_entry(entry)``
    Add a path item to the ``entries``, finding any distributions on it.  You
    should use this when you add additional items to ``sys.path`` and you want
    the global ``working_set`` to reflect the change.  This method is also
    called by the ``WorkingSet()`` constructor during initialization.

    This method uses ``find_distributions(entry,True)`` to find distributions
    corresponding to the path entry, and then ``add()`` them.  ``entry`` is
    always appended to the ``entries`` attribute, even if it is already
    present, however. (This is because ``sys.path`` can contain the same value
    more than once, and the ``entries`` attribute should be able to reflect
    this.)

``__contains__(dist)``
    True if ``dist`` is active in this ``WorkingSet``.  Note that only one
    distribution for a given project can be active in a given ``WorkingSet``.

``__iter__()``
    Yield distributions for non-duplicate projects in the working set.
    The yield order is the order in which the items' path entries were
    added to the working set.

``find(req)``
    Find a distribution matching ``req`` (a ``Requirement`` instance).
    If there is an active distribution for the requested project, this
    returns it, as long as it meets the version requirement specified by
    ``req``.  But, if there is an active distribution for the project and it
    does *not* meet the ``req`` requirement, ``VersionConflict`` is raised.
    If there is no active distribution for the requested project, ``None``
    is returned.

``resolve(requirements, env=None, installer=None)``
    List all distributions needed to (recursively) meet ``requirements``

    ``requirements`` must be a sequence of ``Requirement`` objects.  ``env``,
    if supplied, should be an ``Environment`` instance.  If
    not supplied, an ``Environment`` is created from the working set's
    ``entries``.  ``installer``, if supplied, will be invoked with each
    requirement that cannot be met by an already-installed distribution; it
    should return a ``Distribution`` or ``None``.  (See the ``obtain()`` method
    of `Environment Objects`_, below, for more information on the ``installer``
    argument.)

``add(dist, entry=None)``
    Add ``dist`` to working set, associated with ``entry``

    If ``entry`` is unspecified, it defaults to ``dist.location``.  On exit from
    this routine, ``entry`` is added to the end of the working set's ``.entries``
    (if it wasn't already present).

    ``dist`` is only added to the working set if it's for a project that
    doesn't already have a distribution active in the set.  If it's
    successfully added, any  callbacks registered with the ``subscribe()``
    method will be called.  (See `Receiving Change Notifications`_, below.)

    Note: ``add()`` is automatically called for you by the ``require()``
    method, so you don't normally need to use this method directly.

``entries``
    This attribute represents a "shadow" ``sys.path``, primarily useful for
    debugging.  If you are experiencing import problems, you should check
    the global ``working_set`` object's ``entries`` against ``sys.path``, to
    ensure that they match.  If they do not, then some part of your program
    is manipulating ``sys.path`` without updating the ``working_set``
    accordingly.  IMPORTANT NOTE: do not directly manipulate this attribute!
    Setting it equal to ``sys.path`` will not fix your problem, any more than
    putting black tape over an "engine warning" light will fix your car!  If
    this attribute is out of sync with ``sys.path``, it's merely an *indicator*
    of the problem, not the cause of it.


Receiving Change Notifications
------------------------------

Extensible applications and frameworks may need to receive notification when
a new distribution (such as a plug-in component) has been added to a working
set.  This is what the ``subscribe()`` method and ``add_activation_listener()``
function are for.

``subscribe(callback)``
    Invoke ``callback(distribution)`` once for each active distribution that is
    in the set now, or gets added later.  Because the callback is invoked for
    already-active distributions, you do not need to loop over the working set
    yourself to deal with the existing items; just register the callback and
    be prepared for the fact that it will be called immediately by this method.

    Note that callbacks *must not* allow exceptions to propagate, or they will
    interfere with the operation of other callbacks and possibly result in an
    inconsistent working set state.  Callbacks should use a try/except block
    to ignore, log, or otherwise process any errors, especially since the code
    that caused the callback to be invoked is unlikely to be able to handle
    the errors any better than the callback itself.

``pkg_resources.add_activation_listener()`` is an alternate spelling of
``pkg_resources.working_set.subscribe()``.


Locating Plugins
----------------

Extensible applications will sometimes have a "plugin directory" or a set of
plugin directories, from which they want to load entry points or other
metadata.  The ``find_plugins()`` method allows you to do this, by scanning an
environment for the newest version of each project that can be safely loaded
without conflicts or missing requirements.

``find_plugins(plugin_env, full_env=None, fallback=True)``
   Scan ``plugin_env`` and identify which distributions could be added to this
   working set without version conflicts or missing requirements.

   Example usage::

       distributions, errors = working_set.find_plugins(
           Environment(plugin_dirlist)
       )
       map(working_set.add, distributions)  # add plugins+libs to sys.path
       print "Couldn't load", errors        # display errors

   The ``plugin_env`` should be an ``Environment`` instance that contains only
   distributions that are in the project's "plugin directory" or directories.
   The ``full_env``, if supplied, should be an ``Environment`` instance that
   contains all currently-available distributions.

   If ``full_env`` is not supplied, one is created automatically from the
   ``WorkingSet`` this method is called on, which will typically mean that
   every directory on ``sys.path`` will be scanned for distributions.

   This method returns a 2-tuple: (``distributions``, ``error_info``), where
   ``distributions`` is a list of the distributions found in ``plugin_env`` that
   were loadable, along with any other distributions that are needed to resolve
   their dependencies.  ``error_info`` is a dictionary mapping unloadable plugin
   distributions to an exception instance describing the error that occurred.
   Usually this will be a ``DistributionNotFound`` or ``VersionConflict``
   instance.

   Most applications will use this method mainly on the master ``working_set``
   instance in ``pkg_resources``, and then immediately add the returned
   distributions to the working set so that they are available on sys.path.
   This will make it possible to find any entry points, and allow any other
   metadata tracking and hooks to be activated.

   The resolution algorithm used by ``find_plugins()`` is as follows.  First,
   the project names of the distributions present in ``plugin_env`` are sorted.
   Then, each project's eggs are tried in descending version order (i.e.,
   newest version first).

   An attempt is made to resolve each egg's dependencies. If the attempt is
   successful, the egg and its dependencies are added to the output list and to
   a temporary copy of the working set.  The resolution process continues with
   the next project name, and no older eggs for that project are tried.

   If the resolution attempt fails, however, the error is added to the error
   dictionary.  If the ``fallback`` flag is true, the next older version of the
   plugin is tried, until a working version is found.  If false, the resolution
   process continues with the next plugin project name.

   Some applications may have stricter fallback requirements than others. For
   example, an application that has a database schema or persistent objects
   may not be able to safely downgrade a version of a package. Others may want
   to ensure that a new plugin configuration is either 100% good or else
   revert to a known-good configuration.  (That is, they may wish to revert to
   a known configuration if the ``error_info`` return value is non-empty.)

   Note that this algorithm gives precedence to satisfying the dependencies of
   alphabetically prior project names in case of version conflicts. If two
   projects named "AaronsPlugin" and "ZekesPlugin" both need different versions
   of "TomsLibrary", then "AaronsPlugin" will win and "ZekesPlugin" will be
   disabled due to version conflict.


``Environment`` Objects
=======================

An "environment" is a collection of ``Distribution`` objects, usually ones
that are present and potentially importable on the current platform.
``Environment`` objects are used by ``pkg_resources`` to index available
distributions during dependency resolution.

``Environment(search_path=None, platform=get_supported_platform(), python=PY_MAJOR)``
    Create an environment snapshot by scanning ``search_path`` for distributions
    compatible with ``platform`` and ``python``.  ``search_path`` should be a
    sequence of strings such as might be used on ``sys.path``.  If a
    ``search_path`` isn't supplied, ``sys.path`` is used.

    ``platform`` is an optional string specifying the name of the platform
    that platform-specific distributions must be compatible with.  If
    unspecified, it defaults to the current platform.  ``python`` is an
    optional string naming the desired version of Python (e.g. ``'2.4'``);
    it defaults to the currently-running version.

    You may explicitly set ``platform`` (and/or ``python``) to ``None`` if you
    wish to include *all* distributions, not just those compatible with the
    running platform or Python version.

    Note that ``search_path`` is scanned immediately for distributions, and the
    resulting ``Environment`` is a snapshot of the found distributions.  It
    is not automatically updated if the system's state changes due to e.g.
    installation or removal of distributions.

``__getitem__(project_name)``
    Returns a list of distributions for the given project name, ordered
    from newest to oldest version.  (And highest to lowest format precedence
    for distributions that contain the same version of the project.)  If there
    are no distributions for the project, returns an empty list.

``__iter__()``
    Yield the unique project names of the distributions in this environment.
    The yielded names are always in lower case.

``add(dist)``
    Add ``dist`` to the environment if it matches the platform and python version
    specified at creation time, and only if the distribution hasn't already
    been added. (i.e., adding the same distribution more than once is a no-op.)

``remove(dist)``
    Remove ``dist`` from the environment.

``can_add(dist)``
    Is distribution ``dist`` acceptable for this environment?  If it's not
    compatible with the ``platform`` and ``python`` version values specified
    when the environment was created, a false value is returned.

``__add__(dist_or_env)``  (``+`` operator)
    Add a distribution or environment to an ``Environment`` instance, returning
    a *new* environment object that contains all the distributions previously
    contained by both.  The new environment will have a ``platform`` and
    ``python`` of ``None``, meaning that it will not reject any distributions
    from being added to it; it will simply accept whatever is added.  If you
    want the added items to be filtered for platform and Python version, or
    you want to add them to the *same* environment instance, you should use
    in-place addition (``+=``) instead.

``__iadd__(dist_or_env)``  (``+=`` operator)
    Add a distribution or environment to an ``Environment`` instance
    *in-place*, updating the existing instance and returning it.  The
    ``platform`` and ``python`` filter attributes take effect, so distributions
    in the source that do not have a suitable platform string or Python version
    are silently ignored.

``best_match(req, working_set, installer=None)``
    Find distribution best matching ``req`` and usable on ``working_set``

    This calls the ``find(req)`` method of the ``working_set`` to see if a
    suitable distribution is already active.  (This may raise
    ``VersionConflict`` if an unsuitable version of the project is already
    active in the specified ``working_set``.)  If a suitable distribution isn't
    active, this method returns the newest distribution in the environment
    that meets the ``Requirement`` in ``req``.  If no suitable distribution is
    found, and ``installer`` is supplied, then the result of calling
    the environment's ``obtain(req, installer)`` method will be returned.

``obtain(requirement, installer=None)``
    Obtain a distro that matches requirement (e.g. via download).  In the
    base ``Environment`` class, this routine just returns
    ``installer(requirement)``, unless ``installer`` is None, in which case
    None is returned instead.  This method is a hook that allows subclasses
    to attempt other ways of obtaining a distribution before falling back
    to the ``installer`` argument.

``scan(search_path=None)``
    Scan ``search_path`` for distributions usable on ``platform``

    Any distributions found are added to the environment.  ``search_path`` should
    be a sequence of strings such as might be used on ``sys.path``.  If not
    supplied, ``sys.path`` is used.  Only distributions conforming to
    the platform/python version defined at initialization are added.  This
    method is a shortcut for using the ``find_distributions()`` function to
    find the distributions from each item in ``search_path``, and then calling
    ``add()`` to add each one to the environment.


``Requirement`` Objects
=======================

``Requirement`` objects express what versions of a project are suitable for
some purpose.  These objects (or their string form) are used by various
``pkg_resources`` APIs in order to find distributions that a script or
distribution needs.


Requirements Parsing
--------------------

``parse_requirements(s)``
    Yield ``Requirement`` objects for a string or iterable of lines.  Each
    requirement must start on a new line.  See below for syntax.

``Requirement.parse(s)``
    Create a ``Requirement`` object from a string or iterable of lines.  A
    ``ValueError`` is raised if the string or lines do not contain a valid
    requirement specifier, or if they contain more than one specifier.  (To
    parse multiple specifiers from a string or iterable of strings, use
    ``parse_requirements()`` instead.)

    The syntax of a requirement specifier is defined in full in PEP 508.

    Some examples of valid requirement specifiers::

        FooProject >= 1.2
        Fizzy [foo, bar]
        PickyThing>1.6,<=1.9,!=1.8.6
        SomethingWhoseVersionIDontCareAbout
        SomethingWithMarker[foo]>1.0;python_version<"2.7"

    The project name is the only required portion of a requirement string, and
    if it's the only thing supplied, the requirement will accept any version
    of that project.

    The "extras" in a requirement are used to request optional features of a
    project, that may require additional project distributions in order to
    function.  For example, if the hypothetical "Report-O-Rama" project offered
    optional PDF support, it might require an additional library in order to
    provide that support.  Thus, a project needing Report-O-Rama's PDF features
    could use a requirement of ``Report-O-Rama[PDF]`` to request installation
    or activation of both Report-O-Rama and any libraries it needs in order to
    provide PDF support.  For example, you could use::

        pip install Report-O-Rama[PDF]

    To install the necessary packages using pip, or call
    ``pkg_resources.require('Report-O-Rama[PDF]')`` to add the necessary
    distributions to sys.path at runtime.

    The "markers" in a requirement are used to specify when a requirement
    should be installed -- the requirement will be installed if the marker
    evaluates as true in the current environment. For example, specifying
    ``argparse;python_version<"3.0"`` will not install in an Python 3
    environment, but will in a Python 2 environment.

``Requirement`` Methods and Attributes
--------------------------------------

``__contains__(dist_or_version)``
    Return true if ``dist_or_version`` fits the criteria for this requirement.
    If ``dist_or_version`` is a ``Distribution`` object, its project name must
    match the requirement's project name, and its version must meet the
    requirement's version criteria.  If ``dist_or_version`` is a string, it is
    parsed using the ``parse_version()`` utility function.  Otherwise, it is
    assumed to be an already-parsed version.

    The ``Requirement`` object's version specifiers (``.specs``) are internally
    sorted into ascending version order, and used to establish what ranges of
    versions are acceptable.  Adjacent redundant conditions are effectively
    consolidated (e.g. ``">1, >2"`` produces the same results as ``">2"``, and
    ``"<2,<3"`` produces the same results as ``"<2"``). ``"!="`` versions are
    excised from the ranges they fall within.  The version being tested for
    acceptability is then checked for membership in the resulting ranges.

``__eq__(other_requirement)``
    A requirement compares equal to another requirement if they have
    case-insensitively equal project names, version specifiers, and "extras".
    (The order that extras and version specifiers are in is also ignored.)
    Equal requirements also have equal hashes, so that requirements can be
    used in sets or as dictionary keys.

``__str__()``
    The string form of a ``Requirement`` is a string that, if passed to
    ``Requirement.parse()``, would return an equal ``Requirement`` object.

``project_name``
    The name of the required project

``key``
    An all-lowercase version of the ``project_name``, useful for comparison
    or indexing.

``extras``
    A tuple of names of "extras" that this requirement calls for.  (These will
    be all-lowercase and normalized using the ``safe_extra()`` parsing utility
    function, so they may not exactly equal the extras the requirement was
    created with.)

``specs``
    A list of ``(op,version)`` tuples, sorted in ascending parsed-version
    order.  The ``op`` in each tuple is a comparison operator, represented as
    a string.  The ``version`` is the (unparsed) version number.

``marker``
    An instance of ``packaging.markers.Marker`` that allows evaluation
    against the current environment. May be None if no marker specified.

``url``
    The location to download the requirement from if specified.

Entry Points
============

Entry points are a simple way for distributions to "advertise" Python objects
(such as functions or classes) for use by other distributions.  Extensible
applications and frameworks can search for entry points with a particular name
or group, either from a specific distribution or from all active distributions
on sys.path, and then inspect or load the advertised objects at will.

Entry points belong to "groups" which are named with a dotted name similar to
a Python package or module name.  For example, the ``setuptools`` package uses
an entry point named ``distutils.commands`` in order to find commands defined
by distutils extensions.  ``setuptools`` treats the names of entry points
defined in that group as the acceptable commands for a setup script.

In a similar way, other packages can define their own entry point groups,
either using dynamic names within the group (like ``distutils.commands``), or
possibly using predefined names within the group.  For example, a blogging
framework that offers various pre- or post-publishing hooks might define an
entry point group and look for entry points named "pre_process" and
"post_process" within that group.

To advertise an entry point, a project needs to use ``setuptools`` and provide
an ``entry_points`` argument to ``setup()`` in its setup script, so that the
entry points will be included in the distribution's metadata.  For more
details, see :ref:`Advertising Behavior<dynamic discovery of services and plugins>`.

Each project distribution can advertise at most one entry point of a given
name within the same entry point group.  For example, a distutils extension
could advertise two different ``distutils.commands`` entry points, as long as
they had different names.  However, there is nothing that prevents *different*
projects from advertising entry points of the same name in the same group.  In
some cases, this is a desirable thing, since the application or framework that
uses the entry points may be calling them as hooks, or in some other way
combining them.  It is up to the application or framework to decide what to do
if multiple distributions advertise an entry point; some possibilities include
using both entry points, displaying an error message, using the first one found
in sys.path order, etc.


Convenience API
---------------

In the following functions, the ``dist`` argument can be a ``Distribution``
instance, a ``Requirement`` instance, or a string specifying a requirement
(i.e. project name, version, etc.).  If the argument is a string or
``Requirement``, the specified distribution is located (and added to sys.path
if not already present).  An error will be raised if a matching distribution is
not available.

The ``group`` argument should be a string containing a dotted identifier,
identifying an entry point group.  If you are defining an entry point group,
you should include some portion of your package's name in the group name so as
to avoid collision with other packages' entry point groups.

``load_entry_point(dist, group, name)``
    Load the named entry point from the specified distribution, or raise
    ``ImportError``.

``get_entry_info(dist, group, name)``
    Return an ``EntryPoint`` object for the given ``group`` and ``name`` from
    the specified distribution.  Returns ``None`` if the distribution has not
    advertised a matching entry point.

``get_entry_map(dist, group=None)``
    Return the distribution's entry point map for ``group``, or the full entry
    map for the distribution.  This function always returns a dictionary,
    even if the distribution advertises no entry points.  If ``group`` is given,
    the dictionary maps entry point names to the corresponding ``EntryPoint``
    object.  If ``group`` is None, the dictionary maps group names to
    dictionaries that then map entry point names to the corresponding
    ``EntryPoint`` instance in that group.

``iter_entry_points(group, name=None)``
    Yield entry point objects from ``group`` matching ``name``.

    If ``name`` is None, yields all entry points in ``group`` from all
    distributions in the working set on sys.path, otherwise only ones matching
    both ``group`` and ``name`` are yielded.  Entry points are yielded from
    the active distributions in the order that the distributions appear on
    sys.path.  (Within entry points for a particular distribution, however,
    there is no particular ordering.)

    (This API is actually a method of the global ``working_set`` object; see
    the section above on `Basic WorkingSet Methods`_ for more information.)


Creating and Parsing
--------------------

``EntryPoint(name, module_name, attrs=(), extras=(), dist=None)``
    Create an ``EntryPoint`` instance.  ``name`` is the entry point name.  The
    ``module_name`` is the (dotted) name of the module containing the advertised
    object.  ``attrs`` is an optional tuple of names to look up from the
    module to obtain the advertised object.  For example, an ``attrs`` of
    ``("foo","bar")`` and a ``module_name`` of ``"baz"`` would mean that the
    advertised object could be obtained by the following code::

        import baz
        advertised_object = baz.foo.bar

    The ``extras`` are an optional tuple of "extra feature" names that the
    distribution needs in order to provide this entry point.  When the
    entry point is loaded, these extra features are looked up in the ``dist``
    argument to find out what other distributions may need to be activated
    on sys.path; see the ``load()`` method for more details.  The ``extras``
    argument is only meaningful if ``dist`` is specified.  ``dist`` must be
    a ``Distribution`` instance.

``EntryPoint.parse(src, dist=None)`` (classmethod)
    Parse a single entry point from string ``src``

    Entry point syntax follows the form::

        name = some.module:some.attr [extra1,extra2]

    The entry name and module name are required, but the ``:attrs`` and
    ``[extras]`` parts are optional, as is the whitespace shown between
    some of the items.  The ``dist`` argument is passed through to the
    ``EntryPoint()`` constructor, along with the other values parsed from
    ``src``.

``EntryPoint.parse_group(group, lines, dist=None)`` (classmethod)
    Parse ``lines`` (a string or sequence of lines) to create a dictionary
    mapping entry point names to ``EntryPoint`` objects.  ``ValueError`` is
    raised if entry point names are duplicated, if ``group`` is not a valid
    entry point group name, or if there are any syntax errors.  (Note: the
    ``group`` parameter is used only for validation and to create more
    informative error messages.)  If ``dist`` is provided, it will be used to
    set the ``dist`` attribute of the created ``EntryPoint`` objects.

``EntryPoint.parse_map(data, dist=None)`` (classmethod)
    Parse ``data`` into a dictionary mapping group names to dictionaries mapping
    entry point names to ``EntryPoint`` objects.  If ``data`` is a dictionary,
    then the keys are used as group names and the values are passed to
    ``parse_group()`` as the ``lines`` argument.  If ``data`` is a string or
    sequence of lines, it is first split into .ini-style sections (using
    the ``split_sections()`` utility function) and the section names are used
    as group names.  In either case, the ``dist`` argument is passed through to
    ``parse_group()`` so that the entry points will be linked to the specified
    distribution.


``EntryPoint`` Objects
----------------------

For simple introspection, ``EntryPoint`` objects have attributes that
correspond exactly to the constructor argument names: ``name``,
``module_name``, ``attrs``, ``extras``, and ``dist`` are all available.  In
addition, the following methods are provided:

``load()``
    Load the entry point, returning the advertised Python object.  Effectively
    calls ``self.require()`` then returns ``self.resolve()``.

``require(env=None, installer=None)``
    Ensure that any "extras" needed by the entry point are available on
    sys.path.  ``UnknownExtra`` is raised if the ``EntryPoint`` has ``extras``,
    but no ``dist``, or if the named extras are not defined by the
    distribution.  If ``env`` is supplied, it must be an ``Environment``, and it
    will be used to search for needed distributions if they are not already
    present on sys.path.  If ``installer`` is supplied, it must be a callable
    taking a ``Requirement`` instance and returning a matching importable
    ``Distribution`` instance or None.

``resolve()``
    Resolve the entry point from its module and attrs, returning the advertised
    Python object. Raises ``ImportError`` if it cannot be obtained.

``__str__()``
    The string form of an ``EntryPoint`` is a string that could be passed to
    ``EntryPoint.parse()`` to produce an equivalent ``EntryPoint``.


``Distribution`` Objects
========================

``Distribution`` objects represent collections of Python code that may or may
not be importable, and may or may not have metadata and resources associated
with them.  Their metadata may include information such as what other projects
the distribution depends on, what entry points the distribution advertises, and
so on.


Getting or Creating Distributions
---------------------------------

Most commonly, you'll obtain ``Distribution`` objects from a ``WorkingSet`` or
an ``Environment``.  (See the sections above on `WorkingSet Objects`_ and
`Environment Objects`_, which are containers for active distributions and
available distributions, respectively.)  You can also obtain ``Distribution``
objects from one of these high-level APIs:

``find_distributions(path_item, only=False)``
    Yield distributions accessible via ``path_item``.  If ``only`` is true, yield
    only distributions whose ``location`` is equal to ``path_item``.  In other
    words, if ``only`` is true, this yields any distributions that would be
    importable if ``path_item`` were on ``sys.path``.  If ``only`` is false, this
    also yields distributions that are "in" or "under" ``path_item``, but would
    not be importable unless their locations were also added to ``sys.path``.

``get_distribution(dist_spec)``
    Return a ``Distribution`` object for a given ``Requirement`` or string.
    If ``dist_spec`` is already a ``Distribution`` instance, it is returned.
    If it is a ``Requirement`` object or a string that can be parsed into one,
    it is used to locate and activate a matching distribution, which is then
    returned.

However, if you're creating specialized tools for working with distributions,
or creating a new distribution format, you may also need to create
``Distribution`` objects directly, using one of the three constructors below.

These constructors all take an optional ``metadata`` argument, which is used to
access any resources or metadata associated with the distribution.  ``metadata``
must be an object that implements the ``IResourceProvider`` interface, or None.
If it is None, an ``EmptyProvider`` is used instead.  ``Distribution`` objects
implement both the `IResourceProvider`_ and `IMetadataProvider Methods`_ by
delegating them to the ``metadata`` object.

``Distribution.from_location(location, basename, metadata=None, **kw)`` (classmethod)
    Create a distribution for ``location``, which must be a string such as a
    URL, filename, or other string that might be used on ``sys.path``.
    ``basename`` is a string naming the distribution, like ``Foo-1.2-py2.4.egg``.
    If ``basename`` ends with ``.egg``, then the project's name, version, python
    version and platform are extracted from the filename and used to set those
    properties of the created distribution.  Any additional keyword arguments
    are forwarded to the ``Distribution()`` constructor.

``Distribution.from_filename(filename, metadata=None**kw)`` (classmethod)
    Create a distribution by parsing a local filename.  This is a shorter way
    of saying  ``Distribution.from_location(normalize_path(filename),
    os.path.basename(filename), metadata)``.  In other words, it creates a
    distribution whose location is the normalize form of the filename, parsing
    name and version information from the base portion of the filename.  Any
    additional keyword arguments are forwarded to the ``Distribution()``
    constructor.

``Distribution(location,metadata,project_name,version,py_version,platform,precedence)``
    Create a distribution by setting its properties.  All arguments are
    optional and default to None, except for ``py_version`` (which defaults to
    the current Python version) and ``precedence`` (which defaults to
    ``EGG_DIST``; for more details see ``precedence`` under `Distribution
    Attributes`_ below).  Note that it's usually easier to use the
    ``from_filename()`` or ``from_location()`` constructors than to specify
    all these arguments individually.


``Distribution`` Attributes
---------------------------

location
    A string indicating the distribution's location.  For an importable
    distribution, this is the string that would be added to ``sys.path`` to
    make it actively importable.  For non-importable distributions, this is
    simply a filename, URL, or other way of locating the distribution.

project_name
    A string, naming the project that this distribution is for.  Project names
    are defined by a project's setup script, and they are used to identify
    projects on PyPI.  When a ``Distribution`` is constructed, the
    ``project_name`` argument is passed through the ``safe_name()`` utility
    function to filter out any unacceptable characters.

key
    ``dist.key`` is short for ``dist.project_name.lower()``.  It's used for
    case-insensitive comparison and indexing of distributions by project name.

extras
    A list of strings, giving the names of extra features defined by the
    project's dependency list (the ``extras_require`` argument specified in
    the project's setup script).

version
    A string denoting what release of the project this distribution contains.
    When a ``Distribution`` is constructed, the ``version`` argument is passed
    through the ``safe_version()`` utility function to filter out any
    unacceptable characters.  If no ``version`` is specified at construction
    time, then attempting to access this attribute later will cause the
    ``Distribution`` to try to discover its version by reading its ``PKG-INFO``
    metadata file.  If ``PKG-INFO`` is unavailable or can't be parsed,
    ``ValueError`` is raised.

parsed_version
    The ``parsed_version`` is an object representing a "parsed" form of the
    distribution's ``version``.  ``dist.parsed_version`` is a shortcut for
    calling ``parse_version(dist.version)``.  It is used to compare or sort
    distributions by version.  (See the `Parsing Utilities`_ section below for
    more information on the ``parse_version()`` function.)  Note that accessing
    ``parsed_version`` may result in a ``ValueError`` if the ``Distribution``
    was constructed without a ``version`` and without ``metadata`` capable of
    supplying the missing version info.

py_version
    The major/minor Python version the distribution supports, as a string.
    For example, "2.7" or "3.4".  The default is the current version of Python.

platform
    A string representing the platform the distribution is intended for, or
    ``None`` if the distribution is "pure Python" and therefore cross-platform.
    See `Platform Utilities`_ below for more information on platform strings.

precedence
    A distribution's ``precedence`` is used to determine the relative order of
    two distributions that have the same ``project_name`` and
    ``parsed_version``.  The default precedence is ``pkg_resources.EGG_DIST``,
    which is the highest (i.e. most preferred) precedence.  The full list
    of predefined precedences, from most preferred to least preferred, is:
    ``EGG_DIST``, ``BINARY_DIST``, ``SOURCE_DIST``, ``CHECKOUT_DIST``, and
    ``DEVELOP_DIST``.  Normally, precedences other than ``EGG_DIST`` are used
    only by the ``setuptools.package_index`` module, when sorting distributions
    found in a package index to determine their suitability for installation.
    "System" and "Development" eggs (i.e., ones that use the ``.egg-info``
    format), however, are automatically given a precedence of ``DEVELOP_DIST``.



``Distribution`` Methods
------------------------

``activate(path=None)``
    Ensure distribution is importable on ``path``.  If ``path`` is None,
    ``sys.path`` is used instead.  This ensures that the distribution's
    ``location`` is in the ``path`` list, and it also performs any necessary
    namespace package fixups or declarations.  (That is, if the distribution
    contains namespace packages, this method ensures that they are declared,
    and that the distribution's contents for those namespace packages are
    merged with the contents provided by any other active distributions.  See
    the section above on `Namespace Package Support`_ for more information.)

    ``pkg_resources`` adds a notification callback to the global ``working_set``
    that ensures this method is called whenever a distribution is added to it.
    Therefore, you should not normally need to explicitly call this method.
    (Note that this means that namespace packages on ``sys.path`` are always
    imported as soon as ``pkg_resources`` is, which is another reason why
    namespace packages should not contain any code or import statements.)

``as_requirement()``
    Return a ``Requirement`` instance that matches this distribution's project
    name and version.

``requires(extras=())``
    List the ``Requirement`` objects that specify this distribution's
    dependencies.  If ``extras`` is specified, it should be a sequence of names
    of "extras" defined by the distribution, and the list returned will then
    include any dependencies needed to support the named "extras".

``clone(**kw)``
    Create a copy of the distribution.  Any supplied keyword arguments override
    the corresponding argument to the ``Distribution()`` constructor, allowing
    you to change some of the copied distribution's attributes.

``egg_name()``
    Return what this distribution's standard filename should be, not including
    the ".egg" extension.  For example, a distribution for project "Foo"
    version 1.2 that runs on Python 2.3 for Windows would have an ``egg_name()``
    of ``Foo-1.2-py2.3-win32``.  Any dashes in the name or version are
    converted to underscores.  (``Distribution.from_location()`` will convert
    them back when parsing a ".egg" file name.)

``__cmp__(other)``, ``__hash__()``
    Distribution objects are hashed and compared on the basis of their parsed
    version and precedence, followed by their key (lowercase project name),
    location, Python version, and platform.

The following methods are used to access ``EntryPoint`` objects advertised
by the distribution.  See the section above on `Entry Points`_ for more
detailed information about these operations:

``get_entry_info(group, name)``
    Return the ``EntryPoint`` object for ``group`` and ``name``, or None if no
    such point is advertised by this distribution.

``get_entry_map(group=None)``
    Return the entry point map for ``group``.  If ``group`` is None, return
    a dictionary mapping group names to entry point maps for all groups.
    (An entry point map is a dictionary of entry point names to ``EntryPoint``
    objects.)

``load_entry_point(group, name)``
    Short for ``get_entry_info(group, name).load()``.  Returns the object
    advertised by the named entry point, or raises ``ImportError`` if
    the entry point isn't advertised by this distribution, or there is some
    other import problem.

In addition to the above methods, ``Distribution`` objects also implement all
of the `IResourceProvider`_ and `IMetadataProvider Methods`_ (which are
documented in later sections):

* ``has_metadata(name)``
* ``metadata_isdir(name)``
* ``metadata_listdir(name)``
* ``get_metadata(name)``
* ``get_metadata_lines(name)``
* ``run_script(script_name, namespace)``
* ``get_resource_filename(manager, resource_name)``
* ``get_resource_stream(manager, resource_name)``
* ``get_resource_string(manager, resource_name)``
* ``has_resource(resource_name)``
* ``resource_isdir(resource_name)``
* ``resource_listdir(resource_name)``

If the distribution was created with a ``metadata`` argument, these resource and
metadata access methods are all delegated to that ``metadata`` provider.
Otherwise, they are delegated to an ``EmptyProvider``, so that the distribution
will appear to have no resources or metadata.  This delegation approach is used
so that supporting custom importers or new distribution formats can be done
simply by creating an appropriate `IResourceProvider`_ implementation; see the
section below on `Supporting Custom Importers`_ for more details.

.. _ResourceManager API:

``ResourceManager`` API
=======================

The ``ResourceManager`` class provides uniform access to package resources,
whether those resources exist as files and directories or are compressed in
an archive of some kind.

Normally, you do not need to create or explicitly manage ``ResourceManager``
instances, as the ``pkg_resources`` module creates a global instance for you,
and makes most of its methods available as top-level names in the
``pkg_resources`` module namespace.  So, for example, this code actually
calls the ``resource_string()`` method of the global ``ResourceManager``::

    import pkg_resources
    my_data = pkg_resources.resource_string(__name__, "foo.dat")

Thus, you can use the APIs below without needing an explicit
``ResourceManager`` instance; just import and use them as needed.


Basic Resource Access
---------------------

In the following methods, the ``package_or_requirement`` argument may be either
a Python package/module name (e.g. ``foo.bar``) or a ``Requirement`` instance.
If it is a package or module name, the named module or package must be
importable (i.e., be in a distribution or directory on ``sys.path``), and the
``resource_name`` argument is interpreted relative to the named package.  (Note
that if a module name is used, then the resource name is relative to the
package immediately containing the named module.  Also, you should not use use
a namespace package name, because a namespace package can be spread across
multiple distributions, and is therefore ambiguous as to which distribution
should be searched for the resource.)

If it is a ``Requirement``, then the requirement is automatically resolved
(searching the current ``Environment`` if necessary) and a matching
distribution is added to the ``WorkingSet`` and ``sys.path`` if one was not
already present.  (Unless the ``Requirement`` can't be satisfied, in which
case an exception is raised.)  The ``resource_name`` argument is then interpreted
relative to the root of the identified distribution; i.e. its first path
segment will be treated as a peer of the top-level modules or packages in the
distribution.

Note that resource names must be ``/``-separated paths rooted at the package,
cannot contain relative names like ``".."``, and cannot be absolute.  Do *not* use
``os.path`` routines to manipulate resource paths, as they are *not* filesystem
paths.

``resource_exists(package_or_requirement, resource_name)``
    Does the named resource exist?  Return ``True`` or ``False`` accordingly.

``resource_stream(package_or_requirement, resource_name)``
    Return a readable file-like object for the specified resource; it may be
    an actual file, a ``StringIO``, or some similar object.  The stream is
    in "binary mode", in the sense that whatever bytes are in the resource
    will be read as-is.

``resource_string(package_or_requirement, resource_name)``
    Return the specified resource as a string.  The resource is read in
    binary fashion, such that the returned string contains exactly the bytes
    that are stored in the resource.

``resource_isdir(package_or_requirement, resource_name)``
    Is the named resource a directory?  Return ``True`` or ``False``
    accordingly.

``resource_listdir(package_or_requirement, resource_name)``
    List the contents of the named resource directory, just like ``os.listdir``
    except that it works even if the resource is in a zipfile.

Note that only ``resource_exists()`` and ``resource_isdir()`` are insensitive
as to the resource type.  You cannot use ``resource_listdir()`` on a file
resource, and you can't use ``resource_string()`` or ``resource_stream()`` on
directory resources.  Using an inappropriate method for the resource type may
result in an exception or undefined behavior, depending on the platform and
distribution format involved.


Resource Extraction
-------------------

``resource_filename(package_or_requirement, resource_name)``
    Sometimes, it is not sufficient to access a resource in string or stream
    form, and a true filesystem filename is needed.  In such cases, you can
    use this method (or module-level function) to obtain a filename for a
    resource.  If the resource is in an archive distribution (such as a zipped
    egg), it will be extracted to a cache directory, and the filename within
    the cache will be returned.  If the named resource is a directory, then
    all resources within that directory (including subdirectories) are also
    extracted.  If the named resource is a C extension or "eager resource"
    (see the ``setuptools`` documentation for details), then all C extensions
    and eager resources are extracted at the same time.

    Archived resources are extracted to a cache location that can be managed by
    the following two methods:

``set_extraction_path(path)``
    Set the base path where resources will be extracted to, if needed.

    If you do not call this routine before any extractions take place, the
    path defaults to the return value of ``get_default_cache()``.  (Which is
    based on the ``PYTHON_EGG_CACHE`` environment variable, with various
    platform-specific fallbacks.  See that routine's documentation for more
    details.)

    Resources are extracted to subdirectories of this path based upon
    information given by the resource provider.  You may set this to a
    temporary directory, but then you must call ``cleanup_resources()`` to
    delete the extracted files when done.  There is no guarantee that
    ``cleanup_resources()`` will be able to remove all extracted files.  (On
    Windows, for example, you can't unlink .pyd or .dll files that are still
    in use.)

    Note that you may not change the extraction path for a given resource
    manager once resources have been extracted, unless you first call
    ``cleanup_resources()``.

``cleanup_resources(force=False)``
    Delete all extracted resource files and directories, returning a list
    of the file and directory names that could not be successfully removed.
    This function does not have any concurrency protection, so it should
    generally only be called when the extraction path is a temporary
    directory exclusive to a single process.  This method is not
    automatically called; you must call it explicitly or register it as an
    ``atexit`` function if you wish to ensure cleanup of a temporary
    directory used for extractions.


"Provider" Interface
--------------------

If you are implementing an ``IResourceProvider`` and/or ``IMetadataProvider``
for a new distribution archive format, you may need to use the following
``IResourceManager`` methods to coordinate extraction of resources to the
filesystem.  If you're not implementing an archive format, however, you have
no need to use these methods.  Unlike the other methods listed above, they are
*not* available as top-level functions tied to the global ``ResourceManager``;
you must therefore have an explicit ``ResourceManager`` instance to use them.

``get_cache_path(archive_name, names=())``
    Return absolute location in cache for ``archive_name`` and ``names``

    The parent directory of the resulting path will be created if it does
    not already exist.  ``archive_name`` should be the base filename of the
    enclosing egg (which may not be the name of the enclosing zipfile!),
    including its ".egg" extension.  ``names``, if provided, should be a
    sequence of path name parts "under" the egg's extraction location.

    This method should only be called by resource providers that need to
    obtain an extraction location, and only for names they intend to
    extract, as it tracks the generated names for possible cleanup later.

``extraction_error()``
    Raise an ``ExtractionError`` describing the active exception as interfering
    with the extraction process.  You should call this if you encounter any
    OS errors extracting the file to the cache path; it will format the
    operating system exception for you, and add other information to the
    ``ExtractionError`` instance that may be needed by programs that want to
    wrap or handle extraction errors themselves.

``postprocess(tempname, filename)``
    Perform any platform-specific postprocessing of ``tempname``.
    Resource providers should call this method ONLY after successfully
    extracting a compressed resource.  They must NOT call it on resources
    that are already in the filesystem.

    ``tempname`` is the current (temporary) name of the file, and ``filename``
    is the name it will be renamed to by the caller after this routine
    returns.


Metadata API
============

The metadata API is used to access metadata resources bundled in a pluggable
distribution.  Metadata resources are virtual files or directories containing
information about the distribution, such as might be used by an extensible
application or framework to connect "plugins".  Like other kinds of resources,
metadata resource names are ``/``-separated and should not contain ``..`` or
begin with a ``/``.  You should not use ``os.path`` routines to manipulate
resource paths.

The metadata API is provided by objects implementing the ``IMetadataProvider``
or ``IResourceProvider`` interfaces.  ``Distribution`` objects implement this
interface, as do objects returned by the ``get_provider()`` function:

``get_provider(package_or_requirement)``
    If a package name is supplied, return an ``IResourceProvider`` for the
    package.  If a ``Requirement`` is supplied, resolve it by returning a
    ``Distribution`` from the current working set (searching the current
    ``Environment`` if necessary and adding the newly found ``Distribution``
    to the working set).  If the named package can't be imported, or the
    ``Requirement`` can't be satisfied, an exception is raised.

    NOTE: if you use a package name rather than a ``Requirement``, the object
    you get back may not be a pluggable distribution, depending on the method
    by which the package was installed.  In particular, "development" packages
    and "single-version externally-managed" packages do not have any way to
    map from a package name to the corresponding project's metadata.  Do not
    write code that passes a package name to ``get_provider()`` and then tries
    to retrieve project metadata from the returned object.  It may appear to
    work when the named package is in an ``.egg`` file or directory, but
    it will fail in other installation scenarios.  If you want project
    metadata, you need to ask for a *project*, not a package.


``IMetadataProvider`` Methods
-----------------------------

The methods provided by objects (such as ``Distribution`` instances) that
implement the ``IMetadataProvider`` or ``IResourceProvider`` interfaces are:

``has_metadata(name)``
    Does the named metadata resource exist?

``metadata_isdir(name)``
    Is the named metadata resource a directory?

``metadata_listdir(name)``
    List of metadata names in the directory (like ``os.listdir()``)

``get_metadata(name)``
    Return the named metadata resource as a string.  The data is read in binary
    mode; i.e., the exact bytes of the resource file are returned.

``get_metadata_lines(name)``
    Yield named metadata resource as list of non-blank non-comment lines.  This
    is short for calling ``yield_lines(provider.get_metadata(name))``.  See the
    section on `yield_lines()`_ below for more information on the syntax it
    recognizes.

``run_script(script_name, namespace)``
    Execute the named script in the supplied namespace dictionary.  Raises
    ``ResolutionError`` if there is no script by that name in the ``scripts``
    metadata directory.  ``namespace`` should be a Python dictionary, usually
    a module dictionary if the script is being run as a module.


Exceptions
==========

``pkg_resources`` provides a simple exception hierarchy for problems that may
occur when processing requests to locate and activate packages::

    ResolutionError
        DistributionNotFound
        VersionConflict
        UnknownExtra

    ExtractionError

``ResolutionError``
    This class is used as a base class for the other three exceptions, so that
    you can catch all of them with a single "except" clause.  It is also raised
    directly for miscellaneous requirement-resolution problems like trying to
    run a script that doesn't exist in the distribution it was requested from.

``DistributionNotFound``
    A distribution needed to fulfill a requirement could not be found.

``VersionConflict``
    The requested version of a project conflicts with an already-activated
    version of the same project.

``UnknownExtra``
    One of the "extras" requested was not recognized by the distribution it
    was requested from.

``ExtractionError``
    A problem occurred extracting a resource to the Python Egg cache.  The
    following attributes are available on instances of this exception:

    manager
        The resource manager that raised this exception

    cache_path
        The base directory for resource extraction

    original_error
        The exception instance that caused extraction to fail


Supporting Custom Importers
===========================

By default, ``pkg_resources`` supports normal filesystem imports, and
``zipimport`` importers.  If you wish to use the ``pkg_resources`` features
with other (PEP 302-compatible) importers or module loaders, you may need to
register various handlers and support functions using these APIs:

``register_finder(importer_type, distribution_finder)``
    Register ``distribution_finder`` to find distributions in ``sys.path`` items.
    ``importer_type`` is the type or class of a PEP 302 "Importer" (``sys.path``
    item handler), and ``distribution_finder`` is a callable that, when passed a
    path item, the importer instance, and an ``only`` flag, yields
    ``Distribution`` instances found under that path item.  (The ``only`` flag,
    if true, means the finder should yield only ``Distribution`` objects whose
    ``location`` is equal to the path item provided.)

    See the source of the ``pkg_resources.find_on_path`` function for an
    example finder function.

``register_loader_type(loader_type, provider_factory)``
    Register ``provider_factory`` to make ``IResourceProvider`` objects for
    ``loader_type``.  ``loader_type`` is the type or class of a PEP 302
    ``module.__loader__``, and ``provider_factory`` is a function that, when
    passed a module object, returns an `IResourceProvider`_ for that module,
    allowing it to be used with the `ResourceManager API`_.

``register_namespace_handler(importer_type, namespace_handler)``
    Register ``namespace_handler`` to declare namespace packages for the given
    ``importer_type``.  ``importer_type`` is the type or class of a PEP 302
    "importer" (sys.path item handler), and ``namespace_handler`` is a callable
    with a signature like this::

        def namespace_handler(importer, path_entry, moduleName, module):
            # return a path_entry to use for child packages

    Namespace handlers are only called if the relevant importer object has
    already agreed that it can handle the relevant path item.  The handler
    should only return a subpath if the module ``__path__`` does not already
    contain an equivalent subpath.  Otherwise, it should return None.

    For an example namespace handler, see the source of the
    ``pkg_resources.file_ns_handler`` function, which is used for both zipfile
    importing and regular importing.


IResourceProvider
-----------------

``IResourceProvider`` is an abstract class that documents what methods are
required of objects returned by a ``provider_factory`` registered with
``register_loader_type()``.  ``IResourceProvider`` is a subclass of
``IMetadataProvider``, so objects that implement this interface must also
implement all of the `IMetadataProvider Methods`_ as well as the methods
shown here.  The ``manager`` argument to the methods below must be an object
that supports the full `ResourceManager API`_ documented above.

``get_resource_filename(manager, resource_name)``
    Return a true filesystem path for ``resource_name``, coordinating the
    extraction with ``manager``, if the resource must be unpacked to the
    filesystem.

``get_resource_stream(manager, resource_name)``
    Return a readable file-like object for ``resource_name``.

``get_resource_string(manager, resource_name)``
    Return a string containing the contents of ``resource_name``.

``has_resource(resource_name)``
    Does the package contain the named resource?

``resource_isdir(resource_name)``
    Is the named resource a directory?  Return a false value if the resource
    does not exist or is not a directory.

``resource_listdir(resource_name)``
    Return a list of the contents of the resource directory, ala
    ``os.listdir()``.  Requesting the contents of a non-existent directory may
    raise an exception.

Note, by the way, that your provider classes need not (and should not) subclass
``IResourceProvider`` or ``IMetadataProvider``!  These classes exist solely
for documentation purposes and do not provide any useful implementation code.
You may instead wish to subclass one of the `built-in resource providers`_.


Built-in Resource Providers
---------------------------

``pkg_resources`` includes several provider classes that are automatically used
where appropriate.  Their inheritance tree looks like this::

    NullProvider
        EggProvider
            DefaultProvider
                PathMetadata
            ZipProvider
                EggMetadata
        EmptyProvider
            FileMetadata


``NullProvider``
    This provider class is just an abstract base that provides for common
    provider behaviors (such as running scripts), given a definition for just
    a few abstract methods.

``EggProvider``
    This provider class adds in some egg-specific features that are common
    to zipped and unzipped eggs.

``DefaultProvider``
    This provider class is used for unpacked eggs and "plain old Python"
    filesystem modules.

``ZipProvider``
    This provider class is used for all zipped modules, whether they are eggs
    or not.

``EmptyProvider``
    This provider class always returns answers consistent with a provider that
    has no metadata or resources.  ``Distribution`` objects created without
    a ``metadata`` argument use an instance of this provider class instead.
    Since all ``EmptyProvider`` instances are equivalent, there is no need
    to have more than one instance.  ``pkg_resources`` therefore creates a
    global instance of this class under the name ``empty_provider``, and you
    may use it if you have need of an ``EmptyProvider`` instance.

``PathMetadata(path, egg_info)``
    Create an ``IResourceProvider`` for a filesystem-based distribution, where
    ``path`` is the filesystem location of the importable modules, and ``egg_info``
    is the filesystem location of the distribution's metadata directory.
    ``egg_info`` should usually be the ``EGG-INFO`` subdirectory of ``path`` for an
    "unpacked egg", and a ``ProjectName.egg-info`` subdirectory of ``path`` for
    a "development egg".  However, other uses are possible for custom purposes.

``EggMetadata(zipimporter)``
    Create an ``IResourceProvider`` for a zipfile-based distribution.  The
    ``zipimporter`` should be a ``zipimport.zipimporter`` instance, and may
    represent a "basket" (a zipfile containing multiple ".egg" subdirectories)
    a specific egg *within* a basket, or a zipfile egg (where the zipfile
    itself is a ".egg").  It can also be a combination, such as a zipfile egg
    that also contains other eggs.

``FileMetadata(path_to_pkg_info)``
    Create an ``IResourceProvider`` that provides exactly one metadata
    resource: ``PKG-INFO``.  The supplied path should be a distutils PKG-INFO
    file.  This is basically the same as an ``EmptyProvider``, except that
    requests for ``PKG-INFO`` will be answered using the contents of the
    designated file.  (This provider is used to wrap ``.egg-info`` files
    installed by vendor-supplied system packages.)


Utility Functions
=================

In addition to its high-level APIs, ``pkg_resources`` also includes several
generally-useful utility routines.  These routines are used to implement the
high-level APIs, but can also be quite useful by themselves.


Parsing Utilities
-----------------

``parse_version(version)``
    Parsed a project's version string as defined by PEP 440. The returned
    value will be an object that represents the version. These objects may
    be compared to each other and sorted. The sorting algorithm is as defined
    by PEP 440 with the addition that any version which is not a valid PEP 440
    version will be considered less than any valid PEP 440 version and the
    invalid versions will continue sorting using the original algorithm.

.. _yield_lines():

``yield_lines(strs)``
    Yield non-empty/non-comment lines from a string/unicode or a possibly-
    nested sequence thereof.  If ``strs`` is an instance of ``basestring``, it
    is split into lines, and each non-blank, non-comment line is yielded after
    stripping leading and trailing whitespace.  (Lines whose first non-blank
    character is ``#`` are considered comment lines.)

    If ``strs`` is not an instance of ``basestring``, it is iterated over, and
    each item is passed recursively to ``yield_lines()``, so that an arbitrarily
    nested sequence of strings, or sequences of sequences of strings can be
    flattened out to the lines contained therein.  So for example, passing
    a file object or a list of strings to ``yield_lines`` will both work.
    (Note that between each string in a sequence of strings there is assumed to
    be an implicit line break, so lines cannot bridge two strings in a
    sequence.)

    This routine is used extensively by ``pkg_resources`` to parse metadata
    and file formats of various kinds, and most other ``pkg_resources``
    parsing functions that yield multiple values will use it to break up their
    input.  However, this routine is idempotent, so calling ``yield_lines()``
    on the output of another call to ``yield_lines()`` is completely harmless.

``split_sections(strs)``
    Split a string (or possibly-nested iterable thereof), yielding ``(section,
    content)`` pairs found using an ``.ini``-like syntax.  Each ``section`` is
    a whitespace-stripped version of the section name ("``[section]``")
    and each ``content`` is a list of stripped lines excluding blank lines and
    comment-only lines.  If there are any non-blank, non-comment lines before
    the first section header, they're yielded in a first ``section`` of
    ``None``.

    This routine uses ``yield_lines()`` as its front end, so you can pass in
    anything that ``yield_lines()`` accepts, such as an open text file, string,
    or sequence of strings.  ``ValueError`` is raised if a malformed section
    header is found (i.e. a line starting with ``[`` but not ending with
    ``]``).

    Note that this simplistic parser assumes that any line whose first nonblank
    character is ``[`` is a section heading, so it can't support .ini format
    variations that allow ``[`` as the first nonblank character on other lines.

``safe_name(name)``
    Return a "safe" form of a project's name, suitable for use in a
    ``Requirement`` string, as a distribution name, or a PyPI project name.
    All non-alphanumeric runs are condensed to single "-" characters, such that
    a name like "The $$$ Tree" becomes "The-Tree".  Note that if you are
    generating a filename from this value you should combine it with a call to
    ``to_filename()`` so all dashes ("-") are replaced by underscores ("_").
    See ``to_filename()``.

``safe_version(version)``
    This will return the normalized form of any PEP 440 version. If the version
    string is not PEP 440 compatible, this function behaves similar to
    ``safe_name()`` except that spaces in the input become dots, and dots are
    allowed to exist in the output.  As with ``safe_name()``, if you are
    generating a filename from this you should replace any "-" characters in
    the output with underscores.

``safe_extra(extra)``
    Return a "safe" form of an extra's name, suitable for use in a requirement
    string or a setup script's ``extras_require`` keyword.  This routine is
    similar to ``safe_name()`` except that non-alphanumeric runs are replaced
    by a single underbar (``_``), and the result is lowercased.

``to_filename(name_or_version)``
    Escape a name or version string so it can be used in a dash-separated
    filename (or ``#egg=name-version`` tag) without ambiguity.  You
    should only pass in values that were returned by ``safe_name()`` or
    ``safe_version()``.


Platform Utilities
------------------

``get_build_platform()``
    Return this platform's identifier string.  For Windows, the return value
    is ``"win32"``, and for macOS it is a string of the form
    ``"macosx-10.4-ppc"``.  All other platforms return the same uname-based
    string that the ``distutils.util.get_platform()`` function returns.
    This string is the minimum platform version required by distributions built
    on the local machine.  (Backward compatibility note: setuptools versions
    prior to 0.6b1 called this function ``get_platform()``, and the function is
    still available under that name for backward compatibility reasons.)

``get_supported_platform()`` (New in 0.6b1)
    This is the similar to ``get_build_platform()``, but is the maximum
    platform version that the local machine supports.  You will usually want
    to use this value as the ``provided`` argument to the
    ``compatible_platforms()`` function.

``compatible_platforms(provided, required)``
    Return true if a distribution built on the ``provided`` platform may be used
    on the ``required`` platform.  If either platform value is ``None``, it is
    considered a wildcard, and the platforms are therefore compatible.
    Likewise, if the platform strings are equal, they're also considered
    compatible, and ``True`` is returned.  Currently, the only non-equal
    platform strings that are considered compatible are macOS platform
    strings with the same hardware type (e.g. ``ppc``) and major version
    (e.g. ``10``) with the ``provided`` platform's minor version being less than
    or equal to the ``required`` platform's minor version.

``get_default_cache()``
    Determine the default cache location for extracting resources from zipped
    eggs.  This routine returns the ``PYTHON_EGG_CACHE`` environment variable,
    if set.  Otherwise, on Windows, it returns a "Python-Eggs" subdirectory of
    the user's "Application Data" directory.  On all other systems, it returns
    ``os.path.expanduser("~/.python-eggs")`` if ``PYTHON_EGG_CACHE`` is not
    set.


PEP 302 Utilities
-----------------

``get_importer(path_item)``
    A deprecated alias for ``pkgutil.get_importer()``


File/Path Utilities
-------------------

``ensure_directory(path)``
    Ensure that the parent directory (``os.path.dirname``) of ``path`` actually
    exists, using ``os.makedirs()`` if necessary.

``normalize_path(path)``
    Return a "normalized" version of ``path``, such that two paths represent
    the same filesystem location if they have equal ``normalized_path()``
    values.  Specifically, this is a shortcut for calling ``os.path.realpath``
    and ``os.path.normcase`` on ``path``.  Unfortunately, on certain platforms
    (notably Cygwin and macOS) the ``normcase`` function does not accurately
    reflect the platform's case-sensitivity, so there is always the possibility
    of two apparently-different paths being equal on such platforms.

History
-------

0.6c9
 * Fix ``resource_listdir('')`` always returning an empty list for zipped eggs.

0.6c7
 * Fix package precedence problem where single-version eggs installed in
   ``site-packages`` would take precedence over ``.egg`` files (or directories)
   installed in ``site-packages``.

0.6c6
 * Fix extracted C extensions not having executable permissions under Cygwin.

 * Allow ``.egg-link`` files to contain relative paths.

 * Fix cache dir defaults on Windows when multiple environment vars are needed
   to construct a path.

0.6c4
 * Fix "dev" versions being considered newer than release candidates.

0.6c3
 * Python 2.5 compatibility fixes.

0.6c2
 * Fix a problem with eggs specified directly on ``PYTHONPATH`` on
   case-insensitive filesystems possibly not showing up in the default
   working set, due to differing normalizations of ``sys.path`` entries.

0.6b3
 * Fixed a duplicate path insertion problem on case-insensitive filesystems.

0.6b1
 * Split ``get_platform()`` into ``get_supported_platform()`` and
   ``get_build_platform()`` to work around a Mac versioning problem that caused
   the behavior of ``compatible_platforms()`` to be platform specific.

 * Fix entry point parsing when a standalone module name has whitespace
   between it and the extras.

0.6a11
 * Added ``ExtractionError`` and ``ResourceManager.extraction_error()`` so that
   cache permission problems get a more user-friendly explanation of the
   problem, and so that programs can catch and handle extraction errors if they
   need to.

0.6a10
 * Added the ``extras`` attribute to ``Distribution``, the ``find_plugins()``
   method to ``WorkingSet``, and the ``__add__()`` and ``__iadd__()`` methods
   to ``Environment``.

 * ``safe_name()`` now allows dots in project names.

 * There is a new ``to_filename()`` function that escapes project names and
   versions for safe use in constructing egg filenames from a Distribution
   object's metadata.

 * Added ``Distribution.clone()`` method, and keyword argument support to other
   ``Distribution`` constructors.

 * Added the ``DEVELOP_DIST`` precedence, and automatically assign it to
   eggs using ``.egg-info`` format.

0.6a9
 * Don't raise an error when an invalid (unfinished) distribution is found
   unless absolutely necessary.  Warn about skipping invalid/unfinished eggs
   when building an Environment.

 * Added support for ``.egg-info`` files or directories with version/platform
   information embedded in the filename, so that system packagers have the
   option of including ``PKG-INFO`` files to indicate the presence of a
   system-installed egg, without needing to use ``.egg`` directories, zipfiles,
   or ``.pth`` manipulation.

 * Changed ``parse_version()`` to remove dashes before pre-release tags, so
   that ``0.2-rc1`` is considered an *older* version than ``0.2``, and is equal
   to ``0.2rc1``.  The idea that a dash *always* meant a post-release version
   was highly non-intuitive to setuptools users and Python developers, who
   seem to want to use ``-rc`` version numbers a lot.

0.6a8
 * Fixed a problem with ``WorkingSet.resolve()`` that prevented version
   conflicts from being detected at runtime.

 * Improved runtime conflict warning message to identify a line in the user's
   program, rather than flagging the ``warn()`` call in ``pkg_resources``.

 * Avoid giving runtime conflict warnings for namespace packages, even if they
   were declared by a different package than the one currently being activated.

 * Fix path insertion algorithm for case-insensitive filesystems.

 * Fixed a problem with nested namespace packages (e.g. ``peak.util``) not
   being set as an attribute of their parent package.

0.6a6
 * Activated distributions are now inserted in ``sys.path`` (and the working
   set) just before the directory that contains them, instead of at the end.
   This allows e.g. eggs in ``site-packages`` to override unmanaged modules in
   the same location, and allows eggs found earlier on ``sys.path`` to override
   ones found later.

 * When a distribution is activated, it now checks whether any contained
   non-namespace modules have already been imported and issues a warning if
   a conflicting module has already been imported.

 * Changed dependency processing so that it's breadth-first, allowing a
   depender's preferences to override those of a dependee, to prevent conflicts
   when a lower version is acceptable to the dependee, but not the depender.

 * Fixed a problem extracting zipped files on Windows, when the egg in question
   has had changed contents but still has the same version number.

0.6a4
 * Fix a bug in ``WorkingSet.resolve()`` that was introduced in 0.6a3.

0.6a3
 * Added ``safe_extra()`` parsing utility routine, and use it for Requirement,
   EntryPoint, and Distribution objects' extras handling.

0.6a1
 * Enhanced performance of ``require()`` and related operations when all
   requirements are already in the working set, and enhanced performance of
   directory scanning for distributions.

 * Fixed some problems using ``pkg_resources`` w/PEP 302 loaders other than
   ``zipimport``, and the previously-broken "eager resource" support.

 * Fixed ``pkg_resources.resource_exists()`` not working correctly, along with
   some other resource API bugs.

 * Many API changes and enhancements:

   * Added ``EntryPoint``, ``get_entry_map``, ``load_entry_point``, and
     ``get_entry_info`` APIs for dynamic plugin discovery.

   * ``list_resources`` is now ``resource_listdir`` (and it actually works)

   * Resource API functions like ``resource_string()`` that accepted a package
     name and resource name, will now also accept a ``Requirement`` object in
     place of the package name (to allow access to non-package data files in
     an egg).

   * ``get_provider()`` will now accept a ``Requirement`` instance or a module
     name.  If it is given a ``Requirement``, it will return a corresponding
     ``Distribution`` (by calling ``require()`` if a suitable distribution
     isn't already in the working set), rather than returning a metadata and
     resource provider for a specific module.  (The difference is in how
     resource paths are interpreted; supplying a module name means resources
     path will be module-relative, rather than relative to the distribution's
     root.)

   * ``Distribution`` objects now implement the ``IResourceProvider`` and
     ``IMetadataProvider`` interfaces, so you don't need to reference the (no
     longer available) ``metadata`` attribute to get at these interfaces.

   * ``Distribution`` and ``Requirement`` both have a ``project_name``
     attribute for the project name they refer to.  (Previously these were
     ``name`` and ``distname`` attributes.)

   * The ``path`` attribute of ``Distribution`` objects is now ``location``,
     because it isn't necessarily a filesystem path (and hasn't been for some
     time now).  The ``location`` of ``Distribution`` objects in the filesystem
     should always be normalized using ``pkg_resources.normalize_path()``; all
     of the setuptools' code that generates distributions from the filesystem
     (including ``Distribution.from_filename()``) ensure this invariant, but if
     you use a more generic API like ``Distribution()`` or
     ``Distribution.from_location()`` you should take care that you don't
     create a distribution with an un-normalized filesystem path.

   * ``Distribution`` objects now have an ``as_requirement()`` method that
     returns a ``Requirement`` for the distribution's project name and version.

   * Distribution objects no longer have an ``installed_on()`` method, and the
     ``install_on()`` method is now ``activate()`` (but may go away altogether
     soon).  The ``depends()`` method has also been renamed to ``requires()``,
     and ``InvalidOption`` is now ``UnknownExtra``.

   * ``find_distributions()`` now takes an additional argument called ``only``,
     that tells it to only yield distributions whose location is the passed-in
     path.  (It defaults to False, so that the default behavior is unchanged.)

   * ``AvailableDistributions`` is now called ``Environment``, and the
     ``get()``, ``__len__()``, and ``__contains__()`` methods were removed,
     because they weren't particularly useful.  ``__getitem__()`` no longer
     raises ``KeyError``; it just returns an empty list if there are no
     distributions for the named project.

   * The ``resolve()`` method of ``Environment`` is now a method of
     ``WorkingSet`` instead, and the ``best_match()`` method now uses a working
     set instead of a path list as its second argument.

   * There is a new ``pkg_resources.add_activation_listener()`` API that lets
     you register a callback for notifications about distributions added to
     ``sys.path`` (including the distributions already on it).  This is
     basically a hook for extensible applications and frameworks to be able to
     search for plugin metadata in distributions added at runtime.

0.5a13
 * Fixed a bug in resource extraction from nested packages in a zipped egg.

0.5a12
 * Updated extraction/cache mechanism for zipped resources to avoid inter-
   process and inter-thread races during extraction.  The default cache
   location can now be set via the ``PYTHON_EGGS_CACHE`` environment variable,
   and the default Windows cache is now a ``Python-Eggs`` subdirectory of the
   current user's "Application Data" directory, if the ``PYTHON_EGGS_CACHE``
   variable isn't set.

0.5a10
 * Fix a problem with ``pkg_resources`` being confused by non-existent eggs on
   ``sys.path`` (e.g. if a user deletes an egg without removing it from the
   ``easy-install.pth`` file).

 * Fix a problem with "basket" support in ``pkg_resources``, where egg-finding
   never actually went inside ``.egg`` files.

 * Made ``pkg_resources`` import the module you request resources from, if it's
   not already imported.

0.5a4
 * ``pkg_resources.AvailableDistributions.resolve()`` and related methods now
   accept an ``installer`` argument: a callable taking one argument, a
   ``Requirement`` instance.  The callable must return a ``Distribution``
   object, or ``None`` if no distribution is found.  This feature is used by
   EasyInstall to resolve dependencies by recursively invoking itself.

0.4a4
 * Fix problems with ``resource_listdir()``, ``resource_isdir()`` and resource
   directory extraction for zipped eggs.

0.4a3
 * Fixed scripts not being able to see a ``__file__`` variable in ``__main__``

 * Fixed a problem with ``resource_isdir()`` implementation that was introduced
   in 0.4a2.

0.4a1
 * Fixed a bug in requirements processing for exact versions (i.e. ``==`` and
   ``!=``) when only one condition was included.

 * Added ``safe_name()`` and ``safe_version()`` APIs to clean up handling of
   arbitrary distribution names and versions found on PyPI.

0.3a4
 * ``pkg_resources`` now supports resource directories, not just the resources
   in them.  In particular, there are ``resource_listdir()`` and
   ``resource_isdir()`` APIs.

 * ``pkg_resources`` now supports "egg baskets" -- .egg zipfiles which contain
   multiple distributions in subdirectories whose names end with ``.egg``.
   Having such a "basket" in a directory on ``sys.path`` is equivalent to
   having the individual eggs in that directory, but the contained eggs can
   be individually added (or not) to ``sys.path``.  Currently, however, there
   is no automated way to create baskets.

 * Namespace package manipulation is now protected by the Python import lock.

0.3a1
 * Initial release.
PK��\�0�7�74alt-python39-setuptools/docs/references/keywords.rstnu�[���========
Keywords
========

``name``
    A string specifying the name of the package.

``version``
    A string specifying the version number of the package.

``description``
    A string describing the package in a single line.

``long_description``
    A string providing a longer description of the package.

``long_description_content_type``
    A string specifying the content type is used for the ``long_description``
    (e.g. ``text/markdown``)

``author``
    A string specifying the author of the package.

``author_email``
    A string specifying the email address of the package author.

``maintainer``
    A string specifying the name of the current maintainer, if different from
    the author. Note that if the maintainer is provided, setuptools will use it
    as the author in ``PKG-INFO``.

``maintainer_email``
    A string specifying the email address of the current maintainer, if
    different from the author.

``url``
    A string specifying the URL for the package homepage.

``download_url``
    A string specifying the URL to download the package.

``packages``
    A list of strings specifying the packages that setuptools will manipulate.

``py_modules``
    A list of strings specifying the modules that setuptools will manipulate.

``scripts``
    A list of strings specifying the standalone script files to be built and
    installed.

``ext_package``
    A string specifying the base package name for the extensions provided by
    this package.

``ext_modules``
    A list of instances of ``setuptools.Extension`` providing the list of
    Python extensions to be built.

``classifiers``
    A list of strings describing the categories for the package.

``distclass``
    A subclass of ``Distribution`` to use.

``script_name``
    A string specifying the name of the setup.py script -- defaults to
    ``sys.argv[0]``

``script_args``
    A list of strings defining the arguments to supply to the setup script.

``options``
    A dictionary providing the default options for the setup script.

``license``
    A string specifying the license of the package.

``license_file``

    .. warning::
        ``license_file`` is deprecated. Use ``license_files`` instead.

``license_files``

    A list of glob patterns for license related files that should be included.
    If neither ``license_file`` nor ``license_files`` is specified, this option
    defaults to ``LICEN[CS]E*``, ``COPYING*``, ``NOTICE*``, and ``AUTHORS*``.

``keywords``
    A list of strings or a comma-separated string providing descriptive
    meta-data. See: `PEP 0314`_.

.. _PEP 0314: https://www.python.org/dev/peps/pep-0314/

``platforms``
    A list of strings or comma-separated string.

``cmdclass``
    A dictionary providing a mapping of command names to ``Command``
    subclasses.

``data_files``

    .. warning::
        ``data_files`` is deprecated. It does not work with wheels, so it
        should be avoided.

    A list of strings specifying the data files to install.

``package_dir``
    A dictionary providing a mapping of package to directory names.

``requires``

   .. warning::
      ``requires`` is superseded by ``install_requires`` and should not be used
      anymore.

``obsoletes``

   .. warning::
      ``obsoletes`` is currently ignored by ``pip``.

   List of strings describing packages which this package renders obsolete,
   meaning that the two projects should not be installed at the same time.

   Version declarations can be supplied. Version numbers must be in the format
   specified in Version specifiers (e.g. ``foo (<3.0)``).

   This field may be followed by an environment marker after a semicolon (e.g.
   ``foo; os_name == "posix"``)

   The most common use of this field will be in case a project name changes,
   e.g. Gorgon 2.3 gets subsumed into Torqued Python 1.0. When you install
   Torqued Python, the Gorgon distribution should be removed.

``provides``

   .. warning::
      ``provides`` is currently ignored by ``pip``.

   List of strings describing package- and virtual package names contained
   within this package.

   A package may provide additional names, e.g. to indicate that multiple
   projects have been bundled together. For instance, source distributions of
   the ZODB project have historically included the transaction project, which
   is now available as a separate distribution. Installing such a source
   distribution satisfies requirements for both ZODB and transaction.

   A package may also provide a “virtual” project name, which does not
   correspond to any separately-distributed project: such a name might be used
   to indicate an abstract capability which could be supplied by one of
   multiple projects. E.g., multiple projects might supply RDBMS bindings for
   use by a given ORM: each project might declare that it provides
   ORM-bindings, allowing other projects to depend only on having at most one
   of them installed.

   A version declaration may be supplied and must follow the rules described in
   Version specifiers. The distribution’s version number will be implied if
   none is specified (e.g. ``foo (<3.0)``).

   Each package may be followed by an environment marker after a semicolon
   (e.g. ``foo; os_name == "posix"``).

.. Below are setuptools keywords, above are distutils

``include_package_data``
    If set to ``True``, this tells ``setuptools`` to automatically include any
    data files it finds inside your package directories that are specified by
    your ``MANIFEST.in`` file.  For more information, see the section on
    :ref:`Including Data Files`.

``exclude_package_data``
    A dictionary mapping package names to lists of glob patterns that should
    be *excluded* from your package directories.  You can use this to trim back
    any excess files included by ``include_package_data``.  For a complete
    description and examples, see the section on :ref:`Including Data Files`.

``package_data``
    A dictionary mapping package names to lists of glob patterns.  For a
    complete description and examples, see the section on :ref:`Including Data
    Files`.  You do not need to use this option if you are using
    ``include_package_data``, unless you need to add e.g. files that are
    generated by your setup script and build process.  (And are therefore not
    in source control or are files that you don't want to include in your
    source distribution.)

``zip_safe``
    A boolean (True or False) flag specifying whether the project can be
    safely installed and run from a zip file.  If this argument is not
    supplied, the ``bdist_egg`` command will have to analyze all of your
    project's contents for possible problems each time it builds an egg.

``install_requires``
    A string or list of strings specifying what other distributions need to
    be installed when this one is.  See the section on :ref:`Declaring
    Dependencies` for details and examples of the format of this argument.

``entry_points``
    A dictionary mapping entry point group names to strings or lists of strings
    defining the entry points.  Entry points are used to support dynamic
    discovery of services or plugins provided by a project.  See :ref:`Dynamic
    Discovery of Services and Plugins` for details and examples of the format
    of this argument.  In addition, this keyword is used to support
    :ref:`Automatic Script Creation <entry_points>`.

``extras_require``
    A dictionary mapping names of "extras" (optional features of your project)
    to strings or lists of strings specifying what other distributions must be
    installed to support those features.  See the section on :ref:`Declaring
    Dependencies` for details and examples of the format of this argument.

``python_requires``
    A string corresponding to a version specifier (as defined in PEP 440) for
    the Python version, used to specify the Requires-Python defined in PEP 345.

``setup_requires``

    .. warning::
        Using ``setup_requires`` is discouraged in favor of `PEP-518`_

    A string or list of strings specifying what other distributions need to
    be present in order for the *setup script* to run.  ``setuptools`` will
    attempt to obtain these (even going so far as to download them using
    ``EasyInstall``) before processing the rest of the setup script or commands.
    This argument is needed if you are using distutils extensions as part of
    your build process; for example, extensions that process setup() arguments
    and turn them into EGG-INFO metadata files.

    (Note: projects listed in ``setup_requires`` will NOT be automatically
    installed on the system where the setup script is being run.  They are
    simply downloaded to the ./.eggs directory if they're not locally available
    already.  If you want them to be installed, as well as being available
    when the setup script is run, you should add them to ``install_requires``
    **and** ``setup_requires``.)

.. _PEP-518: http://www.python.org/dev/peps/pep-0518/

``dependency_links``

    .. warning::
        ``dependency_links`` is deprecated. It is not supported anymore by pip.

    A list of strings naming URLs to be searched when satisfying dependencies.
    These links will be used if needed to install packages specified by
    ``setup_requires`` or ``tests_require``.  They will also be written into
    the egg's metadata for use by tools like EasyInstall to use when installing
    an ``.egg`` file.

``namespace_packages``
    A list of strings naming the project's "namespace packages".  A namespace
    package is a package that may be split across multiple project
    distributions.  For example, Zope 3's ``zope`` package is a namespace
    package, because subpackages like ``zope.interface`` and ``zope.publisher``
    may be distributed separately.  The egg runtime system can automatically
    merge such subpackages into a single parent package at runtime, as long
    as you declare them in each project that contains any subpackages of the
    namespace package, and as long as the namespace package's ``__init__.py``
    does not contain any code other than a namespace declaration.  See the
    section on :ref:`Namespace Packages` for more information.

``test_suite``
    A string naming a ``unittest.TestCase`` subclass (or a package or module
    containing one or more of them, or a method of such a subclass), or naming
    a function that can be called with no arguments and returns a
    ``unittest.TestSuite``.  If the named suite is a module, and the module
    has an ``additional_tests()`` function, it is called and the results are
    added to the tests to be run.  If the named suite is a package, any
    submodules and subpackages are recursively added to the overall test suite.

    Specifying this argument enables use of the :ref:`test` command to run the
    specified test suite, e.g. via ``setup.py test``.  See the section on the
    :ref:`test` command below for more details.

    New in 41.5.0: Deprecated the test command.

``tests_require``
    If your project's tests need one or more additional packages besides those
    needed to install it, you can use this option to specify them.  It should
    be a string or list of strings specifying what other distributions need to
    be present for the package's tests to run.  When you run the ``test``
    command, ``setuptools`` will  attempt to obtain these (even going
    so far as to download them using ``EasyInstall``).  Note that these
    required projects will *not* be installed on the system where the tests
    are run, but only downloaded to the project's setup directory if they're
    not already installed locally.

    New in 41.5.0: Deprecated the test command.

.. _test_loader:

``test_loader``
    If you would like to use a different way of finding tests to run than what
    setuptools normally uses, you can specify a module name and class name in
    this argument.  The named class must be instantiable with no arguments, and
    its instances must support the ``loadTestsFromNames()`` method as defined
    in the Python ``unittest`` module's ``TestLoader`` class.  Setuptools will
    pass only one test "name" in the ``names`` argument: the value supplied for
    the ``test_suite`` argument.  The loader you specify may interpret this
    string in any way it likes, as there are no restrictions on what may be
    contained in a ``test_suite`` string.

    The module name and class name must be separated by a ``:``.  The default
    value of this argument is ``"setuptools.command.test:ScanningLoader"``.  If
    you want to use the default ``unittest`` behavior, you can specify
    ``"unittest:TestLoader"`` as your ``test_loader`` argument instead.  This
    will prevent automatic scanning of submodules and subpackages.

    The module and class you specify here may be contained in another package,
    as long as you use the ``tests_require`` option to ensure that the package
    containing the loader class is available when the ``test`` command is run.

    New in 41.5.0: Deprecated the test command.

``eager_resources``
    A list of strings naming resources that should be extracted together, if
    any of them is needed, or if any C extensions included in the project are
    imported.  This argument is only useful if the project will be installed as
    a zipfile, and there is a need to have all of the listed resources be
    extracted to the filesystem *as a unit*.  Resources listed here
    should be '/'-separated paths, relative to the source root, so to list a
    resource ``foo.png`` in package ``bar.baz``, you would include the string
    ``bar/baz/foo.png`` in this argument.

    If you only need to obtain resources one at a time, or you don't have any C
    extensions that access other files in the project (such as data files or
    shared libraries), you probably do NOT need this argument and shouldn't
    mess with it.  For more details on how this argument works, see the section
    below on :ref:`Automatic Resource Extraction`.

``project_urls``
    An arbitrary map of URL names to hyperlinks, allowing more extensible
    documentation of where various resources can be found than the simple
    ``url`` and ``download_url`` options provide.
PK��\�'��ss+alt-python39-setuptools/docs/build_meta.rstnu�[���=======================================
Build System Support
=======================================

What is it?
-------------

Python packaging has come `a long way <https://bernat.tech/posts/pep-517-518/>`_.

The traditional ``setuptools`` way of packaging Python modules
uses a ``setup()`` function within the ``setup.py`` script. Commands such as
``python setup.py bdist`` or ``python setup.py bdist_wheel`` generate a 
distribution bundle and ``python setup.py install`` installs the distribution. 
This interface makes it difficult to choose other packaging tools without an 
overhaul. Because ``setup.py`` scripts allowed for arbitrary execution, it
proved difficult to provide a reliable user experience across environments
and history.

`PEP 517 <https://www.python.org/dev/peps/pep-0517/>`_ therefore came to
rescue and specified a new standard to 
package and distribute Python modules. Under PEP 517:

    a ``pyproject.toml`` file is used to specify what program to use
    for generating distribution. 

    Then, two functions provided by the program, ``build_wheel(directory: str)`` 
    and ``build_sdist(directory: str)`` create the distribution bundle at the 
    specified ``directory``. The program is free to use its own configuration 
    script or extend the ``.toml`` file. 

    Lastly, ``pip install *.whl`` or ``pip install *.tar.gz`` does the actual
    installation. If ``*.whl`` is available, ``pip`` will go ahead and copy
    the files into ``site-packages`` directory. If not, ``pip`` will look at
    ``pyproject.toml`` and decide what program to use to 'build from source' 
    (the default is ``setuptools``)

With this standard, switching between packaging tools becomes a lot easier. ``build_meta``
implements ``setuptools``' build system support.

How to use it?
--------------

Starting with a package that you want to distribute. You will need your source
scripts, a ``pyproject.toml`` file and a ``setup.cfg`` file::

    ~/meowpkg/
        pyproject.toml
        setup.cfg
        meowpkg/__init__.py

The pyproject.toml file is required to specify the build system (i.e. what is 
being used to package your scripts and install from source). To use it with 
setuptools, the content would be::

    [build-system]
    requires = ["setuptools", "wheel"]
    build-backend = "setuptools.build_meta"

The ``setuptools`` package implements the ``build_sdist``
command and the ``wheel`` package implements the ``build_wheel``
command; both are required to be compliant with PEP 517.

Use ``setuptools``' :ref:`declarative config <declarative config>` to
specify the package information::

    [metadata]
    name = meowpkg
    version = 0.0.1
    description = a package that meows
    
    [options]
    packages = find:

Now generate the distribution. To build the package, use
`PyPA build <https://pypa-build.readthedocs.io/en/latest/>`_::

    $ pip install -q build
    $ python -m build

And now it's done! The ``.whl`` file  and ``.tar.gz`` can then be distributed 
and installed::

    dist/
        meowpkg-0.0.1.whl
        meowpkg-0.0.1.tar.gz

    $ pip install dist/meowpkg-0.0.1.whl

or::

    $ pip install dist/meowpkg-0.0.1.tar.gz
PK��\��s���+alt-python39-setuptools/docs/setuptools.rstnu�[���==================================================
Building and Distributing Packages with Setuptools
==================================================

``Setuptools`` is a collection of enhancements to the Python ``distutils``
that allow developers to more easily build and
distribute Python packages, especially ones that have dependencies on other
packages.

Packages built and distributed using ``setuptools`` look to the user like
ordinary Python packages based on the ``distutils``.

Feature Highlights:

* Create `Python Eggs <http://peak.telecommunity.com/DevCenter/PythonEggs>`_ -
  a single-file importable distribution format

* Enhanced support for accessing data files hosted in zipped packages.

* Automatically include all packages in your source tree, without listing them
  individually in setup.py

* Automatically include all relevant files in your source distributions,
  without needing to create a ``MANIFEST.in`` file, and without having to force
  regeneration of the ``MANIFEST`` file when your source tree changes.

* Automatically generate wrapper scripts or Windows (console and GUI) .exe
  files for any number of "main" functions in your project.  (Note: this is not
  a py2exe replacement; the .exe files rely on the local Python installation.)

* Transparent Cython support, so that your setup.py can list ``.pyx`` files and
  still work even when the end-user doesn't have Cython installed (as long as
  you include the Cython-generated C in your source distribution)

* Command aliases - create project-specific, per-user, or site-wide shortcut
  names for commonly used commands and options

* Deploy your project in "development mode", such that it's available on
  ``sys.path``, yet can still be edited directly from its source checkout.

* Easily extend the distutils with new commands or ``setup()`` arguments, and
  distribute/reuse your extensions for multiple projects, without copying code.

* Create extensible applications and frameworks that automatically discover
  extensions, using simple "entry points" declared in a project's setup script.

* Full support for PEP 420 via ``find_namespace_packages()``, which is also backwards
  compatible to the existing ``find_packages()`` for Python >= 3.3.

-----------------
Developer's Guide
-----------------

The developer's guide has been updated. See the :doc:`most recent version <userguide/index>`.































TRANSITIONAL NOTE
~~~~~~~~~~~~~~~~~

Setuptools automatically calls ``declare_namespace()`` for you at runtime,
but future versions may *not*.  This is because the automatic declaration
feature has some negative side effects, such as needing to import all namespace
packages during the initialization of the ``pkg_resources`` runtime, and also
the need for ``pkg_resources`` to be explicitly imported before any namespace
packages work at all.  In some future releases, you'll be responsible
for including your own declaration lines, and the automatic declaration feature
will be dropped to get rid of the negative side effects.

During the remainder of the current development cycle, therefore, setuptools
will warn you about missing ``declare_namespace()`` calls in your
``__init__.py`` files, and you should correct these as soon as possible
before the compatibility support is removed.
Namespace packages without declaration lines will not work
correctly once a user has upgraded to a later version, so it's important that
you make this change now in order to avoid having your code break in the field.
Our apologies for the inconvenience, and thank you for your patience.

















setup.cfg-only projects
=======================

.. versionadded:: 40.9.0

If ``setup.py`` is missing from the project directory when a :pep:`517`
build is invoked, ``setuptools`` emulates a dummy ``setup.py`` file containing
only a ``setuptools.setup()`` call.

.. note::

    :pep:`517` doesn't support editable installs so this is currently
    incompatible with ``pip install -e .``.

This means that you can have a Python project with all build configuration
specified in ``setup.cfg``, without a ``setup.py`` file, if you **can rely
on** your project always being built by a :pep:`517`/:pep:`518` compatible
frontend.

To use this feature:

* Specify build requirements and :pep:`517` build backend in
  ``pyproject.toml``.
  For example:

  .. code-block:: toml

      [build-system]
      requires = [
        "setuptools >= 40.9.0",
        "wheel",
      ]
      build-backend = "setuptools.build_meta"

* Use a :pep:`517` compatible build frontend, such as ``pip >= 19`` or ``build``.

  .. warning::

      As :pep:`517` is new, support is not universal, and frontends that
      do support it may still have bugs. For compatibility, you may want to
      put a ``setup.py`` file containing only a ``setuptools.setup()``
      invocation.


Configuration API
=================

Some automation tools may wish to access data from a configuration file.

``Setuptools`` exposes a ``read_configuration()`` function for
parsing ``metadata`` and ``options`` sections into a dictionary.


.. code-block:: python

    from setuptools.config import read_configuration

    conf_dict = read_configuration("/home/user/dev/package/setup.cfg")


By default, ``read_configuration()`` will read only the file provided
in the first argument. To include values from other configuration files
which could be in various places, set the ``find_others`` keyword argument
to ``True``.

If you have only a configuration file but not the whole package, you can still
try to get data out of it with the help of the ``ignore_option_errors`` keyword
argument. When it is set to ``True``, all options with errors possibly produced
by directives, such as ``attr:`` and others, will be silently ignored.
As a consequence, the resulting dictionary will include no such options.











Mailing List and Bug Tracker
============================

Please use the `distutils-sig mailing list`_ for questions and discussion about
setuptools, and the `setuptools bug tracker`_ ONLY for issues you have
confirmed via the list are actual bugs, and which you have reduced to a minimal
set of steps to reproduce.

.. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/
.. _setuptools bug tracker: https://github.com/pypa/setuptools/
PK��\ƺ4�{{0alt-python39-setuptools/docs/userguide/index.rstnu�[���==================================================
Building and Distributing Packages with Setuptools
==================================================

``Setuptools`` is a collection of enhancements to the Python ``distutils``
that allow developers to more easily build and
distribute Python packages, especially ones that have dependencies on other
packages.

Packages built and distributed using ``setuptools`` look to the user like
ordinary Python packages based on the ``distutils``.

Transition to PEP517
====================

Since setuptools no longer serves as the default build tool, one must explicitly
opt in (by providing a :file:`pyproject.toml` file) to use this library. The user
facing part is provided by tools such as pip and
backend interface is described :doc:`in this document <../build_meta>`. The
quickstart provides an overview of the new workflow.

.. toctree::
    :maxdepth: 1

    quickstart
    package_discovery
    entry_point
    dependency_management
    datafiles
    development_mode
    distribution
    extension
    declarative_config
    keywords
    commands
    functionalities_rewrite
    miscellaneous
PK��\���@�
�
;alt-python39-setuptools/docs/userguide/development_mode.rstnu�[���"Development Mode"
==================

Under normal circumstances, the ``distutils`` assume that you are going to
build a distribution of your project, not use it in its "raw" or "unbuilt"
form.  However, if you were to use the ``distutils`` to build a distribution,
you would have to rebuild and reinstall your project every time you made a
change to it during development.

Another problem that sometimes comes up with the ``distutils`` is that you may
need to do development on two related projects at the same time.  You may need
to put both projects' packages in the same directory to run them, but need to
keep them separate for revision control purposes.  How can you do this?

Setuptools allows you to deploy your projects for use in a common directory or
staging area, but without copying any files.  Thus, you can edit each project's
code in its checkout directory, and only need to run build commands when you
change a project's C extensions or similarly compiled files.  You can even
deploy a project into another project's checkout directory, if that's your
preferred way of working (as opposed to using a common independent staging area
or the site-packages directory).

To do this, use the ``setup.py develop`` command.  It works very similarly to
``setup.py install``, except that it doesn't actually install anything.
Instead, it creates a special ``.egg-link`` file in the deployment directory,
that links to your project's source code.  And, if your deployment directory is
Python's ``site-packages`` directory, it will also update the
``easy-install.pth`` file to include your project's source code, thereby making
it available on ``sys.path`` for all programs using that Python installation.

In addition, the ``develop`` command creates wrapper scripts in the target
script directory that will run your in-development scripts after ensuring that
all your ``install_requires`` packages are available on ``sys.path``.

You can deploy the same project to multiple staging areas, e.g. if you have
multiple projects on the same machine that are sharing the same project you're
doing development work.

When you're done with a given development task, you can remove the project
source from a staging area using ``setup.py develop --uninstall``, specifying
the desired staging area if it's not the default.

There are several options to control the precise behavior of the ``develop``
command; see the section on the :ref:`develop <develop>` command below for more details.

Note that you can also apply setuptools commands to non-setuptools projects,
using commands like this::

   python -c "import setuptools; with open('setup.py') as f: exec(compile(f.read(), 'setup.py', 'exec'))" develop

That is, you can simply list the normal setup commands and options following
the quoted part.
PK��\G��N��5alt-python39-setuptools/docs/userguide/quickstart.rstnu�[���==========================
``setuptools`` Quickstart
==========================

Installation
============

To install the latest version of setuptools, use::

    pip install --upgrade setuptools


Python packaging at a glance
============================
The landscape of Python packaging is shifting and ``Setuptools`` has evolved to
only provide backend support, no longer being the de-facto packaging tool in
the market. All python package must provide a ``pyproject.toml`` and specify
the backend (build system) it wants to use. The distribution can then
be generated with whatever tools that provides a ``build sdist``-alike
functionality. While this may appear cumbersome, given the added pieces,
it in fact tremendously enhances the portability of your package. The
change is driven under :pep:`PEP 517 <517#build-requirements>`. To learn more about Python packaging in general,
navigate to the :ref:`bottom <packaging-resources>` of this page.


Basic Use
=========
For basic use of setuptools, you will need a ``pyproject.toml`` with the
exact following info, which declares you want to use ``setuptools`` to
package your project:

.. code-block:: toml

    [build-system]
    requires = ["setuptools", "wheel"]
    build-backend = "setuptools.build_meta"

Then, you will need a ``setup.cfg`` or ``setup.py`` to specify your package
information, such as metadata, contents, dependencies, etc. Here we demonstrate
the minimum

.. tab:: setup.cfg

    .. code-block:: ini

        [metadata]
        name = mypackage
        version = 0.0.1

        [options]
        packages = mypackage
        install_requires =
            requests
            importlib; python_version == "2.6"

.. tab:: setup.py

    .. code-block:: python

        from setuptools import setup

        setup(
            name='mypackage',
            version='0.0.1',
            packages=['mypackage'],
            install_requires=[
                'requests',
                'importlib; python_version == "2.6"',
            ],
        )

This is what your project would look like::

    ~/mypackage/
        pyproject.toml
        setup.cfg # or setup.py
        mypackage/__init__.py

Then, you need an builder, such as :std:doc:`PyPA build <pypa-build:index>`
which you can obtain via ``pip install build``. After downloading it, invoke
the builder::

    python -m build

You now have your distribution ready (e.g. a ``tar.gz`` file and a ``.whl``
file in the ``dist`` directory), which you can upload to PyPI!

Of course, before you release your project to PyPI, you'll want to add a bit
more information to your setup script to help people find or learn about your
project.  And maybe your project will have grown by then to include a few
dependencies, and perhaps some data files and scripts. In the next few sections,
we will walk through those additional but essential information you need
to specify to properly package your project.


Automatic package discovery
===========================
For simple projects, it's usually easy enough to manually add packages to
the ``packages`` keyword in ``setup.cfg``.  However, for very large projects
, it can be a big burden to keep the package list updated. ``setuptools``
therefore provides two convenient tools to ease the burden: :literal:`find:\ ` and
:literal:`find_namespace:\ `. To use it in your project:

.. code-block:: ini

    [options]
    packages = find:

    [options.packages.find] #optional
    include=pkg1, pkg2
    exclude=pk3, pk4

When you pass the above information, alongside other necessary ones,
``setuptools`` walks through the directory specified in ``where`` (omitted
here as the package reside in current directory) and filters the packages
it can find following the ``include``  (default to none), then remove
those that match the ``exclude`` and return a list of Python packages. Note
that each entry in the ``[options.packages.find]`` is optional. The above
setup also allows you to adopt a ``src/`` layout. For more details and advanced
use, go to :ref:`package_discovery`


Entry points and automatic script creation
===========================================
Setuptools support automatic creation of scripts upon installation, that runs
code within your package if you specify them with the ``entry_points`` keyword.
This is what allows you to run commands like ``pip install`` instead of having
to type ``python -m pip install``. To accomplish this, add the entry_points
keyword in your ``setup.cfg``:

.. code-block:: ini

    [options.entry_points]
    console_scripts =
        main = mypkg:some_func

When this project is installed, a ``main`` script will be installed and will
invoke the ``some_func`` in the ``__init__.py`` file when called by the user.
For detailed usage, including managing the additional or optional dependencies,
go to :doc:`entry_point`.


Dependency management
=====================
``setuptools`` supports automatically installing dependencies when a package is
installed. The simplest way to include requirement specifiers is to use the
``install_requires`` argument to ``setup.cfg``.  It takes a string or list of
strings containing requirement specifiers (A version specifier is one of the
operators <, >, <=, >=, == or !=, followed by a version identifier):

.. code-block:: ini

    [options]
    install_requires =
        docutils >= 0.3
        requests <= 0.4

When your project is installed, all of the dependencies not already installed
will be located (via PyPI), downloaded, built (if necessary), and installed.
This, of course, is a simplified scenarios. ``setuptools`` also provide
additional keywords such as ``setup_requires`` that allows you to install
dependencies before running the script, and ``extras_requires`` that take
care of those needed by automatically generated scripts. It also provides
mechanisms to handle dependencies that are not in PyPI. For more advanced use,
see :doc:`dependency_management`


.. _Including Data Files:

Including Data Files
====================
The distutils have traditionally allowed installation of "data files", which
are placed in a platform-specific location. Setuptools offers three ways to
specify data files to be included in your packages. For the simplest use, you
can simply use the ``include_package_data`` keyword:

.. code-block:: ini

    [options]
    include_package_data = True

This tells setuptools to install any data files it finds in your packages.
The data files must be specified via the distutils' ``MANIFEST.in`` file.
For more details, see :doc:`datafiles`


Development mode
================
``setuptools`` allows you to install a package without copying any files
to your interpreter directory (e.g. the ``site-packages`` directory). This
allows you to modify your source code and have the changes take effect without
you having to rebuild and reinstall. This is currently incompatible with
PEP 517 and therefore it requires a ``setup.py`` script with the following
content::

    import setuptools
    setuptools.setup()

Then::

    pip install --editable .

This creates a link file in your interpreter site package directory which
associate with your source code. For more information, see :doc:`development_mode`.


Uploading your package to PyPI
==============================
After generating the distribution files, next step would be to upload your
distribution so others can use it. This functionality is provided by
`twine <https://pypi.org/project/twine/>`_ and we will only demonstrate the
basic use here.


Transitioning from ``setup.py`` to ``setup.cfg``
================================================
To avoid executing arbitrary scripts and boilerplate code, we are transitioning
into a full-fledged ``setup.cfg`` to declare your package information instead
of running ``setup()``. This inevitably brings challenges due to a different
syntax. Here we provide a quick guide to understanding how ``setup.cfg`` is
parsed by ``setuptool`` to ease the pain of transition.

.. _packaging-resources:

Resources on Python packaging
=============================
Packaging in Python is hard. Here we provide a list of links for those that
want to learn more.
PK��\P��d��4alt-python39-setuptools/docs/userguide/datafiles.rstnu�[���====================
Data Files Support
====================

The distutils have traditionally allowed installation of "data files", which
are placed in a platform-specific location.  However, the most common use case
for data files distributed with a package is for use *by* the package, usually
by including the data files in the package directory.

Setuptools offers three ways to specify data files to be included in your
packages.  First, you can simply use the ``include_package_data`` keyword,
e.g.::

    from setuptools import setup, find_packages
    setup(
        ...
        include_package_data=True
    )

This tells setuptools to install any data files it finds in your packages.
The data files must be specified via the distutils' ``MANIFEST.in`` file.
(They can also be tracked by a revision control system, using an appropriate
plugin.  See the section below on :ref:`Adding Support for Revision
Control Systems` for information on how to write such plugins.)

If you want finer-grained control over what files are included (for example,
if you have documentation files in your package directories and want to exclude
them from installation), then you can also use the ``package_data`` keyword,
e.g.::

    from setuptools import setup, find_packages
    setup(
        ...
        package_data={
            # If any package contains *.txt or *.rst files, include them:
            "": ["*.txt", "*.rst"],
            # And include any *.msg files found in the "hello" package, too:
            "hello": ["*.msg"],
        }
    )

The ``package_data`` argument is a dictionary that maps from package names to
lists of glob patterns.  The globs may include subdirectory names, if the data
files are contained in a subdirectory of the package.  For example, if the
package tree looks like this::

    setup.py
    src/
        mypkg/
            __init__.py
            mypkg.txt
            data/
                somefile.dat
                otherdata.dat

The setuptools setup file might look like this::

    from setuptools import setup, find_packages
    setup(
        ...
        packages=find_packages("src"),  # include all packages under src
        package_dir={"": "src"},   # tell distutils packages are under src

        package_data={
            # If any package contains *.txt files, include them:
            "": ["*.txt"],
            # And include any *.dat files found in the "data" subdirectory
            # of the "mypkg" package, also:
            "mypkg": ["data/*.dat"],
        }
    )

Notice that if you list patterns in ``package_data`` under the empty string,
these patterns are used to find files in every package, even ones that also
have their own patterns listed.  Thus, in the above example, the ``mypkg.txt``
file gets included even though it's not listed in the patterns for ``mypkg``.

Also notice that if you use paths, you *must* use a forward slash (``/``) as
the path separator, even if you are on Windows.  Setuptools automatically
converts slashes to appropriate platform-specific separators at build time.

If datafiles are contained in a subdirectory of a package that isn't a package
itself (no ``__init__.py``), then the subdirectory names (or ``*``) are required
in the ``package_data`` argument (as shown above with ``"data/*.dat"``).

When building an ``sdist``, the datafiles are also drawn from the
``package_name.egg-info/SOURCES.txt`` file, so make sure that this is removed if
the ``setup.py`` ``package_data`` list is updated before calling ``setup.py``.

(Note: although the ``package_data`` argument was previously only available in
``setuptools``, it was also added to the Python ``distutils`` package as of
Python 2.4; there is `some documentation for the feature`__ available on the
python.org website.  If using the setuptools-specific ``include_package_data``
argument, files specified by ``package_data`` will *not* be automatically
added to the manifest unless they are listed in the MANIFEST.in file.)

__ https://docs.python.org/3/distutils/setupscript.html#installing-package-data

Sometimes, the ``include_package_data`` or ``package_data`` options alone
aren't sufficient to precisely define what files you want included.  For
example, you may want to include package README files in your revision control
system and source distributions, but exclude them from being installed.  So,
setuptools offers an ``exclude_package_data`` option as well, that allows you
to do things like this::

    from setuptools import setup, find_packages
    setup(
        ...
        packages=find_packages("src"),  # include all packages under src
        package_dir={"": "src"},   # tell distutils packages are under src

        include_package_data=True,    # include everything in source control

        # ...but exclude README.txt from all packages
        exclude_package_data={"": ["README.txt"]},
    )

The ``exclude_package_data`` option is a dictionary mapping package names to
lists of wildcard patterns, just like the ``package_data`` option.  And, just
as with that option, a key of ``""`` will apply the given pattern(s) to all
packages.  However, any files that match these patterns will be *excluded*
from installation, even if they were listed in ``package_data`` or were
included as a result of using ``include_package_data``.

In summary, the three options allow you to:

``include_package_data``
    Accept all data files and directories matched by ``MANIFEST.in``.

``package_data``
    Specify additional patterns to match files that may or may
    not be matched by ``MANIFEST.in`` or found in source control.

``exclude_package_data``
    Specify patterns for data files and directories that should *not* be
    included when a package is installed, even if they would otherwise have
    been included due to the use of the preceding options.

NOTE: Due to the way the distutils build process works, a data file that you
include in your project and then stop including may be "orphaned" in your
project's build directories, requiring you to run ``setup.py clean --all`` to
fully remove them.  This may also be important for your users and contributors
if they track intermediate revisions of your project using Subversion; be sure
to let them know when you make changes that remove files from inclusion so they
can run ``setup.py clean --all``.


.. _Accessing Data Files at Runtime:

Accessing Data Files at Runtime
-------------------------------

Typically, existing programs manipulate a package's ``__file__`` attribute in
order to find the location of data files.  However, this manipulation isn't
compatible with PEP 302-based import hooks, including importing from zip files
and Python Eggs.  It is strongly recommended that, if you are using data files,
you should use the :ref:`ResourceManager API` of ``pkg_resources`` to access
them.  The ``pkg_resources`` module is distributed as part of setuptools, so if
you're using setuptools to distribute your package, there is no reason not to
use its resource management API.  See also `Importlib Resources`_ for
a quick example of converting code that uses ``__file__`` to use
``pkg_resources`` instead.

.. _Importlib Resources: https://docs.python.org/3/library/importlib.html#module-importlib.resources


Non-Package Data Files
----------------------

Historically, ``setuptools`` by way of ``easy_install`` would encapsulate data
files from the distribution into the egg (see `the old docs
<https://github.com/pypa/setuptools/blob/52aacd5b276fedd6849c3a648a0014f5da563e93/docs/setuptools.txt#L970-L1001>`_). As eggs are deprecated and pip-based installs
fall back to the platform-specific location for installing data files, there is
no supported facility to reliably retrieve these resources.

Instead, the PyPA recommends that any data files you wish to be accessible at
run time be included in the package.
PK��\��f�n)n)=alt-python39-setuptools/docs/userguide/declarative_config.rstnu�[���.. _declarative config:

-----------------------------------------
Configuring setup() using setup.cfg files
-----------------------------------------

.. note:: New in 30.3.0 (8 Dec 2016).

.. important::
    If compatibility with legacy builds (i.e. those not using the :pep:`517`
    build API) is desired, a ``setup.py`` file containing a ``setup()`` function
    call is still required even if your configuration resides in ``setup.cfg``.

``Setuptools`` allows using configuration files (usually :file:`setup.cfg`)
to define a package’s metadata and other options that are normally supplied
to the ``setup()`` function (declarative config).

This approach not only allows automation scenarios but also reduces
boilerplate code in some cases.

.. _example-setup-config:

.. code-block:: ini

    [metadata]
    name = my_package
    version = attr: src.VERSION
    description = My package description
    long_description = file: README.rst, CHANGELOG.rst, LICENSE.rst
    keywords = one, two
    license = BSD 3-Clause License
    classifiers =
        Framework :: Django
        License :: OSI Approved :: BSD License
        Programming Language :: Python :: 3
        Programming Language :: Python :: 3.5

    [options]
    zip_safe = False
    include_package_data = True
    packages = find:
    scripts =
        bin/first.py
        bin/second.py
    install_requires =
        requests
        importlib; python_version == "2.6"

    [options.package_data]
    * = *.txt, *.rst
    hello = *.msg

    [options.entry_points]
    console_scripts =
        executable-name = package.module:function

    [options.extras_require]
    pdf = ReportLab>=1.2; RXP
    rest = docutils>=0.3; pack ==1.1, ==1.3

    [options.packages.find]
    exclude =
        src.subpackage1
        src.subpackage2

    [options.data_files]
    /etc/my_package =
        site.d/00_default.conf
        host.d/00_default.conf
    data = data/img/logo.png, data/svg/icon.svg
    fonts = data/fonts/*.ttf, data/fonts/*.otf

Metadata and options are set in the config sections of the same name.

* Keys are the same as the keyword arguments one provides to the ``setup()``
  function.

* Complex values can be written comma-separated or placed one per line
  in *dangling* config values. The following are equivalent:

  .. code-block:: ini

      [metadata]
      keywords = one, two

      [metadata]
      keywords =
          one
          two

* In some cases, complex values can be provided in dedicated subsections for
  clarity.

* Some keys allow ``file:``, ``attr:``, ``find:``, and ``find_namespace:`` directives in
  order to cover common usecases.

* Unknown keys are ignored.


Using a ``src/`` layout
=======================

One commonly used package configuration has all the module source code in a
subdirectory (often called the ``src/`` layout), like this::

    ├── src
    │   └── mypackage
    │       ├── __init__.py
    │       └── mod1.py
    ├── setup.py
    └── setup.cfg

You can set up your ``setup.cfg`` to automatically find all your packages in
the subdirectory like this:

.. code-block:: ini

    # This example contains just the necessary options for a src-layout, set up
    # the rest of the file as described above.

    [options]
    package_dir=
        =src
    packages=find:

    [options.packages.find]
    where=src

Specifying values
=================

Some values are treated as simple strings, some allow more logic.

Type names used below:

* ``str`` - simple string
* ``list-comma`` - dangling list or string of comma-separated values
* ``list-semi`` - dangling list or string of semicolon-separated values
* ``bool`` - ``True`` is 1, yes, true
* ``dict`` - list-comma where keys are separated from values by ``=``
* ``section`` - values are read from a dedicated (sub)section


Special directives:

* ``attr:`` - Value is read from a module attribute.  ``attr:`` supports
  callables and iterables; unsupported types are cast using ``str()``.

  In order to support the common case of a literal value assigned to a variable
  in a module containing (directly or indirectly) third-party imports,
  ``attr:`` first tries to read the value from the module by examining the
  module's AST.  If that fails, ``attr:`` falls back to importing the module.

* ``file:`` - Value is read from a list of files and then concatenated

  .. note::
      The ``file:`` directive is sandboxed and won't reach anything outside
      the directory containing ``setup.py``.


Metadata
--------

.. note::
    The aliases given below are supported for compatibility reasons,
    but their use is not advised.

==============================  =================  =================  =============== ==========
Key                             Aliases            Type               Minimum Version Notes
==============================  =================  =================  =============== ==========
name                                               str
version                                            attr:, file:, str  39.2.0          [#meta-1]_
url                             home-page          str
download_url                    download-url       str
project_urls                                       dict               38.3.0
author                                             str
author_email                    author-email       str
maintainer                                         str
maintainer_email                maintainer-email   str
classifiers                     classifier         file:, list-comma
license                                            str
license_files                   license_file       list-comma         42.0.0
description                     summary            file:, str
long_description                long-description   file:, str
long_description_content_type                      str                38.6.0
keywords                                           list-comma
platforms                       platform           list-comma
provides                                           list-comma
requires                                           list-comma
obsoletes                                          list-comma
==============================  =================  =================  =============== ==========

**Notes**:

.. [#meta-1] The ``version`` file attribute has only been supported since 39.2.0.

   A version loaded using the ``file:`` directive must comply with PEP 440.
   It is easy to accidentally put something other than a valid version
   string in such a file, so validation is stricter in this case.


Options
-------

=======================  ===================================  =============== =========
Key                      Type                                 Minimum Version Notes
=======================  ===================================  =============== =========
zip_safe                 bool
setup_requires           list-semi                            36.7.0
install_requires         list-semi
extras_require           section                                              [#opt-2]_
python_requires          str                                  34.4.0
entry_points             file:, section                       51.0.0
scripts                  list-comma
eager_resources          list-comma
dependency_links         list-comma
tests_require            list-semi
include_package_data     bool
packages                 find:, find_namespace:, list-comma                   [#opt-3]_
package_dir              dict
package_data             section                                              [#opt-1]_
exclude_package_data     section
namespace_packages       list-comma
py_modules               list-comma                            34.4.0
data_files               dict                                 40.6.0
=======================  ===================================  =============== =========

**Notes**:

.. [#opt-1] In the ``package_data`` section, a key named with a single asterisk
   (``*``) refers to all packages, in lieu of the empty string used in ``setup.py``.
 
.. [#opt-2] In the ``extras_require`` section, values are parsed as ``list-semi``.
   This implies that in order to include markers, they **must** be *dangling*:
 
   .. code-block:: ini

      [options.extras_require]
      rest = docutils>=0.3; pack ==1.1, ==1.3
      pdf =
        ReportLab>=1.2
        RXP
        importlib-metadata; python_version < "3.8"

.. [#opt-3] The ``find:`` and ``find_namespace:`` directive can be further configured
   in a dedicated subsection ``options.packages.find``. This subsection accepts the
   same keys as the ``setuptools.find_packages`` and the
   ``setuptools.find_namespace_packages`` function:
   ``where``, ``include``, and ``exclude``.

   The ``find_namespace:`` directive is supported since Python >=3.3.


Compatibility with other tools
==============================

Historically, several tools explored declarative package configuration
in parallel. And several of them chose to place the packaging
configuration within the project's :file:`setup.cfg` file.
One of the first was ``distutils2``, which development has stopped in
2013. Other include ``pbr`` which is still under active development or
``d2to1``, which was a plug-in that backports declarative configuration
to ``distutils``, but has had no release since Oct. 2015.
As a way to harmonize packaging tools, ``setuptools``, having held the
position of *de facto* standard, has gradually integrated those
features as part of its core features.

Still this has lead to some confusion and feature incompatibilities:

- some tools support features others don't;
- some have similar features but the declarative syntax differs;

The table below tries to summarize the differences. But, please, refer
to each tool documentation for up-to-date information.

=========================== ========== ========== ===== ===
feature                     setuptools distutils2 d2to1 pbr
=========================== ========== ========== ===== ===
[metadata] description-file S          Y          Y     Y
[files]                     S          Y          Y     Y
entry_points                Y          Y          Y     S
[backwards_compat]          N          Y          Y     Y
=========================== ========== ========== ===== ===

Y: supported, N: unsupported, S: syntax differs (see
:ref:`above example<example-setup-config>`).

Also note that some features were only recently added to ``setuptools``.
Please refer to the previous sections to find out when.
PK��\��cߑ�<alt-python39-setuptools/docs/userguide/package_discovery.rstnu�[���.. _`package_discovery`:

========================================
Package Discovery and Namespace Package
========================================

.. note::
    a full specification for the keyword supplied to ``setup.cfg`` or
    ``setup.py`` can be found at :doc:`keywords reference <keywords>`

.. note::
    the examples provided here are only to demonstrate the functionality
    introduced. More metadata and options arguments need to be supplied
    if you want to replicate them on your system. If you are completely
    new to setuptools, the :doc:`quickstart section <quickstart>` is a good
    place to start.

``Setuptools`` provide powerful tools to handle package discovery, including
support for namespace package. Normally, you would specify the package to be
included manually in the following manner:

.. tab:: setup.cfg

    .. code-block:: ini

        [options]
        #...
        packages =
            mypkg1
            mypkg2

.. tab:: setup.py

    .. code-block:: python

        setup(
            # ...
            packages=['mypkg1', 'mypkg2']
        )

This can get tiresome really quickly. To speed things up, we introduce two
functions provided by setuptools:

.. tab:: setup.cfg

    .. code-block:: ini

        [options]
        packages = find:
        #or
        packages = find_namespace:

.. tab:: setup.py

    .. code-block:: python

        from setuptools import find_packages

        # or
        from setuptools import find_namespace_packages


Using ``find:`` or ``find_packages``
====================================
Let's start with the first tool. ``find:`` (``find_packages``) takes a source
directory and two lists of package name patterns to exclude and include, and
then return a list of ``str`` representing the packages it could find. To use
it, consider the following directory

.. code-block:: bash

    mypkg/
        src/
            pkg1/__init__.py
            pkg2/__init__.py
            additional/__init__.py

        setup.cfg #or setup.py

To have your setup.cfg or setup.py to automatically include packages found
in ``src`` that starts with the name ``pkg`` and not ``additional``:

.. tab:: setup.cfg

    .. code-block:: ini

        [options]
        packages = find:
        package_dir =
            =src

        [options.packages.find]
        where = src
        include = pkg*
        exclude = additional

.. tab:: setup.py

    .. code-block:: python

        setup(
            # ...
            packages=find_packages(
                where='src',
                include=['pkg*'],
                exclude=['additional'],
            ),
            package_dir={"": "src"}
            # ...
        )


.. _Namespace Packages:

Using ``find_namespace:`` or ``find_namespace_packages``
========================================================
``setuptools``  provides the ``find_namespace:`` (``find_namespace_packages``)
which behaves similarly to ``find:`` but works with namespace package. Before
diving in, it is important to have a good understanding of what namespace
packages are. Here is a quick recap:

Suppose you have two packages named as follows:

.. code-block:: bash

    /Users/Desktop/timmins/foo/__init__.py
    /Library/timmins/bar/__init__.py

If both ``Desktop`` and ``Library`` are on your ``PYTHONPATH``, then a
namespace package called ``timmins`` will be created automatically for you when
you invoke the import mechanism, allowing you to accomplish the following

.. code-block:: pycon

    >>> import timmins.foo
    >>> import timmins.bar

as if there is only one ``timmins`` on your system. The two packages can then
be distributed separately and installed individually without affecting the
other one. Suppose you are packaging the ``foo`` part:

.. code-block:: bash

    foo/
        src/
            timmins/foo/__init__.py
        setup.cfg # or setup.py

and you want the ``foo`` to be automatically included, ``find:`` won't work
because timmins doesn't contain ``__init__.py`` directly, instead, you have
to use ``find_namespace:``:

.. code-block:: ini

    [options]
    package_dir =
        =src
    packages = find_namespace:

    [options.packages.find]
    where = src

When you install the zipped distribution, ``timmins.foo`` would become
available to your interpreter.

You can think of ``find_namespace:`` as identical to ``find:`` except it
would count a directory as a package even if it doesn't contain ``__init__.py``
file directly. As a result, this creates an interesting side effect. If you
organize your package like this:

.. code-block:: bash

    foo/
        timmins/
            foo/__init__.py
        setup.cfg # or setup.py
        tests/
            test_foo/__init__.py

a naive ``find_namespace:`` would include tests as part of your package to
be installed. A simple way to fix it is to adopt the aforementioned
``src`` layout.


Legacy Namespace Packages
=========================
The fact you can create namespace package so effortlessly above is credited
to `PEP 420 <https://www.python.org/dev/peps/pep-0420/>`_. It use to be more
cumbersome to accomplish the same result. Historically, there were two methods
to create namespace packages. One is the ``pkg_resources`` style supported by
``setuptools`` and the other one being ``pkgutils`` style offered by
``pkgutils`` module in Python. Both are now considered deprecated despite the
fact they still linger in many existing packages. These two differ in many
subtle yet significant aspects and you can find out more on `Python packaging
user guide <https://packaging.python.org/guides/packaging-namespace-packages/>`_


``pkg_resource`` style namespace package
----------------------------------------
This is the method ``setuptools`` directly supports. Starting with the same
layout, there are two pieces you need to add to it. First, an ``__init__.py``
file directly under your namespace package directory that contains the
following:

.. code-block:: python

    __import__("pkg_resources").declare_namespace(__name__)

And the ``namespace_packages`` keyword in your ``setup.cfg`` or ``setup.py``:

.. tab:: setup.cfg

    .. code-block:: ini

        [options]
        namespace_packages = timmins

.. tab:: setup.py

    .. code-block:: python

        setup(
            # ...
            namespace_packages=['timmins']
        )

And your directory should look like this

.. code-block:: bash

    /foo/
        src/
            timmins/
                __init__.py
                foo/__init__.py
        setup.cfg #or setup.py

Repeat the same for other packages and you can achieve the same result as
the previous section.

``pkgutil`` style namespace package
-----------------------------------
This method is almost identical to the ``pkg_resource`` except that the
``namespace_packages`` declaration is omitted and the ``__init__.py``
file contains the following:

.. code-block:: python

    __path__ = __import__('pkgutil').extend_path(__path__, __name__)

The project layout remains the same and ``setup.cfg`` remains the same.
PK��\�!�**4alt-python39-setuptools/docs/userguide/extension.rstnu�[���.. _Creating ``distutils`` Extensions:

Creating ``distutils`` Extensions
=================================

It can be hard to add new commands or setup arguments to the distutils.  But
the ``setuptools`` package makes it a bit easier, by allowing you to distribute
a distutils extension as a separate project, and then have projects that need
the extension just refer to it in their ``setup_requires`` argument.

With ``setuptools``, your distutils extension projects can hook in new
commands and ``setup()`` arguments just by defining "entry points".  These
are mappings from command or argument names to a specification of where to
import a handler from.  (See the section on :ref:`Dynamic Discovery of
Services and Plugins` above for some more background on entry points.)


Adding Commands
---------------

You can add new ``setup`` commands by defining entry points in the
``distutils.commands`` group.  For example, if you wanted to add a ``foo``
command, you might add something like this to your distutils extension
project's setup script::

    setup(
        # ...
        entry_points={
            "distutils.commands": [
                "foo = mypackage.some_module:foo",
            ],
        },
    )

(Assuming, of course, that the ``foo`` class in ``mypackage.some_module`` is
a ``setuptools.Command`` subclass.)

Once a project containing such entry points has been activated on ``sys.path``,
(e.g. by running "install" or "develop" with a site-packages installation
directory) the command(s) will be available to any ``setuptools``-based setup
scripts.  It is not necessary to use the ``--command-packages`` option or
to monkeypatch the ``distutils.command`` package to install your commands;
``setuptools`` automatically adds a wrapper to the distutils to search for
entry points in the active distributions on ``sys.path``.  In fact, this is
how setuptools' own commands are installed: the setuptools project's setup
script defines entry points for them!

Adding ``setup()`` Arguments
----------------------------

.. warning:: Adding arguments to setup is discouraged as such arguments
   are only supported through imperative execution and not supported through
   declarative config.

Sometimes, your commands may need additional arguments to the ``setup()``
call.  You can enable this by defining entry points in the
``distutils.setup_keywords`` group.  For example, if you wanted a ``setup()``
argument called ``bar_baz``, you might add something like this to your
distutils extension project's setup script::

    setup(
        # ...
        entry_points={
            "distutils.commands": [
                "foo = mypackage.some_module:foo",
            ],
            "distutils.setup_keywords": [
                "bar_baz = mypackage.some_module:validate_bar_baz",
            ],
        },
    )

The idea here is that the entry point defines a function that will be called
to validate the ``setup()`` argument, if it's supplied.  The ``Distribution``
object will have the initial value of the attribute set to ``None``, and the
validation function will only be called if the ``setup()`` call sets it to
a non-None value.  Here's an example validation function::

    def assert_bool(dist, attr, value):
        """Verify that value is True, False, 0, or 1"""
        if bool(value) != value:
            raise DistutilsSetupError(
                "%r must be a boolean value (got %r)" % (attr,value)
            )

Your function should accept three arguments: the ``Distribution`` object,
the attribute name, and the attribute value.  It should raise a
``DistutilsSetupError`` (from the ``distutils.errors`` module) if the argument
is invalid.  Remember, your function will only be called with non-None values,
and the default value of arguments defined this way is always None.  So, your
commands should always be prepared for the possibility that the attribute will
be ``None`` when they access it later.

If more than one active distribution defines an entry point for the same
``setup()`` argument, *all* of them will be called.  This allows multiple
distutils extensions to define a common argument, as long as they agree on
what values of that argument are valid.

Also note that as with commands, it is not necessary to subclass or monkeypatch
the distutils ``Distribution`` class in order to add your arguments; it is
sufficient to define the entry points in your extension, as long as any setup
script using your extension lists your project in its ``setup_requires``
argument.


Customizing Distribution Options
--------------------------------

Plugins may wish to extend or alter the options on a Distribution object to
suit the purposes of that project. For example, a tool that infers the
``Distribution.version`` from SCM-metadata may need to hook into the
option finalization. To enable this feature, Setuptools offers an entry
point "setuptools.finalize_distribution_options". That entry point must
be a callable taking one argument (the Distribution instance).

If the callable has an ``.order`` property, that value will be used to
determine the order in which the hook is called. Lower numbers are called
first and the default is zero (0).

Plugins may read, alter, and set properties on the distribution, but each
plugin is encouraged to load the configuration/settings for their behavior
independently.


.. _Adding new EGG-INFO Files:

Adding new EGG-INFO Files
-------------------------

Some extensible applications or frameworks may want to allow third parties to
develop plugins with application or framework-specific metadata included in
the plugins' EGG-INFO directory, for easy access via the ``pkg_resources``
metadata API.  The easiest way to allow this is to create a distutils extension
to be used from the plugin projects' setup scripts (via ``setup_requires``)
that defines a new setup keyword, and then uses that data to write an EGG-INFO
file when the ``egg_info`` command is run.

The ``egg_info`` command looks for extension points in an ``egg_info.writers``
group, and calls them to write the files.  Here's a simple example of a
distutils extension defining a setup argument ``foo_bar``, which is a list of
lines that will be written to ``foo_bar.txt`` in the EGG-INFO directory of any
project that uses the argument::

    setup(
        # ...
        entry_points={
            "distutils.setup_keywords": [
                "foo_bar = setuptools.dist:assert_string_list",
            ],
            "egg_info.writers": [
                "foo_bar.txt = setuptools.command.egg_info:write_arg",
            ],
        },
    )

This simple example makes use of two utility functions defined by setuptools
for its own use: a routine to validate that a setup keyword is a sequence of
strings, and another one that looks up a setup argument and writes it to
a file.  Here's what the writer utility looks like::

    def write_arg(cmd, basename, filename):
        argname = os.path.splitext(basename)[0]
        value = getattr(cmd.distribution, argname, None)
        if value is not None:
            value = "\n".join(value) + "\n"
        cmd.write_or_delete_file(argname, filename, value)

As you can see, ``egg_info.writers`` entry points must be a function taking
three arguments: a ``egg_info`` command instance, the basename of the file to
write (e.g. ``foo_bar.txt``), and the actual full filename that should be
written to.

In general, writer functions should honor the command object's ``dry_run``
setting when writing files, and use the ``distutils.log`` object to do any
console output.  The easiest way to conform to this requirement is to use
the ``cmd`` object's ``write_file()``, ``delete_file()``, and
``write_or_delete_file()`` methods exclusively for your file operations.  See
those methods' docstrings for more details.


.. _Adding Support for Revision Control Systems:

Adding Support for Revision Control Systems
-------------------------------------------------

If the files you want to include in the source distribution are tracked using
Git, Mercurial or SVN, you can use the following packages to achieve that:

- Git and Mercurial: `setuptools_scm <https://pypi.org/project/setuptools_scm/>`_
- SVN: `setuptools_svn <https://pypi.org/project/setuptools_svn/>`_

If you would like to create a plugin for ``setuptools`` to find files tracked
by another revision control system, you can do so by adding an entry point to
the ``setuptools.file_finders`` group.  The entry point should be a function
accepting a single directory name, and should yield all the filenames within
that directory (and any subdirectories thereof) that are under revision
control.

For example, if you were going to create a plugin for a revision control system
called "foobar", you would write a function something like this:

.. code-block:: python

    def find_files_for_foobar(dirname):
        ...  # loop to yield paths that start with `dirname`

And you would register it in a setup script using something like this::

    entry_points={
        "setuptools.file_finders": [
            "foobar = my_foobar_module:find_files_for_foobar",
        ]
    }

Then, anyone who wants to use your plugin can simply install it, and their
local setuptools installation will be able to find the necessary files.

It is not necessary to distribute source control plugins with projects that
simply use the other source control system, or to specify the plugins in
``setup_requires``.  When you create a source distribution with the ``sdist``
command, setuptools automatically records what files were found in the
``SOURCES.txt`` file.  That way, recipients of source distributions don't need
to have revision control at all.  However, if someone is working on a package
by checking out with that system, they will need the same plugin(s) that the
original author is using.

A few important points for writing revision control file finders:

* Your finder function MUST return relative paths, created by appending to the
  passed-in directory name.  Absolute paths are NOT allowed, nor are relative
  paths that reference a parent directory of the passed-in directory.

* Your finder function MUST accept an empty string as the directory name,
  meaning the current directory.  You MUST NOT convert this to a dot; just
  yield relative paths.  So, yielding a subdirectory named ``some/dir`` under
  the current directory should NOT be rendered as ``./some/dir`` or
  ``/somewhere/some/dir``, but *always* as simply ``some/dir``

* Your finder function SHOULD NOT raise any errors, and SHOULD deal gracefully
  with the absence of needed programs (i.e., ones belonging to the revision
  control system itself.  It *may*, however, use ``distutils.log.warn()`` to
  inform the user of the missing program(s).
PK��\C���"�"3alt-python39-setuptools/docs/userguide/keywords.rstnu�[���New and Changed ``setup()`` Keywords
====================================

The following keyword arguments to ``setup()`` are added or changed by
``setuptools``.  All of them are optional; you do not have to supply them
unless you need the associated ``setuptools`` feature.

``include_package_data``
    If set to ``True``, this tells ``setuptools`` to automatically include any
    data files it finds inside your package directories that are specified by
    your ``MANIFEST.in`` file.  For more information, see the section on
    :ref:`Including Data Files`.

``exclude_package_data``
    A dictionary mapping package names to lists of glob patterns that should
    be *excluded* from your package directories.  You can use this to trim back
    any excess files included by ``include_package_data``.  For a complete
    description and examples, see the section on :ref:`Including Data Files`.

``package_data``
    A dictionary mapping package names to lists of glob patterns.  For a
    complete description and examples, see the section on :ref:`Including
    Data Files`.  You do not need to use this option if you are using
    ``include_package_data``, unless you need to add e.g. files that are
    generated by your setup script and build process.  (And are therefore not
    in source control or are files that you don't want to include in your
    source distribution.)

``zip_safe``
    A boolean (True or False) flag specifying whether the project can be
    safely installed and run from a zip file.  If this argument is not
    supplied, the ``bdist_egg`` command will have to analyze all of your
    project's contents for possible problems each time it builds an egg.

``install_requires``
    A string or list of strings specifying what other distributions need to
    be installed when this one is.  See the section on :ref:`Declaring
    Dependencies` for details and examples of the format of this argument.

``entry_points``
    A dictionary mapping entry point group names to strings or lists of strings
    defining the entry points.  Entry points are used to support dynamic
    discovery of services or plugins provided by a project.  See :ref:`Dynamic
    Discovery of Services and Plugins` for details and examples of the format
    of this argument.  In addition, this keyword is used to support
    :ref:`Automatic Script Creation <entry_points>`.

``extras_require``
    A dictionary mapping names of "extras" (optional features of your project)
    to strings or lists of strings specifying what other distributions must be
    installed to support those features.  See the section on :ref:`Declaring
    Dependencies` for details and examples of the format of this argument.

``python_requires``
    A string corresponding to a version specifier (as defined in PEP 440) for
    the Python version, used to specify the Requires-Python defined in PEP 345.

``setup_requires``
    A string or list of strings specifying what other distributions need to
    be present in order for the *setup script* to run.  ``setuptools`` will
    attempt to obtain these (using pip if available) before processing the
    rest of the setup script or commands.  This argument is needed if you
    are using distutils extensions as part of your build process; for
    example, extensions that process setup() arguments and turn them into
    EGG-INFO metadata files.

    (Note: projects listed in ``setup_requires`` will NOT be automatically
    installed on the system where the setup script is being run.  They are
    simply downloaded to the ./.eggs directory if they're not locally available
    already.  If you want them to be installed, as well as being available
    when the setup script is run, you should add them to ``install_requires``
    **and** ``setup_requires``.)

``dependency_links``
    A list of strings naming URLs to be searched when satisfying dependencies.
    These links will be used if needed to install packages specified by
    ``setup_requires`` or ``tests_require``.  They will also be written into
    the egg's metadata for use during install by tools that support them.

``namespace_packages``
    A list of strings naming the project's "namespace packages".  A namespace
    package is a package that may be split across multiple project
    distributions.  For example, Zope 3's ``zope`` package is a namespace
    package, because subpackages like ``zope.interface`` and ``zope.publisher``
    may be distributed separately.  The egg runtime system can automatically
    merge such subpackages into a single parent package at runtime, as long
    as you declare them in each project that contains any subpackages of the
    namespace package, and as long as the namespace package's ``__init__.py``
    does not contain any code other than a namespace declaration.  See the
    section below on :ref:`Namespace Packages` for more information.

``test_suite``
    A string naming a ``unittest.TestCase`` subclass (or a package or module
    containing one or more of them, or a method of such a subclass), or naming
    a function that can be called with no arguments and returns a
    ``unittest.TestSuite``.  If the named suite is a module, and the module
    has an ``additional_tests()`` function, it is called and the results are
    added to the tests to be run.  If the named suite is a package, any
    submodules and subpackages are recursively added to the overall test suite.

    Specifying this argument enables use of the :ref:`test <test>` command to run the
    specified test suite, e.g. via ``setup.py test``.  See the section on the
    :ref:`test <test>` command below for more details.

    New in 41.5.0: Deprecated the test command.

``tests_require``
    If your project's tests need one or more additional packages besides those
    needed to install it, you can use this option to specify them.  It should
    be a string or list of strings specifying what other distributions need to
    be present for the package's tests to run.  When you run the ``test``
    command, ``setuptools`` will  attempt to obtain these (using pip if
    available).  Note that these required projects will *not* be installed on
    the system where the tests are run, but only downloaded to the project's setup
    directory if they're not already installed locally.

    New in 41.5.0: Deprecated the test command.

.. _test_loader:

``test_loader``
    If you would like to use a different way of finding tests to run than what
    setuptools normally uses, you can specify a module name and class name in
    this argument.  The named class must be instantiable with no arguments, and
    its instances must support the ``loadTestsFromNames()`` method as defined
    in the Python ``unittest`` module's ``TestLoader`` class.  Setuptools will
    pass only one test "name" in the ``names`` argument: the value supplied for
    the ``test_suite`` argument.  The loader you specify may interpret this
    string in any way it likes, as there are no restrictions on what may be
    contained in a ``test_suite`` string.

    The module name and class name must be separated by a ``:``.  The default
    value of this argument is ``"setuptools.command.test:ScanningLoader"``.  If
    you want to use the default ``unittest`` behavior, you can specify
    ``"unittest:TestLoader"`` as your ``test_loader`` argument instead.  This
    will prevent automatic scanning of submodules and subpackages.

    The module and class you specify here may be contained in another package,
    as long as you use the ``tests_require`` option to ensure that the package
    containing the loader class is available when the ``test`` command is run.

    New in 41.5.0: Deprecated the test command.

``eager_resources``
    A list of strings naming resources that should be extracted together, if
    any of them is needed, or if any C extensions included in the project are
    imported.  This argument is only useful if the project will be installed as
    a zipfile, and there is a need to have all of the listed resources be
    extracted to the filesystem *as a unit*.  Resources listed here
    should be "/"-separated paths, relative to the source root, so to list a
    resource ``foo.png`` in package ``bar.baz``, you would include the string
    ``bar/baz/foo.png`` in this argument.

    If you only need to obtain resources one at a time, or you don't have any C
    extensions that access other files in the project (such as data files or
    shared libraries), you probably do NOT need this argument and shouldn't
    mess with it.  For more details on how this argument works, see the section
    below on :ref:`Automatic Resource Extraction`.

``project_urls``
    An arbitrary map of URL names to hyperlinks, allowing more extensible
    documentation of where various resources can be found than the simple
    ``url`` and ``download_url`` options provide.
PK��\4/o�*�*@alt-python39-setuptools/docs/userguide/dependency_management.rstnu�[���=====================================
Dependencies Management in Setuptools
=====================================

There are three types of dependency styles offered by setuptools:
1) build system requirement, 2) required dependency and 3) optional
dependency.

.. Note::
    Packages that are added to dependency can be optionally specified with the
    version by following `PEP 440 <https://www.python.org/dev/peps/pep-0440/>`_


Build system requirement
========================

Package requirement
-------------------
After organizing all the scripts and files and getting ready for packaging,
there needs to be a way to tell Python what programs it needs to actually
do the packaging (in our case, ``setuptools`` of course). Usually,
you also need the ``wheel`` package as well since it is recommended that you
upload a ``.whl`` file to PyPI alongside your ``.tar.gz`` file. Unlike the
other two types of dependency keyword, this one is specified in your
``pyproject.toml`` file (if you have forgot what this is, go to
:doc:`quickstart` or (WIP)):

.. code-block:: ini

    [build-system]
    requires = ["setuptools", "wheel"]
    #...

.. note::
    This used to be accomplished with the ``setup_requires`` keyword but is
    now considered deprecated in favor of the PEP 517 style described above.
    To peek into how this legacy keyword is used, consult our :doc:`guide on
    deprecated practice (WIP) <../deprecated/index>`


.. _Declaring Dependencies:

Declaring required dependency
=============================
This is where a package declares its core dependencies, without which it won't
be able to run. ``setuptools`` support automatically download and install
these dependencies when the package is installed. Although there is more
finesse to it, let's start with a simple example.

.. tab:: setup.cfg

    .. code-block:: ini

        [options]
        #...
        install_requires =
            docutils
            BazSpam ==1.1

.. tab:: setup.py

    .. code-block:: python

        setup(
            ...,
            install_requires=[
                'docutils',
                'BazSpam ==1.1',
            ],
        )


When your project is installed (e.g. using pip), all of the dependencies not
already installed will be located (via PyPI), downloaded, built (if necessary),
and installed and 2) Any scripts in your project will be installed with wrappers
that verify the availability of the specified dependencies at runtime.


Platform specific dependencies
------------------------------
Setuptools offer the capability to evaluate certain conditions before blindly
installing everything listed in ``install_requires``. This is great for platform
specific dependencies. For example, the ``enum`` package was added in Python
3.4, therefore, package that depends on it can elect to install it only when
the Python version is older than 3.4. To accomplish this

.. tab:: setup.cfg

    .. code-block:: ini

        [options]
        #...
        install_requires =
            enum34;python_version<'3.4'

.. tab:: setup.py

    .. code-block:: python

        setup(
            ...,
            install_requires=[
                "enum34;python_version<'3.4'",
            ],
        )

Similarly, if you also wish to declare ``pywin32`` with a minimal version of 1.0
and only install it if the user is using a Windows operating system:

.. tab:: setup.cfg

    .. code-block:: ini

        [options]
        #...
        install_requires =
            enum34;python_version<'3.4'
            pywin32 >= 1.0;platform_system=='Windows'

.. tab:: setup.py

    .. code-block:: python

        setup(
            ...,
            install_requires=[
                "enum34;python_version<'3.4'",
                "pywin32 >= 1.0;platform_system=='Windows'",
            ],
        )

The environmental markers that may be used for testing platform types are
detailed in `PEP 508 <https://www.python.org/dev/peps/pep-0508/>`_.


Dependencies that aren't in PyPI
--------------------------------
.. warning::
    Dependency links support has been dropped by pip starting with version
    19.0 (released 2019-01-22).

If your project depends on packages that don't exist on PyPI, you may still be
able to depend on them, as long as they are available for download as:

- an egg, in the standard distutils ``sdist`` format,
- a single ``.py`` file, or
- a VCS repository (Subversion, Mercurial, or Git).

You just need to add some URLs to the ``dependency_links`` argument to
``setup()``.

The URLs must be either:

1. direct download URLs,
2. the URLs of web pages that contain direct download links, or
3. the repository's URL

In general, it's better to link to web pages, because it is usually less
complex to update a web page than to release a new version of your project.
You can also use a SourceForge ``showfiles.php`` link in the case where a
package you depend on is distributed via SourceForge.

If you depend on a package that's distributed as a single ``.py`` file, you
must include an ``"#egg=project-version"`` suffix to the URL, to give a project
name and version number.  (Be sure to escape any dashes in the name or version
by replacing them with underscores.)  EasyInstall will recognize this suffix
and automatically create a trivial ``setup.py`` to wrap the single ``.py`` file
as an egg.

In the case of a VCS checkout, you should also append ``#egg=project-version``
in order to identify for what package that checkout should be used. You can
append ``@REV`` to the URL's path (before the fragment) to specify a revision.
Additionally, you can also force the VCS being used by prepending the URL with
a certain prefix. Currently available are:

-  ``svn+URL`` for Subversion,
-  ``git+URL`` for Git, and
-  ``hg+URL`` for Mercurial

A more complete example would be:

    ``vcs+proto://host/path@revision#egg=project-version``

Be careful with the version. It should match the one inside the project files.
If you want to disregard the version, you have to omit it both in the
``requires`` and in the URL's fragment.

This will do a checkout (or a clone, in Git and Mercurial parlance) to a
temporary folder and run ``setup.py bdist_egg``.

The ``dependency_links`` option takes the form of a list of URL strings.  For
example, this will cause a search of the specified page for eggs or source
distributions, if the package's dependencies aren't already installed:

.. tab:: setup.cfg

    .. code-block:: ini

        [options]
        #...
        dependency_links = http://peak.telecommunity.com/snapshots/

.. tab:: setup.py

    .. code-block:: python

        setup(
            ...,
            dependency_links=[
                "http://peak.telecommunity.com/snapshots/",
            ],
        )


Optional dependencies
=====================
Setuptools allows you to declare dependencies that only get installed under
specific circumstances. These dependencies are specified with ``extras_require``
keyword and are only installed if another package depends on it (either
directly or indirectly) This makes it convenient to declare dependencies for
ancillary functions such as "tests" and "docs".

.. note::
    ``tests_require`` is now deprecated

For example, Package-A offers optional PDF support and requires two other
dependencies for it to work:

.. tab:: setup.cfg

    .. code-block:: ini

        [metadata]
        name = Package-A

        [options.extras_require]
        PDF = ReportLab>=1.2; RXP


.. tab:: setup.py

    .. code-block:: python

        setup(
            name="Project-A",
            ...,
            extras_require={
                "PDF": ["ReportLab>=1.2", "RXP"],
            },
        )

The name ``PDF`` is an arbitrary identifier of such a list of dependencies, to
which other components can refer and have them installed. There are two common
use cases.

First is the console_scripts entry point:

.. tab:: setup.cfg

    .. code-block:: ini

        [metadata]
        name = Project A
        #...

        [options]
        #...
        entry_points=
            [console_scripts]
            rst2pdf = project_a.tools.pdfgen [PDF]
            rst2html = project_a.tools.htmlgen

.. tab:: setup.py

    .. code-block:: python

        setup(
            name="Project-A",
            ...,
            entry_points={
                "console_scripts": [
                    "rst2pdf = project_a.tools.pdfgen [PDF]",
                    "rst2html = project_a.tools.htmlgen",
                ],
            },
        )

This syntax indicates that the entry point (in this case a console script)
is only valid when the PDF extra is installed. It is up to the installer
to determine how to handle the situation where PDF was not indicated
(e.g. omit the console script, provide a warning when attempting to load
the entry point, assume the extras are present and let the implementation
fail later).

The second use case is that other package can use this "extra" for their
own dependencies. For example, if "Project-B" needs "project A" with PDF support
installed, it might declare the dependency like this:

.. tab:: setup.cfg

    .. code-block:: ini

        [metadata]
        name = Project-B
        #...

        [options]
        #...
        install_requires =
            Project-A[PDF]

.. tab:: setup.py

    .. code-block:: python

        setup(
            name="Project-B",
            install_requires=["Project-A[PDF]"],
            ...,
        )

This will cause ReportLab to be installed along with project A, if project B is
installed -- even if project A was already installed.  In this way, a project
can encapsulate groups of optional "downstream dependencies" under a feature
name, so that packages that depend on it don't have to know what the downstream
dependencies are.  If a later version of Project A builds in PDF support and
no longer needs ReportLab, or if it ends up needing other dependencies besides
ReportLab in order to provide PDF support, Project B's setup information does
not need to change, but the right packages will still be installed if needed.

.. note::
    Best practice: if a project ends up not needing any other packages to
    support a feature, it should keep an empty requirements list for that feature
    in its ``extras_require`` argument, so that packages depending on that feature
    don't break (due to an invalid feature name).


Python requirement
==================
In some cases, you might need to specify the minimum required python version.
This is handled with the ``python_requires`` keyword supplied to ``setup.cfg``
or ``setup.py``.


.. tab:: setup.cfg

    .. code-block:: ini

        [metadata]
        name = Project-B
        #...

        [options]
        #...
        python_requires = >=3.6

.. tab:: setup.py

    .. code-block:: python

        setup(
            name="Project-B",
            python_requires=[">=3.6"],
            ...,
        )
PK��\Ѩ���1�17alt-python39-setuptools/docs/userguide/distribution.rstnu�[���Tagging and "Daily Build" or "Snapshot" Releases
------------------------------------------------

When a set of related projects are under development, it may be important to
track finer-grained version increments than you would normally use for e.g.
"stable" releases.  While stable releases might be measured in dotted numbers
with alpha/beta/etc. status codes, development versions of a project often
need to be tracked by revision or build number or even build date.  This is
especially true when projects in development need to refer to one another, and
therefore may literally need an up-to-the-minute version of something!

To support these scenarios, ``setuptools`` allows you to "tag" your source and
egg distributions by adding one or more of the following to the project's
"official" version identifier:

* A manually-specified pre-release tag, such as "build" or "dev", or a
  manually-specified post-release tag, such as a build or revision number
  (``--tag-build=STRING, -bSTRING``)

* An 8-character representation of the build date (``--tag-date, -d``), as
  a postrelease tag

You can add these tags by adding ``egg_info`` and the desired options to
the command line ahead of the ``sdist`` or ``bdist`` commands that you want
to generate a daily build or snapshot for.  See the section below on the
:ref:`egg_info <egg_info>` command for more details.

(Also, before you release your project, be sure to see the section on
:ref:`Specifying Your Project's Version` for more information about how pre- and
post-release tags affect how version numbers are interpreted.  This is
important in order to make sure that dependency processing tools will know
which versions of your project are newer than others.)

Finally, if you are creating builds frequently, and either building them in a
downloadable location or are copying them to a distribution server, you should
probably also check out the :ref:`rotate <rotate>` command, which lets you automatically
delete all but the N most-recently-modified distributions matching a glob
pattern.  So, you can use a command line like::

    setup.py egg_info -rbDEV bdist_egg rotate -m.egg -k3

to build an egg whose version info includes "DEV-rNNNN" (where NNNN is the
most recent Subversion revision that affected the source tree), and then
delete any egg files from the distribution directory except for the three
that were built most recently.

If you have to manage automated builds for multiple packages, each with
different tagging and rotation policies, you may also want to check out the
:ref:`alias <alias>` command, which would let each package define an alias like ``daily``
that would perform the necessary tag, build, and rotate commands.  Then, a
simpler script or cron job could just run ``setup.py daily`` in each project
directory.  (And, you could also define sitewide or per-user default versions
of the ``daily`` alias, so that projects that didn't define their own would
use the appropriate defaults.)

Generating Source Distributions
-------------------------------

``setuptools`` enhances the distutils' default algorithm for source file
selection with pluggable endpoints for looking up files to include. If you are
using a revision control system, and your source distributions only need to
include files that you're tracking in revision control, use a corresponding
plugin instead of writing a ``MANIFEST.in`` file. See the section below on
:ref:`Adding Support for Revision Control Systems` for information on plugins.

If you need to include automatically generated files, or files that are kept in
an unsupported revision control system, you'll need to create a ``MANIFEST.in``
file to specify any files that the default file location algorithm doesn't
catch.  See the distutils documentation for more information on the format of
the ``MANIFEST.in`` file.

But, be sure to ignore any part of the distutils documentation that deals with
``MANIFEST`` or how it's generated from ``MANIFEST.in``; setuptools shields you
from these issues and doesn't work the same way in any case.  Unlike the
distutils, setuptools regenerates the source distribution manifest file
every time you build a source distribution, and it builds it inside the
project's ``.egg-info`` directory, out of the way of your main project
directory.  You therefore need not worry about whether it is up-to-date or not.

Indeed, because setuptools' approach to determining the contents of a source
distribution is so much simpler, its ``sdist`` command omits nearly all of
the options that the distutils' more complex ``sdist`` process requires.  For
all practical purposes, you'll probably use only the ``--formats`` option, if
you use any option at all.


Making "Official" (Non-Snapshot) Releases
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When you make an official release, creating source or binary distributions,
you will need to override the tag settings from ``setup.cfg``, so that you
don't end up registering versions like ``foobar-0.7a1.dev-r34832``.  This is
easy to do if you are developing on the trunk and using tags or branches for
your releases - just make the change to ``setup.cfg`` after branching or
tagging the release, so the trunk will still produce development snapshots.

Alternately, if you are not branching for releases, you can override the
default version options on the command line, using something like::

    setup.py egg_info -Db "" sdist bdist_egg

The first part of this command (``egg_info -Db ""``) will override the
configured tag information, before creating source and binary eggs. Thus, these
commands will use the plain version from your ``setup.py``, without adding the
build designation string.

Of course, if you will be doing this a lot, you may wish to create a personal
alias for this operation, e.g.::

    setup.py alias -u release egg_info -Db ""

You can then use it like this::

    setup.py release sdist bdist_egg

Or of course you can create more elaborate aliases that do all of the above.
See the sections below on the :ref:`egg_info <egg_info>` and
:ref:`alias <alias>` commands for more ideas.

Distributing Extensions compiled with Cython
--------------------------------------------

``setuptools`` will detect at build time whether Cython is installed or not.
If Cython is not found ``setuptools`` will ignore pyx files.

To ensure Cython is available, include Cython in the build-requires section
of your pyproject.toml::

    [build-system]
    requires=[..., "cython"]

Built with pip 10 or later, that declaration is sufficient to include Cython
in the build. For broader compatibility, declare the dependency in your
setup-requires of setup.cfg::

    [options]
    setup_requires =
        ...
        cython

As long as Cython is present in the build environment, ``setuptools`` includes
transparent support for building Cython extensions, as
long as extensions are defined using ``setuptools.Extension``.

If you follow these rules, you can safely list ``.pyx`` files as the source
of your ``Extension`` objects in the setup script.  If it is, then ``setuptools``
will use it.

Of course, for this to work, your source distributions must include the C
code generated by Cython, as well as your original ``.pyx`` files.  This means
that you will probably want to include current ``.c`` files in your revision
control system, rebuilding them whenever you check changes in for the ``.pyx``
source files.  This will ensure that people tracking your project in a revision
control system will be able to build it even if they don't have Cython
installed, and that your source releases will be similarly usable with or
without Cython.


.. _Specifying Your Project's Version:

Specifying Your Project's Version
---------------------------------

Setuptools can work well with most versioning schemes. Over the years,
setuptools has tried to closely follow the 
`PEP 440 <https://www.python.org/dev/peps/pep-0440/>`_ scheme, but it
also supports legacy versions. There are, however, a
few special things to watch out for, in order to ensure that setuptools and
other tools can always tell what version of your package is newer than another
version.  Knowing these things will also help you correctly specify what
versions of other projects your project depends on.

A version consists of an alternating series of release numbers and pre-release
or post-release tags.  A release number is a series of digits punctuated by
dots, such as ``2.4`` or ``0.5``.  Each series of digits is treated
numerically, so releases ``2.1`` and ``2.1.0`` are different ways to spell the
same release number, denoting the first subrelease of release 2.  But  ``2.10``
is the *tenth* subrelease of release 2, and so is a different and newer release
from ``2.1`` or ``2.1.0``.  Leading zeros within a series of digits are also
ignored, so ``2.01`` is the same as ``2.1``, and different from ``2.0.1``.

Following a release number, you can have either a pre-release or post-release
tag.  Pre-release tags make a version be considered *older* than the version
they are appended to.  So, revision ``2.4`` is *newer* than revision ``2.4c1``,
which in turn is newer than ``2.4b1`` or ``2.4a1``.  Postrelease tags make
a version be considered *newer* than the version they are appended to.  So,
revisions like ``2.4-1`` are newer than ``2.4``, but *older*
than ``2.4.1`` (which has a higher release number).

In the case of legacy versions (for example, ``2.4pl1``), they are considered
older than non-legacy versions. Taking that in count, a revision ``2.4pl1``
is *older* than ``2.4``

A pre-release tag is a series of letters that are alphabetically before
"final".  Some examples of prerelease tags would include ``alpha``, ``beta``,
``a``, ``c``, ``dev``, and so on.  You do not have to place a dot or dash
before the prerelease tag if it's immediately after a number, but it's okay to
do so if you prefer.  Thus, ``2.4c1`` and ``2.4.c1`` and ``2.4-c1`` all
represent release candidate 1 of version ``2.4``, and are treated as identical
by setuptools.

In addition, there are three special prerelease tags that are treated as if
they were the letter ``c``: ``pre``, ``preview``, and ``rc``.  So, version
``2.4rc1``, ``2.4pre1`` and ``2.4preview1`` are all the exact same version as
``2.4c1``, and are treated as identical by setuptools.

A post-release tag is either a series of letters that are alphabetically
greater than or equal to "final", or a dash (``-``).  Post-release tags are
generally used to separate patch numbers, port numbers, build numbers, revision
numbers, or date stamps from the release number.  For example, the version
``2.4-r1263`` might denote Subversion revision 1263 of a post-release patch of
version ``2.4``.  Or you might use ``2.4-20051127`` to denote a date-stamped
post-release.

Notice that after each pre or post-release tag, you are free to place another
release number, followed again by more pre- or post-release tags.  For example,
``0.6a9.dev-r41475`` could denote Subversion revision 41475 of the in-
development version of the ninth alpha of release 0.6.  Notice that ``dev`` is
a pre-release tag, so this version is a *lower* version number than ``0.6a9``,
which would be the actual ninth alpha of release 0.6.  But the ``-r41475`` is
a post-release tag, so this version is *newer* than ``0.6a9.dev``.

For the most part, setuptools' interpretation of version numbers is intuitive,
but here are a few tips that will keep you out of trouble in the corner cases:

* Don't stick adjoining pre-release tags together without a dot or number
  between them.  Version ``1.9adev`` is the ``adev`` prerelease of ``1.9``,
  *not* a development pre-release of ``1.9a``.  Use ``.dev`` instead, as in
  ``1.9a.dev``, or separate the prerelease tags with a number, as in
  ``1.9a0dev``.  ``1.9a.dev``, ``1.9a0dev``, and even ``1.9.a.dev`` are
  identical versions from setuptools' point of view, so you can use whatever
  scheme you prefer.

* If you want to be certain that your chosen numbering scheme works the way
  you think it will, you can use the ``pkg_resources.parse_version()`` function
  to compare different version numbers::

    >>> from pkg_resources import parse_version
    >>> parse_version("1.9.a.dev") == parse_version("1.9a0dev")
    True
    >>> parse_version("2.1-rc2") < parse_version("2.1")
    True
    >>> parse_version("0.6a9dev-r41475") < parse_version("0.6a9")
    True

Once you've decided on a version numbering scheme for your project, you can
have setuptools automatically tag your in-development releases with various
pre- or post-release tags.  See the following sections for more details:

* `Tagging and "Daily Build" or "Snapshot" Releases`_
* The :ref:`egg_info <egg_info>` command
PK��\\��ى�6alt-python39-setuptools/docs/userguide/entry_point.rstnu�[���.. _`entry_points`:

============
Entry Points
============

Packages may provide commands to be run at the console (console scripts),
such as the ``pip`` command. These commands are defined for a package
as a specific kind of entry point in the ``setup.cfg`` or
``setup.py``.


Console Scripts
===============

First consider an example without entry points. Imagine a package
defined thus:

.. code-block:: bash

    timmins/
        timmins/__init__.py
        timmins/__main__.py
        setup.cfg # or setup.py
        #other necessary files

with ``__init__.py`` as:

.. code-block:: python

    def hello_world():
        print("Hello world")

and ``__main__.py`` providing a hook:

.. code-block:: python

    from . import hello_world

    if __name__ == '__main__':
        hello_world()

After installing the package, the function may be invoked through the
`runpy <https://docs.python.org/3/library/runpy.html>`_ module:

.. code-block:: bash

    python -m timmins

Adding a console script entry point allows the package to define a
user-friendly name for installers of the package to execute. Installers
like pip will create wrapper scripts to execute a function. In the
above example, to create a command ``hello-world`` that invokes
``timmins.hello_world``, add a console script entry point to
``setup.cfg``:

.. code-block:: ini

    [options.entry_points]
    console_scripts =
        hello-world = timmins:hello_world

After installing the package, a user may invoke that function by simply calling
``hello-world`` on the command line.

The syntax for entry points is specified as follows:

.. code-block:: ini

    <name> = [<package>.[<subpackage>.]]<module>[:<object>.<object>]

where ``name`` is the name for the script you want to create, the left hand
side of ``:`` is the module that contains your function and the right hand
side is the object you want to invoke (e.g. a function).

In addition to ``console_scripts``, Setuptools supports ``gui_scripts``, which
will launch a GUI application without running in a terminal window.


.. _dynamic discovery of services and plugins:

Advertising Behavior
====================

Console scripts are one use of the more general concept of entry points. Entry
points more generally allow a packager to advertise behavior for discovery by
other libraries and applications. This feature enables "plug-in"-like
functionality, where one library solicits entry points and any number of other
libraries provide those entry points.

A good example of this plug-in behavior can be seen in
`pytest plugins <https://docs.pytest.org/en/latest/writing_plugins.html>`_,
where pytest is a test framework that allows other libraries to extend
or modify its functionality through the ``pytest11`` entry point.

The console scripts work similarly, where libraries advertise their commands
and tools like ``pip`` create wrapper scripts that invoke those commands.

For a project wishing to solicit entry points, Setuptools recommends the
`importlib.metadata <https://docs.python.org/3/library/importlib.metadata.html>`_
module (part of stdlib since Python 3.8) or its backport,
`importlib_metadata <https://pypi.org/project/importlib_metadata>`_.

For example, to find the console script entry points from the example above:

.. code-block:: pycon

    >>> from importlib import metadata
    >>> eps = metadata.entry_points()['console_scripts']

``eps`` is now a list of ``EntryPoint`` objects, one of which corresponds
to the ``hello-world = timmins:hello_world`` defined above. Each ``EntryPoint``
contains the ``name``, ``group``, and ``value``. It also supplies a ``.load()``
method to import and load that entry point (module or object).

.. code-block:: ini

    [options.entry_points]
    my.plugins =
        hello-world = timmins:hello_world

Then, a different project wishing to load 'my.plugins' plugins could run
the following routine to load (and invoke) such plugins:

.. code-block:: pycon

    >>> from importlib import metadata
    >>> eps = metadata.entry_points()['my.plugins']
    >>> for ep in eps:
    ...     plugin = ep.load()
    ...     plugin()
    ...

The project soliciting the entry points needs not to have any dependency
or prior knowledge about the libraries implementing the entry points, and
downstream users are able to compose functionality by pulling together
libraries implementing the entry points.


Dependency Management
=====================

Some entry points may require additional dependencies to properly function.
For such an entry point, declare in square brackets any number of dependency
``extras`` following the entry point definition. Such entry points will only
be viable if their extras were declared and installed. See the
:doc:`guide on dependencies management <dependency_management>` for
more information on defining extra requirements. Consider from the
above example:

.. code-block:: ini

    [options.entry_points]
    console_scripts =
        hello-world = timmins:hello_world [pretty-printer]

In this case, the ``hello-world`` script is only viable if the ``pretty-printer``
extra is indicated, and so a plugin host might exclude that entry point
(i.e. not install a console script) if the relevant extra dependencies are not
installed.
PK��\'��*VVBalt-python39-setuptools/docs/userguide/functionalities_rewrite.rstnu�[���========================================================
Using setuptools to package and distribute your project
========================================================

``setuptools`` offers a variety of functionalities that make it easy to
build and distribute your python package. Here we provide an overview on
the commonly used ones.


PK��\���@�j�j3alt-python39-setuptools/docs/userguide/commands.rstnu�[���-----------------
Command Reference
-----------------

.. _alias:

``alias`` - Define shortcuts for commonly used commands
=======================================================

Sometimes, you need to use the same commands over and over, but you can't
necessarily set them as defaults.  For example, if you produce both development
snapshot releases and "stable" releases of a project, you may want to put
the distributions in different places, or use different ``egg_info`` tagging
options, etc.  In these cases, it doesn't make sense to set the options in
a distutils configuration file, because the values of the options changed based
on what you're trying to do.

Setuptools therefore allows you to define "aliases" - shortcut names for
an arbitrary string of commands and options, using ``setup.py alias aliasname
expansion``, where aliasname is the name of the new alias, and the remainder of
the command line supplies its expansion.  For example, this command defines
a sitewide alias called "daily", that sets various ``egg_info`` tagging
options::

    setup.py alias --global-config daily egg_info --tag-build=development

Once the alias is defined, it can then be used with other setup commands,
e.g.::

    setup.py daily bdist_egg        # generate a daily-build .egg file
    setup.py daily sdist            # generate a daily-build source distro
    setup.py daily sdist bdist_egg  # generate both

The above commands are interpreted as if the word ``daily`` were replaced with
``egg_info --tag-build=development``.

Note that setuptools will expand each alias *at most once* in a given command
line.  This serves two purposes.  First, if you accidentally create an alias
loop, it will have no effect; you'll instead get an error message about an
unknown command.  Second, it allows you to define an alias for a command, that
uses that command.  For example, this (project-local) alias::

    setup.py alias bdist_egg bdist_egg rotate -k1 -m.egg

redefines the ``bdist_egg`` command so that it always runs the ``rotate``
command afterwards to delete all but the newest egg file.  It doesn't loop
indefinitely on ``bdist_egg`` because the alias is only expanded once when
used.

You can remove a defined alias with the ``--remove`` (or ``-r``) option, e.g.::

    setup.py alias --global-config --remove daily

would delete the "daily" alias we defined above.

Aliases can be defined on a project-specific, per-user, or sitewide basis.  The
default is to define or remove a project-specific alias, but you can use any of
the `configuration file options`_ (listed under the `saveopts`_ command, below)
to determine which distutils configuration file an aliases will be added to
(or removed from).

Note that if you omit the "expansion" argument to the ``alias`` command,
you'll get output showing that alias' current definition (and what
configuration file it's defined in).  If you omit the alias name as well,
you'll get a listing of all current aliases along with their configuration
file locations.


``bdist_egg`` - Create a Python Egg for the project
===================================================

.. warning::
    **eggs** are deprecated in favor of wheels, and not supported by pip.

This command generates a Python Egg (``.egg`` file) for the project.  Python
Eggs are the preferred binary distribution format for EasyInstall, because they
are cross-platform (for "pure" packages), directly importable, and contain
project metadata including scripts and information about the project's
dependencies.  They can be simply downloaded and added to ``sys.path``
directly, or they can be placed in a directory on ``sys.path`` and then
automatically discovered by the egg runtime system.

This command runs the `egg_info`_ command (if it hasn't already run) to update
the project's metadata (``.egg-info``) directory.  If you have added any extra
metadata files to the ``.egg-info`` directory, those files will be included in
the new egg file's metadata directory, for use by the egg runtime system or by
any applications or frameworks that use that metadata.

You won't usually need to specify any special options for this command; just
use ``bdist_egg`` and you're done.  But there are a few options that may
be occasionally useful:

``--dist-dir=DIR, -d DIR``
    Set the directory where the ``.egg`` file will be placed.  If you don't
    supply this, then the ``--dist-dir`` setting of the ``bdist`` command
    will be used, which is usually a directory named ``dist`` in the project
    directory.

``--plat-name=PLATFORM, -p PLATFORM``
    Set the platform name string that will be embedded in the egg's filename
    (assuming the egg contains C extensions).  This can be used to override
    the distutils default platform name with something more meaningful.  Keep
    in mind, however, that the egg runtime system expects to see eggs with
    distutils platform names, so it may ignore or reject eggs with non-standard
    platform names.  Similarly, the EasyInstall program may ignore them when
    searching web pages for download links.  However, if you are
    cross-compiling or doing some other unusual things, you might find a use
    for this option.

``--exclude-source-files``
    Don't include any modules' ``.py`` files in the egg, just compiled Python,
    C, and data files.  (Note that this doesn't affect any ``.py`` files in the
    EGG-INFO directory or its subdirectories, since for example there may be
    scripts with a ``.py`` extension which must still be retained.)  We don't
    recommend that you use this option except for packages that are being
    bundled for proprietary end-user applications, or for "embedded" scenarios
    where space is at an absolute premium.  On the other hand, if your package
    is going to be installed and used in compressed form, you might as well
    exclude the source because Python's ``traceback`` module doesn't currently
    understand how to display zipped source code anyway, or how to deal with
    files that are in a different place from where their code was compiled.

There are also some options you will probably never need, but which are there
because they were copied from similar ``bdist`` commands used as an example for
creating this one.  They may be useful for testing and debugging, however,
which is why we kept them:

``--keep-temp, -k``
    Keep the contents of the ``--bdist-dir`` tree around after creating the
    ``.egg`` file.

``--bdist-dir=DIR, -b DIR``
    Set the temporary directory for creating the distribution.  The entire
    contents of this directory are zipped to create the ``.egg`` file, after
    running various installation commands to copy the package's modules, data,
    and extensions here.

``--skip-build``
    Skip doing any "build" commands; just go straight to the
    install-and-compress phases.


.. _develop:

``develop`` - Deploy the project source in "Development Mode"
=============================================================

This command allows you to deploy your project's source for use in one or more
"staging areas" where it will be available for importing.  This deployment is
done in such a way that changes to the project source are immediately available
in the staging area(s), without needing to run a build or install step after
each change.

The ``develop`` command works by creating an ``.egg-link`` file (named for the
project) in the given staging area.  If the staging area is Python's
``site-packages`` directory, it also updates an ``easy-install.pth`` file so
that the project is on ``sys.path`` by default for all programs run using that
Python installation.

The ``develop`` command also installs wrapper scripts in the staging area (or
a separate directory, as specified) that will ensure the project's dependencies
are available on ``sys.path`` before running the project's source scripts.
And, it ensures that any missing project dependencies are available in the
staging area, by downloading and installing them if necessary.

Last, but not least, the ``develop`` command invokes the ``build_ext -i``
command to ensure any C extensions in the project have been built and are
up-to-date, and the ``egg_info`` command to ensure the project's metadata is
updated (so that the runtime and wrappers know what the project's dependencies
are).  If you make any changes to the project's setup script or C extensions,
you should rerun the ``develop`` command against all relevant staging areas to
keep the project's scripts, metadata and extensions up-to-date.  Most other
kinds of changes to your project should not require any build operations or
rerunning ``develop``, but keep in mind that even minor changes to the setup
script (e.g. changing an entry point definition) require you to re-run the
``develop`` or ``test`` commands to keep the distribution updated.

Here are some of the options that the ``develop`` command accepts.  Note that
they affect the project's dependencies as well as the project itself, so if you
have dependencies that need to be installed and you use ``--exclude-scripts``
(for example), the dependencies' scripts will not be installed either!  For
this reason, you may want to use pip to install the project's dependencies
before using the ``develop`` command, if you need finer control over the
installation options for dependencies.

``--uninstall, -u``
    Un-deploy the current project.  You may use the ``--install-dir`` or ``-d``
    option to designate the staging area.  The created ``.egg-link`` file will
    be removed, if present and it is still pointing to the project directory.
    The project directory will be removed from ``easy-install.pth`` if the
    staging area is Python's ``site-packages`` directory.

    Note that this option currently does *not* uninstall script wrappers!  You
    must uninstall them yourself, or overwrite them by using pip to install a
    different version of the package.  You can also avoid installing script
    wrappers in the first place, if you use the ``--exclude-scripts`` (aka
    ``-x``) option when you run ``develop`` to deploy the project.

``--multi-version, -m``
    "Multi-version" mode. Specifying this option prevents ``develop`` from
    adding an ``easy-install.pth`` entry for the project(s) being deployed, and
    if an entry for any version of a project already exists, the entry will be
    removed upon successful deployment.  In multi-version mode, no specific
    version of the package is available for importing, unless you use
    ``pkg_resources.require()`` to put it on ``sys.path``, or you are running
    a wrapper script generated by ``setuptools``.  (In which case the wrapper
    script calls ``require()`` for you.)

    Note that if you install to a directory other than ``site-packages``,
    this option is automatically in effect, because ``.pth`` files can only be
    used in ``site-packages`` (at least in Python 2.3 and 2.4). So, if you use
    the ``--install-dir`` or ``-d`` option (or they are set via configuration
    file(s)) your project and its dependencies will be deployed in multi-
    version mode.

``--install-dir=DIR, -d DIR``
    Set the installation directory (staging area).  If this option is not
    directly specified on the command line or in a distutils configuration
    file, the distutils default installation location is used.  Normally, this
    will be the ``site-packages`` directory, but if you are using distutils
    configuration files, setting things like ``prefix`` or ``install_lib``,
    then those settings are taken into account when computing the default
    staging area.

``--script-dir=DIR, -s DIR``
    Set the script installation directory.  If you don't supply this option
    (via the command line or a configuration file), but you *have* supplied
    an ``--install-dir`` (via command line or config file), then this option
    defaults to the same directory, so that the scripts will be able to find
    their associated package installation.  Otherwise, this setting defaults
    to the location where the distutils would normally install scripts, taking
    any distutils configuration file settings into account.

``--exclude-scripts, -x``
    Don't deploy script wrappers.  This is useful if you don't want to disturb
    existing versions of the scripts in the staging area.

``--always-copy, -a``
    Copy all needed distributions to the staging area, even if they
    are already present in another directory on ``sys.path``.  By default, if
    a requirement can be met using a distribution that is already available in
    a directory on ``sys.path``, it will not be copied to the staging area.

``--egg-path=DIR``
    Force the generated ``.egg-link`` file to use a specified relative path
    to the source directory.  This can be useful in circumstances where your
    installation directory is being shared by code running under multiple
    platforms (e.g. Mac and Windows) which have different absolute locations
    for the code under development, but the same *relative* locations with
    respect to the installation directory.  If you use this option when
    installing, you must supply the same relative path when uninstalling.

In addition to the above options, the ``develop`` command also accepts all of
the same options accepted by ``easy_install``.  If you've configured any
``easy_install`` settings in your ``setup.cfg`` (or other distutils config
files), the ``develop`` command will use them as defaults, unless you override
them in a ``[develop]`` section or on the command line.


.. _egg_info:

``egg_info`` - Create egg metadata and set build tags
=====================================================

This command performs two operations: it updates a project's ``.egg-info``
metadata directory (used by the ``bdist_egg``, ``develop``, and ``test``
commands), and it allows you to temporarily change a project's version string,
to support "daily builds" or "snapshot" releases.  It is run automatically by
the ``sdist``, ``bdist_egg``, ``develop``, and ``test`` commands in order to
update the project's metadata, but you can also specify it explicitly in order
to temporarily change the project's version string while executing other
commands.  (It also generates the ``.egg-info/SOURCES.txt`` manifest file, which
is used when you are building source distributions.)

In addition to writing the core egg metadata defined by ``setuptools`` and
required by ``pkg_resources``, this command can be extended to write other
metadata files as well, by defining entry points in the ``egg_info.writers``
group.  See the section on :ref:`Adding new EGG-INFO Files` below for more details.
Note that using additional metadata writers may require you to include a
``setup_requires`` argument to ``setup()`` in order to ensure that the desired
writers are available on ``sys.path``.


Release Tagging Options
-----------------------

The following options can be used to modify the project's version string for
all remaining commands on the setup command line.  The options are processed
in the order shown, so if you use more than one, the requested tags will be
added in the following order:

``--tag-build=NAME, -b NAME``
    Append NAME to the project's version string.  Due to the way setuptools
    processes "pre-release" version suffixes beginning with the letters "a"
    through "e" (like "alpha", "beta", and "candidate"), you will usually want
    to use a tag like ".build" or ".dev", as this will cause the version number
    to be considered *lower* than the project's default version.  (If you
    want to make the version number *higher* than the default version, you can
    always leave off --tag-build and then use one or both of the following
    options.)

    If you have a default build tag set in your ``setup.cfg``, you can suppress
    it on the command line using ``-b ""`` or ``--tag-build=""`` as an argument
    to the ``egg_info`` command.

``--tag-date, -d``
    Add a date stamp of the form "-YYYYMMDD" (e.g. "-20050528") to the
    project's version number.

``--no-date, -D``
    Don't include a date stamp in the version number.  This option is included
    so you can override a default setting in ``setup.cfg``.


(Note: Because these options modify the version number used for source and
binary distributions of your project, you should first make sure that you know
how the resulting version numbers will be interpreted by automated tools
like pip.  See the section above on :ref:`Specifying Your Project's Version` for an
explanation of pre- and post-release tags, as well as tips on how to choose and
verify a versioning scheme for your project.)

For advanced uses, there is one other option that can be set, to change the
location of the project's ``.egg-info`` directory.  Commands that need to find
the project's source directory or metadata should get it from this setting:


Other ``egg_info`` Options
--------------------------

``--egg-base=SOURCEDIR, -e SOURCEDIR``
    Specify the directory that should contain the .egg-info directory.  This
    should normally be the root of your project's source tree (which is not
    necessarily the same as your project directory; some projects use a ``src``
    or ``lib`` subdirectory as the source root).  You should not normally need
    to specify this directory, as it is normally determined from the
    ``package_dir`` argument to the ``setup()`` function, if any.  If there is
    no ``package_dir`` set, this option defaults to the current directory.


``egg_info`` Examples
---------------------

Creating a dated "nightly build" snapshot egg::

    setup.py egg_info --tag-date --tag-build=DEV bdist_egg

Creating a release with no version tags, even if some default tags are
specified in ``setup.cfg``::

    setup.py egg_info -RDb "" sdist bdist_egg

(Notice that ``egg_info`` must always appear on the command line *before* any
commands that you want the version changes to apply to.)

.. _rotate:

``rotate`` - Delete outdated distribution files
===============================================

As you develop new versions of your project, your distribution (``dist``)
directory will gradually fill up with older source and/or binary distribution
files.  The ``rotate`` command lets you automatically clean these up, keeping
only the N most-recently modified files matching a given pattern.

``--match=PATTERNLIST, -m PATTERNLIST``
    Comma-separated list of glob patterns to match.  This option is *required*.
    The project name and ``-*`` is prepended to the supplied patterns, in order
    to match only distributions belonging to the current project (in case you
    have a shared distribution directory for multiple projects).  Typically,
    you will use a glob pattern like ``.zip`` or ``.egg`` to match files of
    the specified type.  Note that each supplied pattern is treated as a
    distinct group of files for purposes of selecting files to delete.

``--keep=COUNT, -k COUNT``
    Number of matching distributions to keep.  For each group of files
    identified by a pattern specified with the ``--match`` option, delete all
    but the COUNT most-recently-modified files in that group.  This option is
    *required*.

``--dist-dir=DIR, -d DIR``
    Directory where the distributions are.  This defaults to the value of the
    ``bdist`` command's ``--dist-dir`` option, which will usually be the
    project's ``dist`` subdirectory.

**Example 1**: Delete all .tar.gz files from the distribution directory, except
for the 3 most recently modified ones::

    setup.py rotate --match=.tar.gz --keep=3

**Example 2**: Delete all Python 2.3 or Python 2.4 eggs from the distribution
directory, except the most recently modified one for each Python version::

    setup.py rotate --match=-py2.3*.egg,-py2.4*.egg --keep=1


.. _saveopts:

``saveopts`` - Save used options to a configuration file
========================================================

Finding and editing ``distutils`` configuration files can be a pain, especially
since you also have to translate the configuration options from command-line
form to the proper configuration file format.  You can avoid these hassles by
using the ``saveopts`` command.  Just add it to the command line to save the
options you used.  For example, this command builds the project using
the ``mingw32`` C compiler, then saves the --compiler setting as the default
for future builds (even those run implicitly by the ``install`` command)::

    setup.py build --compiler=mingw32 saveopts

The ``saveopts`` command saves all options for every command specified on the
command line to the project's local ``setup.cfg`` file, unless you use one of
the `configuration file options`_ to change where the options are saved.  For
example, this command does the same as above, but saves the compiler setting
to the site-wide (global) distutils configuration::

    setup.py build --compiler=mingw32 saveopts -g

Note that it doesn't matter where you place the ``saveopts`` command on the
command line; it will still save all the options specified for all commands.
For example, this is another valid way to spell the last example::

    setup.py saveopts -g build --compiler=mingw32

Note, however, that all of the commands specified are always run, regardless of
where ``saveopts`` is placed on the command line.


Configuration File Options
--------------------------

Normally, settings such as options and aliases are saved to the project's
local ``setup.cfg`` file.  But you can override this and save them to the
global or per-user configuration files, or to a manually-specified filename.

``--global-config, -g``
    Save settings to the global ``distutils.cfg`` file inside the ``distutils``
    package directory.  You must have write access to that directory to use
    this option.  You also can't combine this option with ``-u`` or ``-f``.

``--user-config, -u``
    Save settings to the current user's ``~/.pydistutils.cfg`` (POSIX) or
    ``$HOME/pydistutils.cfg`` (Windows) file.  You can't combine this option
    with ``-g`` or ``-f``.

``--filename=FILENAME, -f FILENAME``
    Save settings to the specified configuration file to use.  You can't
    combine this option with ``-g`` or ``-u``.  Note that if you specify a
    non-standard filename, the ``distutils`` and ``setuptools`` will not
    use the file's contents.  This option is mainly included for use in
    testing.

These options are used by other ``setuptools`` commands that modify
configuration files, such as the `alias`_ and `setopt`_ commands.


.. _setopt:

``setopt`` - Set a distutils or setuptools option in a config file
==================================================================

This command is mainly for use by scripts, but it can also be used as a quick
and dirty way to change a distutils configuration option without having to
remember what file the options are in and then open an editor.

**Example 1**.  Set the default C compiler to ``mingw32`` (using long option
names)::

    setup.py setopt --command=build --option=compiler --set-value=mingw32

**Example 2**.  Remove any setting for the distutils default package
installation directory (short option names)::

    setup.py setopt -c install -o install_lib -r


Options for the ``setopt`` command:

``--command=COMMAND, -c COMMAND``
    Command to set the option for.  This option is required.

``--option=OPTION, -o OPTION``
    The name of the option to set.  This option is required.

``--set-value=VALUE, -s VALUE``
    The value to set the option to.  Not needed if ``-r`` or ``--remove`` is
    set.

``--remove, -r``
    Remove (unset) the option, instead of setting it.

In addition to the above options, you may use any of the `configuration file
options`_ (listed under the `saveopts`_ command, above) to determine which
distutils configuration file the option will be added to (or removed from).


.. _test:

``test`` - Build package and run a unittest suite
=================================================

.. warning::
    ``test`` is deprecated and will be removed in a future version. Users
    looking for a generic test entry point independent of test runner are
    encouraged to use `tox <https://tox.readthedocs.io>`_.

When doing test-driven development, or running automated builds that need
testing before they are deployed for downloading or use, it's often useful
to be able to run a project's unit tests without actually deploying the project
anywhere, even using the ``develop`` command.  The ``test`` command runs a
project's unit tests without actually deploying it, by temporarily putting the
project's source on ``sys.path``, after first running ``build_ext -i`` and
``egg_info`` to ensure that any C extensions and project metadata are
up-to-date.

To use this command, your project's tests must be wrapped in a ``unittest``
test suite by either a function, a ``TestCase`` class or method, or a module
or package containing ``TestCase`` classes.  If the named suite is a module,
and the module has an ``additional_tests()`` function, it is called and the
result (which must be a ``unittest.TestSuite``) is added to the tests to be
run.  If the named suite is a package, any submodules and subpackages are
recursively added to the overall test suite.  (Note: if your project specifies
a ``test_loader``, the rules for processing the chosen ``test_suite`` may
differ; see the :ref:`test_loader <test_loader>` documentation for more details.)

Note that many test systems including ``doctest`` support wrapping their
non-``unittest`` tests in ``TestSuite`` objects.  So, if you are using a test
package that does not support this, we suggest you encourage its developers to
implement test suite support, as this is a convenient and standard way to
aggregate a collection of tests to be run under a common test harness.

By default, tests will be run in the "verbose" mode of the ``unittest``
package's text test runner, but you can get the "quiet" mode (just dots) if
you supply the ``-q`` or ``--quiet`` option, either as a global option to
the setup script (e.g. ``setup.py -q test``) or as an option for the ``test``
command itself (e.g. ``setup.py test -q``).  There is one other option
available:

``--test-suite=NAME, -s NAME``
    Specify the test suite (or module, class, or method) to be run
    (e.g. ``some_module.test_suite``).  The default for this option can be
    set by giving a ``test_suite`` argument to the ``setup()`` function, e.g.::

        setup(
            # ...
            test_suite="my_package.tests.test_all"
        )

    If you did not set a ``test_suite`` in your ``setup()`` call, and do not
    provide a ``--test-suite`` option, an error will occur.

New in 41.5.0: Deprecated the test command.


.. _upload:

``upload`` - Upload source and/or egg distributions to PyPI
===========================================================

The ``upload`` command was deprecated in version 40.0 and removed in version
42.0. Use `twine <https://pypi.org/p/twine>`_ instead.

For  more information on the current best practices in uploading your packages
to PyPI, see the Python Packaging User Guide's "Packaging Python Projects"
tutorial specifically the section on `uploading the distribution archives
<https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives>`_.
PK��\��`''8alt-python39-setuptools/docs/userguide/miscellaneous.rstnu�[���.. _Automatic Resource Extraction:

Automatic Resource Extraction
-----------------------------

If you are using tools that expect your resources to be "real" files, or your
project includes non-extension native libraries or other files that your C
extensions expect to be able to access, you may need to list those files in
the ``eager_resources`` argument to ``setup()``, so that the files will be
extracted together, whenever a C extension in the project is imported.

This is especially important if your project includes shared libraries *other*
than distutils-built C extensions, and those shared libraries use file
extensions other than ``.dll``, ``.so``, or ``.dylib``, which are the
extensions that setuptools 0.6a8 and higher automatically detects as shared
libraries and adds to the ``native_libs.txt`` file for you.  Any shared
libraries whose names do not end with one of those extensions should be listed
as ``eager_resources``, because they need to be present in the filesystem when
he C extensions that link to them are used.

The ``pkg_resources`` runtime for compressed packages will automatically
extract *all* C extensions and ``eager_resources`` at the same time, whenever
*any* C extension or eager resource is requested via the ``resource_filename()``
API.  (C extensions are imported using ``resource_filename()`` internally.)
This ensures that C extensions will see all of the "real" files that they
expect to see.

Note also that you can list directory resource names in ``eager_resources`` as
well, in which case the directory's contents (including subdirectories) will be
extracted whenever any C extension or eager resource is requested.

Please note that if you're not sure whether you need to use this argument, you
don't!  It's really intended to support projects with lots of non-Python
dependencies and as a last resort for crufty projects that can't otherwise
handle being compressed.  If your package is pure Python, Python plus data
files, or Python plus C, you really don't need this.  You've got to be using
either C or an external program that needs "real" files in your project before
there's any possibility of ``eager_resources`` being relevant to your project.

Defining Additional Metadata
----------------------------

Some extensible applications and frameworks may need to define their own kinds
of metadata to include in eggs, which they can then access using the
``pkg_resources`` metadata APIs.  Ordinarily, this is done by having plugin
developers include additional files in their ``ProjectName.egg-info``
directory.  However, since it can be tedious to create such files by hand, you
may want to create a distutils extension that will create the necessary files
from arguments to ``setup()``, in much the same way that ``setuptools`` does
for many of the ``setup()`` arguments it adds.  See the section below on
:ref:`Creating ``distutils\`\` Extensions` for more details, especially the
subsection on :ref:`Adding new EGG-INFO Files`.

Setting the ``zip_safe`` flag
-----------------------------

For some use cases (such as bundling as part of a larger application), Python
packages may be run directly from a zip file.
Not all packages, however, are capable of running in compressed form, because
they may expect to be able to access either source code or data files as
normal operating system files.  So, ``setuptools`` can install your project
as a zipfile or a directory, and its default choice is determined by the
project's ``zip_safe`` flag.

You can pass a True or False value for the ``zip_safe`` argument to the
``setup()`` function, or you can omit it.  If you omit it, the ``bdist_egg``
command will analyze your project's contents to see if it can detect any
conditions that would prevent it from working in a zipfile.  It will output
notices to the console about any such conditions that it finds.

Currently, this analysis is extremely conservative: it will consider the
project unsafe if it contains any C extensions or datafiles whatsoever.  This
does *not* mean that the project can't or won't work as a zipfile!  It just
means that the ``bdist_egg`` authors aren't yet comfortable asserting that
the project *will* work.  If the project contains no C or data files, and does
no ``__file__`` or ``__path__`` introspection or source code manipulation, then
there is an extremely solid chance the project will work when installed as a
zipfile.  (And if the project uses ``pkg_resources`` for all its data file
access, then C extensions and other data files shouldn't be a problem at all.
See the :ref:`Accessing Data Files at Runtime` section above for more information.)

However, if ``bdist_egg`` can't be *sure* that your package will work, but
you've checked over all the warnings it issued, and you are either satisfied it
*will* work (or if you want to try it for yourself), then you should set
``zip_safe`` to ``True`` in your ``setup()`` call.  If it turns out that it
doesn't work, you can always change it to ``False``, which will force
``setuptools`` to install your project as a directory rather than as a zipfile.

In the future, as we gain more experience with different packages and become
more satisfied with the robustness of the ``pkg_resources`` runtime, the
"zip safety" analysis may become less conservative.  However, we strongly
recommend that you determine for yourself whether your project functions
correctly when installed as a zipfile, correct any problems if you can, and
then make an explicit declaration of ``True`` or ``False`` for the ``zip_safe``
flag, so that it will not be necessary for ``bdist_egg`` to try to guess
whether your project can work as a zipfile.
PK��\�S��
�
0alt-python39-setuptools/docs/python 2 sunset.rstnu�[���:orphan:

Python 2 Sunset
===============

Since January 2020 and the release of Setuptools 45, Python 2 is no longer
supported by the most current release (`discussion
<https://github.com/pypa/setuptools/issues/1458>`_). Setuptools as a project
continues to support Python 2 with bugfixes and important features on
Setuptools 44.x.

By design, most users will be unaffected by this change. That's because
Setuptools 45 declares its supported Python versions to exclude Python 2.7,
and installers such as pip 9 or later will honor this declaration and prevent
installation of Setuptools 45 or later in Python 2 environments.

Users that do import any portion of Setuptools 45 or later on Python 2 are
directed to this documentation to provide guidance on how to work around the
issues.

Workarounds
-----------

The best recommendation is to avoid Python 2 and move to Python 3 where
possible. This project acknowledges that not all environments can drop Python
2 support, so provides other options.

In less common scenarios, later versions of Setuptools can be installed on
unsupported Python versions. In these environments, the installer is advised
to first install ``setuptools<45`` to "pin Setuptools" to a compatible
version.

- When using older versions of pip (before 9.0), the ``Requires-Python``
  directive is not honored and invalid versions can be installed. Users are
  advised first to upgrade pip and retry or to pin Setuptools. Use ``pip
  --version`` to determine the version of pip.
- When using ``easy_install``, ``Requires-Python`` is not honored and later
  versions can be installed. In this case, users are advised to pin
  Setuptools. This applies to ``setup.py install`` invocations as well, as
  they use Setuptools under the hood.

It's still not working
----------------------

If after trying the above steps, the Python environment still has incompatible
versions of Setuptools installed, here are some things to try.

1. Uninstall and reinstall Setuptools. Run ``pip uninstall -y setuptools`` for
   the relevant environment. Repeat until there is no Setuptools installed.
   Then ``pip install setuptools``.
2. If possible, attempt to replicate the problem in a second environment
   (virtual machine, friend's computer, etc). If the issue is isolated to just
   one unique environment, first determine what is different about those
   environments (or reinstall/reset the failing one to defaults).
3. End users who are not themselves the maintainers for the package they are
   trying to install should contact the support channels for the relevant
   application. Please be considerate of those projects by searching for
   existing issues and following the latest guidance before reaching out for
   support. When filing an issue, be sure to give as much detail as possible
   to help the maintainers understand what factors led to the issue after
   following their recommended guidance.
4. Reach out to your local support groups. There's a good chance someone
   nearby has the expertise and willingness to help.
5. If all else fails, `file this template
   <https://github.com/pypa/setuptools/issues/new?assignees=&labels=Python+2&template=setuptools-warns-about-python-2-incompatibility.md&title=Incompatible+install+in+(summarize+your+environment)>`_
   with Setuptools. Please complete the whole template, providing as much
   detail about what factors led to the issue. Setuptools maintainers will
   summarily close tickets filed without any meaningful detail or engagement
   with the issue.
PK��\
`�nPP$alt-python39-setuptools/docs/conf.pynu�[���extensions = ['sphinx.ext.autodoc', 'jaraco.packaging.sphinx', 'rst.linker']

master_doc = "index"

link_files = {
    '../CHANGES.rst': dict(
        using=dict(
            BB='https://bitbucket.org',
            GH='https://github.com',
        ),
        replace=[
            dict(
                pattern=r'(Issue )?#(?P<issue>\d+)',
                url='{package_url}/issues/{issue}',
            ),
            dict(
                pattern=r'BB Pull Request ?#(?P<bb_pull_request>\d+)',
                url='{BB}/pypa/setuptools/pull-request/{bb_pull_request}',
            ),
            dict(
                pattern=r'Distribute #(?P<distribute>\d+)',
                url='{BB}/tarek/distribute/issue/{distribute}',
            ),
            dict(
                pattern=r'Buildout #(?P<buildout>\d+)',
                url='{GH}/buildout/buildout/issues/{buildout}',
            ),
            dict(
                pattern=r'Old Setuptools #(?P<old_setuptools>\d+)',
                url='http://bugs.python.org/setuptools/issue{old_setuptools}',
            ),
            dict(
                pattern=r'Jython #(?P<jython>\d+)',
                url='http://bugs.jython.org/issue{jython}',
            ),
            dict(
                pattern=r'(Python #|bpo-)(?P<python>\d+)',
                url='http://bugs.python.org/issue{python}',
            ),
            dict(
                pattern=r'Interop #(?P<interop>\d+)',
                url='{GH}/pypa/interoperability-peps/issues/{interop}',
            ),
            dict(
                pattern=r'Pip #(?P<pip>\d+)',
                url='{GH}/pypa/pip/issues/{pip}',
            ),
            dict(
                pattern=r'Packaging #(?P<packaging>\d+)',
                url='{GH}/pypa/packaging/issues/{packaging}',
            ),
            dict(
                pattern=r'[Pp]ackaging (?P<packaging_ver>\d+(\.\d+)+)',
                url='{GH}/pypa/packaging/blob/{packaging_ver}/CHANGELOG.rst',
            ),
            dict(
                pattern=r'PEP[- ](?P<pep_number>\d+)',
                url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/',
            ),
            dict(
                pattern=r'setuptools_svn #(?P<setuptools_svn>\d+)',
                url='{GH}/jaraco/setuptools_svn/issues/{setuptools_svn}',
            ),
            dict(
                pattern=r'pypa/distutils#(?P<distutils>\d+)',
                url='{GH}/pypa/distutils/issues/{distutils}',
            ),
            dict(
                pattern=r'^(?m)((?P<scm_version>v?\d+(\.\d+){1,2}))\n[-=]+\n',
                with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n',
            ),
        ],
    ),
}

# Be strict about any broken references:
nitpicky = True

intersphinx_mapping = {
    'pypa-build': ('https://pypa-build.readthedocs.io/en/latest/', None)
}

# Add support for linking usernames
github_url = 'https://github.com'
github_sponsors_url = f'{github_url}/sponsors'
extlinks = {
    'user': (f'{github_sponsors_url}/%s', '@'),  # noqa: WPS323
}
extensions += ['sphinx.ext.extlinks', 'sphinx.ext.intersphinx']

# Ref: https://github.com/python-attrs/attrs/pull/571/files\
#      #diff-85987f48f1258d9ee486e3191495582dR82
default_role = 'any'

# HTML theme
html_theme = 'furo'

# Add support for inline tabs
extensions += ['sphinx_inline_tabs']

# Support for distutils

# Ref: https://stackoverflow.com/a/30624034/595220
nitpick_ignore = [
    ('c:func', 'SHGetSpecialFolderPath'),  # ref to MS docs
    ('envvar', 'DISTUTILS_DEBUG'),  # undocumented
    ('envvar', 'HOME'),  # undocumented
    ('envvar', 'PLAT'),  # undocumented
    ('py:attr', 'CCompiler.language_map'),  # undocumented
    ('py:attr', 'CCompiler.language_order'),  # undocumented
    ('py:class', 'distutils.dist.Distribution'),  # undocumented
    ('py:class', 'distutils.extension.Extension'),  # undocumented
    ('py:class', 'BorlandCCompiler'),  # undocumented
    ('py:class', 'CCompiler'),  # undocumented
    ('py:class', 'CygwinCCompiler'),  # undocumented
    ('py:class', 'distutils.dist.DistributionMetadata'),  # undocumented
    ('py:class', 'FileList'),  # undocumented
    ('py:class', 'IShellLink'),  # ref to MS docs
    ('py:class', 'MSVCCompiler'),  # undocumented
    ('py:class', 'OptionDummy'),  # undocumented
    ('py:class', 'UnixCCompiler'),  # undocumented
    ('py:exc', 'CompileError'),  # undocumented
    ('py:exc', 'DistutilsExecError'),  # undocumented
    ('py:exc', 'DistutilsFileError'),  # undocumented
    ('py:exc', 'LibError'),  # undocumented
    ('py:exc', 'LinkError'),  # undocumented
    ('py:exc', 'PreprocessError'),  # undocumented
    ('py:func', 'distutils.CCompiler.new_compiler'),  # undocumented
    # undocumented:
    ('py:func', 'distutils.dist.DistributionMetadata.read_pkg_file'),
    ('py:func', 'distutils.file_util._copy_file_contents'),  # undocumented
    ('py:func', 'distutils.log.debug'),  # undocumented
    ('py:func', 'distutils.spawn.find_executable'),  # undocumented
    ('py:func', 'distutils.spawn.spawn'),  # undocumented
    # TODO: check https://docutils.rtfd.io in the future
    ('py:mod', 'docutils'),  # there's no Sphinx site documenting this
]

# Allow linking objects on other Sphinx sites seamlessly:
intersphinx_mapping.update(
    python=('https://docs.python.org/3', None),
    python2=('https://docs.python.org/2', None),
)

# Add support for the unreleased "next-version" change notes
extensions += ['sphinxcontrib.towncrier']
# Extension needs a path from here to the towncrier config.
towncrier_draft_working_directory = '..'
# Avoid an empty section for unpublished changes.
towncrier_draft_include_empty = False

extensions += ['jaraco.tidelift']
PK��\c�g���(alt-python39-setuptools/docs/roadmap.rstnu�[���=======
Roadmap
=======

Setuptools maintains a series of `milestones
<https://github.com/pypa/setuptools/milestones>`_ to track
a roadmap of large-scale goals.
PK��\�&5pp(alt-python39-setuptools/docs/history.rstnu�[���:tocdepth: 2

.. _changes:

History
*******

.. towncrier-draft-entries:: DRAFT, unreleased as on |today|

.. include:: ../CHANGES (links).rst

Credits
*******

* The original design for the ``.egg`` format and the ``pkg_resources`` API was
  co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first
  version of ``pkg_resources``, and supplied the macOS operating system version
  compatibility algorithm.

* Ian Bicking implemented many early "creature comfort" features of
  easy_install, including support for downloading via Sourceforge and
  Subversion repositories. Ian's comments on the Web-SIG about WSGI
  application deployment also inspired the concept of "entry points" in eggs,
  and he has given talks at PyCon and elsewhere to inform and educate the
  community about eggs and setuptools.

* Jim Fulton contributed time and effort to build automated tests of various
  aspects of ``easy_install``, and supplied the doctests for the command-line
  ``.exe`` wrappers on Windows.

* Phillip J. Eby is the seminal author of setuptools, and
  first proposed the idea of an importable binary distribution format for
  Python application plug-ins.

* Significant parts of the implementation of setuptools were funded by the Open
  Source Applications Foundation, to provide a plug-in infrastructure for the
  Chandler PIM application. In addition, many OSAF staffers (such as Mike
  "Code Bear" Taylor) contributed their time and stress as guinea pigs for the
  use of eggs and setuptools, even before eggs were "cool".  (Thanks, guys!)

* Tarek Ziadé is the principal author of the Distribute fork, which
  re-invigorated the community on the project, encouraged renewed innovation,
  and addressed many defects.

* Jason R. Coombs performed the merge with Distribute, maintaining the
  project for several years in coordination with the Python Packaging
  Authority (PyPA).
PK��\$dگ�2alt-python39-setuptools/docs/development/index.rstnu�[���-------------------------
Development on Setuptools
-------------------------

Setuptools is maintained by the Python community under the Python Packaging
Authority (PyPA) and led by Jason R. Coombs.

This document describes the process by which Setuptools is developed.
This document assumes the reader has some passing familiarity with
*using* setuptools, the ``pkg_resources`` module, and pip.  It
does not attempt to explain basic concepts like inter-project
dependencies, nor does it contain detailed lexical syntax for most
file formats.  Neither does it explain concepts like "namespace
packages" or "resources" in any detail, as all of these subjects are
covered at length in the setuptools developer's guide and the
``pkg_resources`` reference manual.

Instead, this is **internal** documentation for how those concepts and
features are *implemented* in concrete terms.  It is intended for people
who are working on the setuptools code base, who want to be able to
troubleshoot setuptools problems, want to write code that reads the file
formats involved, or want to otherwise tinker with setuptools-generated
files and directories.

Note, however, that these are all internal implementation details and
are therefore subject to change; stick to the published API if you don't
want to be responsible for keeping your code from breaking when
setuptools changes.  You have been warned.

.. toctree::
   :maxdepth: 1

   developer-guide
   releases
PK��\�rE��5alt-python39-setuptools/docs/development/releases.rstnu�[���===============
Release Process
===============

In order to allow for rapid, predictable releases, Setuptools uses a
mechanical technique for releases, enacted on tagged commits by
continuous integration.

To finalize a release, run ``tox -e finalize``, review, then push
the changes.

If tests pass, the release will be uploaded to PyPI.

Release Frequency
-----------------

Some have asked why Setuptools is released so frequently. Because Setuptools
uses a mechanical release process, it's very easy to make releases whenever the
code is stable (tests are passing). As a result, the philosophy is to release
early and often.

While some find the frequent releases somewhat surprising, they only empower
the user. Although releases are made frequently, users can choose the frequency
at which they use those releases. If instead Setuptools contributions were only
released in batches, the user would be constrained to only use Setuptools when
those official releases were made. With frequent releases, the user can govern
exactly how often he wishes to update.

Frequent releases also then obviate the need for dev or beta releases in most
cases. Because releases are made early and often, bugs are discovered and
corrected quickly, in many cases before other users have yet to encounter them.

Release Managers
----------------

Additionally, anyone with push access to the master branch has access to cut
releases.
PK��\�
���<alt-python39-setuptools/docs/development/developer-guide.rstnu�[���================================
Developer's Guide for Setuptools
================================

If you want to know more about contributing on Setuptools, this is the place.


-------------------
Recommended Reading
-------------------

Please read `How to write the perfect pull request
<https://blog.jaraco.com/how-to-write-perfect-pull-request/>`_ for some tips
on contributing to open source projects. Although the article is not
authoritative, it was authored by the maintainer of Setuptools, so reflects
his opinions and will improve the likelihood of acceptance and quality of
contribution.

------------------
Project Management
------------------

Setuptools is maintained primarily in GitHub at `this home
<https://github.com/pypa/setuptools>`_. Setuptools is maintained under the
Python Packaging Authority (PyPA) with several core contributors. All bugs
for Setuptools are filed and the canonical source is maintained in GitHub.

User support and discussions are done through the issue tracker (for specific)
issues, through the `distutils-sig mailing list <https://mail.python.org/mailman3/lists/distutils-sig.python.org/>`_, or on IRC (Freenode) at
#pypa.

Discussions about development happen on the distutils-sig mailing list or on
`Gitter <https://gitter.im/pypa/setuptools>`_.

-----------------
Authoring Tickets
-----------------

Before authoring any source code, it's often prudent to file a ticket
describing the motivation behind making changes. First search to see if a
ticket already exists for your issue. If not, create one. Try to think from
the perspective of the reader. Explain what behavior you expected, what you
got instead, and what factors might have contributed to the unexpected
behavior. In GitHub, surround a block of code or traceback with the triple
backtick "\`\`\`" so that it is formatted nicely.

Filing a ticket provides a forum for justification, discussion, and
clarification. The ticket provides a record of the purpose for the change and
any hard decisions that were made. It provides a single place for others to
reference when trying to understand why the software operates the way it does
or why certain changes were made.

Setuptools makes extensive use of hyperlinks to tickets in the changelog so
that system integrators and other users can get a quick summary, but then
jump to the in-depth discussion about any subject referenced.

---------------------
Making a pull request
---------------------

When making a pull request, please
:ref:`include a short summary of the changes <Adding change notes
with your PRs>` and a reference to any issue tickets that the PR is
intended to solve.
All PRs with code changes should include tests. All changes should
include a changelog entry.

.. include:: ../../changelog.d/README.rst

-------------------
Auto-Merge Requests
-------------------

To support running all code through CI, even lightweight contributions,
the project employs Mergify to auto-merge pull requests tagged as
auto-merge.

Use ``hub pull-request -l auto-merge`` to create such a pull request
from the command line after pushing a new branch.

-------
Testing
-------

The primary tests are run using tox.  Make sure you have tox installed,
and invoke it::

    $ tox

Under continuous integration, additional tests may be run. See the
``.travis.yml`` file for full details on the tests run under Travis-CI.

-------------------
Semantic Versioning
-------------------

Setuptools follows ``semver``.

.. explain value of reflecting meaning in versions.

----------------------
Building Documentation
----------------------

Setuptools relies on the `Sphinx`_ system for building documentation.
The `published documentation`_ is hosted on Read the Docs.

To build the docs locally, use tox::

    $ tox -e docs

.. _Sphinx: http://www.sphinx-doc.org/en/master/
.. _published documentation: https://setuptools.readthedocs.io/en/latest/

---------------------
Vendored Dependencies
---------------------

Setuptools has some dependencies, but due to `bootstrapping issues
<https://github.com/pypa/setuptools/issues/980>`_, those dependencies
cannot be declared as they won't be resolved soon enough to build
setuptools from source. Eventually, this limitation may be lifted as
PEP 517/518 reach ubiquitous adoption, but for now, Setuptools
cannot declare dependencies other than through
``setuptools/_vendor/vendored.txt`` and
``pkg_resources/_vendor/vendored.txt`` and refreshed by way of
``paver update_vendored`` (pavement.py).
PK���\1-��p�p�pycurl/ChangeLognu�[���PK���\O�X_]]&��pycurl/examples/opensocketexception.pynu�[���PK���\)�����;c�pycurl/examples/__pycache__/ssh_keyfunction.cpython-312.pycnu�[���PK���\����Frpycurl/examples/__pycache__/multi-socket_action-select.cpython-312.pycnu�[���PK���\�g���7�pycurl/examples/__pycache__/file_upload.cpython-312.pycnu�[���PK���\�}O�����3�pycurl/examples/__pycache__/linksys.cpython-312.pycnu�[���PK���\�x���7��pycurl/examples/__pycache__/xmlrpc_curl.cpython-312.pycnu�[���PK���\�^�p��0�pycurl/examples/__pycache__/smtp.cpython-312.pycnu�[���PK���\���M;�pycurl/examples/__pycache__/retriever-multi.cpython-312.pycnu�[���PK���\{*�W6��pycurl/examples/__pycache__/basicfirst.cpython-312.pycnu�[���PK���\������5�pycurl/examples/__pycache__/retriever.cpython-312.pycnu�[���PK���\l�~ܻ�?S�pycurl/examples/__pycache__/opensocketexception.cpython-312.pycnu�[���PK���\���y3}�pycurl/examples/__pycache__/sfquery.cpython-312.pycnu�[���PK���\^�7�.."�pycurl/examples/ssh_keyfunction.pynu�[���PK���\ѹ�+��opycurl/examples/file_upload.pynu�[���PK���\FX�UUXpycurl/examples/linksys.pynu�[���PK���\��	�	�[pycurl/examples/sfquery.pynu�[���PK���\�!#�i
i
�epycurl/examples/retriever.pynu�[���PK���\%�ק

"Eppycurl/examples/retriever-multi.pynu�[���PK���\�p�KK�}pycurl/examples/smtp.pynu�[���PK���\p$���(H�pycurl/examples/quickstart/put_buffer.pynu�[���PK���\��~,��-x�pycurl/examples/quickstart/follow_redirect.pynu�[���PK���\h�PW��4υpycurl/examples/quickstart/file_upload_real_fancy.pynu�[���PK���\�S��||:�pycurl/examples/quickstart/__pycache__/get.cpython-312.pycnu�[���PK���\�Σ�G��pycurl/examples/quickstart/__pycache__/file_upload_real.cpython-312.pycnu�[���PK���\���5��B�pycurl/examples/quickstart/__pycache__/get_python2.cpython-312.pycnu�[���PK���\0�_���I_�pycurl/examples/quickstart/__pycache__/file_upload_buffer.cpython-312.pycnu�[���PK���\�6��Gҕpycurl/examples/quickstart/__pycache__/response_headers.cpython-312.pycnu�[���PK���\ɓ��ppH�pycurl/examples/quickstart/__pycache__/get_python2_https.cpython-312.pycnu�[���PK���\���))@ڡpycurl/examples/quickstart/__pycache__/form_post.cpython-312.pycnu�[���PK���\I��44Ms�pycurl/examples/quickstart/__pycache__/file_upload_real_fancy.cpython-312.pycnu�[���PK���\�5���A$�pycurl/examples/quickstart/__pycache__/write_file.cpython-312.pycnu�[���PK���\#g��AI�pycurl/examples/quickstart/__pycache__/put_buffer.cpython-312.pycnu�[���PK���\��BZ�pycurl/examples/quickstart/__pycache__/get_python3.cpython-312.pycnu�[���PK���\(�fCMMF�pycurl/examples/quickstart/__pycache__/follow_redirect.cpython-312.pycnu�[���PK���\W�G��H��pycurl/examples/quickstart/__pycache__/get_python3_https.cpython-312.pycnu�[���PK���\-�k��?Ⱥpycurl/examples/quickstart/__pycache__/put_file.cpython-312.pycnu�[���PK���\
�����D$�pycurl/examples/quickstart/__pycache__/response_info.cpython-312.pycnu�[���PK���\�ԃ+,,&��pycurl/examples/quickstart/put_file.pynu�[���PK���\�О��/�pycurl/examples/quickstart/get_python3_https.pynu�[���PK���\���ee(4�pycurl/examples/quickstart/write_file.pynu�[���PK���\����)��pycurl/examples/quickstart/get_python3.pynu�[���PK���\WJ�A44'��pycurl/examples/quickstart/form_post.pynu�[���PK���\[�770h�pycurl/examples/quickstart/file_upload_buffer.pynu�[���PK���\�%���/��pycurl/examples/quickstart/get_python2_https.pynu�[���PK���\�~(�##.P�pycurl/examples/quickstart/file_upload_real.pynu�[���PK���\�b,��)��pycurl/examples/quickstart/get_python2.pynu�[���PK���\�YQ11.��pycurl/examples/quickstart/response_headers.pynu�[���PK���\i%���+x�pycurl/examples/quickstart/response_info.pynu�[���PK���\�� �GG!��pycurl/examples/quickstart/get.pynu�[���PK���\8�!!]�pycurl/examples/basicfirst.pynu�[���PK���\�8�����pycurl/examples/xmlrpc_curl.pynu�[���PK���\3���??-��pycurl/examples/multi-socket_action-select.pynu�[���PK���\�h�W�"�"8pycurl/RELEASE-NOTES.rstnu�[���PK���\H�wff2/pycurl/README.rstnu�[���PK���\]��a���Hpycurl/AUTHORSnu�[���PK���\>X"V�g�g	]pycurl/COPYING-LGPLnu�[���PK���\�0l�����pycurl/COPYING-MITnu�[���PK���\�W�z�/�/��pycurl/INSTALL.rstnu�[���PK�*�\Z�\��,�,'�alt-php84-ioncube-loader/USER-GUIDE.txtnu�[���PK�*�\�z)�)�*'alt-php84-ioncube-loader/loader-wizard.phpnu�[���PK�*�\h������'��alt-php84-ioncube-loader/USER-GUIDE.pdfnu�[���PK�*�\&�**$��alt-php84-ioncube-loader/LICENSE.txtnu�[���PK�*�\a����#�alt-php84-ioncube-loader/README.txtnu�[���PK�*�\5k��#�#"F�pear/XML_Util/examples/example.phpnu�[���PK�*�\�f��#`�pear/XML_Util/examples/example2.phpnu�[���PK�*�\�u������pear/PEAR/LICENSEnu�[���PK�*�\�E�����pear/PEAR/README.rstnu�[���PK�*�\���xx�pear/PEAR/INSTALLnu�[���PK�*�\DG�e���pear/Structures_Graph/LICENSEnu�[���PK�*�\x� ���J�/pear/Structures_Graph/docs/tutorials/Structures_Graph/Structures_Graph.pkgnu�[���PK�*�\�r¦J�J%�?pear/Archive_Tar/docs/Archive_Tar.txtnu�[���PK�*�\$�u���!�alt-php84-snuffleupagus/README.mdnu�[���PK�*�\�E#/&&'�alt-php84-snuffleupagus/CONTRIBUTING.mdnu�[���PK��\�Yď%k�alt-python39-setuptools-wheel/LICENSEnu�[���PK��\ԉ�y�'�'ڿalt-python39/README.rstnu�[���PK��\�����alt-python39-pip/README.rstnu�[���PK��\���3{"{"'�alt-python39-devel/valgrind-python.suppnu�[���PK��\蘪zcc�	alt-python39-devel/gdbinitnu�[���PK��\�̈��"�*	alt-python39-devel/README.valgrindnu�[���PK��\ԉ�y�'�'�=	alt-python39-libs/README.rstnu�[���PK��\�Yď�e	alt-python39-setuptools/LICENSEnu�[���PK��\_��5j	alt-python39-setuptools/zpl.txtnu�[���PK��\SoD �1�1 �r	alt-python39-setuptools/psfl.txtnu�[���PK��\���&ɤ	alt-python39-setuptools/docs/index.rstnu�[���PK��\��Uê���8�	alt-python39-setuptools/docs/deprecated/easy_install.rstnu�[���PK��\�Ӹ�1�r
alt-python39-setuptools/docs/deprecated/index.rstnu�[���PK��\@�G���;nv
alt-python39-setuptools/docs/deprecated/distutils/index.rstnu�[���PK��\R����?�{
alt-python39-setuptools/docs/deprecated/distutils/extending.rstnu�[���PK��\�Y�
&
&@#�
alt-python39-setuptools/docs/deprecated/distutils/sourcedist.rstnu�[���PK��\f�m�(�(>��
alt-python39-setuptools/docs/deprecated/distutils/examples.rstnu�[���PK��\U��)�z�zA��
alt-python39-setuptools/docs/deprecated/distutils/setupscript.rstnu�[���PK��\�W��@�Xalt-python39-setuptools/docs/deprecated/distutils/commandref.rstnu�[���PK��\.<��ZZ?0kalt-python39-setuptools/docs/deprecated/distutils/builtdist.rstnu�[���PK��\:X���?��alt-python39-setuptools/docs/deprecated/distutils/uploading.rstnu�[���PK��\n�O���L�alt-python39-setuptools/docs/deprecated/distutils/_setuptools_disclaimer.rstnu�[���PK��\>�yq�!�!Bl�alt-python39-setuptools/docs/deprecated/distutils/introduction.rstnu�[���PK��\I���Be�alt-python39-setuptools/docs/deprecated/distutils/packageindex.rstnu�[���PK��\�ׁ���@��alt-python39-setuptools/docs/deprecated/distutils/configfile.rstnu�[���PK��\I��xzxz<�alt-python39-setuptools/docs/deprecated/distutils/apiref.rstnu�[���PK��\2�����<�
alt-python39-setuptools/docs/deprecated/distutils-legacy.rstnu�[���PK��\�j'��;}�
alt-python39-setuptools/docs/deprecated/functionalities.rstnu�[���PK��\eO<�x�x7��
alt-python39-setuptools/docs/deprecated/python_eggs.rstnu�[���PK��\4W�pppp.�alt-python39-setuptools/docs/pkg_resources.rstnu�[���PK��\�0�7�74�ualt-python39-setuptools/docs/references/keywords.rstnu�[���PK��\�'��ss+խalt-python39-setuptools/docs/build_meta.rstnu�[���PK��\��s���+��alt-python39-setuptools/docs/setuptools.rstnu�[���PK��\ƺ4�{{0��alt-python39-setuptools/docs/userguide/index.rstnu�[���PK��\���@�
�
;��alt-python39-setuptools/docs/userguide/development_mode.rstnu�[���PK��\G��N��5
�alt-python39-setuptools/docs/userguide/quickstart.rstnu�[���PK��\P��d��4Xalt-python39-setuptools/docs/userguide/datafiles.rstnu�[���PK��\��f�n)n)=}#alt-python39-setuptools/docs/userguide/declarative_config.rstnu�[���PK��\��cߑ�<XMalt-python39-setuptools/docs/userguide/package_discovery.rstnu�[���PK��\�!�**4Uialt-python39-setuptools/docs/userguide/extension.rstnu�[���PK��\C���"�"3֓alt-python39-setuptools/docs/userguide/keywords.rstnu�[���PK��\4/o�*�*@�alt-python39-setuptools/docs/userguide/dependency_management.rstnu�[���PK��\Ѩ���1�17>�alt-python39-setuptools/docs/userguide/distribution.rstnu�[���PK��\\��ى�6qalt-python39-setuptools/docs/userguide/entry_point.rstnu�[���PK��\'��*VVB`)alt-python39-setuptools/docs/userguide/functionalities_rewrite.rstnu�[���PK��\���@�j�j3(+alt-python39-setuptools/docs/userguide/commands.rstnu�[���PK��\��`''8{�alt-python39-setuptools/docs/userguide/miscellaneous.rstnu�[���PK��\�S��
�
0
�alt-python39-setuptools/docs/python 2 sunset.rstnu�[���PK��\
`�nPP$<�alt-python39-setuptools/docs/conf.pynu�[���PK��\c�g���(��alt-python39-setuptools/docs/roadmap.rstnu�[���PK��\�&5pp(��alt-python39-setuptools/docs/history.rstnu�[���PK��\$dگ�2��alt-python39-setuptools/docs/development/index.rstnu�[���PK��\�rE��5��alt-python39-setuptools/docs/development/releases.rstnu�[���PK��\�
���<��alt-python39-setuptools/docs/development/developer-guide.rstnu�[���PK���7��