pax_global_header00006660000000000000000000000064147104543730014522gustar00rootroot0000000000000052 comment=eb2588610750b9c687d417e72a743314edb107df python-deadlib-3.13.0/000077500000000000000000000000001471045437300145115ustar00rootroot00000000000000python-deadlib-3.13.0/.github/000077500000000000000000000000001471045437300160515ustar00rootroot00000000000000python-deadlib-3.13.0/.github/workflows/000077500000000000000000000000001471045437300201065ustar00rootroot00000000000000python-deadlib-3.13.0/.github/workflows/_crypt.yml000066400000000000000000000111161471045437300221310ustar00rootroot00000000000000# This file is autogenerated by maturin v1.7.4 # To update, run # # maturin generate-ci github # name: native _crypt on: push: branches: - main tags: - '*' pull_request: workflow_dispatch: permissions: contents: read jobs: linux: runs-on: ${{ matrix.platform.runner }} strategy: matrix: platform: - runner: ubuntu-latest target: x86_64 - runner: ubuntu-latest target: x86 - runner: ubuntu-latest target: aarch64 - runner: ubuntu-latest target: armv7 - runner: ubuntu-latest target: s390x - runner: ubuntu-latest target: ppc64le steps: - uses: actions/checkout@v4 - name: prepare Rust toolchain uses: dtolnay/rust-toolchain@stable - name: Run Rust tests working-directory: ./_crypt run: cargo test --verbose - uses: actions/setup-python@v5 with: python-version: 3.13 - name: Build wheels uses: PyO3/maturin-action@v1 with: working-directory: ./_crypt target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter sccache: 'true' manylinux: auto - name: Upload wheels uses: actions/upload-artifact@v4 with: name: wheels-linux-${{ matrix.platform.target }} path: _crypt/dist musllinux: runs-on: ${{ matrix.platform.runner }} strategy: matrix: platform: - runner: ubuntu-latest target: x86_64 - runner: ubuntu-latest target: x86 - runner: ubuntu-latest target: aarch64 - runner: ubuntu-latest target: armv7 steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: 3.x - name: Build wheels uses: PyO3/maturin-action@v1 with: working-directory: ./_crypt rust-toolchain: 1.82.0 target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter sccache: 'true' manylinux: musllinux_1_2 - name: Upload wheels uses: actions/upload-artifact@v4 with: name: wheels-musllinux-${{ matrix.platform.target }} path: _crypt/dist macos: runs-on: ${{ matrix.platform.runner }} strategy: matrix: platform: - runner: macos-12 target: x86_64 - runner: macos-14 target: aarch64 steps: - uses: actions/checkout@v4 - name: prepare Rust toolchain uses: dtolnay/rust-toolchain@stable - name: Run Rust tests working-directory: ./_crypt run: cargo test --verbose - uses: actions/setup-python@v5 with: python-version: 3.x - name: Build wheels uses: PyO3/maturin-action@v1 with: working-directory: ./_crypt target: ${{ matrix.platform.target }} args: --release --out dist --find-interpreter sccache: 'true' - name: Upload wheels uses: actions/upload-artifact@v4 with: name: wheels-macos-${{ matrix.platform.target }} path: _crypt/dist sdist: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build sdist uses: PyO3/maturin-action@v1 with: rust-toolchain: 1.82.0 working-directory: ./_crypt command: sdist args: --out dist - name: Upload sdist uses: actions/upload-artifact@v4 with: name: wheels-sdist path: _crypt/dist release: name: Release runs-on: ubuntu-latest if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} needs: [linux, musllinux, macos, sdist] permissions: # Use to sign the release artifacts id-token: write # Used to upload release artifacts contents: write # Used to generate artifact attestation attestations: write steps: - uses: actions/download-artifact@v4 - name: Generate artifact attestation uses: actions/attest-build-provenance@v1 with: subject-path: 'wheels-*/*' - name: Publish to PyPI if: "startsWith(github.ref, 'refs/tags/')" uses: PyO3/maturin-action@v1 env: MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }} with: command: upload args: --non-interactive --skip-existing wheels-*/* python-deadlib-3.13.0/.github/workflows/release.yaml000066400000000000000000000070271471045437300224200ustar00rootroot00000000000000name: Releases on: push: branches: - main tags: - '*' workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: generate-test-matrix: name: Generate test matrix runs-on: ubuntu-latest outputs: folders: ${{ steps.get-folders.outputs.folders }} steps: - name: Check out code from Github uses: actions/checkout@v4 - name: Get folders with tests id: get-folders run: | pip install -U pip -r requirements_test.txt FOLDERS=$(python scripts/helper.py list-test-folders --exclude distutils) echo "folders: ${FOLDERS}" echo "folders=${FOLDERS}" >> $GITHUB_OUTPUT build: name: Run build runs-on: ubuntu-latest needs: [generate-test-matrix] strategy: fail-fast: false matrix: folder: ${{ fromJson(needs.generate-test-matrix.outputs.folders) }} version: ["3.13"] steps: - name: Check out code from Github uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.version }} check-latest: true - name: Run tests run: | cd ${{ matrix.folder }} pip install build python -m build - name: Upload packages uses: actions/upload-artifact@v4 with: name: dist-${{ matrix.folder }} path: ${{ matrix.folder }}/dist publish-to-pypi: name: Release runs-on: ubuntu-latest if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }} needs: [build] permissions: # Use to sign the release artifacts id-token: write # Used to upload release artifacts contents: write # Used to generate artifact attestation attestations: write steps: - name: Download all the dists uses: actions/download-artifact@v4 with: pattern: dist-* path: dist merge-multiple: true - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: password: ${{ secrets.PYPI_API_TOKEN }} github-release: name: >- Sign the Python 🐍 distribution 📦 with Sigstore and upload them to GitHub Release needs: - publish-to-pypi runs-on: ubuntu-latest permissions: contents: write # IMPORTANT: mandatory for making GitHub Releases id-token: write # IMPORTANT: mandatory for sigstore steps: - name: Download all the dists uses: actions/download-artifact@v4 with: pattern: dist-* path: dist merge-multiple: true - name: Sign the dists with Sigstore uses: sigstore/gh-action-sigstore-python@v3.0.0 with: inputs: >- ./dist/*.tar.gz ./dist/*.whl - name: Create GitHub Release env: GITHUB_TOKEN: ${{ github.token }} run: >- gh release create '${{ github.ref_name }}' --repo '${{ github.repository }}' --notes "" - name: Upload artifact signatures to GitHub Release env: GITHUB_TOKEN: ${{ github.token }} # Upload to GitHub Release using the `gh` CLI. # `dist/` contains the built packages, and the # sigstore-produced signatures and certificates. run: >- gh release upload '${{ github.ref_name }}' dist/** --repo '${{ github.repository }}'python-deadlib-3.13.0/.github/workflows/tests.yaml000066400000000000000000000031641471045437300221400ustar00rootroot00000000000000name: Tests on: push: branches: - main tags: - '*' pull_request: ~ concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true jobs: generate-test-matrix: name: Generate test matrix runs-on: ubuntu-latest outputs: folders: ${{ steps.get-folders.outputs.folders }} steps: - name: Check out code from Github uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.13" check-latest: true - name: Get folders with tests id: get-folders run: | pip install -U pip -r requirements_test.txt FOLDERS=$(python scripts/helper.py list-test-folders --exclude distutils) echo "folders: ${FOLDERS}" echo "folders=${FOLDERS}" >> $GITHUB_OUTPUT tests: name: Run tests runs-on: ubuntu-latest needs: [generate-test-matrix] strategy: fail-fast: false matrix: folder: ${{ fromJson(needs.generate-test-matrix.outputs.folders) }} version: ["3.11", "3.12", "3.13"] steps: - name: Check out code from Github uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v5 with: python-version: ${{ matrix.version }} check-latest: true - name: Run tests run: | pip install -U pip -r requirements_test.txt python scripts/helper.py install-local-requirements ${{ matrix.folder }} cd ${{ matrix.folder }} python -m unittest python-deadlib-3.13.0/.gitignore000066400000000000000000000001071471045437300164770ustar00rootroot00000000000000.DS_Store .envrc *.pyc __pycache__ */dist */*.egg-info .python-version python-deadlib-3.13.0/README.md000066400000000000000000000117531471045437300157770ustar00rootroot00000000000000# Python dead batteries redistribution Python is moving forward! Python finally started to remove dead batteries. For more information, see [PEP 594](https://peps.python.org/pep-0594/) and [PEP 632](https://peps.python.org/pep-0632/). If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries in pure Python. ```shell pip install standard-{name} # Don't forget to add `standard-` prefix! pip install standard-uu # e.g. `uu` can be installed by standard-uu ``` - [aifc](https://pypi.org/project/standard-aifc/) - [asynchat](https://pypi.org/project/standard-asynchat/) - [asyncore](https://pypi.org/project/standard-asyncore/) - [cgi](https://pypi.org/project/standard-cgi/) - [cgitb](https://pypi.org/project/standard-cgitb/) - [chunk](https://pypi.org/project/standard-chunk/) - [crypt](https://pypi.org/project/standard-crypt/) - ~~[distutils](https://pypi.org/project/standard-distutils/)~~: Not working on Python 3.13 - [imghdr](https://pypi.org/project/standard-imghdr/) - [mailcap](https://pypi.org/project/standard-mailcap/) - [nntplib](https://pypi.org/project/standard-nntplib/) - [pipes](https://pypi.org/project/standard-pipes/) - [smtpd](https://pypi.org/project/standard-smtpd/) - [sndhdr](https://pypi.org/project/standard-sndhdr/) - [sunau](https://pypi.org/project/standard-sunau/) - [telnetlib](https://pypi.org/project/standard-telnetlib/) - [uu](https://pypi.org/project/standard-uu/) - [xdrlib](https://pypi.org/project/standard-xdrlib/) ## Contribution guideline Please do **NOT** submit any new features or anything beyond minimal compatibility work. This repository is intended to archive the old standard library installable via pip, with *very minimal* compatibility support. If you find any missing features or broken packages due to changes in Python, rather than incomplete packaging, please seek out other projects that are actively developing alternative packages or consider forking this project. Feel free to report any bugs or submit patches if you encounter issues with package generation. Exception: Feature patches related to files under `template` and `scripts` are welcome! ## To Python developers I'm really happy that this is happening! I hope that Python development and maintenance will be less of a burden as a result of this decision. At the same time, I'd love to encourage Python developers to more actively deprecate less core-feature related batteries and push them out to PyPI. I hope the Python team will make the similar packages. If you could take over or do something similar to what this repository does, and release at least one version, the final version of removed standard libraries to PyPI, that would be amazing! Unfortunately, this isn't able to work from third-party due to the [package name rule](https://pypi.org/help/#project-name). I'll do my best to support you if you need it. Then please make `__import__` notify users when failed to import removed standard libraries that they are installable via PIP. This step will enhance user experience. But the first step is necessary because Python can't advertise third party libraries in binary due to security issues. I know it might sound like more maintenance work, but it's not as bad as it seems! `PEP 594` strictly defined the really dead libraries. On the other hand, Python developers still have to maintain a huge part of the old standard libraries, which were added in the pre-PyPI era. It's not easy to tell users "We will deprecate this library. Please fork or go to find another library." I know it's not the most pleasant thing to do. Preparing a backup will be a bit more nice. Now telling "We will remove this library from standard library, but you can install it via PIP!" will be a lot easier. The only additional burden for users will be the Internet with SSL. There could be one more step - though this is a rejected idea in PEP 594. If we can get users to install the old standard packages via PIP, we can also get them to install less core-language libraries as separatedly distributing first-party libraries. Libraries that have users but aren't necessarily standard libraries can be turned into non-standard libraries. I'm not sure if this is the best way, but it will give developers the option to choose whether or not to do. I really hope that finally Python developers could decide to remove even more batteries in future. ## Tests The tests depend upon having [pyenv](https://github.com/pyenv/pyenv) installed and can be running with the command: `python scripts/run_test.py ${MODULE_NAME} ${PYTHON_VERSION}` Where: * _MODULE_NAME_ is the name of directory in this repository, or `ALL` to run tests for all modules * _PYTHON_VERSION_ is the major and minor number of a valid python version, seperated by a `.` For example: `python scripts/run_test.py aifc 3.13` Alternatively, if you prefer to run your tests inside a docker container, you can run: `docker build . -f scripts/Dockerfile -t test && docker run -e MODULE_NAME=aifc -e PYTHON_VERSION=3.12 -t test`python-deadlib-3.13.0/_crypt/000077500000000000000000000000001471045437300160115ustar00rootroot00000000000000python-deadlib-3.13.0/_crypt/.gitignore000066400000000000000000000012561471045437300200050ustar00rootroot00000000000000/target # Byte-compiled / optimized / DLL files __pycache__/ .pytest_cache/ *.py[cod] # C extensions *.so # Distribution / packaging .Python .venv/ env/ bin/ build/ develop-eggs/ dist/ eggs/ lib/ lib64/ parts/ sdist/ var/ include/ man/ venv/ *.egg-info/ .installed.cfg *.egg # Installer logs pip-log.txt pip-delete-this-directory.txt pip-selfcheck.json # Unit test / coverage reports htmlcov/ .tox/ .coverage .cache nosetests.xml coverage.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # Rope .ropeproject # Django stuff: *.log *.pot .DS_Store # Sphinx documentation docs/_build/ # PyCharm .idea/ # VSCode .vscode/ # Pyenv .python-version python-deadlib-3.13.0/_crypt/Cargo.lock000066400000000000000000000105701471045437300177210ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "deprecated-crypt-alternative" version = "0.1.2" dependencies = [ "pyo3", ] [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indoc" version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "libc" version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "memoffset" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "portable-atomic" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "proc-macro2" version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "pyo3" version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d922163ba1f79c04bc49073ba7b32fd5a8d3b76a87c955921234b8e77333c51" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", "once_cell", "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", "unindent", ] [[package]] name = "pyo3-build-config" version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc38c5feeb496c8321091edf3d63e9a6829eab4b863b4a6a65f26f3e9cc6b179" dependencies = [ "once_cell", "target-lexicon", ] [[package]] name = "pyo3-ffi" version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94845622d88ae274d2729fcefc850e63d7a3ddff5e3ce11bd88486db9f1d357d" dependencies = [ "libc", "pyo3-build-config", ] [[package]] name = "pyo3-macros" version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e655aad15e09b94ffdb3ce3d217acf652e26bbc37697ef012f5e5e348c716e5e" dependencies = [ "proc-macro2", "pyo3-macros-backend", "quote", "syn", ] [[package]] name = "pyo3-macros-backend" version = "0.22.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1e3f09eecd94618f60a455a23def79f79eba4dc561a97324bf9ac8c6df30ce" dependencies = [ "heck", "proc-macro2", "pyo3-build-config", "quote", "syn", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "target-lexicon" version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unindent" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" python-deadlib-3.13.0/_crypt/Cargo.toml000066400000000000000000000007701471045437300177450ustar00rootroot00000000000000[package] name = "deprecated-crypt-alternative" description = "Python deprecated library `_crypt` replacement." edition = "2021" version = "0.1.2" authors = ["Jeong, YunWon "] repository = "https://github.com/youknowone/python-deadlib" license = "LGPL-2.1" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] name = "_crypt" crate-type = ["cdylib"] [dependencies] pyo3 = { version = "0.22.0", features = ["abi3-py39"] } python-deadlib-3.13.0/_crypt/build.rs000066400000000000000000000001321471045437300174520ustar00rootroot00000000000000fn main() { #[cfg(target_os = "linux")] println!("cargo:rustc-link-lib=crypt"); } python-deadlib-3.13.0/_crypt/pyproject.toml000066400000000000000000000007121471045437300207250ustar00rootroot00000000000000[build-system] requires = ["maturin>=1.7,<2.0"] build-backend = "maturin" [project] name = "deprecated-crypt-alternative" requires-python = ">=3.13" # Use standard _crypt module for lower versions classifiers = [ "Programming Language :: Rust", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", ] dynamic = ["version"] [tool.maturin] features = ["pyo3/extension-module"] python-deadlib-3.13.0/_crypt/src/000077500000000000000000000000001471045437300166005ustar00rootroot00000000000000python-deadlib-3.13.0/_crypt/src/lib.rs000066400000000000000000000032511471045437300177150ustar00rootroot00000000000000use pyo3::{ exceptions::PyValueError, ffi::{PyErr_SetFromErrno, PyExc_OSError}, prelude::*, types::PyString, }; use std::ffi::CStr; // Future Rust versions will support `unsafe extern` // unsafe extern "C" { #[link_name = "crypt"] fn extern_crypt(key: *const u8, salt: *const u8) -> *const u8; } /// Hashes the concatenation of a word and a salt using SHA-256. #[pyfunction] fn crypt(word: &Bound, salt: &Bound) -> PyResult { let word = word.encode_utf8()?; let salt = salt.encode_utf8()?; if word.as_bytes().contains(&b'\0') || salt.as_bytes().contains(&b'\0') { return Err(PyValueError::new_err("embedded null character")); } let crypt_result = unsafe { let word = word.as_bytes(); let salt = salt.as_bytes(); let result = extern_crypt(word.as_ptr(), salt.as_ptr()); if result.is_null() { let err_ptr = PyErr_SetFromErrno(PyExc_OSError); let py = Python::assume_gil_acquired(); return Err(PyErr::from_value_bound(Bound::from_owned_ptr_or_err( py, err_ptr, )?)); } CStr::from_ptr(result as *const _) .to_str() .expect("OS crypt returns weird values") }; Ok(crypt_result.to_string()) } /// A Python module implemented in Rust. #[pymodule] fn _crypt(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(crypt, m)?)?; Ok(()) } #[test] fn test_extern_crypt() { let key = c""; let salt = c"$6$V.wBvD6qcC/2U9B/"; let r = unsafe { extern_crypt(key.as_ptr() as *const u8, salt.as_ptr() as *const u8) }; assert_ne!(r, std::ptr::null()); } python-deadlib-3.13.0/aifc/000077500000000000000000000000001471045437300154135ustar00rootroot00000000000000python-deadlib-3.13.0/aifc/Doc/000077500000000000000000000000001471045437300161205ustar00rootroot00000000000000python-deadlib-3.13.0/aifc/Doc/aifc.rst000066400000000000000000000167661471045437300175740ustar00rootroot00000000000000:mod:`aifc` --- Read and write AIFF and AIFC files ================================================== .. module:: aifc :synopsis: Read and write audio files in AIFF or AIFC format. :deprecated: **Source code:** :source:`Lib/aifc.py` .. index:: single: Audio Interchange File Format single: AIFF single: AIFF-C .. deprecated-removed:: 3.11 3.13 The :mod:`aifc` module is deprecated (see :pep:`PEP 594 <594#aifc>` for details). -------------- This module provides support for reading and writing AIFF and AIFF-C files. AIFF is Audio Interchange File Format, a format for storing digital audio samples in a file. AIFF-C is a newer version of the format that includes the ability to compress the audio data. Audio files have a number of parameters that describe the audio data. The sampling rate or frame rate is the number of times per second the sound is sampled. The number of channels indicate if the audio is mono, stereo, or quadro. Each frame consists of one sample per channel. The sample size is the size in bytes of each sample. Thus a frame consists of ``nchannels * samplesize`` bytes, and a second's worth of audio consists of ``nchannels * samplesize * framerate`` bytes. For example, CD quality audio has a sample size of two bytes (16 bits), uses two channels (stereo) and has a frame rate of 44,100 frames/second. This gives a frame size of 4 bytes (2\*2), and a second's worth occupies 2\*2\*44100 bytes (176,400 bytes). Module :mod:`aifc` defines the following function: .. function:: open(file, mode=None) Open an AIFF or AIFF-C file and return an object instance with methods that are described below. The argument *file* is either a string naming a file or a :term:`file object`. *mode* must be ``'r'`` or ``'rb'`` when the file must be opened for reading, or ``'w'`` or ``'wb'`` when the file must be opened for writing. If omitted, ``file.mode`` is used if it exists, otherwise ``'rb'`` is used. When used for writing, the file object should be seekable, unless you know ahead of time how many samples you are going to write in total and use :meth:`writeframesraw` and :meth:`setnframes`. The :func:`.open` function may be used in a :keyword:`with` statement. When the :keyword:`!with` block completes, the :meth:`~aifc.close` method is called. .. versionchanged:: 3.4 Support for the :keyword:`with` statement was added. Objects returned by :func:`.open` when a file is opened for reading have the following methods: .. method:: aifc.getnchannels() Return the number of audio channels (1 for mono, 2 for stereo). .. method:: aifc.getsampwidth() Return the size in bytes of individual samples. .. method:: aifc.getframerate() Return the sampling rate (number of audio frames per second). .. method:: aifc.getnframes() Return the number of audio frames in the file. .. method:: aifc.getcomptype() Return a bytes array of length 4 describing the type of compression used in the audio file. For AIFF files, the returned value is ``b'NONE'``. .. method:: aifc.getcompname() Return a bytes array convertible to a human-readable description of the type of compression used in the audio file. For AIFF files, the returned value is ``b'not compressed'``. .. method:: aifc.getparams() Returns a :func:`~collections.namedtuple` ``(nchannels, sampwidth, framerate, nframes, comptype, compname)``, equivalent to output of the :meth:`get\*` methods. .. method:: aifc.getmarkers() Return a list of markers in the audio file. A marker consists of a tuple of three elements. The first is the mark ID (an integer), the second is the mark position in frames from the beginning of the data (an integer), the third is the name of the mark (a string). .. method:: aifc.getmark(id) Return the tuple as described in :meth:`getmarkers` for the mark with the given *id*. .. method:: aifc.readframes(nframes) Read and return the next *nframes* frames from the audio file. The returned data is a string containing for each frame the uncompressed samples of all channels. .. method:: aifc.rewind() Rewind the read pointer. The next :meth:`readframes` will start from the beginning. .. method:: aifc.setpos(pos) Seek to the specified frame number. .. method:: aifc.tell() Return the current frame number. .. method:: aifc.close() Close the AIFF file. After calling this method, the object can no longer be used. Objects returned by :func:`.open` when a file is opened for writing have all the above methods, except for :meth:`readframes` and :meth:`setpos`. In addition the following methods exist. The :meth:`get\*` methods can only be called after the corresponding :meth:`set\*` methods have been called. Before the first :meth:`writeframes` or :meth:`writeframesraw`, all parameters except for the number of frames must be filled in. .. method:: aifc.aiff() Create an AIFF file. The default is that an AIFF-C file is created, unless the name of the file ends in ``'.aiff'`` in which case the default is an AIFF file. .. method:: aifc.aifc() Create an AIFF-C file. The default is that an AIFF-C file is created, unless the name of the file ends in ``'.aiff'`` in which case the default is an AIFF file. .. method:: aifc.setnchannels(nchannels) Specify the number of channels in the audio file. .. method:: aifc.setsampwidth(width) Specify the size in bytes of audio samples. .. method:: aifc.setframerate(rate) Specify the sampling frequency in frames per second. .. method:: aifc.setnframes(nframes) Specify the number of frames that are to be written to the audio file. If this parameter is not set, or not set correctly, the file needs to support seeking. .. method:: aifc.setcomptype(type, name) .. index:: single: u-LAW single: A-LAW single: G.722 Specify the compression type. If not specified, the audio data will not be compressed. In AIFF files, compression is not possible. The name parameter should be a human-readable description of the compression type as a bytes array, the type parameter should be a bytes array of length 4. Currently the following compression types are supported: ``b'NONE'``, ``b'ULAW'``, ``b'ALAW'``, ``b'G722'``. .. method:: aifc.setparams(nchannels, sampwidth, framerate, comptype, compname) Set all the above parameters at once. The argument is a tuple consisting of the various parameters. This means that it is possible to use the result of a :meth:`getparams` call as argument to :meth:`setparams`. .. method:: aifc.setmark(id, pos, name) Add a mark with the given id (larger than 0), and the given name at the given position. This method can be called at any time before :meth:`close`. .. method:: aifc.tell() :noindex: Return the current write position in the output file. Useful in combination with :meth:`setmark`. .. method:: aifc.writeframes(data) Write data to the output file. This method can only be called after the audio file parameters have been set. .. versionchanged:: 3.4 Any :term:`bytes-like object` is now accepted. .. method:: aifc.writeframesraw(data) Like :meth:`writeframes`, except that the header of the audio file is not updated. .. versionchanged:: 3.4 Any :term:`bytes-like object` is now accepted. .. method:: aifc.close() :noindex: Close the AIFF file. The header of the file is updated to reflect the actual size of the audio data. After calling this method, the object can no longer be used. python-deadlib-3.13.0/aifc/LICENSE000066400000000000000000000050111471045437300164150ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/aifc/README.rst000066400000000000000000000005161471045437300171040ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/aifc/aifc/000077500000000000000000000000001471045437300163155ustar00rootroot00000000000000python-deadlib-3.13.0/aifc/aifc/__init__.py000066400000000000000000001032571471045437300204360ustar00rootroot00000000000000"""Stuff to parse AIFF-C and AIFF files. Unless explicitly stated otherwise, the description below is true both for AIFF-C files and AIFF files. An AIFF-C file has the following structure. +-----------------+ | FORM | +-----------------+ | | +----+------------+ | | AIFC | | +------------+ | | | | | . | | | . | | | . | +----+------------+ An AIFF file has the string "AIFF" instead of "AIFC". A chunk consists of an identifier (4 bytes) followed by a size (4 bytes, big endian order), followed by the data. The size field does not include the size of the 8 byte header. The following chunk types are recognized. FVER (AIFF-C only). MARK <# of markers> (2 bytes) list of markers: (2 bytes, must be > 0) (4 bytes) ("pstring") COMM <# of channels> (2 bytes) <# of sound frames> (4 bytes) (2 bytes) (10 bytes, IEEE 80-bit extended floating point) in AIFF-C files only: (4 bytes) ("pstring") SSND (4 bytes, not used by this program) (4 bytes, not used by this program) A pstring consists of 1 byte length, a string of characters, and 0 or 1 byte pad to make the total length even. Usage. Reading AIFF files: f = aifc.open(file, 'r') where file is either the name of a file or an open file pointer. The open file pointer must have methods read(), seek(), and close(). In some types of audio files, if the setpos() method is not used, the seek() method is not necessary. This returns an instance of a class with the following public methods: getnchannels() -- returns number of audio channels (1 for mono, 2 for stereo) getsampwidth() -- returns sample width in bytes getframerate() -- returns sampling frequency getnframes() -- returns number of audio frames getcomptype() -- returns compression type ('NONE' for AIFF files) getcompname() -- returns human-readable version of compression type ('not compressed' for AIFF files) getparams() -- returns a namedtuple consisting of all of the above in the above order getmarkers() -- get the list of marks in the audio file or None if there are no marks getmark(id) -- get mark with the specified id (raises an error if the mark does not exist) readframes(n) -- returns at most n frames of audio rewind() -- rewind to the beginning of the audio stream setpos(pos) -- seek to the specified position tell() -- return the current position close() -- close the instance (make it unusable) The position returned by tell(), the position given to setpos() and the position of marks are all compatible and have nothing to do with the actual position in the file. The close() method is called automatically when the class instance is destroyed. Writing AIFF files: f = aifc.open(file, 'w') where file is either the name of a file or an open file pointer. The open file pointer must have methods write(), tell(), seek(), and close(). This returns an instance of a class with the following public methods: aiff() -- create an AIFF file (AIFF-C default) aifc() -- create an AIFF-C file setnchannels(n) -- set the number of channels setsampwidth(n) -- set the sample width setframerate(n) -- set the frame rate setnframes(n) -- set the number of frames setcomptype(type, name) -- set the compression type and the human-readable compression type setparams(tuple) -- set all parameters at once setmark(id, pos, name) -- add specified mark to the list of marks tell() -- return current position in output file (useful in combination with setmark()) writeframesraw(data) -- write audio frames without pathing up the file header writeframes(data) -- write audio frames and patch up the file header close() -- patch up the file header and close the output file You should set the parameters before the first writeframesraw or writeframes. The total number of frames does not need to be set, but when it is set to the correct value, the header does not have to be patched up. It is best to first set all parameters, perhaps possibly the compression type, and then write audio frames using writeframesraw. When all frames have been written, either call writeframes(b'') or close() to patch up the sizes in the header. Marks can be added anytime. If there are any marks, you must call close() after all frames have been written. The close() method is called automatically when the class instance is destroyed. When a file is opened with the extension '.aiff', an AIFF file is written, otherwise an AIFF-C file is written. This default can be changed by calling aiff() or aifc() before the first writeframes or writeframesraw. """ import struct import builtins import warnings __all__ = ["Error", "open"] # python-deadlib: Replace deprecation warning not to raise exception warnings.warn( f"{__name__} was removed in Python 3.13. " f"Please be aware that you are currently NOT using standard '{__name__}', " f"but instead a separately installed 'standard-{__name__}'.", DeprecationWarning, stacklevel=2 ) class Error(Exception): pass _AIFC_version = 0xA2805140 # Version 1 of AIFF-C def _read_long(file): try: return struct.unpack('>l', file.read(4))[0] except struct.error: raise EOFError from None def _read_ulong(file): try: return struct.unpack('>L', file.read(4))[0] except struct.error: raise EOFError from None def _read_short(file): try: return struct.unpack('>h', file.read(2))[0] except struct.error: raise EOFError from None def _read_ushort(file): try: return struct.unpack('>H', file.read(2))[0] except struct.error: raise EOFError from None def _read_string(file): length = ord(file.read(1)) if length == 0: data = b'' else: data = file.read(length) if length & 1 == 0: dummy = file.read(1) return data _HUGE_VAL = 1.79769313486231e+308 # See def _read_float(f): # 10 bytes expon = _read_short(f) # 2 bytes sign = 1 if expon < 0: sign = -1 expon = expon + 0x8000 himant = _read_ulong(f) # 4 bytes lomant = _read_ulong(f) # 4 bytes if expon == himant == lomant == 0: f = 0.0 elif expon == 0x7FFF: f = _HUGE_VAL else: expon = expon - 16383 f = (himant * 0x100000000 + lomant) * pow(2.0, expon - 63) return sign * f def _write_short(f, x): f.write(struct.pack('>h', x)) def _write_ushort(f, x): f.write(struct.pack('>H', x)) def _write_long(f, x): f.write(struct.pack('>l', x)) def _write_ulong(f, x): f.write(struct.pack('>L', x)) def _write_string(f, s): if len(s) > 255: raise ValueError("string exceeds maximum pstring length") f.write(struct.pack('B', len(s))) f.write(s) if len(s) & 1 == 0: f.write(b'\x00') def _write_float(f, x): import math if x < 0: sign = 0x8000 x = x * -1 else: sign = 0 if x == 0: expon = 0 himant = 0 lomant = 0 else: fmant, expon = math.frexp(x) if expon > 16384 or fmant >= 1 or fmant != fmant: # Infinity or NaN expon = sign|0x7FFF himant = 0 lomant = 0 else: # Finite expon = expon + 16382 if expon < 0: # denormalized fmant = math.ldexp(fmant, expon) expon = 0 expon = expon | sign fmant = math.ldexp(fmant, 32) fsmant = math.floor(fmant) himant = int(fsmant) fmant = math.ldexp(fmant - fsmant, 32) fsmant = math.floor(fmant) lomant = int(fsmant) _write_ushort(f, expon) _write_ulong(f, himant) _write_ulong(f, lomant) with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) from chunk import Chunk from collections import namedtuple _aifc_params = namedtuple('_aifc_params', 'nchannels sampwidth framerate nframes comptype compname') _aifc_params.nchannels.__doc__ = 'Number of audio channels (1 for mono, 2 for stereo)' _aifc_params.sampwidth.__doc__ = 'Sample width in bytes' _aifc_params.framerate.__doc__ = 'Sampling frequency' _aifc_params.nframes.__doc__ = 'Number of audio frames' _aifc_params.comptype.__doc__ = 'Compression type ("NONE" for AIFF files)' _aifc_params.compname.__doc__ = ("""\ A human-readable version of the compression type ('not compressed' for AIFF files)""") class Aifc_read: # Variables used in this class: # # These variables are available to the user though appropriate # methods of this class: # _file -- the open file with methods read(), close(), and seek() # set through the __init__() method # _nchannels -- the number of audio channels # available through the getnchannels() method # _nframes -- the number of audio frames # available through the getnframes() method # _sampwidth -- the number of bytes per audio sample # available through the getsampwidth() method # _framerate -- the sampling frequency # available through the getframerate() method # _comptype -- the AIFF-C compression type ('NONE' if AIFF) # available through the getcomptype() method # _compname -- the human-readable AIFF-C compression type # available through the getcomptype() method # _markers -- the marks in the audio file # available through the getmarkers() and getmark() # methods # _soundpos -- the position in the audio stream # available through the tell() method, set through the # setpos() method # # These variables are used internally only: # _version -- the AIFF-C version number # _decomp -- the decompressor from builtin module cl # _comm_chunk_read -- 1 iff the COMM chunk has been read # _aifc -- 1 iff reading an AIFF-C file # _ssnd_seek_needed -- 1 iff positioned correctly in audio # file for readframes() # _ssnd_chunk -- instantiation of a chunk class for the SSND chunk # _framesize -- size of one frame in the file _file = None # Set here since __del__ checks it def initfp(self, file): self._version = 0 self._convert = None self._markers = [] self._soundpos = 0 self._file = file chunk = Chunk(file) if chunk.getname() != b'FORM': raise Error('file does not start with FORM id') formdata = chunk.read(4) if formdata == b'AIFF': self._aifc = 0 elif formdata == b'AIFC': self._aifc = 1 else: raise Error('not an AIFF or AIFF-C file') self._comm_chunk_read = 0 self._ssnd_chunk = None while 1: self._ssnd_seek_needed = 1 try: chunk = Chunk(self._file) except EOFError: break chunkname = chunk.getname() if chunkname == b'COMM': self._read_comm_chunk(chunk) self._comm_chunk_read = 1 elif chunkname == b'SSND': self._ssnd_chunk = chunk dummy = chunk.read(8) self._ssnd_seek_needed = 0 elif chunkname == b'FVER': self._version = _read_ulong(chunk) elif chunkname == b'MARK': self._readmark(chunk) chunk.skip() if not self._comm_chunk_read or not self._ssnd_chunk: raise Error('COMM chunk and/or SSND chunk missing') def __init__(self, f): if isinstance(f, str): file_object = builtins.open(f, 'rb') try: self.initfp(file_object) except: file_object.close() raise else: # assume it is an open file object already self.initfp(f) def __enter__(self): return self def __exit__(self, *args): self.close() # # User visible methods. # def getfp(self): return self._file def rewind(self): self._ssnd_seek_needed = 1 self._soundpos = 0 def close(self): file = self._file if file is not None: self._file = None file.close() def tell(self): return self._soundpos def getnchannels(self): return self._nchannels def getnframes(self): return self._nframes def getsampwidth(self): return self._sampwidth def getframerate(self): return self._framerate def getcomptype(self): return self._comptype def getcompname(self): return self._compname ## def getversion(self): ## return self._version def getparams(self): return _aifc_params(self.getnchannels(), self.getsampwidth(), self.getframerate(), self.getnframes(), self.getcomptype(), self.getcompname()) def getmarkers(self): if len(self._markers) == 0: return None return self._markers def getmark(self, id): for marker in self._markers: if id == marker[0]: return marker raise Error('marker {0!r} does not exist'.format(id)) def setpos(self, pos): if pos < 0 or pos > self._nframes: raise Error('position not in range') self._soundpos = pos self._ssnd_seek_needed = 1 def readframes(self, nframes): if self._ssnd_seek_needed: self._ssnd_chunk.seek(0) dummy = self._ssnd_chunk.read(8) pos = self._soundpos * self._framesize if pos: self._ssnd_chunk.seek(pos + 8) self._ssnd_seek_needed = 0 if nframes == 0: return b'' data = self._ssnd_chunk.read(nframes * self._framesize) if self._convert and data: data = self._convert(data) self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth) return data # # Internal methods. # def _alaw2lin(self, data): with warnings.catch_warnings(): warnings.simplefilter('ignore', category=DeprecationWarning) import audioop return audioop.alaw2lin(data, 2) def _ulaw2lin(self, data): with warnings.catch_warnings(): warnings.simplefilter('ignore', category=DeprecationWarning) import audioop return audioop.ulaw2lin(data, 2) def _adpcm2lin(self, data): with warnings.catch_warnings(): warnings.simplefilter('ignore', category=DeprecationWarning) import audioop if not hasattr(self, '_adpcmstate'): # first time self._adpcmstate = None data, self._adpcmstate = audioop.adpcm2lin(data, 2, self._adpcmstate) return data def _sowt2lin(self, data): with warnings.catch_warnings(): warnings.simplefilter('ignore', category=DeprecationWarning) import audioop return audioop.byteswap(data, 2) def _read_comm_chunk(self, chunk): self._nchannels = _read_short(chunk) self._nframes = _read_long(chunk) self._sampwidth = (_read_short(chunk) + 7) // 8 self._framerate = int(_read_float(chunk)) if self._sampwidth <= 0: raise Error('bad sample width') if self._nchannels <= 0: raise Error('bad # of channels') self._framesize = self._nchannels * self._sampwidth if self._aifc: #DEBUG: SGI's soundeditor produces a bad size :-( kludge = 0 if chunk.chunksize == 18: kludge = 1 warnings.warn('Warning: bad COMM chunk size') chunk.chunksize = 23 #DEBUG end self._comptype = chunk.read(4) #DEBUG start if kludge: length = ord(chunk.file.read(1)) if length & 1 == 0: length = length + 1 chunk.chunksize = chunk.chunksize + length chunk.file.seek(-1, 1) #DEBUG end self._compname = _read_string(chunk) if self._comptype != b'NONE': if self._comptype == b'G722': self._convert = self._adpcm2lin elif self._comptype in (b'ulaw', b'ULAW'): self._convert = self._ulaw2lin elif self._comptype in (b'alaw', b'ALAW'): self._convert = self._alaw2lin elif self._comptype in (b'sowt', b'SOWT'): self._convert = self._sowt2lin else: raise Error('unsupported compression type') self._sampwidth = 2 else: self._comptype = b'NONE' self._compname = b'not compressed' def _readmark(self, chunk): nmarkers = _read_short(chunk) # Some files appear to contain invalid counts. # Cope with this by testing for EOF. try: for i in range(nmarkers): id = _read_short(chunk) pos = _read_long(chunk) name = _read_string(chunk) if pos or name: # some files appear to have # dummy markers consisting of # a position 0 and name '' self._markers.append((id, pos, name)) except EOFError: w = ('Warning: MARK chunk contains only %s marker%s instead of %s' % (len(self._markers), '' if len(self._markers) == 1 else 's', nmarkers)) warnings.warn(w) class Aifc_write: # Variables used in this class: # # These variables are user settable through appropriate methods # of this class: # _file -- the open file with methods write(), close(), tell(), seek() # set through the __init__() method # _comptype -- the AIFF-C compression type ('NONE' in AIFF) # set through the setcomptype() or setparams() method # _compname -- the human-readable AIFF-C compression type # set through the setcomptype() or setparams() method # _nchannels -- the number of audio channels # set through the setnchannels() or setparams() method # _sampwidth -- the number of bytes per audio sample # set through the setsampwidth() or setparams() method # _framerate -- the sampling frequency # set through the setframerate() or setparams() method # _nframes -- the number of audio frames written to the header # set through the setnframes() or setparams() method # _aifc -- whether we're writing an AIFF-C file or an AIFF file # set through the aifc() method, reset through the # aiff() method # # These variables are used internally only: # _version -- the AIFF-C version number # _comp -- the compressor from builtin module cl # _nframeswritten -- the number of audio frames actually written # _datalength -- the size of the audio samples written to the header # _datawritten -- the size of the audio samples actually written _file = None # Set here since __del__ checks it def __init__(self, f): if isinstance(f, str): file_object = builtins.open(f, 'wb') try: self.initfp(file_object) except: file_object.close() raise # treat .aiff file extensions as non-compressed audio if f.endswith('.aiff'): self._aifc = 0 else: # assume it is an open file object already self.initfp(f) def initfp(self, file): self._file = file self._version = _AIFC_version self._comptype = b'NONE' self._compname = b'not compressed' self._convert = None self._nchannels = 0 self._sampwidth = 0 self._framerate = 0 self._nframes = 0 self._nframeswritten = 0 self._datawritten = 0 self._datalength = 0 self._markers = [] self._marklength = 0 self._aifc = 1 # AIFF-C is default def __del__(self): self.close() def __enter__(self): return self def __exit__(self, *args): self.close() # # User visible methods. # def aiff(self): if self._nframeswritten: raise Error('cannot change parameters after starting to write') self._aifc = 0 def aifc(self): if self._nframeswritten: raise Error('cannot change parameters after starting to write') self._aifc = 1 def setnchannels(self, nchannels): if self._nframeswritten: raise Error('cannot change parameters after starting to write') if nchannels < 1: raise Error('bad # of channels') self._nchannels = nchannels def getnchannels(self): if not self._nchannels: raise Error('number of channels not set') return self._nchannels def setsampwidth(self, sampwidth): if self._nframeswritten: raise Error('cannot change parameters after starting to write') if sampwidth < 1 or sampwidth > 4: raise Error('bad sample width') self._sampwidth = sampwidth def getsampwidth(self): if not self._sampwidth: raise Error('sample width not set') return self._sampwidth def setframerate(self, framerate): if self._nframeswritten: raise Error('cannot change parameters after starting to write') if framerate <= 0: raise Error('bad frame rate') self._framerate = framerate def getframerate(self): if not self._framerate: raise Error('frame rate not set') return self._framerate def setnframes(self, nframes): if self._nframeswritten: raise Error('cannot change parameters after starting to write') self._nframes = nframes def getnframes(self): return self._nframeswritten def setcomptype(self, comptype, compname): if self._nframeswritten: raise Error('cannot change parameters after starting to write') if comptype not in (b'NONE', b'ulaw', b'ULAW', b'alaw', b'ALAW', b'G722', b'sowt', b'SOWT'): raise Error('unsupported compression type') self._comptype = comptype self._compname = compname def getcomptype(self): return self._comptype def getcompname(self): return self._compname ## def setversion(self, version): ## if self._nframeswritten: ## raise Error, 'cannot change parameters after starting to write' ## self._version = version def setparams(self, params): nchannels, sampwidth, framerate, nframes, comptype, compname = params if self._nframeswritten: raise Error('cannot change parameters after starting to write') if comptype not in (b'NONE', b'ulaw', b'ULAW', b'alaw', b'ALAW', b'G722', b'sowt', b'SOWT'): raise Error('unsupported compression type') self.setnchannels(nchannels) self.setsampwidth(sampwidth) self.setframerate(framerate) self.setnframes(nframes) self.setcomptype(comptype, compname) def getparams(self): if not self._nchannels or not self._sampwidth or not self._framerate: raise Error('not all parameters set') return _aifc_params(self._nchannels, self._sampwidth, self._framerate, self._nframes, self._comptype, self._compname) def setmark(self, id, pos, name): if id <= 0: raise Error('marker ID must be > 0') if pos < 0: raise Error('marker position must be >= 0') if not isinstance(name, bytes): raise Error('marker name must be bytes') for i in range(len(self._markers)): if id == self._markers[i][0]: self._markers[i] = id, pos, name return self._markers.append((id, pos, name)) def getmark(self, id): for marker in self._markers: if id == marker[0]: return marker raise Error('marker {0!r} does not exist'.format(id)) def getmarkers(self): if len(self._markers) == 0: return None return self._markers def tell(self): return self._nframeswritten def writeframesraw(self, data): if not isinstance(data, (bytes, bytearray)): data = memoryview(data).cast('B') self._ensure_header_written(len(data)) nframes = len(data) // (self._sampwidth * self._nchannels) if self._convert: data = self._convert(data) self._file.write(data) self._nframeswritten = self._nframeswritten + nframes self._datawritten = self._datawritten + len(data) def writeframes(self, data): self.writeframesraw(data) if self._nframeswritten != self._nframes or \ self._datalength != self._datawritten: self._patchheader() def close(self): if self._file is None: return try: self._ensure_header_written(0) if self._datawritten & 1: # quick pad to even size self._file.write(b'\x00') self._datawritten = self._datawritten + 1 self._writemarkers() if self._nframeswritten != self._nframes or \ self._datalength != self._datawritten or \ self._marklength: self._patchheader() finally: # Prevent ref cycles self._convert = None f = self._file self._file = None f.close() # # Internal methods. # def _lin2alaw(self, data): with warnings.catch_warnings(): warnings.simplefilter('ignore', category=DeprecationWarning) import audioop return audioop.lin2alaw(data, 2) def _lin2ulaw(self, data): with warnings.catch_warnings(): warnings.simplefilter('ignore', category=DeprecationWarning) import audioop return audioop.lin2ulaw(data, 2) def _lin2adpcm(self, data): with warnings.catch_warnings(): warnings.simplefilter('ignore', category=DeprecationWarning) import audioop if not hasattr(self, '_adpcmstate'): self._adpcmstate = None data, self._adpcmstate = audioop.lin2adpcm(data, 2, self._adpcmstate) return data def _lin2sowt(self, data): with warnings.catch_warnings(): warnings.simplefilter('ignore', category=DeprecationWarning) import audioop return audioop.byteswap(data, 2) def _ensure_header_written(self, datasize): if not self._nframeswritten: if self._comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722', b'sowt', b'SOWT'): if not self._sampwidth: self._sampwidth = 2 if self._sampwidth != 2: raise Error('sample width must be 2 when compressing ' 'with ulaw/ULAW, alaw/ALAW, sowt/SOWT ' 'or G7.22 (ADPCM)') if not self._nchannels: raise Error('# channels not specified') if not self._sampwidth: raise Error('sample width not specified') if not self._framerate: raise Error('sampling rate not specified') self._write_header(datasize) def _init_compression(self): if self._comptype == b'G722': self._convert = self._lin2adpcm elif self._comptype in (b'ulaw', b'ULAW'): self._convert = self._lin2ulaw elif self._comptype in (b'alaw', b'ALAW'): self._convert = self._lin2alaw elif self._comptype in (b'sowt', b'SOWT'): self._convert = self._lin2sowt def _write_header(self, initlength): if self._aifc and self._comptype != b'NONE': self._init_compression() self._file.write(b'FORM') if not self._nframes: self._nframes = initlength // (self._nchannels * self._sampwidth) self._datalength = self._nframes * self._nchannels * self._sampwidth if self._datalength & 1: self._datalength = self._datalength + 1 if self._aifc: if self._comptype in (b'ulaw', b'ULAW', b'alaw', b'ALAW'): self._datalength = self._datalength // 2 if self._datalength & 1: self._datalength = self._datalength + 1 elif self._comptype == b'G722': self._datalength = (self._datalength + 3) // 4 if self._datalength & 1: self._datalength = self._datalength + 1 try: self._form_length_pos = self._file.tell() except (AttributeError, OSError): self._form_length_pos = None commlength = self._write_form_length(self._datalength) if self._aifc: self._file.write(b'AIFC') self._file.write(b'FVER') _write_ulong(self._file, 4) _write_ulong(self._file, self._version) else: self._file.write(b'AIFF') self._file.write(b'COMM') _write_ulong(self._file, commlength) _write_short(self._file, self._nchannels) if self._form_length_pos is not None: self._nframes_pos = self._file.tell() _write_ulong(self._file, self._nframes) if self._comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722'): _write_short(self._file, 8) else: _write_short(self._file, self._sampwidth * 8) _write_float(self._file, self._framerate) if self._aifc: self._file.write(self._comptype) _write_string(self._file, self._compname) self._file.write(b'SSND') if self._form_length_pos is not None: self._ssnd_length_pos = self._file.tell() _write_ulong(self._file, self._datalength + 8) _write_ulong(self._file, 0) _write_ulong(self._file, 0) def _write_form_length(self, datalength): if self._aifc: commlength = 18 + 5 + len(self._compname) if commlength & 1: commlength = commlength + 1 verslength = 12 else: commlength = 18 verslength = 0 _write_ulong(self._file, 4 + verslength + self._marklength + \ 8 + commlength + 16 + datalength) return commlength def _patchheader(self): curpos = self._file.tell() if self._datawritten & 1: datalength = self._datawritten + 1 self._file.write(b'\x00') else: datalength = self._datawritten if datalength == self._datalength and \ self._nframes == self._nframeswritten and \ self._marklength == 0: self._file.seek(curpos, 0) return self._file.seek(self._form_length_pos, 0) dummy = self._write_form_length(datalength) self._file.seek(self._nframes_pos, 0) _write_ulong(self._file, self._nframeswritten) self._file.seek(self._ssnd_length_pos, 0) _write_ulong(self._file, datalength + 8) self._file.seek(curpos, 0) self._nframes = self._nframeswritten self._datalength = datalength def _writemarkers(self): if len(self._markers) == 0: return self._file.write(b'MARK') length = 2 for marker in self._markers: id, pos, name = marker length = length + len(name) + 1 + 6 if len(name) & 1 == 0: length = length + 1 _write_ulong(self._file, length) self._marklength = length + 8 _write_short(self._file, len(self._markers)) for marker in self._markers: id, pos, name = marker _write_short(self._file, id) _write_ulong(self._file, pos) _write_string(self._file, name) def open(f, mode=None): if mode is None: if hasattr(f, 'mode'): mode = f.mode else: mode = 'rb' if mode in ('r', 'rb'): return Aifc_read(f) elif mode in ('w', 'wb'): return Aifc_write(f) else: raise Error("mode must be 'r', 'rb', 'w', or 'wb'") if __name__ == '__main__': import sys if not sys.argv[1:]: sys.argv.append('/usr/demos/data/audio/bach.aiff') fn = sys.argv[1] with open(fn, 'r') as f: print("Reading", fn) print("nchannels =", f.getnchannels()) print("nframes =", f.getnframes()) print("sampwidth =", f.getsampwidth()) print("framerate =", f.getframerate()) print("comptype =", f.getcomptype()) print("compname =", f.getcompname()) if sys.argv[2:]: gn = sys.argv[2] print("Writing", gn) with open(gn, 'w') as g: g.setparams(f.getparams()) while 1: data = f.readframes(1024) if not data: break g.writeframes(data) print("Done.") python-deadlib-3.13.0/aifc/pyproject.toml000066400000000000000000000017701471045437300203340ustar00rootroot00000000000000[project] name = "standard-aifc" version = "3.13.0" description = "Standard library aifc redistribution. \"dead battery\"." keywords = ["stdlib"] authors = [ { name = "Python Developers", email = "python-deadlib@youknowone.org" } ] readme = "README.rst" license = { text = "PSF-2.0" } classifiers = [ "License :: OSI Approved :: Python Software Foundation License", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", ] dependencies = [ "standard-chunk; python_version >= '3.13'", # chunk uses same namespace as python core module, so only install it on versions where it's not included "audioop-lts; python_version >= '3.13'", # audioop uses same namespace as python core module, so only install it on versions where it's not included ] [tool.setuptools.packages] find = {include = ["aifc*"]} [project.urls] "Homepage" = "https://github.com/youknowone/python-deadlib" [build-system] requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" python-deadlib-3.13.0/aifc/tests/000077500000000000000000000000001471045437300165555ustar00rootroot00000000000000python-deadlib-3.13.0/aifc/tests/__init__.py000066400000000000000000000000001471045437300206540ustar00rootroot00000000000000python-deadlib-3.13.0/aifc/tests/audiodata/000077500000000000000000000000001471045437300205105ustar00rootroot00000000000000python-deadlib-3.13.0/aifc/tests/audiodata/Sine-1000Hz-300ms.aif000066400000000000000000001704001471045437300236110ustar00rootroot00000000000000FORMAIFFCOMM8@@FLLRSSND tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,, tt""-O-O7*7*@@GGNzNzSSWWYYZZYYWWSSNzNzGG@@7*7*-O-O""tt ,,SSұұֿHHxx((bb((xxHHұұSS,,python-deadlib-3.13.0/aifc/tests/audiodata/pluck-alaw.aifc000066400000000000000000000153761471045437300234100ustar00rootroot00000000000000FORMAIFCFVERQ@COMM @ DalawNAMEPluckAUTHSerhiy StorchakaANNOAudacity Pluck + WahwahSSNDTڽ*?%M$b' 9  f6/gzd5fbg%_=套:**=('16f- c5;4774Kk(gspv}u&\*# 43ꆗ ,4/O >5f4`45(r5YY[uH:\چ斖 * >52͔=*2ap-m 35445,E8̓d7YC4\Ѻ3*.06aꠓk*9ZN!4 75445h'!1кAzZo^콒0.*;5껓*e8o@;85444445tm2"6]Z5@緗9&.:1옕(02D7' 5 4644 bd-4\J3rM(;g08#& 0[& 4 4044 q"4m_}6ge\t*|?Y&ܔ>,9B$47<4|5U&?Aaih{g풴('ὕ 둺);u<47?45~:%5}hFQ"p$㣕'=ח)`=w4417724 k<$ڝ fyYk%036'괝-0 q<677550`1'kf5$o\ ⟐$'B975 775= zb5$ bsco&&öז6 Y==5 77z52Je %1C `'ƾD>=ӺջĖ!CW >6T6650 ܌u9si 5 wPa1nkw;Y٧쵞w87D3>5115 0t鴄09A77 ʀ 7]e>y<3x 97501 2l7>  671op3w3< >73163S逝7?P 417ie7 5669d x527?6{4l47띲a4?}7464ta[ӆ r 05; 7585~0 9q3M57 ŅR`ҙ i0`:u4  9 7 Ju6 콖>F>57 nJ  n78M= }22p5鴓e=: 4~es=8 =z ωඐ4:0 4[p L ܷ Լ5; 0g֙>? 7䴘V  j}e紛9d3d xdKڌc0? 7 䵕qb෌ю joఆ0L= |ggI耞5< 47{ z_xi㊍jW2 c nqy_jhz耚3 16djocፀg67  wdyzw@gn6501 mP~encEW 45njbbspdgai 510hsEQhih͘䓃 7ccRInjldn5705 Vuc`RߝoGW57 Qj{ylma떇b5 15uafyߔ^UeM|76Xbbyh_쇐d5 1uaGxg򴝊sS77 flnPᆔqo 1 i`tEKŴ눔eU64bfښပve51  wscjԇgX75vW  qNy@56 i@f]dD4 _v S}a 7ϒ瀙v}en{  egl k5{텙vAрscc ol ahKUme`畐MG Fb sꕃ`vtꝅ@ߝdiFXUfr`h[闆z jn^څsBve˜T`hfFa필K}Fr흙TLJ@z}jct쏑kvxqdᘖTmqiݒ܄ńT`raevnBTodsekœ̜C{jP}ǞٛĞoNNEgZ^؂}tJo`bMnaeZfݔ̑ܜޖЄpSрVZ@gokkzߐalF`F`@ҖʇRنʐ_ExhmR`@{k}CbinauÄQ\rmgClrqFB{`cgwnQwaW~thd_BCJ~ext`]OdiLtrctRXC_upqL{KY|mrGxo{SkI@[LLXv}iNktfheWidkaump@TT]\TGəneglAxm`mfb_fxC]TTӗWLȜʑagqf`]pfliaaWodHygBYޗ}ʓʓ|aȖ\mdWjJeimhe`[nfpvXcOEؔidAaΗ˔h~iBjaohj`Mjn`BLRUoUKTFVTޕb`aɕȔSplGsgUbnvhaktknc{\tjD]hStS@SQdmgRȕGub[veG`mMnfipiocepoLEYp^C_RToeB˕lJBa^uytfbYlgn}nobg}`uCD|ZB[_QtotgsGd]kH||g`Tmel~oobg~evNCxFMDXRGlWws}xZzPiMreeacxmxlUlbfx|puOdBO@D_TSmF@|Lg[VlAqf{g`}b{m\lbazt}wKfNIMQFXQ`tQWj`EsTbDwc~davc{mDmbaeC|kqt`KuO\CERgrnb@wa[jum|PefH`{bLbbad_~oswbutHYLF^U{x}blOKg_hIlrD{gAaycJbcadxcrqltquZN@XVrep_fotLeRnMoqHydYf~`wccag{g|pnvrtGHMEStduFxop@xQlAnwqeQg}aq`c`gzxsiqwAJOF\CdSCNvo|F|TbEnu|}{dpfp``ages~hrhqxwBuI@YUYeE^uCmxFs`XoH{sxewgsaS`ageu~n}kpevLtTKB[VV{OtPbeAwf_lLdqU~eKgrfXaageAym}kpgqIwPJMESttagBudRmCgw]|{BdsfFaage_ya}hsfquw]JLD]sqJdfNN{PbFftDrxEepgBffgeTyd|isaqtw^uOF_wrNQxauB~V`DaJLs~_zqdIgfgeyx|nscqqw[uNF^UN}T@YsawA}Ta[`ItqQ{wdJgggeyr}lpbvswDuNAYWF|]XDKfpFpgY`Oqv}Uxueudgdeyw}bpbv|tFJNAYV^}ZP@Eg}Fwd_aB}wr~IztdQddzyO}`pbw~t@KUOAXQPsCBQd~AJz]a@~tVsB{te_dez~DrfqbwxuMHVLFXQvNBzxCNyRfFxu\p|GytzZee{\sevbteJNISMFYQtu@{LB|PgGeHZq}Yyt{Dez{|UID3 ID3@  =?$aTALBPython Test SuiteTIT2PluckTDRC2013TPE1Serhiy StorchakaCOMMAudacity Pluck + Wahwahpython-deadlib-3.13.0/aifc/tests/audiodata/pluck-pcm16.aiff000066400000000000000000000323021471045437300234010ustar00rootroot00000000000000FORM4AIFFCOMM @ DNAMEPluckAUTHSerhiy StorchakaANNOAudacity Pluck + WahwahSSND3.K]1@Hl{X>O)](u *)WXq5?v+'vg0C~6>[yf]C |Q!SeCIR `< cC \ + QJC9D ^օ>3D=$oVu4ۖAD_ݮKWHOL'\\Sl_;,5 2~2# ' fyq m W W M a hNMl'? $ ݜSbQ$dKP6ފܰT-}-c) MeicA4lX$-kD r [k6I{TC~ M R 6Ѕ [@r4Lxa 2G 3R  Y/xuҀެK>$(S| gK۪9Oem*K?7f&- OCe~8$/k F* . (* _ %FT> >o0e ƝMC6sޘ" F.Cp|C cI׬R?]S]<d1P xUsWl r,;u '޾  KF|= 1b332 vPÅj 5Y߫ݠ4,4߅@n{K z_(t Xd$4 y M:b ( 9kM^ ׷ } .*Efk( ,5ԧwC W[%\۾A#4)dl"_ݏ=7` ;[ݩc2 Upo #  !# ó {շ~ \?B @u6ԱF4 խeUJt.c܀ԡ܏ݴ+ymu㣳 oZE^>T9&%aZ> h@ N`L { Y:4l C~>Y: B$qo^7 ANMĻQ77ܼ۽ܭދ?!) ߱@q I0=j:X=WHv ". 0/ Kt(0"6 7@7& M?.wnj9 D$:ò3>":3 O # /,;z` k/OH @q1YP@sQ +{RP woH/YMHh ] o<55+" L4 c ^9|ΐ܅ܘ׎(&ۮФݹr57)Vqc 'h M7>j92 +j'ԞX <jv 1 *JG?" Y (@ A3eA1~Uۚ$S6ެ޻ճ \bT$l  1䷂j E+@ $f*}=p@ v%m,PK)2 'ud{` JE TSD l +}u %5M>eM B۫%Lϑpv8!߄EU !@y= &N#="a i`U1P TOI #e 4Q=m&pS% ' k~ %9BϾ#1=AY o! o=C L\" K ^b _5(' M973VZ?}=3 Zx +Ȃ*P"/޾4RW="lp =:Lʹ F60Vl  3D%S#.:-[j#8$TJ7  R BNs )h3ʚΒ%"A ٻSޛ4 #04&zȄ $) F#)&I |5'"L6h4 JJ M+ŷڍ QCEj+ށX}X{mn" #y-Lյg,pIIFyEN!}'(I v(to1>ɇ{@ 1c _q`  ^K>E.$;9G) h) gh1i~fؒ.) 6 &Y* L(n [ A]F,mYmHE Q1J;( CӅ3'#g )=; d.p>"bTeI\ ۃ ]p  i )^*%CoX(@6" 6S:> "f2ZǍ:rmP߈ۼʦـ>q)1   -"S* YK}\ؤݷKEg! R*m)!o_D8]& .W~JB 9:l.B ޠ&,?* Jߋ[}" G , F% 5, j _ӱ1RګA[n!)F&wiJ P8^"l#= ??0Z5O ݫ*C-}'I؃}dT0)  G i-߀aqR +"H0(4$A  N_\#% zs"+U6 9% C jyyѱ n/%Z[f߉QY)#X 4 x| y&0},Gq"O&/!eo\3*%X #1& 2.R 1œw  HV*(3'R,z& ݴ;Urh,vH+ y"]}%htTOLzGg _7%|`%r$*M28p I  ybo #iLy;|8x!V  ;@3_ۭ;* \Y""u zIwc}&g&2 ^1Y"A ǦS,!RZ-@< B}C_+  4N m ? ~k.~Y].e$!h%=JN8Z+`*IS!]p.ZHT9\sg9jL S^ڰ%  P!\u-N x,$#M FaOk+ g*mv V)Sj2+Wn*NW7Ce?Bp Q%ק M74V: Iwm;#!PY4E%ixv۫ NcY0e Z c6-WX  ?~ ."Fwj T<?CB +$"wu*@)i#C{#u *Y'{ֹDC8 lt-%'*"Y _lopcN[5Z}MU5& I3z dտCO1/'P "vG<[]j 3 ,{ L  Z [LS+Ev>RF7Ya@x!  qJ#MBWߪ%)~D ]9 t2*ohaYA"("zd٥BuGA/5>O&xV$V' M\ր2 :$  d(Ax%,Dyu$ u 8ڭC4 4;: #%(!X wuaTE%dQm8?Yx  y*Q? }Z [ #=]J 0.Q#v if 0@`֝\ Fm Bp 1#cyT4 Q@N#'u'A2 :U#aG 1~.gߣSVT +HR v O* f%"$ {;r]i !^>Oe G|ak<#rKo>ی3   c6_c~ @ F` I : l$}sdE 8P3HAh5 /'p8!{h' {k vg;mN&|L c[ LY 5u~+u !  BC6 heLuN8jL`{v:F y:; D V  M/~l?t Wq#bV1QMK> 3r 8 _ l:3 }  =5+ *5SB  En \Y~  :W%@n=QH/l^-  b R L6)k5</b\O P F"G# 6 @?#  RT zto`;Hp ~z"O'40 Z j-r7! 9 P !o  uw  K sI N$|_9 (f~ gr; ' j& hnGHX BGBt?&  [Y 1 @  v kw.v{!W ?Ms   I b@5Y4 @B m( a  M! I MV?NO >Z4 <T ]6s5ch8;)x v ( !* 'mJb-    z  g SY IQ ;Vs\>V=@PLgcg h f  f&c M _ {  p Fh\$_ X,=0,_IF'&of  :Y>R sWc1~Qs;Z$dd +e:j {h<hh;q  x5N4&f h 1 yFcd2\(?L\ \qmh2!o+2ozQ kL+T zV BCRX \N`p _  ^p %W)g[%++baNR-d7?&AWAbm [$7fS={LYg  7  /ex6P7Gb[GP*C<yi C03BC[!ucl& l37_ VQ# + ) l (w;Lf4F'pcoJ`Fpq`A ] 2x(w! [X9q   EcVYmMMP9#7`>Z/H6}o |]mUg1)QeL9  gPQI^ kpLTmB/#mTPz Hw+s! #QG2<Q5 9;w Q  1:7 <dhXT90?yn5|L(p?P\JL4M/DID G A:( IhFIs/i$ pn%u8&"9`d+ 8y<6}DNd uxfvEOO8Qd]G\)m9f{'v&d%Ts:[tq,lw]% #S5p'3XX8;v0\B0S4Lsk9WJYr>`3$iC2 7n uG u [sQdh5:b0Ab=Z6QD u]VVGp =`hlC'_~zS3Jx*FtLOs'go~(12|]<  2j<#OL_ s5|K[[O&)FOdqSVTk~\maGa^hLB PV;^_Fmpyqvl3@8f#gQ$P-(>eB@.>:M!zgz`0 |S6`~$!31k[=Y./S nsXXiB5 ^<"2|vP@pm.%<h"#C.tP;_s@$2mOWR1!\7p")' FQY^Tf1nHU!|[6PJ8 Z[^Wb[]<Zd[= FHz3t~Ky@ $`#5,o_w<,/F=Ij!`b!-Zc,[z:L;(2:Yahq" ,`Ers>{8 tS>],M^27u.F2o TPy,)<VTj4vqj`@~l8qc}$dd9uxq}s?LS'u9+OMq(.YIf$__f4s@V'c'kSDjCodcoMkH<W(.aC4 g3Zm;L7]j%pcI76{wyRhPt~'C!OYQNWU2;#}=>@5`.rxZ=f>E}0{N>?N7wC;4w<?#! |=n:HKZTz2 uF +& !#xg\/LNW$ >6AAOa Z0k W5 Q :S-bOD!P+v2j<[.?rDw&jM/R'#5 J`pG2*w YNQ2$EfU`z 1,m"+ D={1nD2 (JM'cGO_& *1Ie>j%ozCy8<5ID3 ID3@  =?$aTALBPython Test SuiteTIT2PluckTDRC2013TPE1Serhiy StorchakaCOMMAudacity Pluck + Wahwahpython-deadlib-3.13.0/aifc/tests/audiodata/pluck-pcm24.aiff000066400000000000000000000472301471045437300234060ustar00rootroot00000000000000FORMNAIFFCOMM @ DNAMEPluckAUTHSerhiy StorchakaANNOAudacity Pluck + WahwahSSNDM-eKZT1+CaH$}k{]WV>ɴUP+)0b\ٚ>䑽%x)))"WXpg5W>w,['*wEef5N0@48>Züwf_2uCi z`NQ dTdE?IR> ﲲ_<䴜 -cD Z| + QHoDd;6D [Hֈ:Vy/F;r$n\Sqv2Oۗ9@D^ݯ&I%J]XHjMOLJnl(%^q\Q!S 鼳km(_9,4 ݷ1o23 #T" (3 dys] jK vQ Xi WѪxP  ah hMKGm%? m'BZݡDN>՗`;R$d(KP5ފimܯ Q-!x-ǩe( MidFi d>S=2olSX&*i Dv t Q؝@r [j6I{R&X@| M ŒO C 9Ё ]@pO=4`H|a 1I K3N *L YB]$Y /tx+<ҁީLLD?>#(Qۭ}DI gL ۧ8OfC8mJ*1Jt?8Xf&U- PAe~~8"K/l DB( . (!) ^ k%U}Fߜ)T9 BVk}1e FƚKE6J}sޘ% C/Cq﹟|uˬµA?m 'byGi׬RZ>=]AR]=!pd1N u%!Uo鬓T }lG r.;r" *޺  7}KD}}F^= -_k<430 /tW^M#ÃXEh!5ՏZߨdݢ3-/3߄R@ϛoZ7 {?rJ5 {<N^0(x4 ^U+d7$3 y#䆣 N: d㰫 ( l9ײkN'\ }׹a | .?)EghB( +5ƉԤyC XX(3X۹D Z6)aOxi `PS݌ <7ݲ民A __) !=\#ZݨФ`2fJUoroo b! B ~P!ю 8 ð dxյ1} \?ڷBT :@s7үF3  կm,"dVmG"rp.dj70܂jԞܐݲKш+vn\v 5£ i5 qpWF]>qaQw9<&%n`[; i? L2\}`H= }nҭ W[a;u3lo EL>X4 2Bs{o\7, ?.MLc,P73d=ZyܭĕފJ@n )p- Vi@GpU H1R= fO%9F:X@:&,* H?,%w@38X D$9ô6|:6 %(73s rP"B 5.,9wa k/MJE BTqhc1XMQ”@s| ^Pgv + hɀ~POP sҹpfH.YKgIg [ o<3 5+$= Hn5G Ad;h \q9}GΓY܇Uܗ셌׌s(~&QۮCР<ݻo G4u[8H){Urҭc{'t琹i K8m.<=p׭ g9_M2P L+`i+(Ԡ+X fU M:4j#v~, 1 *KG@U" [x *>("  A3E}bQB;?/?V|}Tۖ$Wo1Pްx޶ն [cdQﲿ$iK  H.i Gr'?L W$:dѻ*|):sd+lK@ @۫`%LuϐNn)r;e!3D߅WET4㔍 >Bz< &N,"*> t}"_i!*LjM^W61~M ܵT!OI :$a /Q@k&oR$ 'ky~^B'^o6BϻͪOd #-? nA݃ד$͗TJ5z!$ S` @)Os|T .)$f4\ʘOΏz&O"L>N#ٿ$Oޝ34 >#03 $|Ȉj k),v /pGFhY=[#$D&E {67'-YQ 6i76 r"JXLI ' M8*AŸ'ڍ QABmBk)ށ=W~U~uEom"o #yR+Mճcg+^pGJYEy~CP*"E!}' (I. {w[&*um1<6KɈ~@ 1aW p]rI va  ]I~6>}D"1xO/$f:G: EsQ,m e) ff.Ixl"B`_ؓQ.' 8 ?\*&Y* N(n] [ A+O\G{,i!Zl_GD Q1I; ) D_Ӄ4m)ױg$\gu  Ϡ)R?KW9Lخ a5.q>E5 aTbL`{q ) hۄ [ot  gh )[p*)%OXEZPkoDQVK&(\@7&! 5ݖR:0> *"fn1XNJ=>oSoN5߉J ۾SʤHق:t[)-- # \{ @-ߤ" OQ3* XKp~o]iأYݶCJ2EHΕe! Q|*lX)`!ne_ E4`Z~4& W,U}YK,B 6L:l.B? 1զޞ&+=>h( K߉Wj]Bz<" D} /, Cg% 3, !j )^Ӱ1x|RګiABZY?Gql!~)F/&awhܚH NZ7E^"j#: @!k1^AV0cZ`5 ,XQ ݫ7 Q +B)+OV.æ{|'J(؃W|dG_Ux0'J , HO lcHm#'߄p_q9R 3?."E/(6X$C$ N_ 2V϶#>%` ?x9t"(Xr6Q ;%} D kſ9{>fy*/{枻+ѱ n-/r%Vr^c;ߌtMY)x#UC 5R wO{ {".|-'F7r{"M&ɋ,(!bSrfPl^2o,%ɩ[ #4{$%̹ 2.PҼ 7(1ŒҼv  JԭS1,钩' 54&hP$+zs&( k S "ݲN7Xps h)4)jtI{. u"_{%koGY6KNh^y.绔Dh[ \22~%}+_%[plQ( *MQ25r: MH ᘌ@ sr ^p( #h݉Kx<%zM:1v!V E K;?E2"`۫>)? ^V_U" o_"qt pN̛0x JyRٸe|&fmѬ&Q3l _"̖1X+"(A ͗ǧi+U>d* }$HU.j\S],n QB7}B{F ]\}-ڲ  0zTL] wkK ,= S i0<|@Y!'C\.du$Q!j%:XL N`R<8[c+`*HT(^Rp. 02 XBIqhRd9|ϙ'`Asd1;gdM $kx%Um\)/ڭ&  H$yM"h\t-&m/sN wa-u$4#M G^QSh, h.*l m׀wFX'S禷ƴh3R)neWm)dM5JW6XDe5@h>8l IPS%cק[Ld y|,_Rq0A3vWR7"Qe/Fxjv6-R#!M\b3GS%@l>zbdktT۪ 2MwcXw #6-rcgF g] 5`Lh5,qWLCWA$ C AF+Kd"rDxiGۥv SC<?:ODh^@K  ?(E>;*䓼$ }ys>r)?u*:Thݘa".Dy$u6 ,V%:E~ִ@Eu]I'c۲6 jp%0X#R$5w(K~ pjX* `k cnp"A~RdXLX7VLHQ7$<6t J2wR \blտC)M2O;-m&,Q" ":v"F:[\^- k;Jy 3@M (phN| ؋ [ Y]N^O-Equ>PH5XQb=Ls.z&$UM {m qIC #M@\YQߧ_'Q(+{TrB3 ^28d\v/.,YES"ne$S`3XWlC?-!j߽~(B". {(c*٢CtqG C.nY.3'?46NG&7uaSI&T'! Ni[~)10 #6vyӧ)W*pEvwI# xAv r4] S}ڬD=2 44<7 $$U&w[s |wuz`U0C&sbr#Oʙm68Ţ=-Zqx1 ~ @zƽñ )mP~> XI ])g 3#٘WѶ+>YM>. q2X-LP!bvq &idʢ0 ?/>`֚ 1["hLɖު  Cn Aq 0g#bMz|V01s V>P#,~ˋ> L#RY)s'@W8/E Ʈ;Rj%Pa;H~6 .?} .IngߢTS U*~| .hC|U+ ʹ!vvu P( fE`>*”( ˞}8ZUnc`f$ W r!j~qXiފ`4< Ne YF{_*`)n{q:U"qIqAoKۊM4 `¾ _6 w*eD!99]Jaob{d > Z\EcaTAJ8 9 " k|&|ppgA 378vN06v$xBD~]g6U 1j)m76"45ziP%) `xTj\ wj05n6nwM%}#&KM ʋȌcZݾ )KX' 3u|si~-ry "5 $ >6Bnj71 icjvc5{@ؒ'JhvM$7iK^A!5y zRxbp {:I%} J|R`<)?vI2̜  @B$  W   L/3 ~Yj؜? wg T0r"=vcuEXcB.&' OKP9!  @ [ŵ/p t.B37Z¿ _ k:s3Or }SPLoc J( >42) B&8P?DI  CCn5 ^[W  9BX(%@ykת=Q9FB>-m[_{+q ) _ U o!KZ7(,k 5'<-]쒾b/[XS J A zF"VVH-"0}e ޳4i A<& %< R,Q ws]m_aڄZ3CǽW5x  >@ m' hcf D~ eN_ v oJa MKCU1 =N<'Oû @Vn7 f ;)R/ A\5t2dg|f78V<'huv u * #[( $"Bcc 7*f;wg@8նm`#AEvhz:jk7X%sh   bw,7EK -E1T(Vb#f8 0R xDEcmm=_e4>&x]&m?;L}gc \ ][BqTkjR/U47$$K'n*&/EqZxuӶnP  i$M),b8S< ;{\a YK ATCQ^VV OY4N:`K,{sBp a T 9]p$V4~M)jf[r3$)-iT-b?`AđIyW.Ǭc8wֱ|<(]?sTKAtcmtn \mX#"6fSm=gzKYg  C 4k  /bcy}4M&5E[dXC TDi O)CKP<9z{Ch A03[@‡A]Sh"jrdjn# i >S1ͪ30[ ?͹t;VQ#PI)n$ ( k )1u=:$MOHf/g2Gx(fAX3(ofxuL'bT*NlL^Gc>omq_@ H_1 1wM(d7S4t# YW:=pI B  ݷF1cV?iXnBdLk(Q) S#6̙$x Z,6a= ZL.nI~E=Θ14}n }Z(k4Ve3&-*P@z#dK_H0<^7 H eQQIrbK n-r I:U(l-?á+:%LqAWgJOg{JֺհKu_*s! p%RG2H50>!N91  7d9>5Rw3i>R'  /=H:߅ 6ΣfL;gkV|)OS۲8m/ŕ=pvz~Qx&<m6{PFH(HmrC9ODZLPjDM2Fպ}9L/aF0 J~B F qA8"&6$ FQm3gGT K`r0ge}#S~vwrullhW(z#rr9%"9U^_ Ϊb\`X ,46}yj@Ao6{(BCKwe ȃvHvgwrؚENATM94 5QNe[ H[)Om`;P˄c܅z 'u$(fo&hSdswHX7E2,Wq)s*kVyYuˣ v &[Q. 5 :"2N9xs҅qh)!H1|ZE[5i:gB$v$/'\HC.S04LMqlH7X:HXD*Ikr@]4% Rǥj@2%g 6h l< w:G ,w O˛YqQ; dh2;b/{h E7\9[S:?6j5?Lۄ|TvA'w,"`vZheWToImZ@ n+nBZ do'C3,[ֶj}{jRF26R9}jI/-IwO(uFwv%HgO?Ũo{}\*eO2nٲ|±'=00ՀzZS < J \4M.Yk;2#d&RhlK._ V\!;s5ySzK[[XL](*(DsMe qRTaCVD̙Mj}[maH]{YL]iCIQ>vIQV8#bYK1gnmoxqDM{vDkx2@8vXfa#,L1DeO$;N/XX+mI;g?CQ/ ŷ>:&Mpղ ԝw!fz]/ҍ zS4a{qOb!u"gWť20em[A;@[X.+Tݡn`d}lauVܝpLU lC7Ҿ2?/+5V^:S'/ w{z!tӧO Bml1[b/3!B~`<7Um"gs# >?<4o yP79G(n[b7tA< Si&31l {jQ$T Ǣ|arO|3\ 6 g>\FpV${%)ʔ&t2 rDP=Q SX>={D qidծbg 1- F*w 4Wv7yk@rsEa&r I.%Oq"'N`n2rc  EJ}hwaAy`JFz**Rj$$g5`Xכ3yQ_(&d[S?LY1]Th1[oKRkh. ~HݢWh2RC0L}}ݴA6\'ZW?ZvA[^  TdXPX?@W9Pzr&k5_Lvk vW,'U.YR/q{5Zfwghnq"mxzY;WeHGh!\ݢ *T:% g+nK D:G ~ujv])Rx:>[D#ײ!$OGSGL c5I4"OaPb9<A;xoA-G\Ee1Z+=zٸ] bORC<bsIix0v+3Lx;̞@n &$^#4*Zr8\w=v+bSׁ.Fz 9}\MnP4dA!aWŋҁaM!,DZzűb- \'>3 +~Z=M iUr=$HB>@2J:[Wgs^Hg6)p" D-+֘_}GnsQ1]>~}5W vOy:$._ـ/&I\417e#kΞTr,Kr+ԩ|ںCh#o2p>JSۼNy7 (D+9LBOԉWh\l7@ur?gKcvx;'?K,JrT(/9D֑`ܻZFg"`Z\kժ=d%b 5swBgU&oeb&(]OImO!@hC pNfcmwPgH<1W(-bN@t1\p <f~4QYq>J\@8Zl"dwnduG*48nyw6yNNE QkL~*ds~&`C 8sNF.YRNVU:1<&5]7~S;?*?x#h(63c~W/C ]rUxX^`=)i!rA:@-}/L@#=q=/P6\q3y(By<6gt=]?#H W֡ };n97GjI h_>UX;_cmMeY18AyjH:I:sYNi8^Q_DT3?!z1?h޶ UuEsx *1%9 !J$% vGLvfZ.cL[ϦdOɕ!YUY*"1 b>&:S)BB PW` = uZ.ep;UdĘ4$V, ~Rd<`ATYl/^MzC1ʥQzc)1sQ܎ɺ/jB>;6D\-t6=R=tAn5t)&êiOcp"/-bU#}8@Er^7b:lwB/2%'y #Y*OnʷMz5VvCgSU˩va%eTy^82+`Fk$9׻l*kT 'A?K{AT2jcG<0 D'$JX`L_&P~.J6CdBF O^&=%R 9%4/I(d ={he%nxE@z}C1< UP+)0b@\ٚ`>䑽@%x))@)"WXpg5W>@w,['*wE`edf5N 0@@(48>@ZwTfڀ_2uCi z`NQ dT`dE?@IpR> ﲲ_؀<@p䴜 -cD Z| + QHoDᐋd;60D [Hֈ:Vy/F;r8 $n\`Sqv2O`ۗ90@D^܀ݯ&I%J]>XHjMOLJnl(%^xqL\Q!S (km(_9L ,4 @ݷ1 o23 #T" (3@ dys] jK@ vPQ Xi` WѪxpP 悠 Ҡah h0MҀKGm%ܠ?@ m'BZ ݡDN>ė@`;R@$d(K@P:͠5ފimܯ Q@-!x΀-ǩe(` Mi`dF0i d@>Sk= 2ol@FSX&*܀i ZD2vP t Q؝`@r [j6I{R&X@ |@ M ŒO@ pC 9PЁ ]0@pO=p4`@H|a 01I K 3NÀ *L8 YBT]$pY /tx+#(`Qܠۭ`}DI gL Hۧ@8OfC8mJ*1Jt"?8@Xf&U - PA@e~~ǀ8"K@`/@l DB( .P (!) `^ k %U@}Fߜ)T9 BVk}1  e F@ƚԕK̭E֐6J@}8sޘ@ % CP/Cq﹟|uˬ€A?m4 'b@yGi׬RZJ>=f]]AXR]=!pd1N u%@!UoT @ }`lG@ 0r.(;r" * ޺  7}pKD}}PF^x=@ -`_k <430@ /tW^M#`ÃXEh!5ՏZ`ߨ؀dݢ@@3ߐ-/3H߄R@ϛo@Z7@ P{?rJ5 {<N^0L(x4 ^U+Zd7$3 y#䆣 N:@` dఫ (P l9ײkN'\ƈ }׹a@ | .?)EghB@(@ +5@ỜԤ@yC X`X@(3HX۹D Z6@)aO@x i@  `PЀSH݌ @0<`7ݲ@民Ahp __H) !P=\#ZݨФ `A2fJ`Uoroo b! B ~P!ю@ 8  ð dpxյ1}@ \`?@ڷ`pBT :@s7ү F3  կm,"d@VmG@"rp.dj7@0܂jԞܐ@ݲK|ш@+vn\v @5   i5 qPǠpWF`]>q@a@QwH9@<&%n`'ը[;< i?@ L2@\}`H= }n@ҭ W[a;u3<lpo E'L>X4 2Bʀ s@{o\`7, ?. MLˀ(c,@PPր73d=pZ@yܭĕފ@JH@n )0p- Vi@G 伀@pU@ HP1R= fO %p9F:X@@:X&,*8p H?,%`w@38X@` D$9@0ô(6|:6 %(73s rPȀ"0B 5.,9P0wa k/MJE  BT`qhc1X@$MQ”@@s| ^Pgv0 + h ɀ~POP@P spԀfH.YKgIg P[ o@<3@ 5+@h$= Hn5G @pAd;h \qp9}@GΓY܇Uܗ셌׌s(~@&QۮCР<ݻ@o` G 4u`[@8H@){Uؠrҭc@{'tiŀj K`8m .<<=p׭ @g9_@M2aPP L+`i+`(@Ԡ+X fU Mp:4`j#v~,@ 1д *K8G@U`H"@ [x *>("  A3Ep}bQB;? /?@V|}@T`ۖ$Wo1Pްx޶ն [cdQﲿ$iK Ƞ H0 .ip Gr'(`?L W$:@@d@ѻ@*|݀`):(sp@ɰd+lK@@ @۫`%L@uϐNn),r;e!3D@`߅@WET4㔍 >BzP< &N,@"*> dt`}̀"@_Pi0!*L@ jM ^W61~M ܵ`T!@OI :$ʀaH /Q@kB&oR$Z 'ky~^@B'^o@6BϻͪOd #-?@ @nA݃ vדn$͗T J5z!$Xְ S`` @)O߀s| T .)$1f4\ʘO@Ώ@@z&O"L@>N#@ٿ$@Oޝ34 p>(#03@P $|Ȉj k),v@ @Ƞ@/pGFh0Y=[#$Dۀ&E @{`67'-Y`Q 6@i76 r "ZJXLI '@ M8`*A@Ÿ@'@ڍ QA`Bm&Bk)ށ=W@~`U~uE@om@@@0"o #y@RX+pM܀ճcӨg +^0pG΀J$Y@E`y@~CpP*L"`E!}ހɠ'@ (I .@ {w[&*u m 1}@D"1xO/߀$f`:G@: E@sQ ,m@ e@) fPfP.@Ixl"B`_XؓQ.' 8 ?`\@`*&Y* N(`n] [ נA+@O\G{,i!`Zl_GD Q@1IӰ; )d D_Ӄ4m@) `ױ g$\@gu@ @ Ϡ)R?KW 9L0خ a5.q>E5 PaTb@L`{q p) h@ۄ [ot@ @֠` g@h )[p*)%OXPEZPko2D`Q VK&(\@@7&D! 5PݖR@: 0> *"fn1dX㰠NJ0=>oSoN5߉J ۾SʤHق:єt[)--@ @# \{ @-πߤ" OQ3`* XKp ~o@]P@iأYݶCPJ2@EHΕe@`! Q|`*lX)`T!@neC_ `E4ƀ`@Z~@4& W@,0U8}Y K,B 6L:l.B? 1Pզޞ`&+5=>h( K@߉Wj]Bz<"@ @ DX}@ /,p Cg0% 3@, !8`j @ ) @^TӰ1x|RګiPABZY? Gql !~)F/f&@XawPhܚH NZ7E^"j`#: @!k1^AV0cZ`5 , XQ `ݫ7@ Q  +B)+@O@V.`æ@{|0'J(@؃W@|dG@(_Ux0'J , HO lcPHm@#@'߄p_ q9HR 30?.@"E@/ (6@X$C ߠ$0 N0_ 2V  ϶`#>0%຀` ?x@9 t"(Xr6Q@ ;%} D0 kſ9{>f@y@*`/{枻`x+ѱ@ n -/`r%Vr^c;ߌtM@Yh)x#UC 5R w@זO{ {"`.|-'PPF7r{"M@&ɋ@,(!b΄ PSrPfP`l^@2o`Ҁ@,l%ɩ[ #4{$%@̹@ 2.PҼ 7(1ŒҼpv ` JPԭ S1,@钩@@' 5 4Ր&hpP$+zsP&( k S "ݲN7@X@ps h)4)@j@t`Ix{. u"_{Ӡ%@koGY6KNhPL^y.绔Dh[ \@22~%}+@_%[ `plQ( *MQQ25r:` MH 0ᘌ@@ spr` `^p ( #@h݉И`Kx<%zM:1(v!V @E K@;@?E2񌲀"`ΐ۫>@@0)? ^Vx_ U" o_"qt pPN̛n0x J yRٸe`@`|&fmѬ&Q`3l  ` ˈD_"@̖1X+"(AP ͗ǧi@(+@U>d* @}`@$ H@U.n QB(7@}`B{F ]@\}-ڲ  0z TL]P wkK0 ,=٠ S iX0<|@@Y! 'CH\@.`du$Q!j@%:XL N`ꆠR<8[c8+@`ʐ*HT(l^RpR. @0 2 XBpIqhRd@9|`ϙp '``VAs8d1t;gdMѨ $kx%Um\)@/@ڭ&͘ P Ő` H$yM"Ph\t-p&`m/s N0 wa0-u$4#M G^QSh`,0 h.*l m@׀wF@ X'S 禷ƴh@3RH)neWmD)`d(M5XJXW66XDe5@h>8l IP S%cק[@Ld` 0y|`,@_pRq0A 3vWRx7P"Qe/F xjv 6-΀R@#!M\bṮ3@GS删%@l>zbd`k7tT@۪ 2@Mw0cX&w #46-rcgF g]  5  `Lh5,@qWL`CWA$` C  ( AXӇFX+K0d"rPDǰxiGۥv SC<`?:OlDغh^p@K  ?(E`>`;*@䓼$ }@y`s>r)?uh*:ThݘaH".tDyp$Pu`6 ,PV@%` :PE@~@ִ@@@Eu]8IT'`c ڠ@۲@6 jPp%0Xj#PR$5wH(HʞK ~@ XppjX* ``k @嫈cn p"A@~R d8`XxLX7VL|AH@hQ7$<6@t J`2 wR \blտC)M2O@;-`m &,Q"݀ ":v"F:[\X֠^- k;JyP) 3@ 0@M  (p hNP| ؋P @`[`  JY]N^O-ʀEq  ^u>ΠPH5XQbd=LsP.z&p$UMX {m@ qIC #M@@\@YQ@ߧ_@'Q(+{@ŠT rp@B3@ ^2pj8dP\x`v0/.H,YES"nL`e$pS `3X@W@lCn?-P!j߽h~(B". {(c*٢CtqG  C.@n@Y.3'x?46NGb&7@uaSI#&T'!` Nih[԰~)10p #6pvPyӧ)W`*pE4vwI# xAv rP4] S}ڬD= 2 04 4<7xP $0$8UP&wp[&s( @|w uz `tU0HC&sbr@#@Oʙmp68`Ţ=-Z0`q x1` ~`  @z`ƽñH@ @)mP~`>䬰  XI ` `])xg  3`#٘@(W PѶF+@>YM>. @̀q`2X-LP !bPvq &i"dʢx0 ?/>``@P0֚ @1@["@h0Lɖ`ު0  C`n `Aˀq 0pg#DbM@z| V01s8 V0>XP#@,~ @ˋ> p臘L #R@Y) s 'N@W@8/0Eh P@Ʈ@; Rj@%P`a;H(~6 .P?4@@} @ `.IngߢڀTS U*~ | .hC|`U+ ʹ0!vvu P(٠ f0E`>*p” (` ˞}8`ZUnc` f$ W r !j~q`Xiފ`4< NSe p@YFϰ{_ 6*` )n{pq:U"TqXIڀqAoh5K ۊM4@ `¾` _P6H: w*@eD!99`x]J(aPob{@d > Z \EcaTA@J8p 9@ "@ k| &쎠|pƠpgA 3p78`vN06 v0$x BPD~]pg(6U @ߘ1j)h m768"45`z0iP%) ``x Tj\ w@j05T8nh6nwM %}#&8K`M ʋpȌ c@@@Zpݾ )KX' 3u|si~-`ry@ "5 $ >t6`ѠBnj`7 1 i`c@jvc5@{@ؒ'JhHv M>$7Xi٠K׸8^A`!50y( zR xbp `{:`I%} J|@R``<f) :?vI2p̜  @@0B$  `W @ p L/`3P 0~Yj؜p? wg T0r@"=vcu@`EXcB.&'p OKP@9!  @ [Xŵ/ p` h t.(@B3`7Z¿XX8 _ k:s3pO@r }S`PLoc J(` >@42) BP&X8P?DIp  CCn5 ^[W`   9BX(%`@yk@ת=٠Q9@FB>- m[|_{+q @)@ _` U o!pKZ 7x(, k 5']<@-]x쒾b/p[`XXS J A` zF\"VVH-"0X}4e ޳04ip A<&` D% < 0 R,Q Pw`sP]0m_a@ڄ<@E;.o|`˩ ɉP|PyL!p#p#N'3m0  Yp| ?hh@d@ɐ<&!Ơ*tq4yp#(@ 9] ( O? j8 !SnR ˀ\,< ޻~NxRpt<` 1`D*ΐ8 KP` qPJ0ŷ@ M{"X~(y(^B9ƀh@ '" fT~ Clpl`frnϐZ3`^CǽTWp5x 0ψ >@@P m' hycf P D~ eN_P v oJa, MKD`CU1p =N<'Oû @Vn7 fd ;)`R/z 0A\5ːt@2dڀg|fH78 V@<p'huv҆ u `* #[(X @ נ$<`oW*sL< ,ی|8@b,` @% n5 PL y:  eP|P 6PP@Wa 4\xI O@N̐ 8o Xx}Xĺhҙ?Hǰo L f`|Z'8`:7`RW?@XOpK`fٰ3`api߀q c@ ~pe@ G@2 ?pM ,uͅhq&ޮ0`q@z J,L ]h T{I p@ p  vpԞP4EHhѐ[(x"&z_FƜ _ `рUVH.i;{< /m``X`.D_ GE`% *hj@i90bPa) ְU` +9P`Xx=J8Spn(˫8 ! PrdWT=_4G!`f]PO38r~(Q:#X[h \|W>"B@|cc` ֠7*f;w(hg@@08նm`򆶀h#AEvhz:jkX7 X %Ps`hh   bw,7E@K X-`E1T(Vb(#@f8 0R, x,xDE#cpmm=x_eX4>&xx`]<&mH?;L}gc \D @]p[pBq0T@k,jRx/U 4V7\$$K'n*ܰ&/`EqZ@xu`ӶnـP@ S1ͪ380[ ?͹ht;VLQ@#P<Id)n$Μ (P `kŴ )1ux=:$M`OjHqf/ g02xGxx(fAX3(rofxhuL'bTP*GNhl@L^PGc\>so$mq0_?@P H_ސ1( 1wM( d4u7PS4t@# YW:=0pI8 B4 8 ݷF1cXVl_x?0ipX!+N`-Є901  d7dd9 >5Rw3iPH>R'49  /=H:Śx߅ 6ΣfL;gkVV|@) O1SZ۲8m/pŕP=pvHzx~ ҀPQx@&\<@m6P{P`F8HhX(HmrC`9`ODZLP6jDMW2Fպ9}t9ϲL/aF0@ @J~ PuB Fˠ qpA8P"&6x$ F@Qm3gGT K,`pr0ge}#Sl`~|v`wruHllHhW0(zh#rrR9%"@9U^_ Ϊb\@R`XS{XfD P,4t6}yj @Ao6{( ˸B\CtK~wpe ȃvH@vOg0(wrؚ>ENATML94  x5QN*e$[ HjL[ )OpmPm(`;P(0˄ c܅z 'u$(Tf8o&hxʞSdpstwHX7E2?,sWq)hs*kVPy\Yuˣ vx 8&8[Q. X5 :0H"2L0Nh9xs҅qh )h!HP1|xZE[5ci:gB$dv$/'0h\pH\Ch.ٰS004xLMql֐㷀H7X: HX1D$*IjkrP@]4%Ӕ Rǥ hʠj@hd 2%<@gp 6h l׸<^ w:pG ,4Pw O˛Y6qQ;L d xh2;(b/{(h @E7\89ި[S :B?6pj5?L2ۄ(|J$TvqɀA'` w,"`xvZheWToImZ|@D n+nBPZP ٰdTo',Cx 3,0[ֶnj} {j RF26pR9}j`I/ -IlwOX(huFj,wHv% HdgO?Ũ*o{V}\*eO02hԯnٲ|p±R'=0`0Հzݨ0Z8S1 HvIQVh8#bY\K1xgnBmo xqD߲M0{vDkx2HH@깮׸8vXfia,#,Lh1V:&HhMpղ ԝw݄!$f(z](/Ԭxҍ 8z|HS4a{q0Ob!u"gpWť32(0`\em8[A;@[Xp.8ᩘ+ΠTݡn`Hd}lauV ܝp .LU l^C7 &Ҿ2?X/v+@5X*VH^`:8S<t'D/ wЀ{z!8tӧO B0mll1h[b8/3 !SB$~op`<7@0Um "gs.# >*?<4o, yP79G(Hn[b7DtAH< S @ưi,&31l {jQ$T8 Ǣ |a(4rO|h3\ 86ނ` g\D>\FpV${%)_ʔ`0ݼ 4Pň&t 2 rD.Pnt=Q SX>={FXD 8qi(dծbgP 1- xF*w(Ph 4%WDv L7ydk@rspE߈ah&r I.p%Oq"\'נN `^n2r^c P ECJ`}LhwpaAyD` pJFz**Rhjܶ$$g5|`XכX3yQ_(&8d[S? LY1]T(hۀ1[oK Rkhtۼ. _~$HݢXWqh(2RCT0Lp}}ݴA6\'ZW? ZvA0[@^  Td8XPZX?@PfWV9Pz,r l&k5(_Lvk !vW,'@U.ƾhYR`/qx{P5Zfwghnqx"m4hxzY;WeHGdhr!H\@ݢX( *rT@d:% g̈+|*nK| D:XհG `~ujvh؜]ݞ)RQx:>P[D#ײ!`$ OGSxGHL` Xc\P5Ip`4"lxOPhaTPb(ߢ9d<A;8xoLA-G\EM<ve1Z+`=zٸ] b O:RC3Ҙ +H~Z =̘M TiHUr=$`HB>T@2J1:[Wgcs^Hxg6)p" D-+֘_}@鉈GPn<,shQ1]>0~} 5޼W vjOyJ:$.`_pـ0/&IϘ\4p17e0#\kΞdT r,KPr+ԩ|ںCh#P,oՈ2p>JN(Sl8(ۼʹNy7 (D$d+9L4BXOԉpW)h|\xl`7@ u ـr?[gKc<YV~l7;xRx#q:pvx(Ĝ;'?4K,pJ~rTZ(f/X9D(֑`TܻZތ FXgD"`ѨZ\kժ=d%pb (5Hsw$(BDgU&oPe,b&*(]lOIm8O!\\@80hdC pNXfTcmwPgHڨ<H1Wxl(-bN@<t.1\p l<\f~4QY*q>J\@j8hZpl@"dwXndu$G*(4ۈ8npyw6yN \[NED QktLݰ~*ds~&`ZCݲ 8\sNF.9YRN VU:1Byh<6gtP=]((?#H W|p֡ӌ };n@97GjPI h_>UX(;@_cmMe3Yl1l8楤A8yj@H:I:sYNi` Ӿ8^Q_BDrT3?L!z1?"h޶ UuEsPLx *1K%`9 !J$% vGLv`(f4Z8X.cL[ϦdO<ɕ^!xxYUpY:*"1B b>&P:S.)d"BB tv PW\` xh=| uOZβ.exp0N:;UdĘ4$h0V0,Hj ~Rшdx<`ATYl`/^dxMzC\1ʥ(Qz|cŒ)W1(s8Q@܎ɺd/jB(>;6$Dh\Xа-tX쓾6=dR=tPAn5t)&4êi|Ocp"/w-1b8U#kp}n8Ӑ@dE=r^罬x78橤db:ClwpB/2t%'4y #|tY*4OPn>ʷ`Mz5#VYXvC gSU˩v$a%,exTy^Z8@2+`Fk$9׻l4*kLT 'bAP?K|{AHTzbH2jTcG< 0T욖 DH'$JX`hL_v8&Pv~$.JD6hCTdBhjxF Ǒ^,&=8%(tR0 9%4z /I(d` ={hXe`%VnxE@z}Cx1l<萪4fC QIR  c  + QD $DS_, 2 y W a ? $-- XD [{  @4a 3 /> O?- e8/  %FT  6 C ?]1 Ul r; K= 3 5@ (d$ : 9k E( 5C )7 2U   !  ? @F .+ >9&  `  > B7 7)  =: ". 0 t0 @& ? $3 ,  1@ +P HY <5 4  9&) 9 +X j *G"  A$ $   $* %K d S + 5 %! &"0 O Q& '#!  "  ( 73?= +""    #-#8$J  )" #  #&'6 J  "#!'( 1@ 1  $ )  &* ( , 1;  ) .    )*%( >")  -" !*)!& :.  " % ,  !)& "# 05 '0   "($ %"6 % %)# "&!% # 2.  &  "% %%*2   !   "" && 1"      $!%+*    $#  * #!%                  "    #      #      #        !                                                                                                                       ID3 ID3@  =?$aTALBPython Test SuiteTIT2PluckTDRC2013TPE1Serhiy StorchakaCOMMAudacity Pluck + Wahwahpython-deadlib-3.13.0/aifc/tests/audiodata/pluck-pcm8.wav000066400000000000000000000151441471045437300232170ustar00rootroot00000000000000RIFF\WAVEfmt +"VLISTZINFOINAMPluckIARTSerhiy StorchakaICMTAudacity Pluck + WahwahICRD2013dataˀKȃ?~8z4skfmdFb`o`b{hoXw{u|{_{z{<|OÉAђnɎҍoNd㊈ю 6ĊVmX{ricz_B][q[]d~lxu{n}}l}v|}3߁ih'g]R׈)j]n|tkd~`I^{\z\^dnmv }^ud}~AĀgۖfIn_POPz}uyld`R^m]\^eimw~F{[~\lRTXƆzԋxF~-v]ld`Z^b]]_eonx5.W~|y݂bTՒo򇻇C^ˆ{Ey~CwDmd_]]]\]_e}oyP X^~ndF0AWņ TÊPi~[w4mc^`]Y\]`fpz{er]~R}~wr C{UE'1ƋgUmw1mc]f\T\]`gqs}r#]hy|X{{}|K0HG~sv;lwb\t[M\|^bisuu1Kwh~zlxx|{ 4􅰈)kh C}lvMlbb\[K]q_dkv|u@?_|wutx~ }Ќ9ȅو7N zN}\u\lWb[[P]caemxuL9`{tququ|x?T؍RX4DžTg}LublXb[[^^UbhozwV7rhxpmumr{r_9ˏwy*Ӄ97~Fv[m^c[Zn^Ociq|z_9cuuzmj}jpzqy1kσ3jсY)OvOm^c[Z|_Petks~|f>W~rqifhozt7n~HOq,~cvHmXbYX^Tfhmv}lFM{npdben{zEvy}y|`C~ʄ{9hxvJkN`wXW^Yg^pyoNH}xjv`^cn}zVdytxv}Bʆ}EZ~sShH^kVV^cjUt}pUGqte~[[cpwfZrms{Is|MR|o`eF[_UV`qnQytpZIgo`XZdsvsWylhp}Sd}SLwjnaIZSVXdsRes[Kak]W[hxw{Zqfdp_YXG{rf|_P[JY|]jy\Zz\J|[j]X]l}z`}wlccrjU^Cmoe`[_E^kbn|kTv`G}Sk~^Z`ozfxrhbeuwUziA}]meckcGc[fp~}XgkH~Ilt_[aqy~kunfbgyZmwE~Nogf|hQhPhp}e_{OAnd`\br|{lskedk}bdOErskijajNino|uav]ApT`\bs}nrge~fnj`_CwamjkrjUibn{kpnIqK`r[btsrffvhrt`x|{pG{Sn{jjjbi`n|{ur~zUsJ`e[cu}zshiplvcnxyT~Nmlghkpkept|{v{~_vMa\Z|cv|unlon|xljtxdPk`dzgmyomrt}|xx|iwR`WXqbw{xtppqyzukr{wsXh[aog}p{sttw||}ww~{ryX_UWhcy|{wurryz~opwvcd]^hhuuyxvuzz}}ww{{zx`^VUbdx|}~|wxsrxxtnuvmaa[ejozt|uuzv~yxy{yj^[U_fp}zuzqqvvymuu~v^hZfmlprtxq||yyzzq^aV_hj}zt{oost|jwt{~|[nYjqknoqtlzz|x{v_fVbihw}uznnotw~i|t{}ZsZnvmnmmqg{yyu}y`kVdjgsxzonmts}~it~{Yv\qzponipdw{vq}{_nVfkgo}}zqoltoxju{[v^r}qpofpcu}~sn|~_rXingmx~{vrmumrlt{~]vaqpp}pdpcu|pmz`u[lqhkt}|{uqumoymt|`wcqoo{odpet{nnycx_orjkr|}~wvsontns}dzernmzmdnfrymoygzbqqkkox~y{rsorzpr~i~gv~pmzkfkhnu}nqzk}etpmkmt}~{rx~rrwrrmh{}tozlhjikqxor|ogxopllqy}q~}wtv{tqqizxr{nijkj~ntzps~ri|mt~nmou}~q||xwz}vpujx}w|rkmlk}lqvr{tujly}soorxrz}z{}y~pykv{}vlqmm{mostxt|wll~|xsrsw|sy~~~{o|kt}~yntnpzopsvvuz~ynlz{wttv{tw}plr~{owosyqqsxuux|zoly~z~wvwz~uvpnq~|pxquwrrsyuuwz~|qlx}~zxyz}wuqoq}ryrvvtss{uuw~y}}smw~~|zz{}xtrqp}tzswuust|uvv}x{~unu~~|{|}ztstp~u{txtv~tt}uwv}wz}vot~}}}~|tu~vpw|uytw}uu~uxu|wy|xps~~~~}uw|xp~x}v{tx{vuuxu{vxz~yqs}~~uy{{q|y~v|tyzwvvyv{vwz|{rr|~v~z{}r{zw}t{yxwwzvzwwy{~|tr{~w||zsy{x~u{xyxw{wzwwy{}}urz~~x{~ztx|x~u|xzyx|xzw~xyz|~~vry~~zzzvw}y~v}w{zy}xzx}xyz|}x~sx}~{y{ww~}zw}w{~zy~y{x}xyz{}y~sw}~|y{~yv~~zw}w|}{zy{y|yyz{|z}t~w~|~}y|}{v}~{x~w||{zy{y|yyz{|{}u~v~|~~~~y}}|v||x~w}{{zz|y|yyz{||}v}v~{~~~~y~~|~w{|y~w}{|{z|z|yyzz|}}w}v~{~~~~z}|xz~}z~w}z|{z}z|y~yzz{~|x}v}z~}~~~{}|yz~}zx}y|{{~z|z~zzz{|y}v}z}}~~~|||zy~~{x~y}~|{~z|z~zzz{|z}v}y}}~~~}{}{y}~{y~y}~|{{|z}zzz{|{|w}y}}}~~~~}{}~|y}~|y~y}}|{{|z}zzz{|||w}x}|}~}~~~{~~}y|~|z~y}|||{}{}zzz{{}|x}x}|}~}~~{~}~y|~}zy~|}|{}{}zzz{{}|y|x}{}~}~~|~}z{~}{y~{}|{~{}{zz{{~|y|x}{}~}~~~|}}z{~~{y~{}||~{}{~z{{{|z|x}{}}}}~~}}~{{~~|z~{}~||~{}{~{{{{|{|x}z}}}}~~}}~|z}~|z~z~~}||}{~{{{{|||y}z}}}}~~~|~}z}~|zz~}}||}{~{{{{|}|y}z}}}~}~~~|~~{}~}{z~}}||}{}{{{{|}|z}z}|}~~~~}~~{|~}{z~|}}|~|}{{{||~|z}z}|}~~~~}~~{|~~|z~|}}|~|}{{{||~}{}z}|}~~~~}~~||~~|{~|}}|~|}|{{||}{}z}{~~~~~}}~||~~|{~{~}||}|~{|||python-deadlib-3.13.0/aifc/tests/audiodata/pluck-ulaw.aifc000066400000000000000000000153761471045437300234340ustar00rootroot00000000000000FORMAIFCFVERQ@COMM @ DulawNAMEPluckAUTHSerhiy StorchakaANNOAudacity Pluck + WahwahSSND|Í_ F 5+&-$! .!J'.=K:NLJFKQm͏./$ -J4)"G2#\+?9K.UV,X;R[ n)ř( ' )-P8*#^ JH$-,;Tkj9h$[]nʻ&%-߽ܿIV:E+# ,$(-=f߸Lk`n+ŽI?g^ <,$"$/-? ιcMhCm!ė/ۿMCb=,#%Z.Eĸۛohb-˝# ƅı(e= ,#&0FL%3Ӯm\T_ڳ>3Kǀæ 'i-= ,"% &26W3'EmRKMmZѼRǀj _6<,="7Q!(69~: =(cI+A@NKŷ Ăɗ *œ'[+;+!!2#*;P9S>98@cuVlj •GY:+!" %,?9趭 J8291:Oj?Dž Ǝ뿞'V:!,!"'/H> 3(?0-9-4J4=Cʆm ΊѰƴ _<-".#)1NF#99F,)U*/G1C *Όӟ )ڋ k<-"N$7+5\M&a32(&'.H8 -f廦1 `z#<,!|%(-;[+L-0$!$-LF堬&/Bҡ.UA)< #-XsI#B7?%o3,5M5ϏOO/ $ 1-D7֠/'. $6¦<5C+'0V#Y>*-!$6s%5u +'A>ʜM2&$/ҡL3%ON)DIS)+ZIi T>+##3),.$ *!-Q*;V*_.箬pH&A3("$:=H([-%"*##&/\[&èė+d,7!1ϻŝB\*:.%"'Bՠ,>_.'&S''(0V$њM.#!3٩OM,6*$#*U!$Ř45*)*!))./N9!;ޣȜ/"5hѯV.3'$_%.* ӚȨ=!-*+4*(".K*/ϟ-24"8Ԧ̞x63%&;(4Ϝ8?RM˞0L.L)**").OL½:3\禾G5 $":̟WF5((0+;ȝ#.?CȚҮ_-+'(*0+$/8RK;K]۽; N#<âNm9.,..OAǟ+)8?š#z*#G',B.-47Wڿ̿Om?@N(> 2">ůL?7001CGɦ:*4K>6' .'Y0L678>ONY>=bK3A("EtOM=944BGЬg/0=7M_R.)1+-.18+jFFO@lĭL;&!((>ʥ¬Z:I-./7>ΰe)N7KԝUr6-;---,2&JD˸Bt9ɬVC*$)'5ҬӤAH..,74Zg)8\睿M};1F//.(/#>JȻ<1ɪXM.&+&/Yͪ٥TG2/+8.>¨*9ԬٞǤKj<3X10y/%/":V^ϻ5.ȧOa3(.',AۮӧbJ;4-:,4oث+8ƞæLf; 2l/0X/#0#9qS׾/-ȦI :+1(+8޷˪îXSM:18,/B̬,6N =#1..M.#/$7K--ǥD#?.3)*3NŮƫXc>;6/.8ů.5U$F%4.-G,$-&3C,/ǿçD&I"22+*/@õƩ\EM46/3H˾/4_(e&;c/,F+%+(.:U-1ÿG+X%70,*-8Y¿ŨbL3@d42=j33,'JT7.G+'))+1@i.4N.'?./+,1BٸélT2_X>7;L82µ2(|H@4J-)**)d-7I06ž\4(S-8d.,.9TŶ}b2NS?=GZ<0ֹĥձ:)?X>O3+-+*V+1<4L7ȱ{9*,EW5//4A﷾|3HںTIMXB`/|֦ô͵C*;LU;,1,-M,/68?8Pùߵ=+,`N?6s35=SϾ5Cɻ^^gKg/kζɻS+7Y[C-8-0H./5<<9F_йԸB-+IM=h88üV/sٷĿx,4haK/=/5B115?:9?NͼH/,DdHd><>Haպ:<½ؿܬp/ܸ-2mfO0@19>436E9:>pHbN1,?S`FABI[ҽ=9Ŀ֬1۹Ͼ/1wnS4E3<;756K9:99z67P9;;Z?Ll`9-:a\PMOZH8ԭ6n״ֹϼ70^:L7A8;_88Z9=;T>GZp<.8jam[UV_N8׮9\ָչϼ;0~k=R9E7=T:9e9>:O=BNq?/6]bi__iX9گ־=Sּչм̿A0^?Z;J7@M<:z:A:L<;E;J<>FQmJ44Oao||z<^ݵܺIKۺ׽оW3JHn=V8IDA>=ICL]O74K`j>R۸ܸRIܻڽԾn5ELz?[9MAE@>L=H=i>BJVXj94G^gALټ۷cH޼ܽ׾8@PA`:P?IC@Q?H>_?BHP`^qVDe;U>K~FBX@I?Z?BGNnY?f5n@rZrbrwLE׷Kqܿݾھ>=e[FikVlamp|QCոM`ܾۿD<\_Il>[=OZJFkDKBSBCFKRH[8a=eRg`ilvZCӹQZ۾ۿJYGOLIXGNEeEFIMaQ?W;ZH\[^nagoKSпQBFbYHi?[ERsMJ\HNF_FFILmPCU;YF[Y]j_dl~NOϿSFD^\Km@]DTeNKcIOG\FGHKOGTUAWQYc[^dnaLr`^WCRcR{FfB[RSNKVJVHHIKMUP@T@VOX`[]bk~kMgl[aEOdUHjC]NUOLYJUIqHIJM[PBS?VMX^Z|]`ixxN^ZtFMcYJmD^LW~PM\KUJjIIKMbOES?UKW]Yt\_gsPZZIK`[LpEaKXoRN`LUJdIIKMmOHR@UJW[Yn[_eoUW[LJ^^NtFcIZfSOgMVK_JJKM}OLRAUIWYYk[^dmYT^uOI[`PxHfI\_UPmNWL]KJKMOORBUGWWYi[^cl}]S{bkUIYbS}IiH]ZVQvNYL\KKLMOTRDUFWUYf[^ckzdRoie\JVcVKkH^VXRO[M[LKLMOYSFVFXRZd\^ckykShubfKSdYLmI`SYTO]NZLuLLNO^SHVFYPZa\^cjvuUa`wLPc\NnIbPZxUP`NZMmLMNPfTJWFYN[_]w_cjvX^`NOa^OqJdN[nVReOZNhMMNQnTMXFZM\]]r_djvZ[bPN^`RsKeM]hWSkOZNfMNNRID3 ID3@  =?$aTALBPython Test SuiteTIT2PluckTDRC2013TPE1Serhiy StorchakaCOMMAudacity Pluck + Wahwahpython-deadlib-3.13.0/aifc/tests/audiotests.py000066400000000000000000000302171471045437300213160ustar00rootroot00000000000000from test.support import findfile from test.support.os_helper import TESTFN, unlink import array import io import pickle class UnseekableIO(io.FileIO): def tell(self): raise io.UnsupportedOperation def seek(self, *args, **kwargs): raise io.UnsupportedOperation class AudioTests: close_fd = False def setUp(self): self.f = self.fout = None def tearDown(self): if self.f is not None: self.f.close() if self.fout is not None: self.fout.close() unlink(TESTFN) def check_params(self, f, nchannels, sampwidth, framerate, nframes, comptype, compname): self.assertEqual(f.getnchannels(), nchannels) self.assertEqual(f.getsampwidth(), sampwidth) self.assertEqual(f.getframerate(), framerate) self.assertEqual(f.getnframes(), nframes) self.assertEqual(f.getcomptype(), comptype) self.assertEqual(f.getcompname(), compname) params = f.getparams() self.assertEqual(params, (nchannels, sampwidth, framerate, nframes, comptype, compname)) self.assertEqual(params.nchannels, nchannels) self.assertEqual(params.sampwidth, sampwidth) self.assertEqual(params.framerate, framerate) self.assertEqual(params.nframes, nframes) self.assertEqual(params.comptype, comptype) self.assertEqual(params.compname, compname) for proto in range(pickle.HIGHEST_PROTOCOL + 1): dump = pickle.dumps(params, proto) self.assertEqual(pickle.loads(dump), params) class AudioWriteTests(AudioTests): def create_file(self, testfile): f = self.fout = self.module.open(testfile, 'wb') f.setnchannels(self.nchannels) f.setsampwidth(self.sampwidth) f.setframerate(self.framerate) f.setcomptype(self.comptype, self.compname) return f def check_file(self, testfile, nframes, frames): with self.module.open(testfile, 'rb') as f: self.assertEqual(f.getnchannels(), self.nchannels) self.assertEqual(f.getsampwidth(), self.sampwidth) self.assertEqual(f.getframerate(), self.framerate) self.assertEqual(f.getnframes(), nframes) self.assertEqual(f.readframes(nframes), frames) def test_write_params(self): f = self.create_file(TESTFN) f.setnframes(self.nframes) f.writeframes(self.frames) self.check_params(f, self.nchannels, self.sampwidth, self.framerate, self.nframes, self.comptype, self.compname) f.close() def test_write_context_manager_calls_close(self): # Close checks for a minimum header and will raise an error # if it is not set, so this proves that close is called. with self.assertRaises(self.module.Error): with self.module.open(TESTFN, 'wb'): pass with self.assertRaises(self.module.Error): with open(TESTFN, 'wb') as testfile: with self.module.open(testfile): pass def test_context_manager_with_open_file(self): with open(TESTFN, 'wb') as testfile: with self.module.open(testfile) as f: f.setnchannels(self.nchannels) f.setsampwidth(self.sampwidth) f.setframerate(self.framerate) f.setcomptype(self.comptype, self.compname) self.assertEqual(testfile.closed, self.close_fd) with open(TESTFN, 'rb') as testfile: with self.module.open(testfile) as f: self.assertFalse(f.getfp().closed) params = f.getparams() self.assertEqual(params.nchannels, self.nchannels) self.assertEqual(params.sampwidth, self.sampwidth) self.assertEqual(params.framerate, self.framerate) if not self.close_fd: self.assertIsNone(f.getfp()) self.assertEqual(testfile.closed, self.close_fd) def test_context_manager_with_filename(self): # If the file doesn't get closed, this test won't fail, but it will # produce a resource leak warning. with self.module.open(TESTFN, 'wb') as f: f.setnchannels(self.nchannels) f.setsampwidth(self.sampwidth) f.setframerate(self.framerate) f.setcomptype(self.comptype, self.compname) with self.module.open(TESTFN) as f: self.assertFalse(f.getfp().closed) params = f.getparams() self.assertEqual(params.nchannels, self.nchannels) self.assertEqual(params.sampwidth, self.sampwidth) self.assertEqual(params.framerate, self.framerate) if not self.close_fd: self.assertIsNone(f.getfp()) def test_write(self): f = self.create_file(TESTFN) f.setnframes(self.nframes) f.writeframes(self.frames) f.close() self.check_file(TESTFN, self.nframes, self.frames) def test_write_bytearray(self): f = self.create_file(TESTFN) f.setnframes(self.nframes) f.writeframes(bytearray(self.frames)) f.close() self.check_file(TESTFN, self.nframes, self.frames) def test_write_array(self): f = self.create_file(TESTFN) f.setnframes(self.nframes) f.writeframes(array.array('h', self.frames)) f.close() self.check_file(TESTFN, self.nframes, self.frames) def test_write_memoryview(self): f = self.create_file(TESTFN) f.setnframes(self.nframes) f.writeframes(memoryview(self.frames)) f.close() self.check_file(TESTFN, self.nframes, self.frames) def test_incompleted_write(self): with open(TESTFN, 'wb') as testfile: testfile.write(b'ababagalamaga') f = self.create_file(testfile) f.setnframes(self.nframes + 1) f.writeframes(self.frames) f.close() with open(TESTFN, 'rb') as testfile: self.assertEqual(testfile.read(13), b'ababagalamaga') self.check_file(testfile, self.nframes, self.frames) def test_multiple_writes(self): with open(TESTFN, 'wb') as testfile: testfile.write(b'ababagalamaga') f = self.create_file(testfile) f.setnframes(self.nframes) framesize = self.nchannels * self.sampwidth f.writeframes(self.frames[:-framesize]) f.writeframes(self.frames[-framesize:]) f.close() with open(TESTFN, 'rb') as testfile: self.assertEqual(testfile.read(13), b'ababagalamaga') self.check_file(testfile, self.nframes, self.frames) def test_overflowed_write(self): with open(TESTFN, 'wb') as testfile: testfile.write(b'ababagalamaga') f = self.create_file(testfile) f.setnframes(self.nframes - 1) f.writeframes(self.frames) f.close() with open(TESTFN, 'rb') as testfile: self.assertEqual(testfile.read(13), b'ababagalamaga') self.check_file(testfile, self.nframes, self.frames) def test_unseekable_read(self): with self.create_file(TESTFN) as f: f.setnframes(self.nframes) f.writeframes(self.frames) with UnseekableIO(TESTFN, 'rb') as testfile: self.check_file(testfile, self.nframes, self.frames) def test_unseekable_write(self): with UnseekableIO(TESTFN, 'wb') as testfile: with self.create_file(testfile) as f: f.setnframes(self.nframes) f.writeframes(self.frames) self.check_file(TESTFN, self.nframes, self.frames) def test_unseekable_incompleted_write(self): with UnseekableIO(TESTFN, 'wb') as testfile: testfile.write(b'ababagalamaga') f = self.create_file(testfile) f.setnframes(self.nframes + 1) try: f.writeframes(self.frames) except OSError: pass try: f.close() except OSError: pass with open(TESTFN, 'rb') as testfile: self.assertEqual(testfile.read(13), b'ababagalamaga') self.check_file(testfile, self.nframes + 1, self.frames) def test_unseekable_overflowed_write(self): with UnseekableIO(TESTFN, 'wb') as testfile: testfile.write(b'ababagalamaga') f = self.create_file(testfile) f.setnframes(self.nframes - 1) try: f.writeframes(self.frames) except OSError: pass try: f.close() except OSError: pass with open(TESTFN, 'rb') as testfile: self.assertEqual(testfile.read(13), b'ababagalamaga') framesize = self.nchannels * self.sampwidth self.check_file(testfile, self.nframes - 1, self.frames[:-framesize]) class AudioTestsWithSourceFile(AudioTests): @classmethod def setUpClass(cls): cls.sndfilepath = findfile(cls.sndfilename, subdir='tests/audiodata') def test_read_params(self): f = self.f = self.module.open(self.sndfilepath) #self.assertEqual(f.getfp().name, self.sndfilepath) self.check_params(f, self.nchannels, self.sampwidth, self.framerate, self.sndfilenframes, self.comptype, self.compname) def test_close(self): with open(self.sndfilepath, 'rb') as testfile: f = self.f = self.module.open(testfile) self.assertFalse(testfile.closed) f.close() self.assertEqual(testfile.closed, self.close_fd) with open(TESTFN, 'wb') as testfile: fout = self.fout = self.module.open(testfile, 'wb') self.assertFalse(testfile.closed) with self.assertRaises(self.module.Error): fout.close() self.assertEqual(testfile.closed, self.close_fd) fout.close() # do nothing def test_read(self): framesize = self.nchannels * self.sampwidth chunk1 = self.frames[:2 * framesize] chunk2 = self.frames[2 * framesize: 4 * framesize] f = self.f = self.module.open(self.sndfilepath) self.assertEqual(f.readframes(0), b'') self.assertEqual(f.tell(), 0) self.assertEqual(f.readframes(2), chunk1) f.rewind() pos0 = f.tell() self.assertEqual(pos0, 0) self.assertEqual(f.readframes(2), chunk1) pos2 = f.tell() self.assertEqual(pos2, 2) self.assertEqual(f.readframes(2), chunk2) f.setpos(pos2) self.assertEqual(f.readframes(2), chunk2) f.setpos(pos0) self.assertEqual(f.readframes(2), chunk1) with self.assertRaises(self.module.Error): f.setpos(-1) with self.assertRaises(self.module.Error): f.setpos(f.getnframes() + 1) def test_copy(self): f = self.f = self.module.open(self.sndfilepath) fout = self.fout = self.module.open(TESTFN, 'wb') fout.setparams(f.getparams()) i = 0 n = f.getnframes() while n > 0: i += 1 fout.writeframes(f.readframes(i)) n -= i fout.close() fout = self.fout = self.module.open(TESTFN, 'rb') f.rewind() self.assertEqual(f.getparams(), fout.getparams()) self.assertEqual(f.readframes(f.getnframes()), fout.readframes(fout.getnframes())) def test_read_not_from_start(self): with open(TESTFN, 'wb') as testfile: testfile.write(b'ababagalamaga') with open(self.sndfilepath, 'rb') as f: testfile.write(f.read()) with open(TESTFN, 'rb') as testfile: self.assertEqual(testfile.read(13), b'ababagalamaga') with self.module.open(testfile, 'rb') as f: self.assertEqual(f.getnchannels(), self.nchannels) self.assertEqual(f.getsampwidth(), self.sampwidth) self.assertEqual(f.getframerate(), self.framerate) self.assertEqual(f.getnframes(), self.sndfilenframes) self.assertEqual(f.readframes(self.nframes), self.frames) python-deadlib-3.13.0/aifc/tests/test_aifc.py000066400000000000000000000435731471045437300211040ustar00rootroot00000000000000from test.support import findfile from test.support.os_helper import TESTFN, unlink from test.support.warnings_helper import check_no_resource_warning, import_deprecated import unittest from unittest import mock from tests import audiotests import io import sys import struct aifc = import_deprecated("aifc") audioop = import_deprecated("audioop") class AifcTest(audiotests.AudioWriteTests, audiotests.AudioTestsWithSourceFile): module = aifc close_fd = True test_unseekable_read = None class AifcPCM8Test(AifcTest, unittest.TestCase): sndfilename = 'pluck-pcm8.aiff' sndfilenframes = 3307 nchannels = 2 sampwidth = 1 framerate = 11025 nframes = 48 comptype = b'NONE' compname = b'not compressed' frames = bytes.fromhex("""\ 02FF 4B00 3104 8008 CB06 4803 BF01 03FE B8FA B4F3 29EB 1AE6 \ EDE4 C6E2 0EE0 EFE0 57E2 FBE8 13EF D8F7 97FB F5FC 08FB DFFB \ 11FA 3EFB BCFC 66FF CF04 4309 C10E 5112 EE17 8216 7F14 8012 \ 490E 520D EF0F CE0F E40C 630A 080A 2B0B 510E 8B11 B60E 440A \ """) class AifcPCM16Test(AifcTest, unittest.TestCase): sndfilename = 'pluck-pcm16.aiff' sndfilenframes = 3307 nchannels = 2 sampwidth = 2 framerate = 11025 nframes = 48 comptype = b'NONE' compname = b'not compressed' frames = bytes.fromhex("""\ 022EFFEA 4B5D00F6 311804EA 80E10840 CBE106B1 48A903F5 BFE601B2 036CFE7B \ B858FA3E B4B1F34F 299AEBCA 1A5DE6DA EDFAE491 C628E275 0E09E0B5 EF2AE029 \ 5758E271 FB35E83F 1376EF86 D82BF727 9790FB76 F5FAFC0F 0867FB9C DF30FB43 \ 117EFA36 3EE5FB5B BC79FCB1 66D9FF5D CF150412 431D097C C1BA0EC8 512112A1 \ EEE21753 82071665 7FFF1443 8004128F 49A20EAF 52BB0DBA EFB40F60 CE3C0FBF \ E4B30CEC 63430A5C 08C80A20 2BBB0B08 514A0E43 8BCF1139 B6F60EEB 44120A5E \ """) class AifcPCM24Test(AifcTest, unittest.TestCase): sndfilename = 'pluck-pcm24.aiff' sndfilenframes = 3307 nchannels = 2 sampwidth = 3 framerate = 11025 nframes = 48 comptype = b'NONE' compname = b'not compressed' frames = bytes.fromhex("""\ 022D65FFEB9D 4B5A0F00FA54 3113C304EE2B 80DCD6084303 \ CBDEC006B261 48A99803F2F8 BFE82401B07D 036BFBFE7B5D \ B85756FA3EC9 B4B055F3502B 299830EBCB62 1A5CA7E6D99A \ EDFA3EE491BD C625EBE27884 0E05A9E0B6CF EF2929E02922 \ 5758D8E27067 FB3557E83E16 1377BFEF8402 D82C5BF7272A \ 978F16FB7745 F5F865FC1013 086635FB9C4E DF30FCFB40EE \ 117FE0FA3438 3EE6B8FB5AC3 BC77A3FCB2F4 66D6DAFF5F32 \ CF13B9041275 431D69097A8C C1BB600EC74E 5120B912A2BA \ EEDF641754C0 8207001664B7 7FFFFF14453F 8000001294E6 \ 499C1B0EB3B2 52B73E0DBCA0 EFB2B20F5FD8 CE3CDB0FBE12 \ E4B49C0CEA2D 6344A80A5A7C 08C8FE0A1FFE 2BB9860B0A0E \ 51486F0E44E1 8BCC64113B05 B6F4EC0EEB36 4413170A5B48 \ """) class AifcPCM32Test(AifcTest, unittest.TestCase): sndfilename = 'pluck-pcm32.aiff' sndfilenframes = 3307 nchannels = 2 sampwidth = 4 framerate = 11025 nframes = 48 comptype = b'NONE' compname = b'not compressed' frames = bytes.fromhex("""\ 022D65BCFFEB9D92 4B5A0F8000FA549C 3113C34004EE2BC0 80DCD680084303E0 \ CBDEC0C006B26140 48A9980003F2F8FC BFE8248001B07D92 036BFB60FE7B5D34 \ B8575600FA3EC920 B4B05500F3502BC0 29983000EBCB6240 1A5CA7A0E6D99A60 \ EDFA3E80E491BD40 C625EB80E27884A0 0E05A9A0E0B6CFE0 EF292940E0292280 \ 5758D800E2706700 FB3557D8E83E1640 1377BF00EF840280 D82C5B80F7272A80 \ 978F1600FB774560 F5F86510FC101364 086635A0FB9C4E20 DF30FC40FB40EE28 \ 117FE0A0FA3438B0 3EE6B840FB5AC3F0 BC77A380FCB2F454 66D6DA80FF5F32B4 \ CF13B980041275B0 431D6980097A8C00 C1BB60000EC74E00 5120B98012A2BAA0 \ EEDF64C01754C060 820700001664B780 7FFFFFFF14453F40 800000001294E6E0 \ 499C1B000EB3B270 52B73E000DBCA020 EFB2B2E00F5FD880 CE3CDB400FBE1270 \ E4B49CC00CEA2D90 6344A8800A5A7CA0 08C8FE800A1FFEE0 2BB986C00B0A0E00 \ 51486F800E44E190 8BCC6480113B0580 B6F4EC000EEB3630 441317800A5B48A0 \ """) class AifcULAWTest(AifcTest, unittest.TestCase): sndfilename = 'pluck-ulaw.aifc' sndfilenframes = 3307 nchannels = 2 sampwidth = 2 framerate = 11025 nframes = 48 comptype = b'ulaw' compname = b'' frames = bytes.fromhex("""\ 022CFFE8 497C0104 307C04DC 8284083C CB84069C 497C03DC BE8401AC 036CFE74 \ B684FA24 B684F344 2A7CEC04 19FCE704 EE04E504 C584E204 0E3CE104 EF04DF84 \ 557CE204 FB24E804 12FCEF04 D784F744 9684FB64 F5C4FC24 083CFBA4 DF84FB24 \ 11FCFA24 3E7CFB64 BA84FCB4 657CFF5C CF84041C 417C093C C1840EBC 517C12FC \ EF0416FC 828415FC 7D7C13FC 828412FC 497C0EBC 517C0DBC F0040F3C CD840FFC \ E5040CBC 617C0A3C 08BC0A3C 2C7C0B3C 517C0E3C 8A8410FC B6840EBC 457C0A3C \ """) if sys.byteorder != 'big': frames = audioop.byteswap(frames, 2) class AifcALAWTest(AifcTest, unittest.TestCase): sndfilename = 'pluck-alaw.aifc' sndfilenframes = 3307 nchannels = 2 sampwidth = 2 framerate = 11025 nframes = 48 comptype = b'alaw' compname = b'' frames = bytes.fromhex("""\ 0230FFE8 4A0000F8 310004E0 82000840 CB0006A0 4A0003F0 BE0001A8 0370FE78 \ BA00FA20 B600F340 2900EB80 1A80E680 ED80E480 C700E280 0E40E080 EF80E080 \ 5600E280 FB20E880 1380EF80 D900F740 9600FB60 F5C0FC10 0840FBA0 DF00FB20 \ 1180FA20 3F00FB60 BE00FCB0 6600FF58 CF000420 42000940 C1000EC0 52001280 \ EE801780 82001680 7E001480 82001280 4A000EC0 52000DC0 EF800F40 CF000FC0 \ E4800CC0 62000A40 08C00A40 2B000B40 52000E40 8A001180 B6000EC0 46000A40 \ """) if sys.byteorder != 'big': frames = audioop.byteswap(frames, 2) class AifcMiscTest(unittest.TestCase): def test_skipunknown(self): #Issue 2245 #This file contains chunk types aifc doesn't recognize. f = aifc.open(findfile('Sine-1000Hz-300ms.aif', subdir='tests/audiodata')) f.close() def test_close_opened_files_on_error(self): non_aifc_file = findfile('pluck-pcm8.wav', subdir='tests/audiodata') with check_no_resource_warning(self): with self.assertRaises(aifc.Error): # Try opening a non-AIFC file, with the expectation that # `aifc.open` will fail (without raising a ResourceWarning) self.f = aifc.open(non_aifc_file, 'rb') # Aifc_write.initfp() won't raise in normal case. But some errors # (e.g. MemoryError, KeyboardInterrupt, etc..) can happen. with mock.patch.object(aifc.Aifc_write, 'initfp', side_effect=RuntimeError): with self.assertRaises(RuntimeError): self.fout = aifc.open(TESTFN, 'wb') def test_params_added(self): f = self.f = aifc.open(TESTFN, 'wb') f.aiff() f.setparams((1, 1, 1, 1, b'NONE', b'')) f.close() f = aifc.open(TESTFN, 'rb') self.addCleanup(f.close) params = f.getparams() self.assertEqual(params.nchannels, f.getnchannels()) self.assertEqual(params.sampwidth, f.getsampwidth()) self.assertEqual(params.framerate, f.getframerate()) self.assertEqual(params.nframes, f.getnframes()) self.assertEqual(params.comptype, f.getcomptype()) self.assertEqual(params.compname, f.getcompname()) def test_write_header_comptype_sampwidth(self): for comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722'): fout = aifc.open(io.BytesIO(), 'wb') fout.setnchannels(1) fout.setframerate(1) fout.setcomptype(comptype, b'') fout.close() self.assertEqual(fout.getsampwidth(), 2) fout.initfp(None) def test_write_markers_values(self): fout = aifc.open(io.BytesIO(), 'wb') self.assertEqual(fout.getmarkers(), None) fout.setmark(1, 0, b'foo1') fout.setmark(1, 1, b'foo2') self.assertEqual(fout.getmark(1), (1, 1, b'foo2')) self.assertEqual(fout.getmarkers(), [(1, 1, b'foo2')]) fout.initfp(None) def test_read_markers(self): fout = self.fout = aifc.open(TESTFN, 'wb') fout.aiff() fout.setparams((1, 1, 1, 1, b'NONE', b'')) fout.setmark(1, 0, b'odd') fout.setmark(2, 0, b'even') fout.writeframes(b'\x00') fout.close() f = aifc.open(TESTFN, 'rb') self.addCleanup(f.close) self.assertEqual(f.getmarkers(), [(1, 0, b'odd'), (2, 0, b'even')]) self.assertEqual(f.getmark(1), (1, 0, b'odd')) self.assertEqual(f.getmark(2), (2, 0, b'even')) self.assertRaises(aifc.Error, f.getmark, 3) class AIFCLowLevelTest(unittest.TestCase): def test_read_written(self): def read_written(self, what): f = io.BytesIO() getattr(aifc, '_write_' + what)(f, x) f.seek(0) return getattr(aifc, '_read_' + what)(f) for x in (-1, 0, 0.1, 1): self.assertEqual(read_written(x, 'float'), x) for x in (float('NaN'), float('Inf')): self.assertEqual(read_written(x, 'float'), aifc._HUGE_VAL) for x in (b'', b'foo', b'a' * 255): self.assertEqual(read_written(x, 'string'), x) for x in (-0x7FFFFFFF, -1, 0, 1, 0x7FFFFFFF): self.assertEqual(read_written(x, 'long'), x) for x in (0, 1, 0xFFFFFFFF): self.assertEqual(read_written(x, 'ulong'), x) for x in (-0x7FFF, -1, 0, 1, 0x7FFF): self.assertEqual(read_written(x, 'short'), x) for x in (0, 1, 0xFFFF): self.assertEqual(read_written(x, 'ushort'), x) def test_read_raises(self): f = io.BytesIO(b'\x00') self.assertRaises(EOFError, aifc._read_ulong, f) self.assertRaises(EOFError, aifc._read_long, f) self.assertRaises(EOFError, aifc._read_ushort, f) self.assertRaises(EOFError, aifc._read_short, f) def test_write_long_string_raises(self): f = io.BytesIO() with self.assertRaises(ValueError): aifc._write_string(f, b'too long' * 255) def test_wrong_open_mode(self): with self.assertRaises(aifc.Error): aifc.open(TESTFN, 'wrong_mode') def test_read_wrong_form(self): b1 = io.BytesIO(b'WRNG' + struct.pack('>L', 0)) b2 = io.BytesIO(b'FORM' + struct.pack('>L', 4) + b'WRNG') self.assertRaises(aifc.Error, aifc.open, b1) self.assertRaises(aifc.Error, aifc.open, b2) def test_read_no_comm_chunk(self): b = io.BytesIO(b'FORM' + struct.pack('>L', 4) + b'AIFF') self.assertRaises(aifc.Error, aifc.open, b) def test_read_no_ssnd_chunk(self): b = b'FORM' + struct.pack('>L', 4) + b'AIFC' b += b'COMM' + struct.pack('>LhlhhLL', 38, 1, 0, 8, 0x4000 | 12, 11025<<18, 0) b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00' with self.assertRaisesRegex(aifc.Error, 'COMM chunk and/or SSND chunk' ' missing'): aifc.open(io.BytesIO(b)) def test_read_wrong_compression_type(self): b = b'FORM' + struct.pack('>L', 4) + b'AIFC' b += b'COMM' + struct.pack('>LhlhhLL', 23, 1, 0, 8, 0x4000 | 12, 11025<<18, 0) b += b'WRNG' + struct.pack('B', 0) self.assertRaises(aifc.Error, aifc.open, io.BytesIO(b)) def test_read_wrong_number_of_channels(self): for nchannels in 0, -1: b = b'FORM' + struct.pack('>L', 4) + b'AIFC' b += b'COMM' + struct.pack('>LhlhhLL', 38, nchannels, 0, 8, 0x4000 | 12, 11025<<18, 0) b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00' b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8 with self.assertRaisesRegex(aifc.Error, 'bad # of channels'): aifc.open(io.BytesIO(b)) def test_read_wrong_sample_width(self): for sampwidth in 0, -1: b = b'FORM' + struct.pack('>L', 4) + b'AIFC' b += b'COMM' + struct.pack('>LhlhhLL', 38, 1, 0, sampwidth, 0x4000 | 12, 11025<<18, 0) b += b'NONE' + struct.pack('B', 14) + b'not compressed' + b'\x00' b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8 with self.assertRaisesRegex(aifc.Error, 'bad sample width'): aifc.open(io.BytesIO(b)) def test_read_wrong_marks(self): b = b'FORM' + struct.pack('>L', 4) + b'AIFF' b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8, 0x4000 | 12, 11025<<18, 0) b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8 b += b'MARK' + struct.pack('>LhB', 3, 1, 1) with self.assertWarns(UserWarning) as cm: f = aifc.open(io.BytesIO(b)) self.assertEqual(str(cm.warning), 'Warning: MARK chunk contains ' 'only 0 markers instead of 1') self.assertEqual(f.getmarkers(), None) def test_read_comm_kludge_compname_even(self): b = b'FORM' + struct.pack('>L', 4) + b'AIFC' b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8, 0x4000 | 12, 11025<<18, 0) b += b'NONE' + struct.pack('B', 4) + b'even' + b'\x00' b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8 with self.assertWarns(UserWarning) as cm: f = aifc.open(io.BytesIO(b)) self.assertEqual(str(cm.warning), 'Warning: bad COMM chunk size') self.assertEqual(f.getcompname(), b'even') def test_read_comm_kludge_compname_odd(self): b = b'FORM' + struct.pack('>L', 4) + b'AIFC' b += b'COMM' + struct.pack('>LhlhhLL', 18, 1, 0, 8, 0x4000 | 12, 11025<<18, 0) b += b'NONE' + struct.pack('B', 3) + b'odd' b += b'SSND' + struct.pack('>L', 8) + b'\x00' * 8 with self.assertWarns(UserWarning) as cm: f = aifc.open(io.BytesIO(b)) self.assertEqual(str(cm.warning), 'Warning: bad COMM chunk size') self.assertEqual(f.getcompname(), b'odd') def test_write_params_raises(self): fout = aifc.open(io.BytesIO(), 'wb') wrong_params = (0, 0, 0, 0, b'WRNG', '') self.assertRaises(aifc.Error, fout.setparams, wrong_params) self.assertRaises(aifc.Error, fout.getparams) self.assertRaises(aifc.Error, fout.setnchannels, 0) self.assertRaises(aifc.Error, fout.getnchannels) self.assertRaises(aifc.Error, fout.setsampwidth, 0) self.assertRaises(aifc.Error, fout.getsampwidth) self.assertRaises(aifc.Error, fout.setframerate, 0) self.assertRaises(aifc.Error, fout.getframerate) self.assertRaises(aifc.Error, fout.setcomptype, b'WRNG', '') fout.aiff() fout.setnchannels(1) fout.setsampwidth(1) fout.setframerate(1) fout.setnframes(1) fout.writeframes(b'\x00') self.assertRaises(aifc.Error, fout.setparams, (1, 1, 1, 1, 1, 1)) self.assertRaises(aifc.Error, fout.setnchannels, 1) self.assertRaises(aifc.Error, fout.setsampwidth, 1) self.assertRaises(aifc.Error, fout.setframerate, 1) self.assertRaises(aifc.Error, fout.setnframes, 1) self.assertRaises(aifc.Error, fout.setcomptype, b'NONE', '') self.assertRaises(aifc.Error, fout.aiff) self.assertRaises(aifc.Error, fout.aifc) def test_write_params_singles(self): fout = aifc.open(io.BytesIO(), 'wb') fout.aifc() fout.setnchannels(1) fout.setsampwidth(2) fout.setframerate(3) fout.setnframes(4) fout.setcomptype(b'NONE', b'name') self.assertEqual(fout.getnchannels(), 1) self.assertEqual(fout.getsampwidth(), 2) self.assertEqual(fout.getframerate(), 3) self.assertEqual(fout.getnframes(), 0) self.assertEqual(fout.tell(), 0) self.assertEqual(fout.getcomptype(), b'NONE') self.assertEqual(fout.getcompname(), b'name') fout.writeframes(b'\x00' * 4 * fout.getsampwidth() * fout.getnchannels()) self.assertEqual(fout.getnframes(), 4) self.assertEqual(fout.tell(), 4) def test_write_params_bunch(self): fout = aifc.open(io.BytesIO(), 'wb') fout.aifc() p = (1, 2, 3, 4, b'NONE', b'name') fout.setparams(p) self.assertEqual(fout.getparams(), p) fout.initfp(None) def test_write_header_raises(self): fout = aifc.open(io.BytesIO(), 'wb') self.assertRaises(aifc.Error, fout.close) fout = aifc.open(io.BytesIO(), 'wb') fout.setnchannels(1) self.assertRaises(aifc.Error, fout.close) fout = aifc.open(io.BytesIO(), 'wb') fout.setnchannels(1) fout.setsampwidth(1) self.assertRaises(aifc.Error, fout.close) def test_write_header_comptype_raises(self): for comptype in (b'ULAW', b'ulaw', b'ALAW', b'alaw', b'G722'): fout = aifc.open(io.BytesIO(), 'wb') fout.setsampwidth(1) fout.setcomptype(comptype, b'') self.assertRaises(aifc.Error, fout.close) fout.initfp(None) def test_write_markers_raises(self): fout = aifc.open(io.BytesIO(), 'wb') self.assertRaises(aifc.Error, fout.setmark, 0, 0, b'') self.assertRaises(aifc.Error, fout.setmark, 1, -1, b'') self.assertRaises(aifc.Error, fout.setmark, 1, 0, None) self.assertRaises(aifc.Error, fout.getmark, 1) fout.initfp(None) def test_write_aiff_by_extension(self): sampwidth = 2 filename = TESTFN + '.aiff' fout = self.fout = aifc.open(filename, 'wb') self.addCleanup(unlink, filename) fout.setparams((1, sampwidth, 1, 1, b'ULAW', b'')) frames = b'\x00' * fout.getnchannels() * sampwidth fout.writeframes(frames) fout.close() f = self.f = aifc.open(filename, 'rb') self.assertEqual(f.getcomptype(), b'NONE') f.close() if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/asynchat/000077500000000000000000000000001471045437300163235ustar00rootroot00000000000000python-deadlib-3.13.0/asynchat/Doc/000077500000000000000000000000001471045437300170305ustar00rootroot00000000000000python-deadlib-3.13.0/asynchat/Doc/asynchat.rst000066400000000000000000000207701471045437300214020ustar00rootroot00000000000000:mod:`asynchat` --- Asynchronous socket command/response handler ================================================================ .. module:: asynchat :synopsis: Support for asynchronous command/response protocols. :deprecated: .. moduleauthor:: Sam Rushing .. sectionauthor:: Steve Holden **Source code:** :source:`Lib/asynchat.py` .. deprecated-removed:: 3.6 3.12 The :mod:`asynchat` module is deprecated (see :pep:`PEP 594 <594#asynchat>` for details). Please use :mod:`asyncio` instead. -------------- .. note:: This module exists for backwards compatibility only. For new code we recommend using :mod:`asyncio`. This module builds on the :mod:`asyncore` infrastructure, simplifying asynchronous clients and servers and making it easier to handle protocols whose elements are terminated by arbitrary strings, or are of variable length. :mod:`asynchat` defines the abstract class :class:`async_chat` that you subclass, providing implementations of the :meth:`collect_incoming_data` and :meth:`found_terminator` methods. It uses the same asynchronous loop as :mod:`asyncore`, and the two types of channel, :class:`asyncore.dispatcher` and :class:`asynchat.async_chat`, can freely be mixed in the channel map. Typically an :class:`asyncore.dispatcher` server channel generates new :class:`asynchat.async_chat` channel objects as it receives incoming connection requests. .. include:: ../includes/wasm-notavail.rst .. class:: async_chat() This class is an abstract subclass of :class:`asyncore.dispatcher`. To make practical use of the code you must subclass :class:`async_chat`, providing meaningful :meth:`collect_incoming_data` and :meth:`found_terminator` methods. The :class:`asyncore.dispatcher` methods can be used, although not all make sense in a message/response context. Like :class:`asyncore.dispatcher`, :class:`async_chat` defines a set of events that are generated by an analysis of socket conditions after a :c:func:`select` call. Once the polling loop has been started the :class:`async_chat` object's methods are called by the event-processing framework with no action on the part of the programmer. Two class attributes can be modified, to improve performance, or possibly even to conserve memory. .. data:: ac_in_buffer_size The asynchronous input buffer size (default ``4096``). .. data:: ac_out_buffer_size The asynchronous output buffer size (default ``4096``). Unlike :class:`asyncore.dispatcher`, :class:`async_chat` allows you to define a :abbr:`FIFO (first-in, first-out)` queue of *producers*. A producer need have only one method, :meth:`more`, which should return data to be transmitted on the channel. The producer indicates exhaustion (*i.e.* that it contains no more data) by having its :meth:`more` method return the empty bytes object. At this point the :class:`async_chat` object removes the producer from the queue and starts using the next producer, if any. When the producer queue is empty the :meth:`handle_write` method does nothing. You use the channel object's :meth:`set_terminator` method to describe how to recognize the end of, or an important breakpoint in, an incoming transmission from the remote endpoint. To build a functioning :class:`async_chat` subclass your input methods :meth:`collect_incoming_data` and :meth:`found_terminator` must handle the data that the channel receives asynchronously. The methods are described below. .. method:: async_chat.close_when_done() Pushes a ``None`` on to the producer queue. When this producer is popped off the queue it causes the channel to be closed. .. method:: async_chat.collect_incoming_data(data) Called with *data* holding an arbitrary amount of received data. The default method, which must be overridden, raises a :exc:`NotImplementedError` exception. .. method:: async_chat.discard_buffers() In emergencies this method will discard any data held in the input and/or output buffers and the producer queue. .. method:: async_chat.found_terminator() Called when the incoming data stream matches the termination condition set by :meth:`set_terminator`. The default method, which must be overridden, raises a :exc:`NotImplementedError` exception. The buffered input data should be available via an instance attribute. .. method:: async_chat.get_terminator() Returns the current terminator for the channel. .. method:: async_chat.push(data) Pushes data on to the channel's queue to ensure its transmission. This is all you need to do to have the channel write the data out to the network, although it is possible to use your own producers in more complex schemes to implement encryption and chunking, for example. .. method:: async_chat.push_with_producer(producer) Takes a producer object and adds it to the producer queue associated with the channel. When all currently pushed producers have been exhausted the channel will consume this producer's data by calling its :meth:`more` method and send the data to the remote endpoint. .. method:: async_chat.set_terminator(term) Sets the terminating condition to be recognized on the channel. ``term`` may be any of three types of value, corresponding to three different ways to handle incoming protocol data. +-----------+---------------------------------------------+ | term | Description | +===========+=============================================+ | *string* | Will call :meth:`found_terminator` when the | | | string is found in the input stream | +-----------+---------------------------------------------+ | *integer* | Will call :meth:`found_terminator` when the | | | indicated number of characters have been | | | received | +-----------+---------------------------------------------+ | ``None`` | The channel continues to collect data | | | forever | +-----------+---------------------------------------------+ Note that any data following the terminator will be available for reading by the channel after :meth:`found_terminator` is called. .. _asynchat-example: asynchat Example ---------------- The following partial example shows how HTTP requests can be read with :class:`async_chat`. A web server might create an :class:`http_request_handler` object for each incoming client connection. Notice that initially the channel terminator is set to match the blank line at the end of the HTTP headers, and a flag indicates that the headers are being read. Once the headers have been read, if the request is of type POST (indicating that further data are present in the input stream) then the ``Content-Length:`` header is used to set a numeric terminator to read the right amount of data from the channel. The :meth:`handle_request` method is called once all relevant input has been marshalled, after setting the channel terminator to ``None`` to ensure that any extraneous data sent by the web client are ignored. :: import asynchat class http_request_handler(asynchat.async_chat): def __init__(self, sock, addr, sessions, log): asynchat.async_chat.__init__(self, sock=sock) self.addr = addr self.sessions = sessions self.ibuffer = [] self.obuffer = b"" self.set_terminator(b"\r\n\r\n") self.reading_headers = True self.handling = False self.cgi_data = None self.log = log def collect_incoming_data(self, data): """Buffer the data""" self.ibuffer.append(data) def found_terminator(self): if self.reading_headers: self.reading_headers = False self.parse_headers(b"".join(self.ibuffer)) self.ibuffer = [] if self.op.upper() == b"POST": clen = self.headers.getheader("content-length") self.set_terminator(int(clen)) else: self.handling = True self.set_terminator(None) self.handle_request() elif not self.handling: self.set_terminator(None) # browsers sometimes over-send self.cgi_data = parse(self.headers, b"".join(self.ibuffer)) self.handling = True self.ibuffer = [] self.handle_request() python-deadlib-3.13.0/asynchat/LICENSE000066400000000000000000000050111471045437300173250ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/asynchat/README.rst000066400000000000000000000005161471045437300200140ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/asynchat/asynchat/000077500000000000000000000000001471045437300201355ustar00rootroot00000000000000python-deadlib-3.13.0/asynchat/asynchat/__init__.py000066400000000000000000000266571471045437300222660ustar00rootroot00000000000000# -*- Mode: Python; tab-width: 4 -*- # Id: asynchat.py,v 2.26 2000/09/07 22:29:26 rushing Exp # Author: Sam Rushing # ====================================================================== # Copyright 1996 by Sam Rushing # # 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 Sam # Rushing not be used in advertising or publicity pertaining to # distribution of the software without specific, written prior # permission. # # SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN # NO EVENT SHALL SAM RUSHING 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. # ====================================================================== r"""A class supporting chat-style (command/response) protocols. This class adds support for 'chat' style protocols - where one side sends a 'command', and the other sends a response (examples would be the common internet protocols - smtp, nntp, ftp, etc..). The handle_read() method looks at the input stream for the current 'terminator' (usually '\r\n' for single-line responses, '\r\n.\r\n' for multi-line output), calling self.found_terminator() on its receipt. for example: Say you build an async nntp client using this class. At the start of the connection, you'll have self.terminator set to '\r\n', in order to process the single-line greeting. Just before issuing a 'LIST' command you'll set it to '\r\n.\r\n'. The output of the LIST command will be accumulated (using your own 'collect_incoming_data' method) up to the terminator, and then control will be returned to you - by calling your self.found_terminator() method. """ import asyncore from collections import deque import warnings # python-deadlib: Replace deprecation warning not to raise exception warnings.warn( f"{__name__} was removed in Python 3.12. The recommended replacement is asyncio. " f"Please be aware that you are currently NOT using standard '{__name__}', " f"but instead a separately installed 'standard-{__name__}'.", DeprecationWarning, stacklevel=2 ) class async_chat(asyncore.dispatcher): """This is an abstract class. You must derive from this class, and add the two methods collect_incoming_data() and found_terminator()""" # these are overridable defaults ac_in_buffer_size = 65536 ac_out_buffer_size = 65536 # we don't want to enable the use of encoding by default, because that is a # sign of an application bug that we don't want to pass silently use_encoding = 0 encoding = 'latin-1' def __init__(self, sock=None, map=None): # for string terminator matching self.ac_in_buffer = b'' # we use a list here rather than io.BytesIO for a few reasons... # del lst[:] is faster than bio.truncate(0) # lst = [] is faster than bio.truncate(0) self.incoming = [] # we toss the use of the "simple producer" and replace it with # a pure deque, which the original fifo was a wrapping of self.producer_fifo = deque() asyncore.dispatcher.__init__(self, sock, map) def collect_incoming_data(self, data): raise NotImplementedError("must be implemented in subclass") def _collect_incoming_data(self, data): self.incoming.append(data) def _get_data(self): d = b''.join(self.incoming) del self.incoming[:] return d def found_terminator(self): raise NotImplementedError("must be implemented in subclass") def set_terminator(self, term): """Set the input delimiter. Can be a fixed string of any length, an integer, or None. """ if isinstance(term, str) and self.use_encoding: term = bytes(term, self.encoding) elif isinstance(term, int) and term < 0: raise ValueError('the number of received bytes must be positive') self.terminator = term def get_terminator(self): return self.terminator # grab some more data from the socket, # throw it to the collector method, # check for the terminator, # if found, transition to the next state. def handle_read(self): try: data = self.recv(self.ac_in_buffer_size) except BlockingIOError: return except OSError: self.handle_error() return if isinstance(data, str) and self.use_encoding: data = bytes(str, self.encoding) self.ac_in_buffer = self.ac_in_buffer + data # Continue to search for self.terminator in self.ac_in_buffer, # while calling self.collect_incoming_data. The while loop # is necessary because we might read several data+terminator # combos with a single recv(4096). while self.ac_in_buffer: lb = len(self.ac_in_buffer) terminator = self.get_terminator() if not terminator: # no terminator, collect it all self.collect_incoming_data(self.ac_in_buffer) self.ac_in_buffer = b'' elif isinstance(terminator, int): # numeric terminator n = terminator if lb < n: self.collect_incoming_data(self.ac_in_buffer) self.ac_in_buffer = b'' self.terminator = self.terminator - lb else: self.collect_incoming_data(self.ac_in_buffer[:n]) self.ac_in_buffer = self.ac_in_buffer[n:] self.terminator = 0 self.found_terminator() else: # 3 cases: # 1) end of buffer matches terminator exactly: # collect data, transition # 2) end of buffer matches some prefix: # collect data to the prefix # 3) end of buffer does not match any prefix: # collect data terminator_len = len(terminator) index = self.ac_in_buffer.find(terminator) if index != -1: # we found the terminator if index > 0: # don't bother reporting the empty string # (source of subtle bugs) self.collect_incoming_data(self.ac_in_buffer[:index]) self.ac_in_buffer = self.ac_in_buffer[index+terminator_len:] # This does the Right Thing if the terminator # is changed here. self.found_terminator() else: # check for a prefix of the terminator index = find_prefix_at_end(self.ac_in_buffer, terminator) if index: if index != lb: # we found a prefix, collect up to the prefix self.collect_incoming_data(self.ac_in_buffer[:-index]) self.ac_in_buffer = self.ac_in_buffer[-index:] break else: # no prefix, collect it all self.collect_incoming_data(self.ac_in_buffer) self.ac_in_buffer = b'' def handle_write(self): self.initiate_send() def handle_close(self): self.close() def push(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError('data argument must be byte-ish (%r)', type(data)) sabs = self.ac_out_buffer_size if len(data) > sabs: for i in range(0, len(data), sabs): self.producer_fifo.append(data[i:i+sabs]) else: self.producer_fifo.append(data) self.initiate_send() def push_with_producer(self, producer): self.producer_fifo.append(producer) self.initiate_send() def readable(self): "predicate for inclusion in the readable for select()" # cannot use the old predicate, it violates the claim of the # set_terminator method. # return (len(self.ac_in_buffer) <= self.ac_in_buffer_size) return 1 def writable(self): "predicate for inclusion in the writable for select()" return self.producer_fifo or (not self.connected) def close_when_done(self): "automatically close this channel once the outgoing queue is empty" self.producer_fifo.append(None) def initiate_send(self): while self.producer_fifo and self.connected: first = self.producer_fifo[0] # handle empty string/buffer or None entry if not first: del self.producer_fifo[0] if first is None: self.handle_close() return # handle classic producer behavior obs = self.ac_out_buffer_size try: data = first[:obs] except TypeError: data = first.more() if data: self.producer_fifo.appendleft(data) else: del self.producer_fifo[0] continue if isinstance(data, str) and self.use_encoding: data = bytes(data, self.encoding) # send the data try: num_sent = self.send(data) except OSError: self.handle_error() return if num_sent: if num_sent < len(data) or obs < len(first): self.producer_fifo[0] = first[num_sent:] else: del self.producer_fifo[0] # we tried to send some actual data return def discard_buffers(self): # Emergencies only! self.ac_in_buffer = b'' del self.incoming[:] self.producer_fifo.clear() class simple_producer: def __init__(self, data, buffer_size=512): self.data = data self.buffer_size = buffer_size def more(self): if len(self.data) > self.buffer_size: result = self.data[:self.buffer_size] self.data = self.data[self.buffer_size:] return result else: result = self.data self.data = b'' return result # Given 'haystack', see if any prefix of 'needle' is at its end. This # assumes an exact match has already been checked. Return the number of # characters matched. # for example: # f_p_a_e("qwerty\r", "\r\n") => 1 # f_p_a_e("qwertydkjf", "\r\n") => 0 # f_p_a_e("qwerty\r\n", "\r\n") => # this could maybe be made faster with a computed regex? # [answer: no; circa Python-2.0, Jan 2001] # new python: 28961/s # old python: 18307/s # re: 12820/s # regex: 14035/s def find_prefix_at_end(haystack, needle): l = len(needle) - 1 while l and not haystack.endswith(needle[:l]): l -= 1 return l python-deadlib-3.13.0/asynchat/pyproject.toml000066400000000000000000000014041471045437300212360ustar00rootroot00000000000000[project] name = "standard-asynchat" version = "3.13.0" description = "Standard library asynchat redistribution. \"dead battery\"." keywords = ["stdlib"] authors = [ { name = "Python Developers", email = "python-deadlib@youknowone.org" } ] readme = "README.rst" license = { text = "PSF-2.0" } classifiers = [ "License :: OSI Approved :: Python Software Foundation License", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", ] dependencies = [ "standard-asyncore; python_version >= '3.12'", ] [tool.setuptools.packages] find = {include = ["asynchat*"]} [project.urls] "Homepage" = "https://github.com/youknowone/python-deadlib" [build-system] requires = ["setuptools>=75.0"] build-backend = "setuptools.build_meta" python-deadlib-3.13.0/asynchat/tests/000077500000000000000000000000001471045437300174655ustar00rootroot00000000000000python-deadlib-3.13.0/asynchat/tests/__init__.py000066400000000000000000000000001471045437300215640ustar00rootroot00000000000000python-deadlib-3.13.0/asynchat/tests/test_asynchat.py000066400000000000000000000227231471045437300227160ustar00rootroot00000000000000# test asynchat from test import support from test.support import socket_helper from test.support import threading_helper from test.support import warnings_helper import errno import socket import sys import threading import time import unittest import unittest.mock asynchat = warnings_helper.import_deprecated('asynchat') asyncore = warnings_helper.import_deprecated('asyncore') support.requires_working_socket(module=True) HOST = socket_helper.HOST SERVER_QUIT = b'QUIT\n' class echo_server(threading.Thread): # parameter to determine the number of bytes passed back to the # client each send chunk_size = 1 def __init__(self, event): threading.Thread.__init__(self) self.event = event self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.port = socket_helper.bind_port(self.sock) # This will be set if the client wants us to wait before echoing # data back. self.start_resend_event = None def run(self): self.sock.listen() self.event.set() conn, client = self.sock.accept() self.buffer = b"" # collect data until quit message is seen while SERVER_QUIT not in self.buffer: data = conn.recv(1) if not data: break self.buffer = self.buffer + data # remove the SERVER_QUIT message self.buffer = self.buffer.replace(SERVER_QUIT, b'') if self.start_resend_event: self.start_resend_event.wait() # re-send entire set of collected data try: # this may fail on some tests, such as test_close_when_done, # since the client closes the channel when it's done sending while self.buffer: n = conn.send(self.buffer[:self.chunk_size]) time.sleep(0.001) self.buffer = self.buffer[n:] except: pass conn.close() self.sock.close() class echo_client(asynchat.async_chat): def __init__(self, terminator, server_port): asynchat.async_chat.__init__(self) self.contents = [] self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.connect((HOST, server_port)) self.set_terminator(terminator) self.buffer = b"" def handle_connect(self): pass if sys.platform == 'darwin': # select.poll returns a select.POLLHUP at the end of the tests # on darwin, so just ignore it def handle_expt(self): pass def collect_incoming_data(self, data): self.buffer += data def found_terminator(self): self.contents.append(self.buffer) self.buffer = b"" def start_echo_server(): event = threading.Event() s = echo_server(event) s.start() event.wait() event.clear() time.sleep(0.01) # Give server time to start accepting. return s, event class TestAsynchat(unittest.TestCase): usepoll = False def setUp(self): self._threads = threading_helper.threading_setup() def tearDown(self): threading_helper.threading_cleanup(*self._threads) def line_terminator_check(self, term, server_chunk): event = threading.Event() s = echo_server(event) s.chunk_size = server_chunk s.start() event.wait() event.clear() time.sleep(0.01) # Give server time to start accepting. c = echo_client(term, s.port) c.push(b"hello ") c.push(b"world" + term) c.push(b"I'm not dead yet!" + term) c.push(SERVER_QUIT) asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) threading_helper.join_thread(s) self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"]) # the line terminator tests below check receiving variously-sized # chunks back from the server in order to exercise all branches of # async_chat.handle_read def test_line_terminator1(self): # test one-character terminator for l in (1, 2, 3): self.line_terminator_check(b'\n', l) def test_line_terminator2(self): # test two-character terminator for l in (1, 2, 3): self.line_terminator_check(b'\r\n', l) def test_line_terminator3(self): # test three-character terminator for l in (1, 2, 3): self.line_terminator_check(b'qqq', l) def numeric_terminator_check(self, termlen): # Try reading a fixed number of bytes s, event = start_echo_server() c = echo_client(termlen, s.port) data = b"hello world, I'm not dead yet!\n" c.push(data) c.push(SERVER_QUIT) asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) threading_helper.join_thread(s) self.assertEqual(c.contents, [data[:termlen]]) def test_numeric_terminator1(self): # check that ints & longs both work (since type is # explicitly checked in async_chat.handle_read) self.numeric_terminator_check(1) def test_numeric_terminator2(self): self.numeric_terminator_check(6) def test_none_terminator(self): # Try reading a fixed number of bytes s, event = start_echo_server() c = echo_client(None, s.port) data = b"hello world, I'm not dead yet!\n" c.push(data) c.push(SERVER_QUIT) asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) threading_helper.join_thread(s) self.assertEqual(c.contents, []) self.assertEqual(c.buffer, data) def test_simple_producer(self): s, event = start_echo_server() c = echo_client(b'\n', s.port) data = b"hello world\nI'm not dead yet!\n" p = asynchat.simple_producer(data+SERVER_QUIT, buffer_size=8) c.push_with_producer(p) asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) threading_helper.join_thread(s) self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"]) def test_string_producer(self): s, event = start_echo_server() c = echo_client(b'\n', s.port) data = b"hello world\nI'm not dead yet!\n" c.push_with_producer(data+SERVER_QUIT) asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) threading_helper.join_thread(s) self.assertEqual(c.contents, [b"hello world", b"I'm not dead yet!"]) def test_empty_line(self): # checks that empty lines are handled correctly s, event = start_echo_server() c = echo_client(b'\n', s.port) c.push(b"hello world\n\nI'm not dead yet!\n") c.push(SERVER_QUIT) asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) threading_helper.join_thread(s) self.assertEqual(c.contents, [b"hello world", b"", b"I'm not dead yet!"]) def test_close_when_done(self): s, event = start_echo_server() s.start_resend_event = threading.Event() c = echo_client(b'\n', s.port) c.push(b"hello world\nI'm not dead yet!\n") c.push(SERVER_QUIT) c.close_when_done() asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) # Only allow the server to start echoing data back to the client after # the client has closed its connection. This prevents a race condition # where the server echoes all of its data before we can check that it # got any down below. s.start_resend_event.set() threading_helper.join_thread(s) self.assertEqual(c.contents, []) # the server might have been able to send a byte or two back, but this # at least checks that it received something and didn't just fail # (which could still result in the client not having received anything) self.assertGreater(len(s.buffer), 0) def test_push(self): # Issue #12523: push() should raise a TypeError if it doesn't get # a bytes string s, event = start_echo_server() c = echo_client(b'\n', s.port) data = b'bytes\n' c.push(data) c.push(bytearray(data)) c.push(memoryview(data)) self.assertRaises(TypeError, c.push, 10) self.assertRaises(TypeError, c.push, 'unicode') c.push(SERVER_QUIT) asyncore.loop(use_poll=self.usepoll, count=300, timeout=.01) threading_helper.join_thread(s) self.assertEqual(c.contents, [b'bytes', b'bytes', b'bytes']) class TestAsynchat_WithPoll(TestAsynchat): usepoll = True class TestAsynchatMocked(unittest.TestCase): def test_blockingioerror(self): # Issue #16133: handle_read() must ignore BlockingIOError sock = unittest.mock.Mock() sock.recv.side_effect = BlockingIOError(errno.EAGAIN) dispatcher = asynchat.async_chat() dispatcher.set_socket(sock) self.addCleanup(dispatcher.del_channel) with unittest.mock.patch.object(dispatcher, 'handle_error') as error: dispatcher.handle_read() self.assertFalse(error.called) class TestHelperFunctions(unittest.TestCase): def test_find_prefix_at_end(self): self.assertEqual(asynchat.find_prefix_at_end("qwerty\r", "\r\n"), 1) self.assertEqual(asynchat.find_prefix_at_end("qwertydkjf", "\r\n"), 0) class TestNotConnected(unittest.TestCase): def test_disallow_negative_terminator(self): # Issue #11259 client = asynchat.async_chat() self.assertRaises(ValueError, client.set_terminator, -1) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/asyncore/000077500000000000000000000000001471045437300163345ustar00rootroot00000000000000python-deadlib-3.13.0/asyncore/Doc/000077500000000000000000000000001471045437300170415ustar00rootroot00000000000000python-deadlib-3.13.0/asyncore/Doc/asyncore.rst000066400000000000000000000327571471045437300214340ustar00rootroot00000000000000:mod:`asyncore` --- Asynchronous socket handler =============================================== .. module:: asyncore :synopsis: A base class for developing asynchronous socket handling services. :deprecated: .. moduleauthor:: Sam Rushing .. sectionauthor:: Christopher Petrilli .. sectionauthor:: Steve Holden .. heavily adapted from original documentation by Sam Rushing **Source code:** :source:`Lib/asyncore.py` .. deprecated-removed:: 3.6 3.12 The :mod:`asyncore` module is deprecated (see :pep:`PEP 594 <594#asyncore>` for details). Please use :mod:`asyncio` instead. -------------- .. note:: This module exists for backwards compatibility only. For new code we recommend using :mod:`asyncio`. This module provides the basic infrastructure for writing asynchronous socket service clients and servers. .. include:: ../includes/wasm-notavail.rst There are only two ways to have a program on a single processor do "more than one thing at a time." Multi-threaded programming is the simplest and most popular way to do it, but there is another very different technique, that lets you have nearly all the advantages of multi-threading, without actually using multiple threads. It's really only practical if your program is largely I/O bound. If your program is processor bound, then pre-emptive scheduled threads are probably what you really need. Network servers are rarely processor bound, however. If your operating system supports the :c:func:`select` system call in its I/O library (and nearly all do), then you can use it to juggle multiple communication channels at once; doing other work while your I/O is taking place in the "background." Although this strategy can seem strange and complex, especially at first, it is in many ways easier to understand and control than multi-threaded programming. The :mod:`asyncore` module solves many of the difficult problems for you, making the task of building sophisticated high-performance network servers and clients a snap. For "conversational" applications and protocols the companion :mod:`asynchat` module is invaluable. The basic idea behind both modules is to create one or more network *channels*, instances of class :class:`asyncore.dispatcher` and :class:`asynchat.async_chat`. Creating the channels adds them to a global map, used by the :func:`loop` function if you do not provide it with your own *map*. Once the initial channel(s) is(are) created, calling the :func:`loop` function activates channel service, which continues until the last channel (including any that have been added to the map during asynchronous service) is closed. .. function:: loop([timeout[, use_poll[, map[,count]]]]) Enter a polling loop that terminates after count passes or all open channels have been closed. All arguments are optional. The *count* parameter defaults to ``None``, resulting in the loop terminating only when all channels have been closed. The *timeout* argument sets the timeout parameter for the appropriate :func:`~select.select` or :func:`~select.poll` call, measured in seconds; the default is 30 seconds. The *use_poll* parameter, if true, indicates that :func:`~select.poll` should be used in preference to :func:`~select.select` (the default is ``False``). The *map* parameter is a dictionary whose items are the channels to watch. As channels are closed they are deleted from their map. If *map* is omitted, a global map is used. Channels (instances of :class:`asyncore.dispatcher`, :class:`asynchat.async_chat` and subclasses thereof) can freely be mixed in the map. .. class:: dispatcher() The :class:`dispatcher` class is a thin wrapper around a low-level socket object. To make it more useful, it has a few methods for event-handling which are called from the asynchronous loop. Otherwise, it can be treated as a normal non-blocking socket object. The firing of low-level events at certain times or in certain connection states tells the asynchronous loop that certain higher-level events have taken place. For example, if we have asked for a socket to connect to another host, we know that the connection has been made when the socket becomes writable for the first time (at this point you know that you may write to it with the expectation of success). The implied higher-level events are: +----------------------+----------------------------------------+ | Event | Description | +======================+========================================+ | ``handle_connect()`` | Implied by the first read or write | | | event | +----------------------+----------------------------------------+ | ``handle_close()`` | Implied by a read event with no data | | | available | +----------------------+----------------------------------------+ | ``handle_accepted()``| Implied by a read event on a listening | | | socket | +----------------------+----------------------------------------+ During asynchronous processing, each mapped channel's :meth:`readable` and :meth:`writable` methods are used to determine whether the channel's socket should be added to the list of channels :c:func:`select`\ ed or :c:func:`poll`\ ed for read and write events. Thus, the set of channel events is larger than the basic socket events. The full set of methods that can be overridden in your subclass follows: .. method:: handle_read() Called when the asynchronous loop detects that a :meth:`read` call on the channel's socket will succeed. .. method:: handle_write() Called when the asynchronous loop detects that a writable socket can be written. Often this method will implement the necessary buffering for performance. For example:: def handle_write(self): sent = self.send(self.buffer) self.buffer = self.buffer[sent:] .. method:: handle_expt() Called when there is out of band (OOB) data for a socket connection. This will almost never happen, as OOB is tenuously supported and rarely used. .. method:: handle_connect() Called when the active opener's socket actually makes a connection. Might send a "welcome" banner, or initiate a protocol negotiation with the remote endpoint, for example. .. method:: handle_close() Called when the socket is closed. .. method:: handle_error() Called when an exception is raised and not otherwise handled. The default version prints a condensed traceback. .. method:: handle_accept() Called on listening channels (passive openers) when a connection can be established with a new remote endpoint that has issued a :meth:`connect` call for the local endpoint. Deprecated in version 3.2; use :meth:`handle_accepted` instead. .. deprecated:: 3.2 .. method:: handle_accepted(sock, addr) Called on listening channels (passive openers) when a connection has been established with a new remote endpoint that has issued a :meth:`connect` call for the local endpoint. *sock* is a *new* socket object usable to send and receive data on the connection, and *addr* is the address bound to the socket on the other end of the connection. .. versionadded:: 3.2 .. method:: readable() Called each time around the asynchronous loop to determine whether a channel's socket should be added to the list on which read events can occur. The default method simply returns ``True``, indicating that by default, all channels will be interested in read events. .. method:: writable() Called each time around the asynchronous loop to determine whether a channel's socket should be added to the list on which write events can occur. The default method simply returns ``True``, indicating that by default, all channels will be interested in write events. In addition, each channel delegates or extends many of the socket methods. Most of these are nearly identical to their socket partners. .. method:: create_socket(family=socket.AF_INET, type=socket.SOCK_STREAM) This is identical to the creation of a normal socket, and will use the same options for creation. Refer to the :mod:`socket` documentation for information on creating sockets. .. versionchanged:: 3.3 *family* and *type* arguments can be omitted. .. method:: connect(address) As with the normal socket object, *address* is a tuple with the first element the host to connect to, and the second the port number. .. method:: send(data) Send *data* to the remote end-point of the socket. .. method:: recv(buffer_size) Read at most *buffer_size* bytes from the socket's remote end-point. An empty bytes object implies that the channel has been closed from the other end. Note that :meth:`recv` may raise :exc:`BlockingIOError` , even though :func:`select.select` or :func:`select.poll` has reported the socket ready for reading. .. method:: listen(backlog) Listen for connections made to the socket. The *backlog* argument specifies the maximum number of queued connections and should be at least 1; the maximum value is system-dependent (usually 5). .. method:: bind(address) Bind the socket to *address*. The socket must not already be bound. (The format of *address* depends on the address family --- refer to the :mod:`socket` documentation for more information.) To mark the socket as re-usable (setting the :const:`SO_REUSEADDR` option), call the :class:`dispatcher` object's :meth:`set_reuse_addr` method. .. method:: accept() Accept a connection. The socket must be bound to an address and listening for connections. The return value can be either ``None`` or a pair ``(conn, address)`` where *conn* is a *new* socket object usable to send and receive data on the connection, and *address* is the address bound to the socket on the other end of the connection. When ``None`` is returned it means the connection didn't take place, in which case the server should just ignore this event and keep listening for further incoming connections. .. method:: close() Close the socket. All future operations on the socket object will fail. The remote end-point will receive no more data (after queued data is flushed). Sockets are automatically closed when they are garbage-collected. .. class:: dispatcher_with_send() A :class:`dispatcher` subclass which adds simple buffered output capability, useful for simple clients. For more sophisticated usage use :class:`asynchat.async_chat`. .. class:: file_dispatcher() A file_dispatcher takes a file descriptor or :term:`file object` along with an optional map argument and wraps it for use with the :c:func:`poll` or :c:func:`loop` functions. If provided a file object or anything with a :c:func:`fileno` method, that method will be called and passed to the :class:`file_wrapper` constructor. .. availability:: Unix. .. class:: file_wrapper() A file_wrapper takes an integer file descriptor and calls :func:`os.dup` to duplicate the handle so that the original handle may be closed independently of the file_wrapper. This class implements sufficient methods to emulate a socket for use by the :class:`file_dispatcher` class. .. availability:: Unix. .. _asyncore-example-1: asyncore Example basic HTTP client ---------------------------------- Here is a very basic HTTP client that uses the :class:`dispatcher` class to implement its socket handling:: import asyncore class HTTPClient(asyncore.dispatcher): def __init__(self, host, path): asyncore.dispatcher.__init__(self) self.create_socket() self.connect( (host, 80) ) self.buffer = bytes('GET %s HTTP/1.0\r\nHost: %s\r\n\r\n' % (path, host), 'ascii') def handle_connect(self): pass def handle_close(self): self.close() def handle_read(self): print(self.recv(8192)) def writable(self): return (len(self.buffer) > 0) def handle_write(self): sent = self.send(self.buffer) self.buffer = self.buffer[sent:] client = HTTPClient('www.python.org', '/') asyncore.loop() .. _asyncore-example-2: asyncore Example basic echo server ---------------------------------- Here is a basic echo server that uses the :class:`dispatcher` class to accept connections and dispatches the incoming connections to a handler:: import asyncore class EchoHandler(asyncore.dispatcher_with_send): def handle_read(self): data = self.recv(8192) if data: self.send(data) class EchoServer(asyncore.dispatcher): def __init__(self, host, port): asyncore.dispatcher.__init__(self) self.create_socket() self.set_reuse_addr() self.bind((host, port)) self.listen(5) def handle_accepted(self, sock, addr): print('Incoming connection from %s' % repr(addr)) handler = EchoHandler(sock) server = EchoServer('localhost', 8080) asyncore.loop() python-deadlib-3.13.0/asyncore/LICENSE000066400000000000000000000050111471045437300173360ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/asyncore/README.rst000066400000000000000000000005161471045437300200250ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/asyncore/asyncore/000077500000000000000000000000001471045437300201575ustar00rootroot00000000000000python-deadlib-3.13.0/asyncore/asyncore/__init__.py000066400000000000000000000477341471045437300223070ustar00rootroot00000000000000# -*- Mode: Python -*- # Id: asyncore.py,v 2.51 2000/09/07 22:29:26 rushing Exp # Author: Sam Rushing # ====================================================================== # Copyright 1996 by Sam Rushing # # 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 Sam # Rushing not be used in advertising or publicity pertaining to # distribution of the software without specific, written prior # permission. # # SAM RUSHING DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, # INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN # NO EVENT SHALL SAM RUSHING 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. # ====================================================================== """Basic infrastructure for asynchronous socket service clients and servers. There are only two ways to have a program on a single processor do "more than one thing at a time". Multi-threaded programming is the simplest and most popular way to do it, but there is another very different technique, that lets you have nearly all the advantages of multi-threading, without actually using multiple threads. it's really only practical if your program is largely I/O bound. If your program is CPU bound, then pre-emptive scheduled threads are probably what you really need. Network servers are rarely CPU-bound, however. If your operating system supports the select() system call in its I/O library (and nearly all do), then you can use it to juggle multiple communication channels at once; doing other work while your I/O is taking place in the "background." Although this strategy can seem strange and complex, especially at first, it is in many ways easier to understand and control than multi-threaded programming. The module documented here solves many of the difficult problems for you, making the task of building sophisticated high-performance network servers and clients a snap. """ import select import socket import sys import time import warnings import os from errno import EALREADY, EINPROGRESS, EWOULDBLOCK, ECONNRESET, EINVAL, \ ENOTCONN, ESHUTDOWN, EISCONN, EBADF, ECONNABORTED, EPIPE, EAGAIN, \ errorcode # python-deadlib: Replace deprecation warning not to raise exception warnings.warn( f"{__name__} was removed in Python 3.12. The recommended replacement is asyncio. " f"Please be aware that you are currently NOT using standard '{__name__}', " f"but instead a separately installed 'standard-{__name__}'.", DeprecationWarning, stacklevel=2 ) _DISCONNECTED = frozenset({ECONNRESET, ENOTCONN, ESHUTDOWN, ECONNABORTED, EPIPE, EBADF}) try: socket_map except NameError: socket_map = {} def _strerror(err): try: return os.strerror(err) except (ValueError, OverflowError, NameError): if err in errorcode: return errorcode[err] return "Unknown error %s" %err class ExitNow(Exception): pass _reraised_exceptions = (ExitNow, KeyboardInterrupt, SystemExit) def read(obj): try: obj.handle_read_event() except _reraised_exceptions: raise except: obj.handle_error() def write(obj): try: obj.handle_write_event() except _reraised_exceptions: raise except: obj.handle_error() def _exception(obj): try: obj.handle_expt_event() except _reraised_exceptions: raise except: obj.handle_error() def readwrite(obj, flags): try: if flags & select.POLLIN: obj.handle_read_event() if flags & select.POLLOUT: obj.handle_write_event() if flags & select.POLLPRI: obj.handle_expt_event() if flags & (select.POLLHUP | select.POLLERR | select.POLLNVAL): obj.handle_close() except OSError as e: if e.errno not in _DISCONNECTED: obj.handle_error() else: obj.handle_close() except _reraised_exceptions: raise except: obj.handle_error() def poll(timeout=0.0, map=None): if map is None: map = socket_map if map: r = []; w = []; e = [] for fd, obj in list(map.items()): is_r = obj.readable() is_w = obj.writable() if is_r: r.append(fd) # accepting sockets should not be writable if is_w and not obj.accepting: w.append(fd) if is_r or is_w: e.append(fd) if [] == r == w == e: time.sleep(timeout) return r, w, e = select.select(r, w, e, timeout) for fd in r: obj = map.get(fd) if obj is None: continue read(obj) for fd in w: obj = map.get(fd) if obj is None: continue write(obj) for fd in e: obj = map.get(fd) if obj is None: continue _exception(obj) def poll2(timeout=0.0, map=None): # Use the poll() support added to the select module in Python 2.0 if map is None: map = socket_map if timeout is not None: # timeout is in milliseconds timeout = int(timeout*1000) pollster = select.poll() if map: for fd, obj in list(map.items()): flags = 0 if obj.readable(): flags |= select.POLLIN | select.POLLPRI # accepting sockets should not be writable if obj.writable() and not obj.accepting: flags |= select.POLLOUT if flags: pollster.register(fd, flags) r = pollster.poll(timeout) for fd, flags in r: obj = map.get(fd) if obj is None: continue readwrite(obj, flags) poll3 = poll2 # Alias for backward compatibility def loop(timeout=30.0, use_poll=False, map=None, count=None): if map is None: map = socket_map if use_poll and hasattr(select, 'poll'): poll_fun = poll2 else: poll_fun = poll if count is None: while map: poll_fun(timeout, map) else: while map and count > 0: poll_fun(timeout, map) count = count - 1 class dispatcher: debug = False connected = False accepting = False connecting = False closing = False addr = None ignore_log_types = frozenset({'warning'}) def __init__(self, sock=None, map=None): if map is None: self._map = socket_map else: self._map = map self._fileno = None if sock: # Set to nonblocking just to make sure for cases where we # get a socket from a blocking source. sock.setblocking(False) self.set_socket(sock, map) self.connected = True # The constructor no longer requires that the socket # passed be connected. try: self.addr = sock.getpeername() except OSError as err: if err.errno in (ENOTCONN, EINVAL): # To handle the case where we got an unconnected # socket. self.connected = False else: # The socket is broken in some unknown way, alert # the user and remove it from the map (to prevent # polling of broken sockets). self.del_channel(map) raise else: self.socket = None def __repr__(self): status = [self.__class__.__module__+"."+self.__class__.__qualname__] if self.accepting and self.addr: status.append('listening') elif self.connected: status.append('connected') if self.addr is not None: try: status.append('%s:%d' % self.addr) except TypeError: status.append(repr(self.addr)) return '<%s at %#x>' % (' '.join(status), id(self)) def add_channel(self, map=None): #self.log_info('adding channel %s' % self) if map is None: map = self._map map[self._fileno] = self def del_channel(self, map=None): fd = self._fileno if map is None: map = self._map if fd in map: #self.log_info('closing channel %d:%s' % (fd, self)) del map[fd] self._fileno = None def create_socket(self, family=socket.AF_INET, type=socket.SOCK_STREAM): self.family_and_type = family, type sock = socket.socket(family, type) sock.setblocking(False) self.set_socket(sock) def set_socket(self, sock, map=None): self.socket = sock self._fileno = sock.fileno() self.add_channel(map) def set_reuse_addr(self): # try to re-use a server port if possible try: self.socket.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) | 1 ) except OSError: pass # ================================================== # predicates for select() # these are used as filters for the lists of sockets # to pass to select(). # ================================================== def readable(self): return True def writable(self): return True # ================================================== # socket object methods. # ================================================== def listen(self, num): self.accepting = True if os.name == 'nt' and num > 5: num = 5 return self.socket.listen(num) def bind(self, addr): self.addr = addr return self.socket.bind(addr) def connect(self, address): self.connected = False self.connecting = True err = self.socket.connect_ex(address) if err in (EINPROGRESS, EALREADY, EWOULDBLOCK) \ or err == EINVAL and os.name == 'nt': self.addr = address return if err in (0, EISCONN): self.addr = address self.handle_connect_event() else: raise OSError(err, errorcode[err]) def accept(self): # XXX can return either an address pair or None try: conn, addr = self.socket.accept() except TypeError: return None except OSError as why: if why.errno in (EWOULDBLOCK, ECONNABORTED, EAGAIN): return None else: raise else: return conn, addr def send(self, data): try: result = self.socket.send(data) return result except OSError as why: if why.errno == EWOULDBLOCK: return 0 elif why.errno in _DISCONNECTED: self.handle_close() return 0 else: raise def recv(self, buffer_size): try: data = self.socket.recv(buffer_size) if not data: # a closed connection is indicated by signaling # a read condition, and having recv() return 0. self.handle_close() return b'' else: return data except OSError as why: # winsock sometimes raises ENOTCONN if why.errno in _DISCONNECTED: self.handle_close() return b'' else: raise def close(self): self.connected = False self.accepting = False self.connecting = False self.del_channel() if self.socket is not None: try: self.socket.close() except OSError as why: if why.errno not in (ENOTCONN, EBADF): raise # log and log_info may be overridden to provide more sophisticated # logging and warning methods. In general, log is for 'hit' logging # and 'log_info' is for informational, warning and error logging. def log(self, message): sys.stderr.write('log: %s\n' % str(message)) def log_info(self, message, type='info'): if type not in self.ignore_log_types: print('%s: %s' % (type, message)) def handle_read_event(self): if self.accepting: # accepting sockets are never connected, they "spawn" new # sockets that are connected self.handle_accept() elif not self.connected: if self.connecting: self.handle_connect_event() self.handle_read() else: self.handle_read() def handle_connect_event(self): err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) if err != 0: raise OSError(err, _strerror(err)) self.handle_connect() self.connected = True self.connecting = False def handle_write_event(self): if self.accepting: # Accepting sockets shouldn't get a write event. # We will pretend it didn't happen. return if not self.connected: if self.connecting: self.handle_connect_event() self.handle_write() def handle_expt_event(self): # handle_expt_event() is called if there might be an error on the # socket, or if there is OOB data # check for the error condition first err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) if err != 0: # we can get here when select.select() says that there is an # exceptional condition on the socket # since there is an error, we'll go ahead and close the socket # like we would in a subclassed handle_read() that received no # data self.handle_close() else: self.handle_expt() def handle_error(self): nil, t, v, tbinfo = compact_traceback() # sometimes a user repr method will crash. try: self_repr = repr(self) except: self_repr = '<__repr__(self) failed for object at %0x>' % id(self) self.log_info( 'uncaptured python exception, closing channel %s (%s:%s %s)' % ( self_repr, t, v, tbinfo ), 'error' ) self.handle_close() def handle_expt(self): self.log_info('unhandled incoming priority event', 'warning') def handle_read(self): self.log_info('unhandled read event', 'warning') def handle_write(self): self.log_info('unhandled write event', 'warning') def handle_connect(self): self.log_info('unhandled connect event', 'warning') def handle_accept(self): pair = self.accept() if pair is not None: self.handle_accepted(*pair) def handle_accepted(self, sock, addr): sock.close() self.log_info('unhandled accepted event', 'warning') def handle_close(self): self.log_info('unhandled close event', 'warning') self.close() # --------------------------------------------------------------------------- # adds simple buffered output capability, useful for simple clients. # [for more sophisticated usage use asynchat.async_chat] # --------------------------------------------------------------------------- class dispatcher_with_send(dispatcher): def __init__(self, sock=None, map=None): dispatcher.__init__(self, sock, map) self.out_buffer = b'' def initiate_send(self): num_sent = 0 num_sent = dispatcher.send(self, self.out_buffer[:65536]) self.out_buffer = self.out_buffer[num_sent:] def handle_write(self): self.initiate_send() def writable(self): return (not self.connected) or len(self.out_buffer) def send(self, data): if self.debug: self.log_info('sending %s' % repr(data)) self.out_buffer = self.out_buffer + data self.initiate_send() # --------------------------------------------------------------------------- # used for debugging. # --------------------------------------------------------------------------- def compact_traceback(): t, v, tb = sys.exc_info() tbinfo = [] if not tb: # Must have a traceback raise AssertionError("traceback does not exist") while tb: tbinfo.append(( tb.tb_frame.f_code.co_filename, tb.tb_frame.f_code.co_name, str(tb.tb_lineno) )) tb = tb.tb_next # just to be safe del tb file, function, line = tbinfo[-1] info = ' '.join(['[%s|%s|%s]' % x for x in tbinfo]) return (file, function, line), t, v, info def close_all(map=None, ignore_all=False): if map is None: map = socket_map for x in list(map.values()): try: x.close() except OSError as x: if x.errno == EBADF: pass elif not ignore_all: raise except _reraised_exceptions: raise except: if not ignore_all: raise map.clear() # Asynchronous File I/O: # # After a little research (reading man pages on various unixen, and # digging through the linux kernel), I've determined that select() # isn't meant for doing asynchronous file i/o. # Heartening, though - reading linux/mm/filemap.c shows that linux # supports asynchronous read-ahead. So _MOST_ of the time, the data # will be sitting in memory for us already when we go to read it. # # What other OS's (besides NT) support async file i/o? [VMS?] # # Regardless, this is useful for pipes, and stdin/stdout... if os.name == 'posix': class file_wrapper: # Here we override just enough to make a file # look like a socket for the purposes of asyncore. # The passed fd is automatically os.dup()'d def __init__(self, fd): self.fd = os.dup(fd) def __del__(self): if self.fd >= 0: warnings.warn("unclosed file %r" % self, ResourceWarning, source=self) self.close() def recv(self, *args): return os.read(self.fd, *args) def send(self, *args): return os.write(self.fd, *args) def getsockopt(self, level, optname, buflen=None): if (level == socket.SOL_SOCKET and optname == socket.SO_ERROR and not buflen): return 0 raise NotImplementedError("Only asyncore specific behaviour " "implemented.") read = recv write = send def close(self): if self.fd < 0: return fd = self.fd self.fd = -1 os.close(fd) def fileno(self): return self.fd class file_dispatcher(dispatcher): def __init__(self, fd, map=None): dispatcher.__init__(self, None, map) self.connected = True try: fd = fd.fileno() except AttributeError: pass self.set_file(fd) # set it to non-blocking mode os.set_blocking(fd, False) def set_file(self, fd): self.socket = file_wrapper(fd) self._fileno = self.socket.fileno() self.add_channel() python-deadlib-3.13.0/asyncore/pyproject.toml000066400000000000000000000012761471045437300212560ustar00rootroot00000000000000[project] name = "standard-asyncore" version = "3.13.0" description = "Standard library asyncore redistribution. \"dead battery\"." keywords = ["stdlib"] authors = [ { name = "Python Developers", email = "python-deadlib@youknowone.org" } ] readme = "README.rst" license = { text = "PSF-2.0" } classifiers = [ "License :: OSI Approved :: Python Software Foundation License", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", ] [tool.setuptools.packages] find = {include = ["asyncore*"]} [project.urls] "Homepage" = "https://github.com/youknowone/python-deadlib" [build-system] requires = ["setuptools>=75.0"] build-backend = "setuptools.build_meta" python-deadlib-3.13.0/asyncore/tests/000077500000000000000000000000001471045437300174765ustar00rootroot00000000000000python-deadlib-3.13.0/asyncore/tests/__init__.py000066400000000000000000000000001471045437300215750ustar00rootroot00000000000000python-deadlib-3.13.0/asyncore/tests/test_asyncore.py000066400000000000000000000641541471045437300227440ustar00rootroot00000000000000import unittest import select import os import socket import sys import time import errno import struct import threading from test import support from test.support import os_helper from test.support import socket_helper from test.support import threading_helper from test.support import warnings_helper from io import BytesIO if support.PGO: raise unittest.SkipTest("test is not helpful for PGO") support.requires_working_socket(module=True) asyncore = warnings_helper.import_deprecated('asyncore') HAS_UNIX_SOCKETS = hasattr(socket, 'AF_UNIX') class dummysocket: def __init__(self): self.closed = False def close(self): self.closed = True def fileno(self): return 42 class dummychannel: def __init__(self): self.socket = dummysocket() def close(self): self.socket.close() class exitingdummy: def __init__(self): pass def handle_read_event(self): raise asyncore.ExitNow() handle_write_event = handle_read_event handle_close = handle_read_event handle_expt_event = handle_read_event class crashingdummy: def __init__(self): self.error_handled = False def handle_read_event(self): raise Exception() handle_write_event = handle_read_event handle_close = handle_read_event handle_expt_event = handle_read_event def handle_error(self): self.error_handled = True # used when testing senders; just collects what it gets until newline is sent def capture_server(evt, buf, serv): try: serv.listen() conn, addr = serv.accept() except TimeoutError: pass else: n = 200 start = time.monotonic() while n > 0 and time.monotonic() - start < 3.0: r, w, e = select.select([conn], [], [], 0.1) if r: n -= 1 data = conn.recv(10) # keep everything except for the newline terminator buf.write(data.replace(b'\n', b'')) if b'\n' in data: break time.sleep(0.01) conn.close() finally: serv.close() evt.set() def bind_af_aware(sock, addr): """Helper function to bind a socket according to its family.""" if HAS_UNIX_SOCKETS and sock.family == socket.AF_UNIX: # Make sure the path doesn't exist. os_helper.unlink(addr) socket_helper.bind_unix_socket(sock, addr) else: sock.bind(addr) class HelperFunctionTests(unittest.TestCase): def test_readwriteexc(self): # Check exception handling behavior of read, write and _exception # check that ExitNow exceptions in the object handler method # bubbles all the way up through asyncore read/write/_exception calls tr1 = exitingdummy() self.assertRaises(asyncore.ExitNow, asyncore.read, tr1) self.assertRaises(asyncore.ExitNow, asyncore.write, tr1) self.assertRaises(asyncore.ExitNow, asyncore._exception, tr1) # check that an exception other than ExitNow in the object handler # method causes the handle_error method to get called tr2 = crashingdummy() asyncore.read(tr2) self.assertEqual(tr2.error_handled, True) tr2 = crashingdummy() asyncore.write(tr2) self.assertEqual(tr2.error_handled, True) tr2 = crashingdummy() asyncore._exception(tr2) self.assertEqual(tr2.error_handled, True) # asyncore.readwrite uses constants in the select module that # are not present in Windows systems (see this thread: # http://mail.python.org/pipermail/python-list/2001-October/109973.html) # These constants should be present as long as poll is available @unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required') def test_readwrite(self): # Check that correct methods are called by readwrite() attributes = ('read', 'expt', 'write', 'closed', 'error_handled') expected = ( (select.POLLIN, 'read'), (select.POLLPRI, 'expt'), (select.POLLOUT, 'write'), (select.POLLERR, 'closed'), (select.POLLHUP, 'closed'), (select.POLLNVAL, 'closed'), ) class testobj: def __init__(self): self.read = False self.write = False self.closed = False self.expt = False self.error_handled = False def handle_read_event(self): self.read = True def handle_write_event(self): self.write = True def handle_close(self): self.closed = True def handle_expt_event(self): self.expt = True def handle_error(self): self.error_handled = True for flag, expectedattr in expected: tobj = testobj() self.assertEqual(getattr(tobj, expectedattr), False) asyncore.readwrite(tobj, flag) # Only the attribute modified by the routine we expect to be # called should be True. for attr in attributes: self.assertEqual(getattr(tobj, attr), attr==expectedattr) # check that ExitNow exceptions in the object handler method # bubbles all the way up through asyncore readwrite call tr1 = exitingdummy() self.assertRaises(asyncore.ExitNow, asyncore.readwrite, tr1, flag) # check that an exception other than ExitNow in the object handler # method causes the handle_error method to get called tr2 = crashingdummy() self.assertEqual(tr2.error_handled, False) asyncore.readwrite(tr2, flag) self.assertEqual(tr2.error_handled, True) def test_closeall(self): self.closeall_check(False) def test_closeall_default(self): self.closeall_check(True) def closeall_check(self, usedefault): # Check that close_all() closes everything in a given map l = [] testmap = {} for i in range(10): c = dummychannel() l.append(c) self.assertEqual(c.socket.closed, False) testmap[i] = c if usedefault: socketmap = asyncore.socket_map try: asyncore.socket_map = testmap asyncore.close_all() finally: testmap, asyncore.socket_map = asyncore.socket_map, socketmap else: asyncore.close_all(testmap) self.assertEqual(len(testmap), 0) for c in l: self.assertEqual(c.socket.closed, True) def test_compact_traceback(self): try: raise Exception("I don't like spam!") except: real_t, real_v, real_tb = sys.exc_info() r = asyncore.compact_traceback() else: self.fail("Expected exception") (f, function, line), t, v, info = r self.assertEqual(os.path.split(f)[-1], 'test_asyncore.py') self.assertEqual(function, 'test_compact_traceback') self.assertEqual(t, real_t) self.assertEqual(v, real_v) self.assertEqual(info, '[%s|%s|%s]' % (f, function, line)) class DispatcherTests(unittest.TestCase): def setUp(self): pass def tearDown(self): asyncore.close_all() def test_basic(self): d = asyncore.dispatcher() self.assertEqual(d.readable(), True) self.assertEqual(d.writable(), True) def test_repr(self): d = asyncore.dispatcher() self.assertEqual(repr(d), '' % id(d)) def test_log(self): d = asyncore.dispatcher() # capture output of dispatcher.log() (to stderr) l1 = "Lovely spam! Wonderful spam!" l2 = "I don't like spam!" with support.captured_stderr() as stderr: d.log(l1) d.log(l2) lines = stderr.getvalue().splitlines() self.assertEqual(lines, ['log: %s' % l1, 'log: %s' % l2]) def test_log_info(self): d = asyncore.dispatcher() # capture output of dispatcher.log_info() (to stdout via print) l1 = "Have you got anything without spam?" l2 = "Why can't she have egg bacon spam and sausage?" l3 = "THAT'S got spam in it!" with support.captured_stdout() as stdout: d.log_info(l1, 'EGGS') d.log_info(l2) d.log_info(l3, 'SPAM') lines = stdout.getvalue().splitlines() expected = ['EGGS: %s' % l1, 'info: %s' % l2, 'SPAM: %s' % l3] self.assertEqual(lines, expected) def test_unhandled(self): d = asyncore.dispatcher() d.ignore_log_types = () # capture output of dispatcher.log_info() (to stdout via print) with support.captured_stdout() as stdout: d.handle_expt() d.handle_read() d.handle_write() d.handle_connect() lines = stdout.getvalue().splitlines() expected = ['warning: unhandled incoming priority event', 'warning: unhandled read event', 'warning: unhandled write event', 'warning: unhandled connect event'] self.assertEqual(lines, expected) def test_strerror(self): # refers to bug #8573 err = asyncore._strerror(errno.EPERM) if hasattr(os, 'strerror'): self.assertEqual(err, os.strerror(errno.EPERM)) err = asyncore._strerror(-1) self.assertTrue(err != "") class dispatcherwithsend_noread(asyncore.dispatcher_with_send): def readable(self): return False def handle_connect(self): pass class DispatcherWithSendTests(unittest.TestCase): def setUp(self): pass def tearDown(self): asyncore.close_all() @threading_helper.reap_threads def test_send(self): evt = threading.Event() sock = socket.socket() sock.settimeout(3) port = socket_helper.bind_port(sock) cap = BytesIO() args = (evt, cap, sock) t = threading.Thread(target=capture_server, args=args) t.start() try: # wait a little longer for the server to initialize (it sometimes # refuses connections on slow machines without this wait) time.sleep(0.2) data = b"Suppose there isn't a 16-ton weight?" d = dispatcherwithsend_noread() d.create_socket() d.connect((socket_helper.HOST, port)) # give time for socket to connect time.sleep(0.1) d.send(data) d.send(data) d.send(b'\n') n = 1000 while d.out_buffer and n > 0: asyncore.poll() n -= 1 evt.wait() self.assertEqual(cap.getvalue(), data*2) finally: threading_helper.join_thread(t) @unittest.skipUnless(hasattr(asyncore, 'file_wrapper'), 'asyncore.file_wrapper required') class FileWrapperTest(unittest.TestCase): def setUp(self): self.d = b"It's not dead, it's sleeping!" with open(os_helper.TESTFN, 'wb') as file: file.write(self.d) def tearDown(self): os_helper.unlink(os_helper.TESTFN) def test_recv(self): fd = os.open(os_helper.TESTFN, os.O_RDONLY) w = asyncore.file_wrapper(fd) os.close(fd) self.assertNotEqual(w.fd, fd) self.assertNotEqual(w.fileno(), fd) self.assertEqual(w.recv(13), b"It's not dead") self.assertEqual(w.read(6), b", it's") w.close() self.assertRaises(OSError, w.read, 1) def test_send(self): d1 = b"Come again?" d2 = b"I want to buy some cheese." fd = os.open(os_helper.TESTFN, os.O_WRONLY | os.O_APPEND) w = asyncore.file_wrapper(fd) os.close(fd) w.write(d1) w.send(d2) w.close() with open(os_helper.TESTFN, 'rb') as file: self.assertEqual(file.read(), self.d + d1 + d2) @unittest.skipUnless(hasattr(asyncore, 'file_dispatcher'), 'asyncore.file_dispatcher required') def test_dispatcher(self): fd = os.open(os_helper.TESTFN, os.O_RDONLY) data = [] class FileDispatcher(asyncore.file_dispatcher): def handle_read(self): data.append(self.recv(29)) s = FileDispatcher(fd) os.close(fd) asyncore.loop(timeout=0.01, use_poll=True, count=2) self.assertEqual(b"".join(data), self.d) def test_resource_warning(self): # Issue #11453 fd = os.open(os_helper.TESTFN, os.O_RDONLY) f = asyncore.file_wrapper(fd) os.close(fd) with warnings_helper.check_warnings(('', ResourceWarning)): f = None support.gc_collect() def test_close_twice(self): fd = os.open(os_helper.TESTFN, os.O_RDONLY) f = asyncore.file_wrapper(fd) os.close(fd) os.close(f.fd) # file_wrapper dupped fd with self.assertRaises(OSError): f.close() self.assertEqual(f.fd, -1) # calling close twice should not fail f.close() class BaseTestHandler(asyncore.dispatcher): def __init__(self, sock=None): asyncore.dispatcher.__init__(self, sock) self.flag = False def handle_accept(self): raise Exception("handle_accept not supposed to be called") def handle_accepted(self): raise Exception("handle_accepted not supposed to be called") def handle_connect(self): raise Exception("handle_connect not supposed to be called") def handle_expt(self): raise Exception("handle_expt not supposed to be called") def handle_close(self): raise Exception("handle_close not supposed to be called") def handle_error(self): raise class BaseServer(asyncore.dispatcher): """A server which listens on an address and dispatches the connection to a handler. """ def __init__(self, family, addr, handler=BaseTestHandler): asyncore.dispatcher.__init__(self) self.create_socket(family) self.set_reuse_addr() bind_af_aware(self.socket, addr) self.listen(5) self.handler = handler @property def address(self): return self.socket.getsockname() def handle_accepted(self, sock, addr): self.handler(sock) def handle_error(self): raise class BaseClient(BaseTestHandler): def __init__(self, family, address): BaseTestHandler.__init__(self) self.create_socket(family) self.connect(address) def handle_connect(self): pass class BaseTestAPI: def tearDown(self): asyncore.close_all(ignore_all=True) def loop_waiting_for_flag(self, instance, timeout=5): timeout = float(timeout) / 100 count = 100 while asyncore.socket_map and count > 0: asyncore.loop(timeout=0.01, count=1, use_poll=self.use_poll) if instance.flag: return count -= 1 time.sleep(timeout) self.fail("flag not set") def test_handle_connect(self): # make sure handle_connect is called on connect() class TestClient(BaseClient): def handle_connect(self): self.flag = True server = BaseServer(self.family, self.addr) client = TestClient(self.family, server.address) self.loop_waiting_for_flag(client) def test_handle_accept(self): # make sure handle_accept() is called when a client connects class TestListener(BaseTestHandler): def __init__(self, family, addr): BaseTestHandler.__init__(self) self.create_socket(family) bind_af_aware(self.socket, addr) self.listen(5) self.address = self.socket.getsockname() def handle_accept(self): self.flag = True server = TestListener(self.family, self.addr) client = BaseClient(self.family, server.address) self.loop_waiting_for_flag(server) def test_handle_accepted(self): # make sure handle_accepted() is called when a client connects class TestListener(BaseTestHandler): def __init__(self, family, addr): BaseTestHandler.__init__(self) self.create_socket(family) bind_af_aware(self.socket, addr) self.listen(5) self.address = self.socket.getsockname() def handle_accept(self): asyncore.dispatcher.handle_accept(self) def handle_accepted(self, sock, addr): sock.close() self.flag = True server = TestListener(self.family, self.addr) client = BaseClient(self.family, server.address) self.loop_waiting_for_flag(server) def test_handle_read(self): # make sure handle_read is called on data received class TestClient(BaseClient): def handle_read(self): self.flag = True class TestHandler(BaseTestHandler): def __init__(self, conn): BaseTestHandler.__init__(self, conn) self.send(b'x' * 1024) server = BaseServer(self.family, self.addr, TestHandler) client = TestClient(self.family, server.address) self.loop_waiting_for_flag(client) def test_handle_write(self): # make sure handle_write is called class TestClient(BaseClient): def handle_write(self): self.flag = True server = BaseServer(self.family, self.addr) client = TestClient(self.family, server.address) self.loop_waiting_for_flag(client) def test_handle_close(self): # make sure handle_close is called when the other end closes # the connection class TestClient(BaseClient): def handle_read(self): # in order to make handle_close be called we are supposed # to make at least one recv() call self.recv(1024) def handle_close(self): self.flag = True self.close() class TestHandler(BaseTestHandler): def __init__(self, conn): BaseTestHandler.__init__(self, conn) self.close() server = BaseServer(self.family, self.addr, TestHandler) client = TestClient(self.family, server.address) self.loop_waiting_for_flag(client) def test_handle_close_after_conn_broken(self): # Check that ECONNRESET/EPIPE is correctly handled (issues #5661 and # #11265). data = b'\0' * 128 class TestClient(BaseClient): def handle_write(self): self.send(data) def handle_close(self): self.flag = True self.close() def handle_expt(self): self.flag = True self.close() class TestHandler(BaseTestHandler): def handle_read(self): self.recv(len(data)) self.close() def writable(self): return False server = BaseServer(self.family, self.addr, TestHandler) client = TestClient(self.family, server.address) self.loop_waiting_for_flag(client) @unittest.skipIf(sys.platform.startswith("sunos"), "OOB support is broken on Solaris") def test_handle_expt(self): # Make sure handle_expt is called on OOB data received. # Note: this might fail on some platforms as OOB data is # tenuously supported and rarely used. if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX: self.skipTest("Not applicable to AF_UNIX sockets.") if sys.platform == "darwin" and self.use_poll: self.skipTest("poll may fail on macOS; see issue #28087") class TestClient(BaseClient): def handle_expt(self): self.socket.recv(1024, socket.MSG_OOB) self.flag = True class TestHandler(BaseTestHandler): def __init__(self, conn): BaseTestHandler.__init__(self, conn) self.socket.send(bytes(chr(244), 'latin-1'), socket.MSG_OOB) server = BaseServer(self.family, self.addr, TestHandler) client = TestClient(self.family, server.address) self.loop_waiting_for_flag(client) def test_handle_error(self): class TestClient(BaseClient): def handle_write(self): 1.0 / 0 def handle_error(self): self.flag = True try: raise except ZeroDivisionError: pass else: raise Exception("exception not raised") server = BaseServer(self.family, self.addr) client = TestClient(self.family, server.address) self.loop_waiting_for_flag(client) def test_connection_attributes(self): server = BaseServer(self.family, self.addr) client = BaseClient(self.family, server.address) # we start disconnected self.assertFalse(server.connected) self.assertTrue(server.accepting) # this can't be taken for granted across all platforms #self.assertFalse(client.connected) self.assertFalse(client.accepting) # execute some loops so that client connects to server asyncore.loop(timeout=0.01, use_poll=self.use_poll, count=100) self.assertFalse(server.connected) self.assertTrue(server.accepting) self.assertTrue(client.connected) self.assertFalse(client.accepting) # disconnect the client client.close() self.assertFalse(server.connected) self.assertTrue(server.accepting) self.assertFalse(client.connected) self.assertFalse(client.accepting) # stop serving server.close() self.assertFalse(server.connected) self.assertFalse(server.accepting) def test_create_socket(self): s = asyncore.dispatcher() s.create_socket(self.family) self.assertEqual(s.socket.type, socket.SOCK_STREAM) self.assertEqual(s.socket.family, self.family) self.assertEqual(s.socket.gettimeout(), 0) self.assertFalse(s.socket.get_inheritable()) def test_bind(self): if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX: self.skipTest("Not applicable to AF_UNIX sockets.") s1 = asyncore.dispatcher() s1.create_socket(self.family) s1.bind(self.addr) s1.listen(5) port = s1.socket.getsockname()[1] s2 = asyncore.dispatcher() s2.create_socket(self.family) # EADDRINUSE indicates the socket was correctly bound self.assertRaises(OSError, s2.bind, (self.addr[0], port)) def test_set_reuse_addr(self): if HAS_UNIX_SOCKETS and self.family == socket.AF_UNIX: self.skipTest("Not applicable to AF_UNIX sockets.") with socket.socket(self.family) as sock: try: sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) except OSError: unittest.skip("SO_REUSEADDR not supported on this platform") else: # if SO_REUSEADDR succeeded for sock we expect asyncore # to do the same s = asyncore.dispatcher(socket.socket(self.family)) self.assertFalse(s.socket.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)) s.socket.close() s.create_socket(self.family) s.set_reuse_addr() self.assertTrue(s.socket.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)) @threading_helper.reap_threads def test_quick_connect(self): # see: http://bugs.python.org/issue10340 if self.family not in (socket.AF_INET, getattr(socket, "AF_INET6", object())): self.skipTest("test specific to AF_INET and AF_INET6") server = BaseServer(self.family, self.addr) # run the thread 500 ms: the socket should be connected in 200 ms t = threading.Thread(target=lambda: asyncore.loop(timeout=0.1, count=5)) t.start() try: with socket.socket(self.family, socket.SOCK_STREAM) as s: s.settimeout(.2) s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0)) try: s.connect(server.address) except OSError: pass finally: threading_helper.join_thread(t) class TestAPI_UseIPv4Sockets(BaseTestAPI): family = socket.AF_INET addr = (socket_helper.HOST, 0) @unittest.skipUnless(socket_helper.IPV6_ENABLED, 'IPv6 support required') class TestAPI_UseIPv6Sockets(BaseTestAPI): family = socket.AF_INET6 addr = (socket_helper.HOSTv6, 0) @unittest.skipUnless(HAS_UNIX_SOCKETS, 'Unix sockets required') class TestAPI_UseUnixSockets(BaseTestAPI): if HAS_UNIX_SOCKETS: family = socket.AF_UNIX addr = os_helper.TESTFN def tearDown(self): os_helper.unlink(self.addr) BaseTestAPI.tearDown(self) class TestAPI_UseIPv4Select(TestAPI_UseIPv4Sockets, unittest.TestCase): use_poll = False @unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required') class TestAPI_UseIPv4Poll(TestAPI_UseIPv4Sockets, unittest.TestCase): use_poll = True class TestAPI_UseIPv6Select(TestAPI_UseIPv6Sockets, unittest.TestCase): use_poll = False @unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required') class TestAPI_UseIPv6Poll(TestAPI_UseIPv6Sockets, unittest.TestCase): use_poll = True class TestAPI_UseUnixSocketsSelect(TestAPI_UseUnixSockets, unittest.TestCase): use_poll = False @unittest.skipUnless(hasattr(select, 'poll'), 'select.poll required') class TestAPI_UseUnixSocketsPoll(TestAPI_UseUnixSockets, unittest.TestCase): use_poll = True if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/cgi/000077500000000000000000000000001471045437300152535ustar00rootroot00000000000000python-deadlib-3.13.0/cgi/Doc/000077500000000000000000000000001471045437300157605ustar00rootroot00000000000000python-deadlib-3.13.0/cgi/Doc/cgi.rst000066400000000000000000000565771471045437300173000ustar00rootroot00000000000000:mod:`cgi` --- Common Gateway Interface support =============================================== .. module:: cgi :synopsis: Helpers for running Python scripts via the Common Gateway Interface. :deprecated: **Source code:** :source:`Lib/cgi.py` .. index:: pair: WWW; server pair: CGI; protocol pair: HTTP; protocol pair: MIME; headers single: URL single: Common Gateway Interface .. deprecated-removed:: 3.11 3.13 The :mod:`cgi` module is deprecated (see :pep:`PEP 594 <594#cgi>` for details and alternatives). The :class:`FieldStorage` class can typically be replaced with :func:`urllib.parse.parse_qsl` for ``GET`` and ``HEAD`` requests, and the :mod:`email.message` module or `multipart `_ for ``POST`` and ``PUT``. Most :ref:`utility functions ` have replacements. -------------- Support module for Common Gateway Interface (CGI) scripts. This module defines a number of utilities for use by CGI scripts written in Python. The global variable ``maxlen`` can be set to an integer indicating the maximum size of a POST request. POST requests larger than this size will result in a :exc:`ValueError` being raised during parsing. The default value of this variable is ``0``, meaning the request size is unlimited. .. include:: ../includes/wasm-notavail.rst Introduction ------------ .. _cgi-intro: A CGI script is invoked by an HTTP server, usually to process user input submitted through an HTML ``
`` or ```` element. Most often, CGI scripts live in the server's special :file:`cgi-bin` directory. The HTTP server places all sorts of information about the request (such as the client's hostname, the requested URL, the query string, and lots of other goodies) in the script's shell environment, executes the script, and sends the script's output back to the client. The script's input is connected to the client too, and sometimes the form data is read this way; at other times the form data is passed via the "query string" part of the URL. This module is intended to take care of the different cases and provide a simpler interface to the Python script. It also provides a number of utilities that help in debugging scripts, and the latest addition is support for file uploads from a form (if your browser supports it). The output of a CGI script should consist of two sections, separated by a blank line. The first section contains a number of headers, telling the client what kind of data is following. Python code to generate a minimal header section looks like this:: print("Content-Type: text/html") # HTML is following print() # blank line, end of headers The second section is usually HTML, which allows the client software to display nicely formatted text with header, in-line images, etc. Here's Python code that prints a simple piece of HTML:: print("CGI script output") print("

This is my first CGI script

") print("Hello, world!") .. _using-the-cgi-module: Using the cgi module -------------------- Begin by writing ``import cgi``. When you write a new script, consider adding these lines:: import cgitb cgitb.enable() This activates a special exception handler that will display detailed reports in the web browser if any errors occur. If you'd rather not show the guts of your program to users of your script, you can have the reports saved to files instead, with code like this:: import cgitb cgitb.enable(display=0, logdir="/path/to/logdir") It's very helpful to use this feature during script development. The reports produced by :mod:`cgitb` provide information that can save you a lot of time in tracking down bugs. You can always remove the ``cgitb`` line later when you have tested your script and are confident that it works correctly. To get at submitted form data, use the :class:`FieldStorage` class. If the form contains non-ASCII characters, use the *encoding* keyword parameter set to the value of the encoding defined for the document. It is usually contained in the META tag in the HEAD section of the HTML document or by the :mailheader:`Content-Type` header. This reads the form contents from the standard input or the environment (depending on the value of various environment variables set according to the CGI standard). Since it may consume standard input, it should be instantiated only once. The :class:`FieldStorage` instance can be indexed like a Python dictionary. It allows membership testing with the :keyword:`in` operator, and also supports the standard dictionary method :meth:`~dict.keys` and the built-in function :func:`len`. Form fields containing empty strings are ignored and do not appear in the dictionary; to keep such values, provide a true value for the optional *keep_blank_values* keyword parameter when creating the :class:`FieldStorage` instance. For instance, the following code (which assumes that the :mailheader:`Content-Type` header and blank line have already been printed) checks that the fields ``name`` and ``addr`` are both set to a non-empty string:: form = cgi.FieldStorage() if "name" not in form or "addr" not in form: print("

Error

") print("Please fill in the name and addr fields.") return print("

name:", form["name"].value) print("

addr:", form["addr"].value) ...further form processing here... Here the fields, accessed through ``form[key]``, are themselves instances of :class:`FieldStorage` (or :class:`MiniFieldStorage`, depending on the form encoding). The :attr:`~FieldStorage.value` attribute of the instance yields the string value of the field. The :meth:`~FieldStorage.getvalue` method returns this string value directly; it also accepts an optional second argument as a default to return if the requested key is not present. If the submitted form data contains more than one field with the same name, the object retrieved by ``form[key]`` is not a :class:`FieldStorage` or :class:`MiniFieldStorage` instance but a list of such instances. Similarly, in this situation, ``form.getvalue(key)`` would return a list of strings. If you expect this possibility (when your HTML form contains multiple fields with the same name), use the :meth:`~FieldStorage.getlist` method, which always returns a list of values (so that you do not need to special-case the single item case). For example, this code concatenates any number of username fields, separated by commas:: value = form.getlist("username") usernames = ",".join(value) If a field represents an uploaded file, accessing the value via the :attr:`~FieldStorage.value` attribute or the :meth:`~FieldStorage.getvalue` method reads the entire file in memory as bytes. This may not be what you want. You can test for an uploaded file by testing either the :attr:`~FieldStorage.filename` attribute or the :attr:`~FieldStorage.file` attribute. You can then read the data from the :attr:`!file` attribute before it is automatically closed as part of the garbage collection of the :class:`FieldStorage` instance (the :func:`~io.RawIOBase.read` and :func:`~io.IOBase.readline` methods will return bytes):: fileitem = form["userfile"] if fileitem.file: # It's an uploaded file; count lines linecount = 0 while True: line = fileitem.file.readline() if not line: break linecount = linecount + 1 :class:`FieldStorage` objects also support being used in a :keyword:`with` statement, which will automatically close them when done. If an error is encountered when obtaining the contents of an uploaded file (for example, when the user interrupts the form submission by clicking on a Back or Cancel button) the :attr:`~FieldStorage.done` attribute of the object for the field will be set to the value -1. The file upload draft standard entertains the possibility of uploading multiple files from one field (using a recursive :mimetype:`multipart/\*` encoding). When this occurs, the item will be a dictionary-like :class:`FieldStorage` item. This can be determined by testing its :attr:`!type` attribute, which should be :mimetype:`multipart/form-data` (or perhaps another MIME type matching :mimetype:`multipart/\*`). In this case, it can be iterated over recursively just like the top-level form object. When a form is submitted in the "old" format (as the query string or as a single data part of type :mimetype:`application/x-www-form-urlencoded`), the items will actually be instances of the class :class:`MiniFieldStorage`. In this case, the :attr:`!list`, :attr:`!file`, and :attr:`filename` attributes are always ``None``. A form submitted via POST that also has a query string will contain both :class:`FieldStorage` and :class:`MiniFieldStorage` items. .. versionchanged:: 3.4 The :attr:`~FieldStorage.file` attribute is automatically closed upon the garbage collection of the creating :class:`FieldStorage` instance. .. versionchanged:: 3.5 Added support for the context management protocol to the :class:`FieldStorage` class. Higher Level Interface ---------------------- The previous section explains how to read CGI form data using the :class:`FieldStorage` class. This section describes a higher level interface which was added to this class to allow one to do it in a more readable and intuitive way. The interface doesn't make the techniques described in previous sections obsolete --- they are still useful to process file uploads efficiently, for example. .. XXX: Is this true ? The interface consists of two simple methods. Using the methods you can process form data in a generic way, without the need to worry whether only one or more values were posted under one name. In the previous section, you learned to write following code anytime you expected a user to post more than one value under one name:: item = form.getvalue("item") if isinstance(item, list): # The user is requesting more than one item. else: # The user is requesting only one item. This situation is common for example when a form contains a group of multiple checkboxes with the same name:: In most situations, however, there's only one form control with a particular name in a form and then you expect and need only one value associated with this name. So you write a script containing for example this code:: user = form.getvalue("user").upper() The problem with the code is that you should never expect that a client will provide valid input to your scripts. For example, if a curious user appends another ``user=foo`` pair to the query string, then the script would crash, because in this situation the ``getvalue("user")`` method call returns a list instead of a string. Calling the :meth:`~str.upper` method on a list is not valid (since lists do not have a method of this name) and results in an :exc:`AttributeError` exception. Therefore, the appropriate way to read form data values was to always use the code which checks whether the obtained value is a single value or a list of values. That's annoying and leads to less readable scripts. A more convenient approach is to use the methods :meth:`~FieldStorage.getfirst` and :meth:`~FieldStorage.getlist` provided by this higher level interface. .. method:: FieldStorage.getfirst(name, default=None) This method always returns only one value associated with form field *name*. The method returns only the first value in case that more values were posted under such name. Please note that the order in which the values are received may vary from browser to browser and should not be counted on. [#]_ If no such form field or value exists then the method returns the value specified by the optional parameter *default*. This parameter defaults to ``None`` if not specified. .. method:: FieldStorage.getlist(name) This method always returns a list of values associated with form field *name*. The method returns an empty list if no such form field or value exists for *name*. It returns a list consisting of one item if only one such value exists. Using these methods you can write nice compact code:: import cgi form = cgi.FieldStorage() user = form.getfirst("user", "").upper() # This way it's safe. for item in form.getlist("item"): do_something(item) .. _functions-in-cgi-module: Functions --------- These are useful if you want more control, or if you want to employ some of the algorithms implemented in this module in other circumstances. .. function:: parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False, separator="&") Parse a query in the environment or from a file (the file defaults to ``sys.stdin``). The *keep_blank_values*, *strict_parsing* and *separator* parameters are passed to :func:`urllib.parse.parse_qs` unchanged. .. deprecated-removed:: 3.11 3.13 This function, like the rest of the :mod:`cgi` module, is deprecated. It can be replaced by calling :func:`urllib.parse.parse_qs` directly on the desired query string (except for ``multipart/form-data`` input, which can be handled as described for :func:`parse_multipart`). .. function:: parse_multipart(fp, pdict, encoding="utf-8", errors="replace", separator="&") Parse input of type :mimetype:`multipart/form-data` (for file uploads). Arguments are *fp* for the input file, *pdict* for a dictionary containing other parameters in the :mailheader:`Content-Type` header, and *encoding*, the request encoding. Returns a dictionary just like :func:`urllib.parse.parse_qs`: keys are the field names, each value is a list of values for that field. For non-file fields, the value is a list of strings. This is easy to use but not much good if you are expecting megabytes to be uploaded --- in that case, use the :class:`FieldStorage` class instead which is much more flexible. .. versionchanged:: 3.7 Added the *encoding* and *errors* parameters. For non-file fields, the value is now a list of strings, not bytes. .. versionchanged:: 3.10 Added the *separator* parameter. .. deprecated-removed:: 3.11 3.13 This function, like the rest of the :mod:`cgi` module, is deprecated. It can be replaced with the functionality in the :mod:`email` package (e.g. :class:`email.message.EmailMessage`/:class:`email.message.Message`) which implements the same MIME RFCs, or with the `multipart `__ PyPI project. .. function:: parse_header(string) Parse a MIME header (such as :mailheader:`Content-Type`) into a main value and a dictionary of parameters. .. deprecated-removed:: 3.11 3.13 This function, like the rest of the :mod:`cgi` module, is deprecated. It can be replaced with the functionality in the :mod:`email` package, which implements the same MIME RFCs. For example, with :class:`email.message.EmailMessage`:: from email.message import EmailMessage msg = EmailMessage() msg['content-type'] = 'application/json; charset="utf8"' main, params = msg.get_content_type(), msg['content-type'].params .. function:: test() Robust test CGI script, usable as main program. Writes minimal HTTP headers and formats all information provided to the script in HTML format. .. function:: print_environ() Format the shell environment in HTML. .. function:: print_form(form) Format a form in HTML. .. function:: print_directory() Format the current directory in HTML. .. function:: print_environ_usage() Print a list of useful (used by CGI) environment variables in HTML. .. _cgi-security: Caring about security --------------------- .. index:: pair: CGI; security There's one important rule: if you invoke an external program (via :func:`os.system`, :func:`os.popen` or other functions with similar functionality), make very sure you don't pass arbitrary strings received from the client to the shell. This is a well-known security hole whereby clever hackers anywhere on the web can exploit a gullible CGI script to invoke arbitrary shell commands. Even parts of the URL or field names cannot be trusted, since the request doesn't have to come from your form! To be on the safe side, if you must pass a string gotten from a form to a shell command, you should make sure the string contains only alphanumeric characters, dashes, underscores, and periods. Installing your CGI script on a Unix system ------------------------------------------- Read the documentation for your HTTP server and check with your local system administrator to find the directory where CGI scripts should be installed; usually this is in a directory :file:`cgi-bin` in the server tree. Make sure that your script is readable and executable by "others"; the Unix file mode should be ``0o755`` octal (use ``chmod 0755 filename``). Make sure that the first line of the script contains ``#!`` starting in column 1 followed by the pathname of the Python interpreter, for instance:: #!/usr/local/bin/python Make sure the Python interpreter exists and is executable by "others". Make sure that any files your script needs to read or write are readable or writable, respectively, by "others" --- their mode should be ``0o644`` for readable and ``0o666`` for writable. This is because, for security reasons, the HTTP server executes your script as user "nobody", without any special privileges. It can only read (write, execute) files that everybody can read (write, execute). The current directory at execution time is also different (it is usually the server's cgi-bin directory) and the set of environment variables is also different from what you get when you log in. In particular, don't count on the shell's search path for executables (:envvar:`PATH`) or the Python module search path (:envvar:`PYTHONPATH`) to be set to anything interesting. If you need to load modules from a directory which is not on Python's default module search path, you can change the path in your script, before importing other modules. For example:: import sys sys.path.insert(0, "/usr/home/joe/lib/python") sys.path.insert(0, "/usr/local/lib/python") (This way, the directory inserted last will be searched first!) Instructions for non-Unix systems will vary; check your HTTP server's documentation (it will usually have a section on CGI scripts). Testing your CGI script ----------------------- Unfortunately, a CGI script will generally not run when you try it from the command line, and a script that works perfectly from the command line may fail mysteriously when run from the server. There's one reason why you should still test your script from the command line: if it contains a syntax error, the Python interpreter won't execute it at all, and the HTTP server will most likely send a cryptic error to the client. Assuming your script has no syntax errors, yet it does not work, you have no choice but to read the next section. Debugging CGI scripts --------------------- .. index:: pair: CGI; debugging First of all, check for trivial installation errors --- reading the section above on installing your CGI script carefully can save you a lot of time. If you wonder whether you have understood the installation procedure correctly, try installing a copy of this module file (:file:`cgi.py`) as a CGI script. When invoked as a script, the file will dump its environment and the contents of the form in HTML format. Give it the right mode etc., and send it a request. If it's installed in the standard :file:`cgi-bin` directory, it should be possible to send it a request by entering a URL into your browser of the form: .. code-block:: none http://yourhostname/cgi-bin/cgi.py?name=Joe+Blow&addr=At+Home If this gives an error of type 404, the server cannot find the script -- perhaps you need to install it in a different directory. If it gives another error, there's an installation problem that you should fix before trying to go any further. If you get a nicely formatted listing of the environment and form content (in this example, the fields should be listed as "addr" with value "At Home" and "name" with value "Joe Blow"), the :file:`cgi.py` script has been installed correctly. If you follow the same procedure for your own script, you should now be able to debug it. The next step could be to call the :mod:`cgi` module's :func:`test` function from your script: replace its main code with the single statement :: cgi.test() This should produce the same results as those gotten from installing the :file:`cgi.py` file itself. When an ordinary Python script raises an unhandled exception (for whatever reason: of a typo in a module name, a file that can't be opened, etc.), the Python interpreter prints a nice traceback and exits. While the Python interpreter will still do this when your CGI script raises an exception, most likely the traceback will end up in one of the HTTP server's log files, or be discarded altogether. Fortunately, once you have managed to get your script to execute *some* code, you can easily send tracebacks to the web browser using the :mod:`cgitb` module. If you haven't done so already, just add the lines:: import cgitb cgitb.enable() to the top of your script. Then try running it again; when a problem occurs, you should see a detailed report that will likely make apparent the cause of the crash. If you suspect that there may be a problem in importing the :mod:`cgitb` module, you can use an even more robust approach (which only uses built-in modules):: import sys sys.stderr = sys.stdout print("Content-Type: text/plain") print() ...your code here... This relies on the Python interpreter to print the traceback. The content type of the output is set to plain text, which disables all HTML processing. If your script works, the raw HTML will be displayed by your client. If it raises an exception, most likely after the first two lines have been printed, a traceback will be displayed. Because no HTML interpretation is going on, the traceback will be readable. Common problems and solutions ----------------------------- * Most HTTP servers buffer the output from CGI scripts until the script is completed. This means that it is not possible to display a progress report on the client's display while the script is running. * Check the installation instructions above. * Check the HTTP server's log files. (``tail -f logfile`` in a separate window may be useful!) * Always check a script for syntax errors first, by doing something like ``python script.py``. * If your script does not have any syntax errors, try adding ``import cgitb; cgitb.enable()`` to the top of the script. * When invoking external programs, make sure they can be found. Usually, this means using absolute path names --- :envvar:`PATH` is usually not set to a very useful value in a CGI script. * When reading or writing external files, make sure they can be read or written by the userid under which your CGI script will be running: this is typically the userid under which the web server is running, or some explicitly specified userid for a web server's ``suexec`` feature. * Don't try to give a CGI script a set-uid mode. This doesn't work on most systems, and is a security liability as well. .. rubric:: Footnotes .. [#] Note that some recent versions of the HTML specification do state what order the field values should be supplied in, but knowing whether a request was received from a conforming browser, or even from a browser at all, is tedious and error-prone. python-deadlib-3.13.0/cgi/LICENSE000066400000000000000000000050111471045437300162550ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/cgi/README.rst000066400000000000000000000005161471045437300167440ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/cgi/cgi/000077500000000000000000000000001471045437300160155ustar00rootroot00000000000000python-deadlib-3.13.0/cgi/cgi/__init__.py000077500000000000000000001036021471045437300201330ustar00rootroot00000000000000#! /usr/local/bin/python # NOTE: the above "/usr/local/bin/python" is NOT a mistake. It is # intentionally NOT "/usr/bin/env python". On many systems # (e.g. Solaris), /usr/local/bin is not in $PATH as passed to CGI # scripts, and /usr/local/bin is the default directory where Python is # installed, so /usr/bin/env would be unable to find python. Granted, # binary installations by Linux vendors often install Python in # /usr/bin. So let those vendors patch cgi.py to match their choice # of installation. """Support module for CGI (Common Gateway Interface) scripts. This module defines a number of utilities for use by CGI scripts written in Python. The global variable maxlen can be set to an integer indicating the maximum size of a POST request. POST requests larger than this size will result in a ValueError being raised during parsing. The default value of this variable is 0, meaning the request size is unlimited. """ # History # ------- # # Michael McLay started this module. Steve Majewski changed the # interface to SvFormContentDict and FormContentDict. The multipart # parsing was inspired by code submitted by Andreas Paepcke. Guido van # Rossum rewrote, reformatted and documented the module and is currently # responsible for its maintenance. # __version__ = "2.6" # Imports # ======= from io import StringIO, BytesIO, TextIOWrapper from collections.abc import Mapping import sys import os import urllib.parse from email.parser import FeedParser from email.message import Message import html import locale import tempfile import warnings __all__ = ["MiniFieldStorage", "FieldStorage", "parse", "parse_multipart", "parse_header", "test", "print_exception", "print_environ", "print_form", "print_directory", "print_arguments", "print_environ_usage"] # python-deadlib: Replace deprecation warning not to raise exception warnings.warn( f"{__name__} was removed in Python 3.13. " f"Please be aware that you are currently NOT using standard '{__name__}', " f"but instead a separately installed 'standard-{__name__}'.", DeprecationWarning, stacklevel=2 ) # Logging support # =============== logfile = "" # Filename to log to, if not empty logfp = None # File object to log to, if not None def initlog(*allargs): """Write a log message, if there is a log file. Even though this function is called initlog(), you should always use log(); log is a variable that is set either to initlog (initially), to dolog (once the log file has been opened), or to nolog (when logging is disabled). The first argument is a format string; the remaining arguments (if any) are arguments to the % operator, so e.g. log("%s: %s", "a", "b") will write "a: b" to the log file, followed by a newline. If the global logfp is not None, it should be a file object to which log data is written. If the global logfp is None, the global logfile may be a string giving a filename to open, in append mode. This file should be world writable!!! If the file can't be opened, logging is silently disabled (since there is no safe place where we could send an error message). """ global log, logfile, logfp warnings.warn("cgi.log() is deprecated as of 3.10. Use logging instead", DeprecationWarning, stacklevel=2) if logfile and not logfp: try: logfp = open(logfile, "a", encoding="locale") except OSError: pass if not logfp: log = nolog else: log = dolog log(*allargs) def dolog(fmt, *args): """Write a log message to the log file. See initlog() for docs.""" logfp.write(fmt%args + "\n") def nolog(*allargs): """Dummy function, assigned to log when logging is disabled.""" pass def closelog(): """Close the log file.""" global log, logfile, logfp logfile = '' if logfp: logfp.close() logfp = None log = initlog log = initlog # The current logging function # Parsing functions # ================= # Maximum input we will accept when REQUEST_METHOD is POST # 0 ==> unlimited input maxlen = 0 def parse(fp=None, environ=os.environ, keep_blank_values=0, strict_parsing=0, separator='&'): """Parse a query in the environment or from a file (default stdin) Arguments, all optional: fp : file pointer; default: sys.stdin.buffer environ : environment dictionary; default: os.environ keep_blank_values: flag indicating whether blank values in percent-encoded forms should be treated as blank strings. A true value indicates that blanks should be retained as blank strings. The default false value indicates that blank values are to be ignored and treated as if they were not included. strict_parsing: flag indicating what to do with parsing errors. If false (the default), errors are silently ignored. If true, errors raise a ValueError exception. separator: str. The symbol to use for separating the query arguments. Defaults to &. """ if fp is None: fp = sys.stdin # field keys and values (except for files) are returned as strings # an encoding is required to decode the bytes read from self.fp if hasattr(fp,'encoding'): encoding = fp.encoding else: encoding = 'latin-1' # fp.read() must return bytes if isinstance(fp, TextIOWrapper): fp = fp.buffer if not 'REQUEST_METHOD' in environ: environ['REQUEST_METHOD'] = 'GET' # For testing stand-alone if environ['REQUEST_METHOD'] == 'POST': ctype, pdict = parse_header(environ['CONTENT_TYPE']) if ctype == 'multipart/form-data': return parse_multipart(fp, pdict, separator=separator) elif ctype == 'application/x-www-form-urlencoded': clength = int(environ['CONTENT_LENGTH']) if maxlen and clength > maxlen: raise ValueError('Maximum content length exceeded') qs = fp.read(clength).decode(encoding) else: qs = '' # Unknown content-type if 'QUERY_STRING' in environ: if qs: qs = qs + '&' qs = qs + environ['QUERY_STRING'] elif sys.argv[1:]: if qs: qs = qs + '&' qs = qs + sys.argv[1] environ['QUERY_STRING'] = qs # XXX Shouldn't, really elif 'QUERY_STRING' in environ: qs = environ['QUERY_STRING'] else: if sys.argv[1:]: qs = sys.argv[1] else: qs = "" environ['QUERY_STRING'] = qs # XXX Shouldn't, really return urllib.parse.parse_qs(qs, keep_blank_values, strict_parsing, encoding=encoding, separator=separator) def parse_multipart(fp, pdict, encoding="utf-8", errors="replace", separator='&'): """Parse multipart input. Arguments: fp : input file pdict: dictionary containing other parameters of content-type header encoding, errors: request encoding and error handler, passed to FieldStorage Returns a dictionary just like parse_qs(): keys are the field names, each value is a list of values for that field. For non-file fields, the value is a list of strings. """ # RFC 2046, Section 5.1 : The "multipart" boundary delimiters are always # represented as 7bit US-ASCII. boundary = pdict['boundary'].decode('ascii') ctype = "multipart/form-data; boundary={}".format(boundary) headers = Message() headers.set_type(ctype) try: headers['Content-Length'] = pdict['CONTENT-LENGTH'] except KeyError: pass fs = FieldStorage(fp, headers=headers, encoding=encoding, errors=errors, environ={'REQUEST_METHOD': 'POST'}, separator=separator) return {k: fs.getlist(k) for k in fs} def _parseparam(s): while s[:1] == ';': s = s[1:] end = s.find(';') while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2: end = s.find(';', end + 1) if end < 0: end = len(s) f = s[:end] yield f.strip() s = s[end:] def parse_header(line): """Parse a Content-type like header. Return the main content-type and a dictionary of options. """ parts = _parseparam(';' + line) key = parts.__next__() pdict = {} for p in parts: i = p.find('=') if i >= 0: name = p[:i].strip().lower() value = p[i+1:].strip() if len(value) >= 2 and value[0] == value[-1] == '"': value = value[1:-1] value = value.replace('\\\\', '\\').replace('\\"', '"') pdict[name] = value return key, pdict # Classes for field storage # ========================= class MiniFieldStorage: """Like FieldStorage, for use when no file uploads are possible.""" # Dummy attributes filename = None list = None type = None file = None type_options = {} disposition = None disposition_options = {} headers = {} def __init__(self, name, value): """Constructor from field name and value.""" self.name = name self.value = value # self.file = StringIO(value) def __repr__(self): """Return printable representation.""" return "MiniFieldStorage(%r, %r)" % (self.name, self.value) class FieldStorage: """Store a sequence of fields, reading multipart/form-data. This class provides naming, typing, files stored on disk, and more. At the top level, it is accessible like a dictionary, whose keys are the field names. (Note: None can occur as a field name.) The items are either a Python list (if there's multiple values) or another FieldStorage or MiniFieldStorage object. If it's a single object, it has the following attributes: name: the field name, if specified; otherwise None filename: the filename, if specified; otherwise None; this is the client side filename, *not* the file name on which it is stored (that's a temporary file you don't deal with) value: the value as a *string*; for file uploads, this transparently reads the file every time you request the value and returns *bytes* file: the file(-like) object from which you can read the data *as bytes* ; None if the data is stored a simple string type: the content-type, or None if not specified type_options: dictionary of options specified on the content-type line disposition: content-disposition, or None if not specified disposition_options: dictionary of corresponding options headers: a dictionary(-like) object (sometimes email.message.Message or a subclass thereof) containing *all* headers The class is subclassable, mostly for the purpose of overriding the make_file() method, which is called internally to come up with a file open for reading and writing. This makes it possible to override the default choice of storing all files in a temporary directory and unlinking them as soon as they have been opened. """ def __init__(self, fp=None, headers=None, outerboundary=b'', environ=os.environ, keep_blank_values=0, strict_parsing=0, limit=None, encoding='utf-8', errors='replace', max_num_fields=None, separator='&'): """Constructor. Read multipart/* until last part. Arguments, all optional: fp : file pointer; default: sys.stdin.buffer (not used when the request method is GET) Can be : 1. a TextIOWrapper object 2. an object whose read() and readline() methods return bytes headers : header dictionary-like object; default: taken from environ as per CGI spec outerboundary : terminating multipart boundary (for internal use only) environ : environment dictionary; default: os.environ keep_blank_values: flag indicating whether blank values in percent-encoded forms should be treated as blank strings. A true value indicates that blanks should be retained as blank strings. The default false value indicates that blank values are to be ignored and treated as if they were not included. strict_parsing: flag indicating what to do with parsing errors. If false (the default), errors are silently ignored. If true, errors raise a ValueError exception. limit : used internally to read parts of multipart/form-data forms, to exit from the reading loop when reached. It is the difference between the form content-length and the number of bytes already read encoding, errors : the encoding and error handler used to decode the binary stream to strings. Must be the same as the charset defined for the page sending the form (content-type : meta http-equiv or header) max_num_fields: int. If set, then __init__ throws a ValueError if there are more than n fields read by parse_qsl(). """ method = 'GET' self.keep_blank_values = keep_blank_values self.strict_parsing = strict_parsing self.max_num_fields = max_num_fields self.separator = separator if 'REQUEST_METHOD' in environ: method = environ['REQUEST_METHOD'].upper() self.qs_on_post = None if method == 'GET' or method == 'HEAD': if 'QUERY_STRING' in environ: qs = environ['QUERY_STRING'] elif sys.argv[1:]: qs = sys.argv[1] else: qs = "" qs = qs.encode(locale.getpreferredencoding(), 'surrogateescape') fp = BytesIO(qs) if headers is None: headers = {'content-type': "application/x-www-form-urlencoded"} if headers is None: headers = {} if method == 'POST': # Set default content-type for POST to what's traditional headers['content-type'] = "application/x-www-form-urlencoded" if 'CONTENT_TYPE' in environ: headers['content-type'] = environ['CONTENT_TYPE'] if 'QUERY_STRING' in environ: self.qs_on_post = environ['QUERY_STRING'] if 'CONTENT_LENGTH' in environ: headers['content-length'] = environ['CONTENT_LENGTH'] else: if not (isinstance(headers, (Mapping, Message))): raise TypeError("headers must be mapping or an instance of " "email.message.Message") self.headers = headers if fp is None: self.fp = sys.stdin.buffer # self.fp.read() must return bytes elif isinstance(fp, TextIOWrapper): self.fp = fp.buffer else: if not (hasattr(fp, 'read') and hasattr(fp, 'readline')): raise TypeError("fp must be file pointer") self.fp = fp self.encoding = encoding self.errors = errors if not isinstance(outerboundary, bytes): raise TypeError('outerboundary must be bytes, not %s' % type(outerboundary).__name__) self.outerboundary = outerboundary self.bytes_read = 0 self.limit = limit # Process content-disposition header cdisp, pdict = "", {} if 'content-disposition' in self.headers: cdisp, pdict = parse_header(self.headers['content-disposition']) self.disposition = cdisp self.disposition_options = pdict self.name = None if 'name' in pdict: self.name = pdict['name'] self.filename = None if 'filename' in pdict: self.filename = pdict['filename'] self._binary_file = self.filename is not None # Process content-type header # # Honor any existing content-type header. But if there is no # content-type header, use some sensible defaults. Assume # outerboundary is "" at the outer level, but something non-false # inside a multi-part. The default for an inner part is text/plain, # but for an outer part it should be urlencoded. This should catch # bogus clients which erroneously forget to include a content-type # header. # # See below for what we do if there does exist a content-type header, # but it happens to be something we don't understand. if 'content-type' in self.headers: ctype, pdict = parse_header(self.headers['content-type']) elif self.outerboundary or method != 'POST': ctype, pdict = "text/plain", {} else: ctype, pdict = 'application/x-www-form-urlencoded', {} self.type = ctype self.type_options = pdict if 'boundary' in pdict: self.innerboundary = pdict['boundary'].encode(self.encoding, self.errors) else: self.innerboundary = b"" clen = -1 if 'content-length' in self.headers: try: clen = int(self.headers['content-length']) except ValueError: pass if maxlen and clen > maxlen: raise ValueError('Maximum content length exceeded') self.length = clen if self.limit is None and clen >= 0: self.limit = clen self.list = self.file = None self.done = 0 if ctype == 'application/x-www-form-urlencoded': self.read_urlencoded() elif ctype[:10] == 'multipart/': self.read_multi(environ, keep_blank_values, strict_parsing) else: self.read_single() def __del__(self): try: self.file.close() except AttributeError: pass def __enter__(self): return self def __exit__(self, *args): self.file.close() def __repr__(self): """Return a printable representation.""" return "FieldStorage(%r, %r, %r)" % ( self.name, self.filename, self.value) def __iter__(self): return iter(self.keys()) def __getattr__(self, name): if name != 'value': raise AttributeError(name) if self.file: self.file.seek(0) value = self.file.read() self.file.seek(0) elif self.list is not None: value = self.list else: value = None return value def __getitem__(self, key): """Dictionary style indexing.""" if self.list is None: raise TypeError("not indexable") found = [] for item in self.list: if item.name == key: found.append(item) if not found: raise KeyError(key) if len(found) == 1: return found[0] else: return found def getvalue(self, key, default=None): """Dictionary style get() method, including 'value' lookup.""" if key in self: value = self[key] if isinstance(value, list): return [x.value for x in value] else: return value.value else: return default def getfirst(self, key, default=None): """ Return the first value received.""" if key in self: value = self[key] if isinstance(value, list): return value[0].value else: return value.value else: return default def getlist(self, key): """ Return list of received values.""" if key in self: value = self[key] if isinstance(value, list): return [x.value for x in value] else: return [value.value] else: return [] def keys(self): """Dictionary style keys() method.""" if self.list is None: raise TypeError("not indexable") return list(set(item.name for item in self.list)) def __contains__(self, key): """Dictionary style __contains__ method.""" if self.list is None: raise TypeError("not indexable") return any(item.name == key for item in self.list) def __len__(self): """Dictionary style len(x) support.""" return len(self.keys()) def __bool__(self): if self.list is None: raise TypeError("Cannot be converted to bool.") return bool(self.list) def read_urlencoded(self): """Internal: read data in query string format.""" qs = self.fp.read(self.length) if not isinstance(qs, bytes): raise ValueError("%s should return bytes, got %s" \ % (self.fp, type(qs).__name__)) qs = qs.decode(self.encoding, self.errors) if self.qs_on_post: qs += '&' + self.qs_on_post query = urllib.parse.parse_qsl( qs, self.keep_blank_values, self.strict_parsing, encoding=self.encoding, errors=self.errors, max_num_fields=self.max_num_fields, separator=self.separator) self.list = [MiniFieldStorage(key, value) for key, value in query] self.skip_lines() FieldStorageClass = None def read_multi(self, environ, keep_blank_values, strict_parsing): """Internal: read a part that is itself multipart.""" ib = self.innerboundary if not valid_boundary(ib): raise ValueError('Invalid boundary in multipart form: %r' % (ib,)) self.list = [] if self.qs_on_post: query = urllib.parse.parse_qsl( self.qs_on_post, self.keep_blank_values, self.strict_parsing, encoding=self.encoding, errors=self.errors, max_num_fields=self.max_num_fields, separator=self.separator) self.list.extend(MiniFieldStorage(key, value) for key, value in query) klass = self.FieldStorageClass or self.__class__ first_line = self.fp.readline() # bytes if not isinstance(first_line, bytes): raise ValueError("%s should return bytes, got %s" \ % (self.fp, type(first_line).__name__)) self.bytes_read += len(first_line) # Ensure that we consume the file until we've hit our inner boundary while (first_line.strip() != (b"--" + self.innerboundary) and first_line): first_line = self.fp.readline() self.bytes_read += len(first_line) # Propagate max_num_fields into the sub class appropriately max_num_fields = self.max_num_fields if max_num_fields is not None: max_num_fields -= len(self.list) while True: parser = FeedParser() hdr_text = b"" while True: data = self.fp.readline() hdr_text += data if not data.strip(): break if not hdr_text: break # parser takes strings, not bytes self.bytes_read += len(hdr_text) parser.feed(hdr_text.decode(self.encoding, self.errors)) headers = parser.close() # Some clients add Content-Length for part headers, ignore them if 'content-length' in headers: del headers['content-length'] limit = None if self.limit is None \ else self.limit - self.bytes_read part = klass(self.fp, headers, ib, environ, keep_blank_values, strict_parsing, limit, self.encoding, self.errors, max_num_fields, self.separator) if max_num_fields is not None: max_num_fields -= 1 if part.list: max_num_fields -= len(part.list) if max_num_fields < 0: raise ValueError('Max number of fields exceeded') self.bytes_read += part.bytes_read self.list.append(part) if part.done or self.bytes_read >= self.length > 0: break self.skip_lines() def read_single(self): """Internal: read an atomic part.""" if self.length >= 0: self.read_binary() self.skip_lines() else: self.read_lines() self.file.seek(0) bufsize = 8*1024 # I/O buffering size for copy to file def read_binary(self): """Internal: read binary data.""" self.file = self.make_file() todo = self.length if todo >= 0: while todo > 0: data = self.fp.read(min(todo, self.bufsize)) # bytes if not isinstance(data, bytes): raise ValueError("%s should return bytes, got %s" % (self.fp, type(data).__name__)) self.bytes_read += len(data) if not data: self.done = -1 break self.file.write(data) todo = todo - len(data) def read_lines(self): """Internal: read lines until EOF or outerboundary.""" if self._binary_file: self.file = self.__file = BytesIO() # store data as bytes for files else: self.file = self.__file = StringIO() # as strings for other fields if self.outerboundary: self.read_lines_to_outerboundary() else: self.read_lines_to_eof() def __write(self, line): """line is always bytes, not string""" if self.__file is not None: if self.__file.tell() + len(line) > 1000: self.file = self.make_file() data = self.__file.getvalue() self.file.write(data) self.__file = None if self._binary_file: # keep bytes self.file.write(line) else: # decode to string self.file.write(line.decode(self.encoding, self.errors)) def read_lines_to_eof(self): """Internal: read lines until EOF.""" while 1: line = self.fp.readline(1<<16) # bytes self.bytes_read += len(line) if not line: self.done = -1 break self.__write(line) def read_lines_to_outerboundary(self): """Internal: read lines until outerboundary. Data is read as bytes: boundaries and line ends must be converted to bytes for comparisons. """ next_boundary = b"--" + self.outerboundary last_boundary = next_boundary + b"--" delim = b"" last_line_lfend = True _read = 0 while 1: if self.limit is not None and 0 <= self.limit <= _read: break line = self.fp.readline(1<<16) # bytes self.bytes_read += len(line) _read += len(line) if not line: self.done = -1 break if delim == b"\r": line = delim + line delim = b"" if line.startswith(b"--") and last_line_lfend: strippedline = line.rstrip() if strippedline == next_boundary: break if strippedline == last_boundary: self.done = 1 break odelim = delim if line.endswith(b"\r\n"): delim = b"\r\n" line = line[:-2] last_line_lfend = True elif line.endswith(b"\n"): delim = b"\n" line = line[:-1] last_line_lfend = True elif line.endswith(b"\r"): # We may interrupt \r\n sequences if they span the 2**16 # byte boundary delim = b"\r" line = line[:-1] last_line_lfend = False else: delim = b"" last_line_lfend = False self.__write(odelim + line) def skip_lines(self): """Internal: skip lines until outer boundary if defined.""" if not self.outerboundary or self.done: return next_boundary = b"--" + self.outerboundary last_boundary = next_boundary + b"--" last_line_lfend = True while True: line = self.fp.readline(1<<16) self.bytes_read += len(line) if not line: self.done = -1 break if line.endswith(b"--") and last_line_lfend: strippedline = line.strip() if strippedline == next_boundary: break if strippedline == last_boundary: self.done = 1 break last_line_lfend = line.endswith(b'\n') def make_file(self): """Overridable: return a readable & writable file. The file will be used as follows: - data is written to it - seek(0) - data is read from it The file is opened in binary mode for files, in text mode for other fields This version opens a temporary file for reading and writing, and immediately deletes (unlinks) it. The trick (on Unix!) is that the file can still be used, but it can't be opened by another process, and it will automatically be deleted when it is closed or when the current process terminates. If you want a more permanent file, you derive a class which overrides this method. If you want a visible temporary file that is nevertheless automatically deleted when the script terminates, try defining a __del__ method in a derived class which unlinks the temporary files you have created. """ if self._binary_file: return tempfile.TemporaryFile("wb+") else: return tempfile.TemporaryFile("w+", encoding=self.encoding, newline = '\n') # Test/debug code # =============== def test(environ=os.environ): """Robust test CGI script, usable as main program. Write minimal HTTP headers and dump all information provided to the script in HTML form. """ print("Content-type: text/html") print() sys.stderr = sys.stdout try: form = FieldStorage() # Replace with other classes to test those print_directory() print_arguments() print_form(form) print_environ(environ) print_environ_usage() def f(): exec("testing print_exception() -- italics?") def g(f=f): f() print("

What follows is a test, not an actual exception:

") g() except: print_exception() print("

Second try with a small maxlen...

") global maxlen maxlen = 50 try: form = FieldStorage() # Replace with other classes to test those print_directory() print_arguments() print_form(form) print_environ(environ) except: print_exception() def print_exception(type=None, value=None, tb=None, limit=None): if type is None: type, value, tb = sys.exc_info() import traceback print() print("

Traceback (most recent call last):

") list = traceback.format_tb(tb, limit) + \ traceback.format_exception_only(type, value) print("
%s%s
" % ( html.escape("".join(list[:-1])), html.escape(list[-1]), )) del tb def print_environ(environ=os.environ): """Dump the shell environment as HTML.""" keys = sorted(environ.keys()) print() print("

Shell Environment:

") print("
") for key in keys: print("
", html.escape(key), "
", html.escape(environ[key])) print("
") print() def print_form(form): """Dump the contents of a form as HTML.""" keys = sorted(form.keys()) print() print("

Form Contents:

") if not keys: print("

No form fields.") print("

") for key in keys: print("
" + html.escape(key) + ":", end=' ') value = form[key] print("" + html.escape(repr(type(value))) + "") print("
" + html.escape(repr(value))) print("
") print() def print_directory(): """Dump the current directory as HTML.""" print() print("

Current Working Directory:

") try: pwd = os.getcwd() except OSError as msg: print("OSError:", html.escape(str(msg))) else: print(html.escape(pwd)) print() def print_arguments(): print() print("

Command Line Arguments:

") print() print(sys.argv) print() def print_environ_usage(): """Dump a list of environment variables used by CGI as HTML.""" print("""

These environment variables could have been set:

  • AUTH_TYPE
  • CONTENT_LENGTH
  • CONTENT_TYPE
  • DATE_GMT
  • DATE_LOCAL
  • DOCUMENT_NAME
  • DOCUMENT_ROOT
  • DOCUMENT_URI
  • GATEWAY_INTERFACE
  • LAST_MODIFIED
  • PATH
  • PATH_INFO
  • PATH_TRANSLATED
  • QUERY_STRING
  • REMOTE_ADDR
  • REMOTE_HOST
  • REMOTE_IDENT
  • REMOTE_USER
  • REQUEST_METHOD
  • SCRIPT_NAME
  • SERVER_NAME
  • SERVER_PORT
  • SERVER_PROTOCOL
  • SERVER_ROOT
  • SERVER_SOFTWARE
In addition, HTTP headers sent by the server may be passed in the environment as well. Here are some common variable names:
  • HTTP_ACCEPT
  • HTTP_CONNECTION
  • HTTP_HOST
  • HTTP_PRAGMA
  • HTTP_REFERER
  • HTTP_USER_AGENT
""") # Utilities # ========= def valid_boundary(s): import re if isinstance(s, bytes): _vb_pattern = b"^[ -~]{0,200}[!-~]$" else: _vb_pattern = "^[ -~]{0,200}[!-~]$" return re.match(_vb_pattern, s) # Invoke mainline # =============== # Call test() when this file is run as a script (not imported as a module) if __name__ == '__main__': test() python-deadlib-3.13.0/cgi/pyproject.toml000066400000000000000000000012571471045437300201740ustar00rootroot00000000000000[project] name = "standard-cgi" version = "3.13.0" description = "Standard library cgi redistribution. \"dead battery\"." keywords = ["stdlib"] authors = [ { name = "Python Developers", email = "python-deadlib@youknowone.org" } ] readme = "README.rst" license = { text = "PSF-2.0" } classifiers = [ "License :: OSI Approved :: Python Software Foundation License", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", ] [tool.setuptools.packages] find = {include = ["cgi*"]} [project.urls] "Homepage" = "https://github.com/youknowone/python-deadlib" [build-system] requires = ["setuptools>=75.0"] build-backend = "setuptools.build_meta" python-deadlib-3.13.0/cgi/tests/000077500000000000000000000000001471045437300164155ustar00rootroot00000000000000python-deadlib-3.13.0/cgi/tests/__init__.py000066400000000000000000000000001471045437300205140ustar00rootroot00000000000000python-deadlib-3.13.0/cgi/tests/test_cgi.py000066400000000000000000000544251471045437300206020ustar00rootroot00000000000000import os import sys import tempfile import unittest from collections import namedtuple from io import StringIO, BytesIO from test import support from test.support import warnings_helper cgi = warnings_helper.import_deprecated("cgi") class HackedSysModule: # The regression test will have real values in sys.argv, which # will completely confuse the test of the cgi module argv = [] stdin = sys.stdin cgi.sys = HackedSysModule() class ComparableException: def __init__(self, err): self.err = err def __str__(self): return str(self.err) def __eq__(self, anExc): if not isinstance(anExc, Exception): return NotImplemented return (self.err.__class__ == anExc.__class__ and self.err.args == anExc.args) def __getattr__(self, attr): return getattr(self.err, attr) def do_test(buf, method): env = {} if method == "GET": fp = None env['REQUEST_METHOD'] = 'GET' env['QUERY_STRING'] = buf elif method == "POST": fp = BytesIO(buf.encode('latin-1')) # FieldStorage expects bytes env['REQUEST_METHOD'] = 'POST' env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded' env['CONTENT_LENGTH'] = str(len(buf)) else: raise ValueError("unknown method: %s" % method) try: return cgi.parse(fp, env, strict_parsing=1) except Exception as err: return ComparableException(err) parse_strict_test_cases = [ ("", {}), ("&", ValueError("bad query field: ''")), ("&&", ValueError("bad query field: ''")), # Should the next few really be valid? ("=", {}), ("=&=", {}), # This rest seem to make sense ("=a", {'': ['a']}), ("&=a", ValueError("bad query field: ''")), ("=a&", ValueError("bad query field: ''")), ("=&a", ValueError("bad query field: 'a'")), ("b=a", {'b': ['a']}), ("b+=a", {'b ': ['a']}), ("a=b=a", {'a': ['b=a']}), ("a=+b=a", {'a': [' b=a']}), ("&b=a", ValueError("bad query field: ''")), ("b&=a", ValueError("bad query field: 'b'")), ("a=a+b&b=b+c", {'a': ['a b'], 'b': ['b c']}), ("a=a+b&a=b+a", {'a': ['a b', 'b a']}), ("x=1&y=2.0&z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), ("Hbc5161168c542333633315dee1182227:key_store_seqid=400006&cuyer=r&view=bustomer&order_id=0bb2e248638833d48cb7fed300000f1b&expire=964546263&lobale=en-US&kid=130003.300038&ss=env", {'Hbc5161168c542333633315dee1182227:key_store_seqid': ['400006'], 'cuyer': ['r'], 'expire': ['964546263'], 'kid': ['130003.300038'], 'lobale': ['en-US'], 'order_id': ['0bb2e248638833d48cb7fed300000f1b'], 'ss': ['env'], 'view': ['bustomer'], }), ("group_id=5470&set=custom&_assigned_to=31392&_status=1&_category=100&SUBMIT=Browse", {'SUBMIT': ['Browse'], '_assigned_to': ['31392'], '_category': ['100'], '_status': ['1'], 'group_id': ['5470'], 'set': ['custom'], }) ] def norm(seq): return sorted(seq, key=repr) def first_elts(list): return [p[0] for p in list] def first_second_elts(list): return [(p[0], p[1][0]) for p in list] def gen_result(data, environ): encoding = 'latin-1' fake_stdin = BytesIO(data.encode(encoding)) fake_stdin.seek(0) form = cgi.FieldStorage(fp=fake_stdin, environ=environ, encoding=encoding) result = {} for k, v in dict(form).items(): result[k] = isinstance(v, list) and form.getlist(k) or v.value return result class CgiTests(unittest.TestCase): def test_parse_multipart(self): fp = BytesIO(POSTDATA.encode('latin1')) env = {'boundary': BOUNDARY.encode('latin1'), 'CONTENT-LENGTH': '558'} result = cgi.parse_multipart(fp, env) expected = {'submit': [' Add '], 'id': ['1234'], 'file': [b'Testing 123.\n'], 'title': ['']} self.assertEqual(result, expected) def test_parse_multipart_without_content_length(self): POSTDATA = '''--JfISa01 Content-Disposition: form-data; name="submit-name" just a string --JfISa01-- ''' fp = BytesIO(POSTDATA.encode('latin1')) env = {'boundary': 'JfISa01'.encode('latin1')} result = cgi.parse_multipart(fp, env) expected = {'submit-name': ['just a string\n']} self.assertEqual(result, expected) def test_parse_multipart_invalid_encoding(self): BOUNDARY = "JfISa01" POSTDATA = """--JfISa01 Content-Disposition: form-data; name="submit-name" Content-Length: 3 \u2603 --JfISa01""" fp = BytesIO(POSTDATA.encode('utf8')) env = {'boundary': BOUNDARY.encode('latin1'), 'CONTENT-LENGTH': str(len(POSTDATA.encode('utf8')))} result = cgi.parse_multipart(fp, env, encoding="ascii", errors="surrogateescape") expected = {'submit-name': ["\udce2\udc98\udc83"]} self.assertEqual(result, expected) self.assertEqual("\u2603".encode('utf8'), result["submit-name"][0].encode('utf8', 'surrogateescape')) def test_fieldstorage_properties(self): fs = cgi.FieldStorage() self.assertFalse(fs) self.assertIn("FieldStorage", repr(fs)) self.assertEqual(list(fs), list(fs.keys())) fs.list.append(namedtuple('MockFieldStorage', 'name')('fieldvalue')) self.assertTrue(fs) def test_fieldstorage_invalid(self): self.assertRaises(TypeError, cgi.FieldStorage, "not-a-file-obj", environ={"REQUEST_METHOD":"PUT"}) self.assertRaises(TypeError, cgi.FieldStorage, "foo", "bar") fs = cgi.FieldStorage(headers={'content-type':'text/plain'}) self.assertRaises(TypeError, bool, fs) def test_strict(self): for orig, expect in parse_strict_test_cases: # Test basic parsing d = do_test(orig, "GET") self.assertEqual(d, expect, "Error parsing %s method GET" % repr(orig)) d = do_test(orig, "POST") self.assertEqual(d, expect, "Error parsing %s method POST" % repr(orig)) env = {'QUERY_STRING': orig} fs = cgi.FieldStorage(environ=env) if isinstance(expect, dict): # test dict interface self.assertEqual(len(expect), len(fs)) self.assertCountEqual(expect.keys(), fs.keys()) ##self.assertEqual(norm(expect.values()), norm(fs.values())) ##self.assertEqual(norm(expect.items()), norm(fs.items())) self.assertEqual(fs.getvalue("nonexistent field", "default"), "default") # test individual fields for key in expect.keys(): expect_val = expect[key] self.assertIn(key, fs) if len(expect_val) > 1: self.assertEqual(fs.getvalue(key), expect_val) else: self.assertEqual(fs.getvalue(key), expect_val[0]) def test_separator(self): parse_semicolon = [ ("x=1;y=2.0", {'x': ['1'], 'y': ['2.0']}), ("x=1;y=2.0;z=2-3.%2b0", {'x': ['1'], 'y': ['2.0'], 'z': ['2-3.+0']}), (";", ValueError("bad query field: ''")), (";;", ValueError("bad query field: ''")), ("=;a", ValueError("bad query field: 'a'")), (";b=a", ValueError("bad query field: ''")), ("b;=a", ValueError("bad query field: 'b'")), ("a=a+b;b=b+c", {'a': ['a b'], 'b': ['b c']}), ("a=a+b;a=b+a", {'a': ['a b', 'b a']}), ] for orig, expect in parse_semicolon: env = {'QUERY_STRING': orig} fs = cgi.FieldStorage(separator=';', environ=env) if isinstance(expect, dict): for key in expect.keys(): expect_val = expect[key] self.assertIn(key, fs) if len(expect_val) > 1: self.assertEqual(fs.getvalue(key), expect_val) else: self.assertEqual(fs.getvalue(key), expect_val[0]) @warnings_helper.ignore_warnings(category=DeprecationWarning) def test_log(self): cgi.log("Testing") cgi.logfp = StringIO() cgi.initlog("%s", "Testing initlog 1") cgi.log("%s", "Testing log 2") self.assertEqual(cgi.logfp.getvalue(), "Testing initlog 1\nTesting log 2\n") if os.path.exists(os.devnull): cgi.logfp = None cgi.logfile = os.devnull cgi.initlog("%s", "Testing log 3") self.addCleanup(cgi.closelog) cgi.log("Testing log 4") def test_fieldstorage_readline(self): # FieldStorage uses readline, which has the capacity to read all # contents of the input file into memory; we use readline's size argument # to prevent that for files that do not contain any newlines in # non-GET/HEAD requests class TestReadlineFile: def __init__(self, file): self.file = file self.numcalls = 0 def readline(self, size=None): self.numcalls += 1 if size: return self.file.readline(size) else: return self.file.readline() def __getattr__(self, name): file = self.__dict__['file'] a = getattr(file, name) if not isinstance(a, int): setattr(self, name, a) return a f = TestReadlineFile(tempfile.TemporaryFile("wb+")) self.addCleanup(f.close) f.write(b'x' * 256 * 1024) f.seek(0) env = {'REQUEST_METHOD':'PUT'} fs = cgi.FieldStorage(fp=f, environ=env) self.addCleanup(fs.file.close) # if we're not chunking properly, readline is only called twice # (by read_binary); if we are chunking properly, it will be called 5 times # as long as the chunksize is 1 << 16. self.assertGreater(f.numcalls, 2) f.close() def test_fieldstorage_multipart(self): #Test basic FieldStorage multipart parsing env = { 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY), 'CONTENT_LENGTH': '558'} fp = BytesIO(POSTDATA.encode('latin-1')) fs = cgi.FieldStorage(fp, environ=env, encoding="latin-1") self.assertEqual(len(fs.list), 4) expect = [{'name':'id', 'filename':None, 'value':'1234'}, {'name':'title', 'filename':None, 'value':''}, {'name':'file', 'filename':'test.txt', 'value':b'Testing 123.\n'}, {'name':'submit', 'filename':None, 'value':' Add '}] for x in range(len(fs.list)): for k, exp in expect[x].items(): got = getattr(fs.list[x], k) self.assertEqual(got, exp) def test_fieldstorage_multipart_leading_whitespace(self): env = { 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY), 'CONTENT_LENGTH': '560'} # Add some leading whitespace to our post data that will cause the # first line to not be the innerboundary. fp = BytesIO(b"\r\n" + POSTDATA.encode('latin-1')) fs = cgi.FieldStorage(fp, environ=env, encoding="latin-1") self.assertEqual(len(fs.list), 4) expect = [{'name':'id', 'filename':None, 'value':'1234'}, {'name':'title', 'filename':None, 'value':''}, {'name':'file', 'filename':'test.txt', 'value':b'Testing 123.\n'}, {'name':'submit', 'filename':None, 'value':' Add '}] for x in range(len(fs.list)): for k, exp in expect[x].items(): got = getattr(fs.list[x], k) self.assertEqual(got, exp) def test_fieldstorage_multipart_non_ascii(self): #Test basic FieldStorage multipart parsing env = {'REQUEST_METHOD':'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY), 'CONTENT_LENGTH':'558'} for encoding in ['iso-8859-1','utf-8']: fp = BytesIO(POSTDATA_NON_ASCII.encode(encoding)) fs = cgi.FieldStorage(fp, environ=env,encoding=encoding) self.assertEqual(len(fs.list), 1) expect = [{'name':'id', 'filename':None, 'value':'\xe7\xf1\x80'}] for x in range(len(fs.list)): for k, exp in expect[x].items(): got = getattr(fs.list[x], k) self.assertEqual(got, exp) def test_fieldstorage_multipart_maxline(self): # Issue #18167 maxline = 1 << 16 self.maxDiff = None def check(content): data = """---123 Content-Disposition: form-data; name="upload"; filename="fake.txt" Content-Type: text/plain %s ---123-- """.replace('\n', '\r\n') % content environ = { 'CONTENT_LENGTH': str(len(data)), 'CONTENT_TYPE': 'multipart/form-data; boundary=-123', 'REQUEST_METHOD': 'POST', } self.assertEqual(gen_result(data, environ), {'upload': content.encode('latin1')}) check('x' * (maxline - 1)) check('x' * (maxline - 1) + '\r') check('x' * (maxline - 1) + '\r' + 'y' * (maxline - 1)) def test_fieldstorage_multipart_w3c(self): # Test basic FieldStorage multipart parsing (W3C sample) env = { 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY_W3), 'CONTENT_LENGTH': str(len(POSTDATA_W3))} fp = BytesIO(POSTDATA_W3.encode('latin-1')) fs = cgi.FieldStorage(fp, environ=env, encoding="latin-1") self.assertEqual(len(fs.list), 2) self.assertEqual(fs.list[0].name, 'submit-name') self.assertEqual(fs.list[0].value, 'Larry') self.assertEqual(fs.list[1].name, 'files') files = fs.list[1].value self.assertEqual(len(files), 2) expect = [{'name': None, 'filename': 'file1.txt', 'value': b'... contents of file1.txt ...'}, {'name': None, 'filename': 'file2.gif', 'value': b'...contents of file2.gif...'}] for x in range(len(files)): for k, exp in expect[x].items(): got = getattr(files[x], k) self.assertEqual(got, exp) def test_fieldstorage_part_content_length(self): BOUNDARY = "JfISa01" POSTDATA = """--JfISa01 Content-Disposition: form-data; name="submit-name" Content-Length: 5 Larry --JfISa01""" env = { 'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY), 'CONTENT_LENGTH': str(len(POSTDATA))} fp = BytesIO(POSTDATA.encode('latin-1')) fs = cgi.FieldStorage(fp, environ=env, encoding="latin-1") self.assertEqual(len(fs.list), 1) self.assertEqual(fs.list[0].name, 'submit-name') self.assertEqual(fs.list[0].value, 'Larry') def test_field_storage_multipart_no_content_length(self): fp = BytesIO(b"""--MyBoundary Content-Disposition: form-data; name="my-arg"; filename="foo" Test --MyBoundary-- """) env = { "REQUEST_METHOD": "POST", "CONTENT_TYPE": "multipart/form-data; boundary=MyBoundary", "wsgi.input": fp, } fields = cgi.FieldStorage(fp, environ=env) self.assertEqual(len(fields["my-arg"].file.read()), 5) def test_fieldstorage_as_context_manager(self): fp = BytesIO(b'x' * 10) env = {'REQUEST_METHOD': 'PUT'} with cgi.FieldStorage(fp=fp, environ=env) as fs: content = fs.file.read() self.assertFalse(fs.file.closed) self.assertTrue(fs.file.closed) self.assertEqual(content, 'x' * 10) with self.assertRaisesRegex(ValueError, 'I/O operation on closed file'): fs.file.read() _qs_result = { 'key1': 'value1', 'key2': ['value2x', 'value2y'], 'key3': 'value3', 'key4': 'value4' } def testQSAndUrlEncode(self): data = "key2=value2x&key3=value3&key4=value4" environ = { 'CONTENT_LENGTH': str(len(data)), 'CONTENT_TYPE': 'application/x-www-form-urlencoded', 'QUERY_STRING': 'key1=value1&key2=value2y', 'REQUEST_METHOD': 'POST', } v = gen_result(data, environ) self.assertEqual(self._qs_result, v) def test_max_num_fields(self): # For application/x-www-form-urlencoded data = '&'.join(['a=a']*11) environ = { 'CONTENT_LENGTH': str(len(data)), 'CONTENT_TYPE': 'application/x-www-form-urlencoded', 'REQUEST_METHOD': 'POST', } with self.assertRaises(ValueError): cgi.FieldStorage( fp=BytesIO(data.encode()), environ=environ, max_num_fields=10, ) # For multipart/form-data data = """---123 Content-Disposition: form-data; name="a" 3 ---123 Content-Type: application/x-www-form-urlencoded a=4 ---123 Content-Type: application/x-www-form-urlencoded a=5 ---123-- """ environ = { 'CONTENT_LENGTH': str(len(data)), 'CONTENT_TYPE': 'multipart/form-data; boundary=-123', 'QUERY_STRING': 'a=1&a=2', 'REQUEST_METHOD': 'POST', } # 2 GET entities # 1 top level POST entities # 1 entity within the second POST entity # 1 entity within the third POST entity with self.assertRaises(ValueError): cgi.FieldStorage( fp=BytesIO(data.encode()), environ=environ, max_num_fields=4, ) cgi.FieldStorage( fp=BytesIO(data.encode()), environ=environ, max_num_fields=5, ) def testQSAndFormData(self): data = """---123 Content-Disposition: form-data; name="key2" value2y ---123 Content-Disposition: form-data; name="key3" value3 ---123 Content-Disposition: form-data; name="key4" value4 ---123-- """ environ = { 'CONTENT_LENGTH': str(len(data)), 'CONTENT_TYPE': 'multipart/form-data; boundary=-123', 'QUERY_STRING': 'key1=value1&key2=value2x', 'REQUEST_METHOD': 'POST', } v = gen_result(data, environ) self.assertEqual(self._qs_result, v) def testQSAndFormDataFile(self): data = """---123 Content-Disposition: form-data; name="key2" value2y ---123 Content-Disposition: form-data; name="key3" value3 ---123 Content-Disposition: form-data; name="key4" value4 ---123 Content-Disposition: form-data; name="upload"; filename="fake.txt" Content-Type: text/plain this is the content of the fake file ---123-- """ environ = { 'CONTENT_LENGTH': str(len(data)), 'CONTENT_TYPE': 'multipart/form-data; boundary=-123', 'QUERY_STRING': 'key1=value1&key2=value2x', 'REQUEST_METHOD': 'POST', } result = self._qs_result.copy() result.update({ 'upload': b'this is the content of the fake file\n' }) v = gen_result(data, environ) self.assertEqual(result, v) def test_parse_header(self): self.assertEqual( cgi.parse_header("text/plain"), ("text/plain", {})) self.assertEqual( cgi.parse_header("text/vnd.just.made.this.up ; "), ("text/vnd.just.made.this.up", {})) self.assertEqual( cgi.parse_header("text/plain;charset=us-ascii"), ("text/plain", {"charset": "us-ascii"})) self.assertEqual( cgi.parse_header('text/plain ; charset="us-ascii"'), ("text/plain", {"charset": "us-ascii"})) self.assertEqual( cgi.parse_header('text/plain ; charset="us-ascii"; another=opt'), ("text/plain", {"charset": "us-ascii", "another": "opt"})) self.assertEqual( cgi.parse_header('attachment; filename="silly.txt"'), ("attachment", {"filename": "silly.txt"})) self.assertEqual( cgi.parse_header('attachment; filename="strange;name"'), ("attachment", {"filename": "strange;name"})) self.assertEqual( cgi.parse_header('attachment; filename="strange;name";size=123;'), ("attachment", {"filename": "strange;name", "size": "123"})) self.assertEqual( cgi.parse_header('form-data; name="files"; filename="fo\\"o;bar"'), ("form-data", {"name": "files", "filename": 'fo"o;bar'})) def test_all(self): not_exported = { "logfile", "logfp", "initlog", "dolog", "nolog", "closelog", "log", "maxlen", "valid_boundary"} support.check__all__(self, cgi, not_exported=not_exported) BOUNDARY = "---------------------------721837373350705526688164684" POSTDATA = """-----------------------------721837373350705526688164684 Content-Disposition: form-data; name="id" 1234 -----------------------------721837373350705526688164684 Content-Disposition: form-data; name="title" -----------------------------721837373350705526688164684 Content-Disposition: form-data; name="file"; filename="test.txt" Content-Type: text/plain Testing 123. -----------------------------721837373350705526688164684 Content-Disposition: form-data; name="submit" Add\x20 -----------------------------721837373350705526688164684-- """ POSTDATA_NON_ASCII = """-----------------------------721837373350705526688164684 Content-Disposition: form-data; name="id" \xe7\xf1\x80 -----------------------------721837373350705526688164684 """ # http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4 BOUNDARY_W3 = "AaB03x" POSTDATA_W3 = """--AaB03x Content-Disposition: form-data; name="submit-name" Larry --AaB03x Content-Disposition: form-data; name="files" Content-Type: multipart/mixed; boundary=BbC04y --BbC04y Content-Disposition: file; filename="file1.txt" Content-Type: text/plain ... contents of file1.txt ... --BbC04y Content-Disposition: file; filename="file2.gif" Content-Type: image/gif Content-Transfer-Encoding: binary ...contents of file2.gif... --BbC04y-- --AaB03x-- """ if __name__ == '__main__': unittest.main() python-deadlib-3.13.0/cgitb/000077500000000000000000000000001471045437300156015ustar00rootroot00000000000000python-deadlib-3.13.0/cgitb/Doc/000077500000000000000000000000001471045437300163065ustar00rootroot00000000000000python-deadlib-3.13.0/cgitb/Doc/cgitb.rst000066400000000000000000000074171471045437300201410ustar00rootroot00000000000000:mod:`cgitb` --- Traceback manager for CGI scripts ================================================== .. module:: cgitb :synopsis: Configurable traceback handler for CGI scripts. :deprecated: .. moduleauthor:: Ka-Ping Yee .. sectionauthor:: Fred L. Drake, Jr. **Source code:** :source:`Lib/cgitb.py` .. index:: single: CGI; exceptions single: CGI; tracebacks single: exceptions; in CGI scripts single: tracebacks; in CGI scripts .. deprecated-removed:: 3.11 3.13 The :mod:`cgitb` module is deprecated (see :pep:`PEP 594 <594#cgitb>` for details). -------------- The :mod:`cgitb` module provides a special exception handler for Python scripts. (Its name is a bit misleading. It was originally designed to display extensive traceback information in HTML for CGI scripts. It was later generalized to also display this information in plain text.) After this module is activated, if an uncaught exception occurs, a detailed, formatted report will be displayed. The report includes a traceback showing excerpts of the source code for each level, as well as the values of the arguments and local variables to currently running functions, to help you debug the problem. Optionally, you can save this information to a file instead of sending it to the browser. To enable this feature, simply add this to the top of your CGI script:: import cgitb cgitb.enable() The options to the :func:`enable` function control whether the report is displayed in the browser and whether the report is logged to a file for later analysis. .. function:: enable(display=1, logdir=None, context=5, format="html") .. index:: single: excepthook() (in module sys) This function causes the :mod:`cgitb` module to take over the interpreter's default handling for exceptions by setting the value of :attr:`sys.excepthook`. The optional argument *display* defaults to ``1`` and can be set to ``0`` to suppress sending the traceback to the browser. If the argument *logdir* is present, the traceback reports are written to files. The value of *logdir* should be a directory where these files will be placed. The optional argument *context* is the number of lines of context to display around the current line of source code in the traceback; this defaults to ``5``. If the optional argument *format* is ``"html"``, the output is formatted as HTML. Any other value forces plain text output. The default value is ``"html"``. .. function:: text(info, context=5) This function handles the exception described by *info* (a 3-tuple containing the result of :func:`sys.exc_info`), formatting its traceback as text and returning the result as a string. The optional argument *context* is the number of lines of context to display around the current line of source code in the traceback; this defaults to ``5``. .. function:: html(info, context=5) This function handles the exception described by *info* (a 3-tuple containing the result of :func:`sys.exc_info`), formatting its traceback as HTML and returning the result as a string. The optional argument *context* is the number of lines of context to display around the current line of source code in the traceback; this defaults to ``5``. .. function:: handler(info=None) This function handles an exception using the default settings (that is, show a report in the browser, but don't log to a file). This can be used when you've caught an exception and want to report it using :mod:`cgitb`. The optional *info* argument should be a 3-tuple containing an exception type, exception value, and traceback object, exactly like the tuple returned by :func:`sys.exc_info`. If the *info* argument is not supplied, the current exception is obtained from :func:`sys.exc_info`. python-deadlib-3.13.0/cgitb/LICENSE000066400000000000000000000050111471045437300166030ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/cgitb/README.rst000066400000000000000000000005161471045437300172720ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/cgitb/cgitb/000077500000000000000000000000001471045437300166715ustar00rootroot00000000000000python-deadlib-3.13.0/cgitb/cgitb/__init__.py000066400000000000000000000306221471045437300210050ustar00rootroot00000000000000"""More comprehensive traceback formatting for Python scripts. To enable this module, do: import cgitb; cgitb.enable() at the top of your script. The optional arguments to enable() are: display - if true, tracebacks are displayed in the web browser logdir - if set, tracebacks are written to files in this directory context - number of lines of source code to show for each stack frame format - 'text' or 'html' controls the output format By default, tracebacks are displayed but not saved, the context is 5 lines and the output format is 'html' (for backwards compatibility with the original use of this module) Alternatively, if you have caught an exception and want cgitb to display it for you, call cgitb.handler(). The optional argument to handler() is a 3-item tuple (etype, evalue, etb) just like the value of sys.exc_info(). The default handler displays output as HTML. """ import inspect import keyword import linecache import os import pydoc import sys import tempfile import time import tokenize import traceback import warnings from html import escape as html_escape # python-deadlib: Replace deprecation warning not to raise exception warnings.warn( f"{__name__} was removed in Python 3.13. " f"Please be aware that you are currently NOT using standard '{__name__}', " f"but instead a separately installed 'standard-{__name__}'.", DeprecationWarning, stacklevel=2 ) def reset(): """Return a string that resets the CGI and browser to a known state.""" return ''' --> --> ''' __UNDEF__ = [] # a special sentinel object def small(text): if text: return '' + text + '' else: return '' def strong(text): if text: return '' + text + '' else: return '' def grey(text): if text: return '' + text + '' else: return '' def lookup(name, frame, locals): """Find the value for a given name in the given environment.""" if name in locals: return 'local', locals[name] if name in frame.f_globals: return 'global', frame.f_globals[name] if '__builtins__' in frame.f_globals: builtins = frame.f_globals['__builtins__'] if isinstance(builtins, dict): if name in builtins: return 'builtin', builtins[name] else: if hasattr(builtins, name): return 'builtin', getattr(builtins, name) return None, __UNDEF__ def scanvars(reader, frame, locals): """Scan one logical line of Python and look up values of variables used.""" vars, lasttoken, parent, prefix, value = [], None, None, '', __UNDEF__ for ttype, token, start, end, line in tokenize.generate_tokens(reader): if ttype == tokenize.NEWLINE: break if ttype == tokenize.NAME and token not in keyword.kwlist: if lasttoken == '.': if parent is not __UNDEF__: value = getattr(parent, token, __UNDEF__) vars.append((prefix + token, prefix, value)) else: where, value = lookup(token, frame, locals) vars.append((token, where, value)) elif token == '.': prefix += lasttoken + '.' parent = value else: parent, prefix = None, '' lasttoken = token return vars def html(einfo, context=5): """Return a nice HTML document describing a given traceback.""" etype, evalue, etb = einfo if isinstance(etype, type): etype = etype.__name__ pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable date = time.ctime(time.time()) head = f'''
 
 
{html_escape(str(etype))}
{pyver}
{date}

A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred.

''' indent = '' + small(' ' * 5) + ' ' frames = [] records = inspect.getinnerframes(etb, context) for frame, file, lnum, func, lines, index in records: if file: file = os.path.abspath(file) link = '%s' % (file, pydoc.html.escape(file)) else: file = link = '?' args, varargs, varkw, locals = inspect.getargvalues(frame) call = '' if func != '?': call = 'in ' + strong(pydoc.html.escape(func)) if func != "": call += inspect.formatargvalues(args, varargs, varkw, locals, formatvalue=lambda value: '=' + pydoc.html.repr(value)) highlight = {} def reader(lnum=[lnum]): highlight[lnum[0]] = 1 try: return linecache.getline(file, lnum[0]) finally: lnum[0] += 1 vars = scanvars(reader, frame, locals) rows = ['%s%s %s' % (' ', link, call)] if index is not None: i = lnum - index for line in lines: num = small(' ' * (5-len(str(i))) + str(i)) + ' ' if i in highlight: line = '=>%s%s' % (num, pydoc.html.preformat(line)) rows.append('%s' % line) else: line = '  %s%s' % (num, pydoc.html.preformat(line)) rows.append('%s' % grey(line)) i += 1 done, dump = {}, [] for name, where, value in vars: if name in done: continue done[name] = 1 if value is not __UNDEF__: if where in ('global', 'builtin'): name = ('%s ' % where) + strong(name) elif where == 'local': name = strong(name) else: name = where + strong(name.split('.')[-1]) dump.append('%s = %s' % (name, pydoc.html.repr(value))) else: dump.append(name + ' undefined') rows.append('%s' % small(grey(', '.join(dump)))) frames.append(''' %s
''' % '\n'.join(rows)) exception = ['

%s: %s' % (strong(pydoc.html.escape(str(etype))), pydoc.html.escape(str(evalue)))] for name in dir(evalue): if name[:1] == '_': continue value = pydoc.html.repr(getattr(evalue, name)) exception.append('\n
%s%s =\n%s' % (indent, name, value)) return head + ''.join(frames) + ''.join(exception) + ''' ''' % pydoc.html.escape( ''.join(traceback.format_exception(etype, evalue, etb))) def text(einfo, context=5): """Return a plain text document describing a given traceback.""" etype, evalue, etb = einfo if isinstance(etype, type): etype = etype.__name__ pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable date = time.ctime(time.time()) head = "%s\n%s\n%s\n" % (str(etype), pyver, date) + ''' A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred. ''' frames = [] records = inspect.getinnerframes(etb, context) for frame, file, lnum, func, lines, index in records: file = file and os.path.abspath(file) or '?' args, varargs, varkw, locals = inspect.getargvalues(frame) call = '' if func != '?': call = 'in ' + func if func != "": call += inspect.formatargvalues(args, varargs, varkw, locals, formatvalue=lambda value: '=' + pydoc.text.repr(value)) highlight = {} def reader(lnum=[lnum]): highlight[lnum[0]] = 1 try: return linecache.getline(file, lnum[0]) finally: lnum[0] += 1 vars = scanvars(reader, frame, locals) rows = [' %s %s' % (file, call)] if index is not None: i = lnum - index for line in lines: num = '%5d ' % i rows.append(num+line.rstrip()) i += 1 done, dump = {}, [] for name, where, value in vars: if name in done: continue done[name] = 1 if value is not __UNDEF__: if where == 'global': name = 'global ' + name elif where != 'local': name = where + name.split('.')[-1] dump.append('%s = %s' % (name, pydoc.text.repr(value))) else: dump.append(name + ' undefined') rows.append('\n'.join(dump)) frames.append('\n%s\n' % '\n'.join(rows)) exception = ['%s: %s' % (str(etype), str(evalue))] for name in dir(evalue): value = pydoc.text.repr(getattr(evalue, name)) exception.append('\n%s%s = %s' % (" "*4, name, value)) return head + ''.join(frames) + ''.join(exception) + ''' The above is a description of an error in a Python program. Here is the original traceback: %s ''' % ''.join(traceback.format_exception(etype, evalue, etb)) class Hook: """A hook to replace sys.excepthook that shows tracebacks in HTML.""" def __init__(self, display=1, logdir=None, context=5, file=None, format="html"): self.display = display # send tracebacks to browser if true self.logdir = logdir # log tracebacks to files if not None self.context = context # number of source code lines per frame self.file = file or sys.stdout # place to send the output self.format = format def __call__(self, etype, evalue, etb): self.handle((etype, evalue, etb)) def handle(self, info=None): info = info or sys.exc_info() if self.format == "html": self.file.write(reset()) formatter = (self.format=="html") and html or text plain = False try: doc = formatter(info, self.context) except: # just in case something goes wrong doc = ''.join(traceback.format_exception(*info)) plain = True if self.display: if plain: doc = pydoc.html.escape(doc) self.file.write('

' + doc + '
\n') else: self.file.write(doc + '\n') else: self.file.write('

A problem occurred in a Python script.\n') if self.logdir is not None: suffix = ['.txt', '.html'][self.format=="html"] (fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir) try: with os.fdopen(fd, 'w') as file: file.write(doc) msg = '%s contains the description of this error.' % path except: msg = 'Tried to save traceback to %s, but failed.' % path if self.format == 'html': self.file.write('

%s

\n' % msg) else: self.file.write(msg + '\n') try: self.file.flush() except: pass handler = Hook().handle def enable(display=1, logdir=None, context=5, format="html"): """Install an exception handler that formats tracebacks as HTML. The optional argument 'display' can be set to 0 to suppress sending the traceback to the browser, and 'logdir' can be set to a directory to cause tracebacks to be written to files there.""" sys.excepthook = Hook(display=display, logdir=logdir, context=context, format=format) python-deadlib-3.13.0/cgitb/pyproject.toml000066400000000000000000000012651471045437300205210ustar00rootroot00000000000000[project] name = "standard-cgitb" version = "3.13.0" description = "Standard library cgitb redistribution. \"dead battery\"." keywords = ["stdlib"] authors = [ { name = "Python Developers", email = "python-deadlib@youknowone.org" } ] readme = "README.rst" license = { text = "PSF-2.0" } classifiers = [ "License :: OSI Approved :: Python Software Foundation License", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", ] [tool.setuptools.packages] find = {include = ["cgitb*"]} [project.urls] "Homepage" = "https://github.com/youknowone/python-deadlib" [build-system] requires = ["setuptools>=75.0"] build-backend = "setuptools.build_meta" python-deadlib-3.13.0/cgitb/tests/000077500000000000000000000000001471045437300167435ustar00rootroot00000000000000python-deadlib-3.13.0/cgitb/tests/__init__.py000066400000000000000000000000001471045437300210420ustar00rootroot00000000000000python-deadlib-3.13.0/cgitb/tests/test_cgitb.py000066400000000000000000000051731471045437300214520ustar00rootroot00000000000000from test.support.os_helper import temp_dir from test.support.script_helper import assert_python_failure from test.support.warnings_helper import import_deprecated import unittest import sys cgitb = import_deprecated("cgitb") class TestCgitb(unittest.TestCase): def test_fonts(self): text = "Hello Robbie!" self.assertEqual(cgitb.small(text), "{}".format(text)) self.assertEqual(cgitb.strong(text), "{}".format(text)) self.assertEqual(cgitb.grey(text), '{}'.format(text)) def test_blanks(self): self.assertEqual(cgitb.small(""), "") self.assertEqual(cgitb.strong(""), "") self.assertEqual(cgitb.grey(""), "") def test_html(self): try: raise ValueError("Hello World") except ValueError as err: # If the html was templated we could do a bit more here. # At least check that we get details on what we just raised. html = cgitb.html(sys.exc_info()) self.assertIn("ValueError", html) self.assertIn(str(err), html) def test_text(self): try: raise ValueError("Hello World") except ValueError: text = cgitb.text(sys.exc_info()) self.assertIn("ValueError", text) self.assertIn("Hello World", text) def test_syshook_no_logdir_default_format(self): with temp_dir() as tracedir: rc, out, err = assert_python_failure( '-c', ('import cgitb; cgitb.enable(logdir=%s); ' 'raise ValueError("Hello World")') % repr(tracedir), PYTHONIOENCODING='utf-8') out = out.decode() self.assertIn("ValueError", out) self.assertIn("Hello World", out) self.assertIn("<module>", out) # By default we emit HTML markup. self.assertIn('

', out) self.assertIn('

', out) def test_syshook_no_logdir_text_format(self): # Issue 12890: we were emitting the

tag in text mode. with temp_dir() as tracedir: rc, out, err = assert_python_failure( '-c', ('import cgitb; cgitb.enable(format="text", logdir=%s); ' 'raise ValueError("Hello World")') % repr(tracedir), PYTHONIOENCODING='utf-8') out = out.decode() self.assertIn("ValueError", out) self.assertIn("Hello World", out) self.assertNotIn('

', out) self.assertNotIn('

', out) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/chunk/000077500000000000000000000000001471045437300156215ustar00rootroot00000000000000python-deadlib-3.13.0/chunk/Doc/000077500000000000000000000000001471045437300163265ustar00rootroot00000000000000python-deadlib-3.13.0/chunk/Doc/chunk.rst000066400000000000000000000121631471045437300201730ustar00rootroot00000000000000:mod:`chunk` --- Read IFF chunked data ====================================== .. module:: chunk :synopsis: Module to read IFF chunks. :deprecated: .. moduleauthor:: Sjoerd Mullender .. sectionauthor:: Sjoerd Mullender **Source code:** :source:`Lib/chunk.py` .. index:: single: Audio Interchange File Format single: AIFF single: AIFF-C single: Real Media File Format single: RMFF .. deprecated-removed:: 3.11 3.13 The :mod:`chunk` module is deprecated (see :pep:`PEP 594 <594#chunk>` for details). -------------- This module provides an interface for reading files that use EA IFF 85 chunks. [#]_ This format is used in at least the Audio Interchange File Format (AIFF/AIFF-C) and the Real Media File Format (RMFF). The WAVE audio file format is closely related and can also be read using this module. A chunk has the following structure: +---------+--------+-------------------------------+ | Offset | Length | Contents | +=========+========+===============================+ | 0 | 4 | Chunk ID | +---------+--------+-------------------------------+ | 4 | 4 | Size of chunk in big-endian | | | | byte order, not including the | | | | header | +---------+--------+-------------------------------+ | 8 | *n* | Data bytes, where *n* is the | | | | size given in the preceding | | | | field | +---------+--------+-------------------------------+ | 8 + *n* | 0 or 1 | Pad byte needed if *n* is odd | | | | and chunk alignment is used | +---------+--------+-------------------------------+ The ID is a 4-byte string which identifies the type of chunk. The size field (a 32-bit value, encoded using big-endian byte order) gives the size of the chunk data, not including the 8-byte header. Usually an IFF-type file consists of one or more chunks. The proposed usage of the :class:`Chunk` class defined here is to instantiate an instance at the start of each chunk and read from the instance until it reaches the end, after which a new instance can be instantiated. At the end of the file, creating a new instance will fail with an :exc:`EOFError` exception. .. class:: Chunk(file, align=True, bigendian=True, inclheader=False) Class which represents a chunk. The *file* argument is expected to be a file-like object. An instance of this class is specifically allowed. The only method that is needed is :meth:`~io.IOBase.read`. If the methods :meth:`~io.IOBase.seek` and :meth:`~io.IOBase.tell` are present and don't raise an exception, they are also used. If these methods are present and raise an exception, they are expected to not have altered the object. If the optional argument *align* is true, chunks are assumed to be aligned on 2-byte boundaries. If *align* is false, no alignment is assumed. The default value is true. If the optional argument *bigendian* is false, the chunk size is assumed to be in little-endian order. This is needed for WAVE audio files. The default value is true. If the optional argument *inclheader* is true, the size given in the chunk header includes the size of the header. The default value is false. A :class:`Chunk` object supports the following methods: .. method:: getname() Returns the name (ID) of the chunk. This is the first 4 bytes of the chunk. .. method:: getsize() Returns the size of the chunk. .. method:: close() Close and skip to the end of the chunk. This does not close the underlying file. The remaining methods will raise :exc:`OSError` if called after the :meth:`close` method has been called. Before Python 3.3, they used to raise :exc:`IOError`, now an alias of :exc:`OSError`. .. method:: isatty() Returns ``False``. .. method:: seek(pos, whence=0) Set the chunk's current position. The *whence* argument is optional and defaults to ``0`` (absolute file positioning); other values are ``1`` (seek relative to the current position) and ``2`` (seek relative to the file's end). There is no return value. If the underlying file does not allow seek, only forward seeks are allowed. .. method:: tell() Return the current position into the chunk. .. method:: read(size=-1) Read at most *size* bytes from the chunk (less if the read hits the end of the chunk before obtaining *size* bytes). If the *size* argument is negative or omitted, read all data until the end of the chunk. An empty bytes object is returned when the end of the chunk is encountered immediately. .. method:: skip() Skip to the end of the chunk. All further calls to :meth:`read` for the chunk will return ``b''``. If you are not interested in the contents of the chunk, this method should be called so that the file points to the start of the next chunk. .. rubric:: Footnotes .. [#] "EA IFF 85" Standard for Interchange Format Files, Jerry Morrison, Electronic Arts, January 1985. python-deadlib-3.13.0/chunk/LICENSE000066400000000000000000000050111471045437300166230ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/chunk/README.rst000066400000000000000000000005161471045437300173120ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/chunk/chunk/000077500000000000000000000000001471045437300167315ustar00rootroot00000000000000python-deadlib-3.13.0/chunk/chunk/__init__.py000066400000000000000000000132111471045437300210400ustar00rootroot00000000000000"""Simple class to read IFF chunks. An IFF chunk (used in formats such as AIFF, TIFF, RMFF (RealMedia File Format)) has the following structure: +----------------+ | ID (4 bytes) | +----------------+ | size (4 bytes) | +----------------+ | data | | ... | +----------------+ The ID is a 4-byte string which identifies the type of chunk. The size field (a 32-bit value, encoded using big-endian byte order) gives the size of the whole chunk, including the 8-byte header. Usually an IFF-type file consists of one or more chunks. The proposed usage of the Chunk class defined here is to instantiate an instance at the start of each chunk and read from the instance until it reaches the end, after which a new instance can be instantiated. At the end of the file, creating a new instance will fail with an EOFError exception. Usage: while True: try: chunk = Chunk(file) except EOFError: break chunktype = chunk.getname() while True: data = chunk.read(nbytes) if not data: pass # do something with data The interface is file-like. The implemented methods are: read, close, seek, tell, isatty. Extra methods are: skip() (called by close, skips to the end of the chunk), getname() (returns the name (ID) of the chunk) The __init__ method has one required argument, a file-like object (including a chunk instance), and one optional argument, a flag which specifies whether or not chunks are aligned on 2-byte boundaries. The default is 1, i.e. aligned. """ import warnings # python-deadlib: Replace deprecation warning not to raise exception warnings.warn( f"{__name__} was removed in Python 3.13. " f"Please be aware that you are currently NOT using standard '{__name__}', " f"but instead a separately installed 'standard-{__name__}'.", DeprecationWarning, stacklevel=2 ) class Chunk: def __init__(self, file, align=True, bigendian=True, inclheader=False): import struct self.closed = False self.align = align # whether to align to word (2-byte) boundaries if bigendian: strflag = '>' else: strflag = '<' self.file = file self.chunkname = file.read(4) if len(self.chunkname) < 4: raise EOFError try: self.chunksize = struct.unpack_from(strflag+'L', file.read(4))[0] except struct.error: raise EOFError from None if inclheader: self.chunksize = self.chunksize - 8 # subtract header self.size_read = 0 try: self.offset = self.file.tell() except (AttributeError, OSError): self.seekable = False else: self.seekable = True def getname(self): """Return the name (ID) of the current chunk.""" return self.chunkname def getsize(self): """Return the size of the current chunk.""" return self.chunksize def close(self): if not self.closed: try: self.skip() finally: self.closed = True def isatty(self): if self.closed: raise ValueError("I/O operation on closed file") return False def seek(self, pos, whence=0): """Seek to specified position into the chunk. Default position is 0 (start of chunk). If the file is not seekable, this will result in an error. """ if self.closed: raise ValueError("I/O operation on closed file") if not self.seekable: raise OSError("cannot seek") if whence == 1: pos = pos + self.size_read elif whence == 2: pos = pos + self.chunksize if pos < 0 or pos > self.chunksize: raise RuntimeError self.file.seek(self.offset + pos, 0) self.size_read = pos def tell(self): if self.closed: raise ValueError("I/O operation on closed file") return self.size_read def read(self, size=-1): """Read at most size bytes from the chunk. If size is omitted or negative, read until the end of the chunk. """ if self.closed: raise ValueError("I/O operation on closed file") if self.size_read >= self.chunksize: return b'' if size < 0: size = self.chunksize - self.size_read if size > self.chunksize - self.size_read: size = self.chunksize - self.size_read data = self.file.read(size) self.size_read = self.size_read + len(data) if self.size_read == self.chunksize and \ self.align and \ (self.chunksize & 1): dummy = self.file.read(1) self.size_read = self.size_read + len(dummy) return data def skip(self): """Skip the rest of the chunk. If you are not interested in the contents of the chunk, this method should be called so that the file points to the start of the next chunk. """ if self.closed: raise ValueError("I/O operation on closed file") if self.seekable: try: n = self.chunksize - self.size_read # maybe fix alignment if self.align and (self.chunksize & 1): n = n + 1 self.file.seek(n, 1) self.size_read = self.size_read + n return except OSError: pass while self.size_read < self.chunksize: n = min(8192, self.chunksize - self.size_read) dummy = self.read(n) if not dummy: raise EOFError python-deadlib-3.13.0/chunk/pyproject.toml000066400000000000000000000012651471045437300205410ustar00rootroot00000000000000[project] name = "standard-chunk" version = "3.13.0" description = "Standard library chunk redistribution. \"dead battery\"." keywords = ["stdlib"] authors = [ { name = "Python Developers", email = "python-deadlib@youknowone.org" } ] readme = "README.rst" license = { text = "PSF-2.0" } classifiers = [ "License :: OSI Approved :: Python Software Foundation License", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", ] [tool.setuptools.packages] find = {include = ["chunk*"]} [project.urls] "Homepage" = "https://github.com/youknowone/python-deadlib" [build-system] requires = ["setuptools>=75.0"] build-backend = "setuptools.build_meta" python-deadlib-3.13.0/crypt/000077500000000000000000000000001471045437300156525ustar00rootroot00000000000000python-deadlib-3.13.0/crypt/Doc/000077500000000000000000000000001471045437300163575ustar00rootroot00000000000000python-deadlib-3.13.0/crypt/Doc/crypt.rst000066400000000000000000000137601471045437300202610ustar00rootroot00000000000000:mod:`crypt` --- Function to check Unix passwords ================================================= .. module:: crypt :platform: Unix :synopsis: The crypt() function used to check Unix passwords. :deprecated: .. moduleauthor:: Steven D. Majewski .. sectionauthor:: Steven D. Majewski .. sectionauthor:: Peter Funk **Source code:** :source:`Lib/crypt.py` .. index:: single: crypt(3) pair: cipher; DES .. deprecated-removed:: 3.11 3.13 The :mod:`crypt` module is deprecated (see :pep:`PEP 594 <594#crypt>` for details and alternatives). The :mod:`hashlib` module is a potential replacement for certain use cases. The `passlib `_ package can replace all use cases of this module. -------------- This module implements an interface to the :manpage:`crypt(3)` routine, which is a one-way hash function based upon a modified DES algorithm; see the Unix man page for further details. Possible uses include storing hashed passwords so you can check passwords without storing the actual password, or attempting to crack Unix passwords with a dictionary. .. index:: single: crypt(3) Notice that the behavior of this module depends on the actual implementation of the :manpage:`crypt(3)` routine in the running system. Therefore, any extensions available on the current implementation will also be available on this module. .. availability:: Unix, not VxWorks. .. include:: ../includes/wasm-notavail.rst Hashing Methods --------------- .. versionadded:: 3.3 The :mod:`crypt` module defines the list of hashing methods (not all methods are available on all platforms): .. data:: METHOD_SHA512 A Modular Crypt Format method with 16 character salt and 86 character hash based on the SHA-512 hash function. This is the strongest method. .. data:: METHOD_SHA256 Another Modular Crypt Format method with 16 character salt and 43 character hash based on the SHA-256 hash function. .. data:: METHOD_BLOWFISH Another Modular Crypt Format method with 22 character salt and 31 character hash based on the Blowfish cipher. .. versionadded:: 3.7 .. data:: METHOD_MD5 Another Modular Crypt Format method with 8 character salt and 22 character hash based on the MD5 hash function. .. data:: METHOD_CRYPT The traditional method with a 2 character salt and 13 characters of hash. This is the weakest method. Module Attributes ----------------- .. versionadded:: 3.3 .. attribute:: methods A list of available password hashing algorithms, as ``crypt.METHOD_*`` objects. This list is sorted from strongest to weakest. Module Functions ---------------- The :mod:`crypt` module defines the following functions: .. function:: crypt(word, salt=None) *word* will usually be a user's password as typed at a prompt or in a graphical interface. The optional *salt* is either a string as returned from :func:`mksalt`, one of the ``crypt.METHOD_*`` values (though not all may be available on all platforms), or a full encrypted password including salt, as returned by this function. If *salt* is not provided, the strongest method available in :attr:`methods` will be used. Checking a password is usually done by passing the plain-text password as *word* and the full results of a previous :func:`crypt` call, which should be the same as the results of this call. *salt* (either a random 2 or 16 character string, possibly prefixed with ``$digit$`` to indicate the method) which will be used to perturb the encryption algorithm. The characters in *salt* must be in the set ``[./a-zA-Z0-9]``, with the exception of Modular Crypt Format which prefixes a ``$digit$``. Returns the hashed password as a string, which will be composed of characters from the same alphabet as the salt. .. index:: single: crypt(3) Since a few :manpage:`crypt(3)` extensions allow different values, with different sizes in the *salt*, it is recommended to use the full crypted password as salt when checking for a password. .. versionchanged:: 3.3 Accept ``crypt.METHOD_*`` values in addition to strings for *salt*. .. function:: mksalt(method=None, *, rounds=None) Return a randomly generated salt of the specified method. If no *method* is given, the strongest method available in :attr:`methods` is used. The return value is a string suitable for passing as the *salt* argument to :func:`crypt`. *rounds* specifies the number of rounds for ``METHOD_SHA256``, ``METHOD_SHA512`` and ``METHOD_BLOWFISH``. For ``METHOD_SHA256`` and ``METHOD_SHA512`` it must be an integer between ``1000`` and ``999_999_999``, the default is ``5000``. For ``METHOD_BLOWFISH`` it must be a power of two between ``16`` (2\ :sup:`4`) and ``2_147_483_648`` (2\ :sup:`31`), the default is ``4096`` (2\ :sup:`12`). .. versionadded:: 3.3 .. versionchanged:: 3.7 Added the *rounds* parameter. Examples -------- A simple example illustrating typical use (a constant-time comparison operation is needed to limit exposure to timing attacks. :func:`hmac.compare_digest` is suitable for this purpose):: import pwd import crypt import getpass from hmac import compare_digest as compare_hash def login(): username = input('Python login: ') cryptedpasswd = pwd.getpwnam(username)[1] if cryptedpasswd: if cryptedpasswd == 'x' or cryptedpasswd == '*': raise ValueError('no support for shadow passwords') cleartext = getpass.getpass() return compare_hash(crypt.crypt(cleartext, cryptedpasswd), cryptedpasswd) else: return True To generate a hash of a password using the strongest available method and check it against the original:: import crypt from hmac import compare_digest as compare_hash hashed = crypt.crypt(plaintext) if not compare_hash(hashed, crypt.crypt(plaintext, hashed)): raise ValueError("hashed version doesn't validate against original") python-deadlib-3.13.0/crypt/LICENSE000066400000000000000000000050111471045437300166540ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/crypt/README.rst000066400000000000000000000005161471045437300173430ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/crypt/crypt/000077500000000000000000000000001471045437300170135ustar00rootroot00000000000000python-deadlib-3.13.0/crypt/crypt/__init__.py000066400000000000000000000105171471045437300211300ustar00rootroot00000000000000"""Wrapper to the POSIX crypt library call and associated functionality.""" import sys as _sys try: import _crypt except ModuleNotFoundError: if _sys.platform == 'win32': raise ImportError("The crypt module is not supported on Windows") else: # python-deadlib: Add a note for deprecated-crypt-alternative # raise ImportError("The required _crypt module was not built as part of CPython") raise ImportError( "The required _crypt module is no longer a part of CPython. " "`deprecated-crypt-alternative` can be used as a replacement.") import errno import string as _string import warnings from random import SystemRandom as _SystemRandom from collections import namedtuple as _namedtuple # python-deadlib: Replace deprecation warning not to raise exception warnings.warn( f"{__name__} was removed in Python 3.13. " f"Please be aware that you are currently NOT using standard '{__name__}', " f"but instead a separately installed 'standard-{__name__}'.", DeprecationWarning, stacklevel=2 ) _saltchars = _string.ascii_letters + _string.digits + './' _sr = _SystemRandom() class _Method(_namedtuple('_Method', 'name ident salt_chars total_size')): """Class representing a salt method per the Modular Crypt Format or the legacy 2-character crypt method.""" def __repr__(self): return ''.format(self.name) def mksalt(method=None, *, rounds=None): """Generate a salt for the specified method. If not specified, the strongest available method will be used. """ if method is None: method = methods[0] if rounds is not None and not isinstance(rounds, int): raise TypeError(f'{rounds.__class__.__name__} object cannot be ' f'interpreted as an integer') if not method.ident: # traditional s = '' else: # modular s = f'${method.ident}$' if method.ident and method.ident[0] == '2': # Blowfish variants if rounds is None: log_rounds = 12 else: log_rounds = int.bit_length(rounds-1) if rounds != 1 << log_rounds: raise ValueError('rounds must be a power of 2') if not 4 <= log_rounds <= 31: raise ValueError('rounds out of the range 2**4 to 2**31') s += f'{log_rounds:02d}$' elif method.ident in ('5', '6'): # SHA-2 if rounds is not None: if not 1000 <= rounds <= 999_999_999: raise ValueError('rounds out of the range 1000 to 999_999_999') s += f'rounds={rounds}$' elif rounds is not None: raise ValueError(f"{method} doesn't support the rounds argument") s += ''.join(_sr.choice(_saltchars) for char in range(method.salt_chars)) return s def crypt(word, salt=None): """Return a string representing the one-way hash of a password, with a salt prepended. If ``salt`` is not specified or is ``None``, the strongest available method will be selected and a salt generated. Otherwise, ``salt`` may be one of the ``crypt.METHOD_*`` values, or a string as returned by ``crypt.mksalt()``. """ if salt is None or isinstance(salt, _Method): salt = mksalt(salt) return _crypt.crypt(word, salt) # available salting/crypto methods methods = [] def _add_method(name, *args, rounds=None): method = _Method(name, *args) globals()['METHOD_' + name] = method salt = mksalt(method, rounds=rounds) result = None try: result = crypt('', salt) except OSError as e: # Not all libc libraries support all encryption methods. if e.errno in {errno.EINVAL, errno.EPERM, errno.ENOSYS}: return False raise if result and len(result) == method.total_size: methods.append(method) return True return False _add_method('SHA512', '6', 16, 106) _add_method('SHA256', '5', 16, 63) # Choose the strongest supported version of Blowfish hashing. # Early versions have flaws. Version 'a' fixes flaws of # the initial implementation, 'b' fixes flaws of 'a'. # 'y' is the same as 'b', for compatibility # with openwall crypt_blowfish. for _v in 'b', 'y', 'a', '': if _add_method('BLOWFISH', '2' + _v, 22, 59 + len(_v), rounds=1<<4): break _add_method('MD5', '1', 8, 34) _add_method('CRYPT', None, 2, 13) del _v, _add_method python-deadlib-3.13.0/crypt/pyproject.toml000066400000000000000000000014171471045437300205710ustar00rootroot00000000000000[project] name = "standard-crypt" version = "3.13.0" description = "Standard library crypt redistribution. \"dead battery\"." keywords = ["stdlib"] authors = [ { name = "Python Developers", email = "python-deadlib@youknowone.org" } ] readme = "README.rst" license = { text = "PSF-2.0" } classifiers = [ "License :: OSI Approved :: Python Software Foundation License", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", ] dependencies = [ "deprecated-crypt-alternative >= 0.1.2; python_version >= '3.13'", ] [tool.setuptools.packages] find = {include = ["crypt*"]} [project.urls] "Homepage" = "https://github.com/youknowone/python-deadlib" [build-system] requires = ["setuptools>=75.0"] build-backend = "setuptools.build_meta" python-deadlib-3.13.0/crypt/tests/000077500000000000000000000000001471045437300170145ustar00rootroot00000000000000python-deadlib-3.13.0/crypt/tests/__init__.py000066400000000000000000000000001471045437300211130ustar00rootroot00000000000000python-deadlib-3.13.0/crypt/tests/test_crypt.py000066400000000000000000000103041471045437300215640ustar00rootroot00000000000000import sys import unittest from test.support import check_sanitizer, warnings_helper try: if check_sanitizer(address=True, memory=True): raise unittest.SkipTest("The crypt module SEGFAULTs on ASAN/MSAN builds") crypt = warnings_helper.import_deprecated("crypt") IMPORT_ERROR = None except ImportError as ex: if sys.platform != 'win32': raise unittest.SkipTest(str(ex)) crypt = None IMPORT_ERROR = str(ex) @unittest.skipUnless(sys.platform == 'win32', 'This should only run on windows') @unittest.skipIf(crypt, 'import succeeded') class TestWhyCryptDidNotImport(unittest.TestCase): def test_import_failure_message(self): self.assertIn('not supported', IMPORT_ERROR) @unittest.skipUnless(crypt, 'crypt module is required') class CryptTestCase(unittest.TestCase): def test_crypt(self): cr = crypt.crypt('mypassword') cr2 = crypt.crypt('mypassword', cr) self.assertEqual(cr2, cr) cr = crypt.crypt('mypassword', 'ab') if cr is not None: cr2 = crypt.crypt('mypassword', cr) self.assertEqual(cr2, cr) def test_salt(self): self.assertEqual(len(crypt._saltchars), 64) for method in crypt.methods: salt = crypt.mksalt(method) self.assertIn(len(salt) - method.salt_chars, {0, 1, 3, 4, 6, 7}) if method.ident: self.assertIn(method.ident, salt[:len(salt)-method.salt_chars]) def test_saltedcrypt(self): for method in crypt.methods: cr = crypt.crypt('assword', method) self.assertEqual(len(cr), method.total_size) cr2 = crypt.crypt('assword', cr) self.assertEqual(cr2, cr) cr = crypt.crypt('assword', crypt.mksalt(method)) self.assertEqual(len(cr), method.total_size) def test_methods(self): self.assertTrue(len(crypt.methods) >= 1) if sys.platform.startswith('openbsd'): self.assertEqual(crypt.methods, [crypt.METHOD_BLOWFISH]) else: self.assertEqual(crypt.methods[-1], crypt.METHOD_CRYPT) @unittest.skipUnless( crypt and ( crypt.METHOD_SHA256 in crypt.methods or crypt.METHOD_SHA512 in crypt.methods ), 'requires support of SHA-2', ) def test_sha2_rounds(self): for method in (crypt.METHOD_SHA256, crypt.METHOD_SHA512): for rounds in 1000, 10_000, 100_000: salt = crypt.mksalt(method, rounds=rounds) self.assertIn('$rounds=%d$' % rounds, salt) self.assertEqual(len(salt) - method.salt_chars, 11 + len(str(rounds))) cr = crypt.crypt('mypassword', salt) self.assertTrue(cr) cr2 = crypt.crypt('mypassword', cr) self.assertEqual(cr2, cr) @unittest.skipUnless( crypt and crypt.METHOD_BLOWFISH in crypt.methods, 'requires support of Blowfish' ) def test_blowfish_rounds(self): for log_rounds in range(4, 11): salt = crypt.mksalt(crypt.METHOD_BLOWFISH, rounds=1 << log_rounds) self.assertIn('$%02d$' % log_rounds, salt) self.assertIn(len(salt) - crypt.METHOD_BLOWFISH.salt_chars, {6, 7}) cr = crypt.crypt('mypassword', salt) self.assertTrue(cr) cr2 = crypt.crypt('mypassword', cr) self.assertEqual(cr2, cr) def test_invalid_rounds(self): for method in (crypt.METHOD_SHA256, crypt.METHOD_SHA512, crypt.METHOD_BLOWFISH): with self.assertRaises(TypeError): crypt.mksalt(method, rounds='4096') with self.assertRaises(TypeError): crypt.mksalt(method, rounds=4096.0) for rounds in (0, 1, -1, 1<<999): with self.assertRaises(ValueError): crypt.mksalt(method, rounds=rounds) with self.assertRaises(ValueError): crypt.mksalt(crypt.METHOD_BLOWFISH, rounds=1000) for method in (crypt.METHOD_CRYPT, crypt.METHOD_MD5): with self.assertRaisesRegex(ValueError, 'support'): crypt.mksalt(method, rounds=4096) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/000077500000000000000000000000001471045437300165355ustar00rootroot00000000000000python-deadlib-3.13.0/distutils/Doc/000077500000000000000000000000001471045437300172425ustar00rootroot00000000000000python-deadlib-3.13.0/distutils/Doc/distutils.rst000066400000000000000000000036661471045437300220330ustar00rootroot00000000000000:mod:`distutils` --- Building and installing Python modules =========================================================== .. module:: distutils :synopsis: Support for building and installing Python modules into an existing Python installation. .. sectionauthor:: Fred L. Drake, Jr. -------------- The :mod:`distutils` package provides support for building and installing additional modules into a Python installation. The new modules may be either 100%-pure Python, or may be extension modules written in C, or may be collections of Python packages which include modules coded in both Python and C. Most Python users will *not* want to use this module directly, but instead use the cross-version tools maintained by the Python Packaging Authority. In particular, `setuptools `__ is an enhanced alternative to :mod:`distutils` that provides: * support for declaring project dependencies * additional mechanisms for configuring which files to include in source releases (including plugins for integration with version control systems) * the ability to declare project "entry points", which can be used as the basis for application plugin systems * the ability to automatically generate Windows command line executables at installation time rather than needing to prebuild them * consistent behaviour across all supported Python versions The recommended `pip `__ installer runs all ``setup.py`` scripts with ``setuptools``, even if the script itself only imports ``distutils``. Refer to the `Python Packaging User Guide `_ for more information. For the benefits of packaging tool authors and users seeking a deeper understanding of the details of the current packaging and distribution system, the legacy :mod:`distutils` based user documentation and API reference remain available: * :ref:`install-index` * :ref:`distutils-index` python-deadlib-3.13.0/distutils/LICENSE000066400000000000000000000050111471045437300175370ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/distutils/README.rst000066400000000000000000000005161471045437300202260ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/distutils/distutils/000077500000000000000000000000001471045437300205615ustar00rootroot00000000000000python-deadlib-3.13.0/distutils/distutils/README000066400000000000000000000003641471045437300214440ustar00rootroot00000000000000This directory contains the Distutils package. There's a full documentation available at: https://docs.python.org/distutils/ The Distutils-SIG web page is also a good starting point: https://www.python.org/sigs/distutils-sig/ $Id$ python-deadlib-3.13.0/distutils/distutils/__init__.py000066400000000000000000000010351471045437300226710ustar00rootroot00000000000000"""distutils The main package for the Python Module Distribution Utilities. Normally used from a setup script as from distutils.core import setup setup (...) """ import sys import warnings __version__ = sys.version[:sys.version.index(' ')] _DEPRECATION_MESSAGE = ("The distutils package is deprecated and slated for " "removal in Python 3.12. Use setuptools or check " "PEP 632 for potential alternatives") warnings.warn(_DEPRECATION_MESSAGE, DeprecationWarning, 2) python-deadlib-3.13.0/distutils/distutils/_msvccompiler.py000066400000000000000000000470471471045437300240110ustar00rootroot00000000000000"""distutils._msvccompiler Contains MSVCCompiler, an implementation of the abstract CCompiler class for Microsoft Visual Studio 2015. The module is compatible with VS 2015 and later. You can find legacy support for older versions in distutils.msvc9compiler and distutils.msvccompiler. """ # Written by Perry Stoll # hacked by Robin Becker and Thomas Heller to do a better job of # finding DevStudio (through the registry) # ported to VS 2005 and VS 2008 by Christian Heimes # ported to VS 2015 by Steve Dower import os import subprocess import winreg from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log from distutils.util import get_platform from itertools import count def _find_vc2015(): try: key = winreg.OpenKeyEx( winreg.HKEY_LOCAL_MACHINE, r"Software\Microsoft\VisualStudio\SxS\VC7", access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY ) except OSError: log.debug("Visual C++ is not registered") return None, None best_version = 0 best_dir = None with key: for i in count(): try: v, vc_dir, vt = winreg.EnumValue(key, i) except OSError: break if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir): try: version = int(float(v)) except (ValueError, TypeError): continue if version >= 14 and version > best_version: best_version, best_dir = version, vc_dir return best_version, best_dir def _find_vc2017(): """Returns "15, path" based on the result of invoking vswhere.exe If no install is found, returns "None, None" The version is returned to avoid unnecessarily changing the function result. It may be ignored when the path is not None. If vswhere.exe is not available, by definition, VS 2017 is not installed. """ root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") if not root: return None, None try: path = subprocess.check_output([ os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), "-latest", "-prerelease", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "-property", "installationPath", "-products", "*", ], encoding="mbcs", errors="strict").strip() except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): return None, None path = os.path.join(path, "VC", "Auxiliary", "Build") if os.path.isdir(path): return 15, path return None, None PLAT_SPEC_TO_RUNTIME = { 'x86' : 'x86', 'x86_amd64' : 'x64', 'x86_arm' : 'arm', 'x86_arm64' : 'arm64' } def _find_vcvarsall(plat_spec): # bpo-38597: Removed vcruntime return value _, best_dir = _find_vc2017() if not best_dir: best_version, best_dir = _find_vc2015() if not best_dir: log.debug("No suitable Visual C++ version found") return None, None vcvarsall = os.path.join(best_dir, "vcvarsall.bat") if not os.path.isfile(vcvarsall): log.debug("%s cannot be found", vcvarsall) return None, None return vcvarsall, None def _get_vc_env(plat_spec): if os.getenv("DISTUTILS_USE_SDK"): return { key.lower(): value for key, value in os.environ.items() } vcvarsall, _ = _find_vcvarsall(plat_spec) if not vcvarsall: raise DistutilsPlatformError("Unable to find vcvarsall.bat") try: out = subprocess.check_output( 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec), stderr=subprocess.STDOUT, ).decode('utf-16le', errors='replace') except subprocess.CalledProcessError as exc: log.error(exc.output) raise DistutilsPlatformError("Error executing {}" .format(exc.cmd)) env = { key.lower(): value for key, _, value in (line.partition('=') for line in out.splitlines()) if key and value } return env def _find_exe(exe, paths=None): """Return path to an MSVC executable program. Tries to find the program in several places: first, one of the MSVC program search paths from the registry; next, the directories in the PATH environment variable. If any of those work, return an absolute path that is known to exist. If none of them work, just return the original program name, 'exe'. """ if not paths: paths = os.getenv('path').split(os.pathsep) for p in paths: fn = os.path.join(os.path.abspath(p), exe) if os.path.isfile(fn): return fn return exe # A map keyed by get_platform() return values to values accepted by # 'vcvarsall.bat'. Always cross-compile from x86 to work with the # lighter-weight MSVC installs that do not include native 64-bit tools. PLAT_TO_VCVARS = { 'win32' : 'x86', 'win-amd64' : 'x86_amd64', 'win-arm32' : 'x86_arm', 'win-arm64' : 'x86_arm64' } class MSVCCompiler(CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, as defined by the CCompiler abstract class.""" compiler_type = 'msvc' # Just set this so CCompiler's constructor doesn't barf. We currently # don't use the 'set_executables()' bureaucracy provided by CCompiler, # as it really isn't necessary for this sort of single-compiler class. # Would be nice to have a consistent interface with UnixCCompiler, # though, so it's worth thinking about. executables = {} # Private class data (need to distinguish C from C++ source for compiler) _c_extensions = ['.c'] _cpp_extensions = ['.cc', '.cpp', '.cxx'] _rc_extensions = ['.rc'] _mc_extensions = ['.mc'] # Needed for the filename generation methods provided by the # base class, CCompiler. src_extensions = (_c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions) res_extension = '.res' obj_extension = '.obj' static_lib_extension = '.lib' shared_lib_extension = '.dll' static_lib_format = shared_lib_format = '%s%s' exe_extension = '.exe' def __init__(self, verbose=0, dry_run=0, force=0): CCompiler.__init__ (self, verbose, dry_run, force) # target platform (.plat_name is consistent with 'bdist') self.plat_name = None self.initialized = False def initialize(self, plat_name=None): # multi-init means we would need to check platform same each time... assert not self.initialized, "don't init multiple times" if plat_name is None: plat_name = get_platform() # sanity check for platforms to prevent obscure errors later. if plat_name not in PLAT_TO_VCVARS: raise DistutilsPlatformError("--plat-name must be one of {}" .format(tuple(PLAT_TO_VCVARS))) # Get the vcvarsall.bat spec for the requested platform. plat_spec = PLAT_TO_VCVARS[plat_name] vc_env = _get_vc_env(plat_spec) if not vc_env: raise DistutilsPlatformError("Unable to find a compatible " "Visual Studio installation.") self._paths = vc_env.get('path', '') paths = self._paths.split(os.pathsep) self.cc = _find_exe("cl.exe", paths) self.linker = _find_exe("link.exe", paths) self.lib = _find_exe("lib.exe", paths) self.rc = _find_exe("rc.exe", paths) # resource compiler self.mc = _find_exe("mc.exe", paths) # message compiler self.mt = _find_exe("mt.exe", paths) # message compiler for dir in vc_env.get('include', '').split(os.pathsep): if dir: self.add_include_dir(dir.rstrip(os.sep)) for dir in vc_env.get('lib', '').split(os.pathsep): if dir: self.add_library_dir(dir.rstrip(os.sep)) self.preprocess_options = None # bpo-38597: Always compile with dynamic linking # Future releases of Python 3.x will include all past # versions of vcruntime*.dll for compatibility. self.compile_options = [ '/nologo', '/Ox', '/W3', '/GL', '/DNDEBUG', '/MD' ] self.compile_options_debug = [ '/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG' ] ldflags = [ '/nologo', '/INCREMENTAL:NO', '/LTCG' ] ldflags_debug = [ '/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL' ] self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1'] self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1'] self.ldflags_shared = [*ldflags, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] self.ldflags_shared_debug = [*ldflags_debug, '/DLL', '/MANIFEST:EMBED,ID=2', '/MANIFESTUAC:NO'] self.ldflags_static = [*ldflags] self.ldflags_static_debug = [*ldflags_debug] self._ldflags = { (CCompiler.EXECUTABLE, None): self.ldflags_exe, (CCompiler.EXECUTABLE, False): self.ldflags_exe, (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug, (CCompiler.SHARED_OBJECT, None): self.ldflags_shared, (CCompiler.SHARED_OBJECT, False): self.ldflags_shared, (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug, (CCompiler.SHARED_LIBRARY, None): self.ldflags_static, (CCompiler.SHARED_LIBRARY, False): self.ldflags_static, (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug, } self.initialized = True # -- Worker methods ------------------------------------------------ def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): ext_map = { **{ext: self.obj_extension for ext in self.src_extensions}, **{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions}, } output_dir = output_dir or '' def make_out_path(p): base, ext = os.path.splitext(p) if strip_dir: base = os.path.basename(base) else: _, base = os.path.splitdrive(base) if base.startswith((os.path.sep, os.path.altsep)): base = base[1:] try: # XXX: This may produce absurdly long paths. We should check # the length of the result and trim base until we fit within # 260 characters. return os.path.join(output_dir, base + ext_map[ext]) except LookupError: # Better to raise an exception instead of silently continuing # and later complain about sources and targets having # different lengths raise CompileError("Don't know how to compile {}".format(p)) return list(map(make_out_path, source_filenames)) def compile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): if not self.initialized: self.initialize() compile_info = self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) macros, objects, extra_postargs, pp_opts, build = compile_info compile_opts = extra_preargs or [] compile_opts.append('/c') if debug: compile_opts.extend(self.compile_options_debug) else: compile_opts.extend(self.compile_options) add_cpp_opts = False for obj in objects: try: src, ext = build[obj] except KeyError: continue if debug: # pass the full pathname to MSVC in debug mode, # this allows the debugger to find the source file # without asking the user to browse for it src = os.path.abspath(src) if ext in self._c_extensions: input_opt = "/Tc" + src elif ext in self._cpp_extensions: input_opt = "/Tp" + src add_cpp_opts = True elif ext in self._rc_extensions: # compile .RC to .RES file input_opt = src output_opt = "/fo" + obj try: self.spawn([self.rc] + pp_opts + [output_opt, input_opt]) except DistutilsExecError as msg: raise CompileError(msg) continue elif ext in self._mc_extensions: # Compile .MC to .RC file to .RES file. # * '-h dir' specifies the directory for the # generated include file # * '-r dir' specifies the target directory of the # generated RC file and the binary message resource # it includes # # For now (since there are no options to change this), # we use the source-directory for the include file and # the build directory for the RC file and message # resources. This works at least for win32all. h_dir = os.path.dirname(src) rc_dir = os.path.dirname(obj) try: # first compile .MC to .RC and .H file self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src]) base, _ = os.path.splitext(os.path.basename (src)) rc_file = os.path.join(rc_dir, base + '.rc') # then compile .RC to .RES file self.spawn([self.rc, "/fo" + obj, rc_file]) except DistutilsExecError as msg: raise CompileError(msg) continue else: # how to handle this file? raise CompileError("Don't know how to compile {} to {}" .format(src, obj)) args = [self.cc] + compile_opts + pp_opts if add_cpp_opts: args.append('/EHsc') args.append(input_opt) args.append("/Fo" + obj) args.extend(extra_postargs) try: self.spawn(args) except DistutilsExecError as msg: raise CompileError(msg) return objects def create_static_lib(self, objects, output_libname, output_dir=None, debug=0, target_lang=None): if not self.initialized: self.initialize() objects, output_dir = self._fix_object_args(objects, output_dir) output_filename = self.library_filename(output_libname, output_dir=output_dir) if self._need_link(objects, output_filename): lib_args = objects + ['/OUT:' + output_filename] if debug: pass # XXX what goes here? try: log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args)) self.spawn([self.lib] + lib_args) except DistutilsExecError as msg: raise LibError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) def link(self, 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): if not self.initialized: self.initialize() objects, output_dir = self._fix_object_args(objects, output_dir) fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) libraries, library_dirs, runtime_library_dirs = fixed_args if runtime_library_dirs: self.warn("I don't know what to do with 'runtime_library_dirs': " + str(runtime_library_dirs)) lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) if output_dir is not None: output_filename = os.path.join(output_dir, output_filename) if self._need_link(objects, output_filename): ldflags = self._ldflags[target_desc, debug] export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])] ld_args = (ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]) # The MSVC linker generates .lib and .exp files, which cannot be # suppressed by any linker switches. The .lib files may even be # needed! Make sure they are generated in the temporary build # directory. Since they have different names for debug and release # builds, they can go into the same directory. build_temp = os.path.dirname(objects[0]) if export_symbols is not None: (dll_name, dll_ext) = os.path.splitext( os.path.basename(output_filename)) implib_file = os.path.join( build_temp, self.library_filename(dll_name)) ld_args.append ('/IMPLIB:' + implib_file) if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: ld_args.extend(extra_postargs) output_dir = os.path.dirname(os.path.abspath(output_filename)) self.mkpath(output_dir) try: log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args)) self.spawn([self.linker] + ld_args) except DistutilsExecError as msg: raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) def spawn(self, cmd): old_path = os.getenv('path') try: os.environ['path'] = self._paths return super().spawn(cmd) finally: os.environ['path'] = old_path # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in # ccompiler.py. def library_dir_option(self, dir): return "/LIBPATH:" + dir def runtime_library_dir_option(self, dir): raise DistutilsPlatformError( "don't know how to set runtime library search path for MSVC") def library_option(self, lib): return self.library_filename(lib) def find_library_file(self, dirs, lib, debug=0): # Prefer a debugging library if found (and requested), but deal # with it if we don't have one. if debug: try_names = [lib + "_d", lib] else: try_names = [lib] for dir in dirs: for name in try_names: libfile = os.path.join(dir, self.library_filename(name)) if os.path.isfile(libfile): return libfile else: # Oops, didn't find it in *any* of 'dirs' return None python-deadlib-3.13.0/distutils/distutils/archive_util.py000066400000000000000000000205741471045437300236210ustar00rootroot00000000000000"""distutils.archive_util Utility functions for creating archive files (tarballs, zip files, that sort of thing).""" import os from warnings import warn import sys try: import zipfile except ImportError: zipfile = None from distutils.errors import DistutilsExecError from distutils.spawn import spawn from distutils.dir_util import mkpath from distutils import log try: from pwd import getpwnam except ImportError: getpwnam = None try: from grp import getgrnam except ImportError: getgrnam = None def _get_gid(name): """Returns a gid, given a group name.""" if getgrnam is None or name is None: return None try: result = getgrnam(name) except KeyError: result = None if result is not None: return result[2] return None def _get_uid(name): """Returns an uid, given a user name.""" if getpwnam is None or name is None: return None try: result = getpwnam(name) except KeyError: result = None if result is not None: return result[2] return None def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0, owner=None, group=None): """Create a (possibly compressed) tar file from all the files under 'base_dir'. 'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or None. ("compress" will be deprecated in Python 3.2) 'owner' and 'group' can be used to define an owner and a group for the archive that is being built. If not provided, the current owner and group will be used. The output tar file will be named 'base_dir' + ".tar", possibly plus the appropriate compression extension (".gz", ".bz2", ".xz" or ".Z"). Returns the output filename. """ tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', 'xz': 'xz', None: '', 'compress': ''} compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', 'compress': '.Z'} # flags for compression program, each element of list will be an argument if compress is not None and compress not in compress_ext.keys(): raise ValueError( "bad value for 'compress': must be None, 'gzip', 'bzip2', " "'xz' or 'compress'") archive_name = base_name + '.tar' if compress != 'compress': archive_name += compress_ext.get(compress, '') mkpath(os.path.dirname(archive_name), dry_run=dry_run) # creating the tarball import tarfile # late import so Python build itself doesn't break log.info('Creating tar archive') uid = _get_uid(owner) gid = _get_gid(group) def _set_uid_gid(tarinfo): if gid is not None: tarinfo.gid = gid tarinfo.gname = group if uid is not None: tarinfo.uid = uid tarinfo.uname = owner return tarinfo if not dry_run: tar = tarfile.open(archive_name, 'w|%s' % tar_compression[compress]) try: tar.add(base_dir, filter=_set_uid_gid) finally: tar.close() # compression using `compress` if compress == 'compress': warn("'compress' will be deprecated.", PendingDeprecationWarning) # the option varies depending on the platform compressed_name = archive_name + compress_ext[compress] if sys.platform == 'win32': cmd = [compress, archive_name, compressed_name] else: cmd = [compress, '-f', archive_name] spawn(cmd, dry_run=dry_run) return compressed_name return archive_name def make_zipfile(base_name, base_dir, verbose=0, dry_run=0): """Create a zip file from all the files under 'base_dir'. The output zip file will be named 'base_name' + ".zip". Uses either the "zipfile" Python module (if available) or the InfoZIP "zip" utility (if installed and found on the default search path). If neither tool is available, raises DistutilsExecError. Returns the name of the output zip file. """ zip_filename = base_name + ".zip" mkpath(os.path.dirname(zip_filename), dry_run=dry_run) # If zipfile module is not available, try spawning an external # 'zip' command. if zipfile is None: if verbose: zipoptions = "-r" else: zipoptions = "-rq" try: spawn(["zip", zipoptions, zip_filename, base_dir], dry_run=dry_run) except DistutilsExecError: # XXX really should distinguish between "couldn't find # external 'zip' command" and "zip failed". raise DistutilsExecError(("unable to create zip file '%s': " "could neither import the 'zipfile' module nor " "find a standalone zip utility") % zip_filename) else: log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir) if not dry_run: try: zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_DEFLATED) except RuntimeError: zip = zipfile.ZipFile(zip_filename, "w", compression=zipfile.ZIP_STORED) with zip: if base_dir != os.curdir: path = os.path.normpath(os.path.join(base_dir, '')) zip.write(path, path) log.info("adding '%s'", path) for dirpath, dirnames, filenames in os.walk(base_dir): for name in dirnames: path = os.path.normpath(os.path.join(dirpath, name, '')) zip.write(path, path) log.info("adding '%s'", path) for name in filenames: path = os.path.normpath(os.path.join(dirpath, name)) if os.path.isfile(path): zip.write(path, path) log.info("adding '%s'", path) return zip_filename ARCHIVE_FORMATS = { 'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"), 'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"), 'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"), 'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"), 'tar': (make_tarball, [('compress', None)], "uncompressed tar file"), 'zip': (make_zipfile, [],"ZIP file") } def check_archive_formats(formats): """Returns the first format from the 'format' list that is unknown. If all formats are known, returns None """ for format in formats: if format not in ARCHIVE_FORMATS: return format return None def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0, dry_run=0, owner=None, group=None): """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. 'owner' and 'group' are used when creating a tar archive. By default, uses the current owner and group. """ save_cwd = os.getcwd() if root_dir is not None: log.debug("changing into '%s'", root_dir) base_name = os.path.abspath(base_name) if not dry_run: os.chdir(root_dir) if base_dir is None: base_dir = os.curdir kwargs = {'dry_run': dry_run} try: format_info = ARCHIVE_FORMATS[format] except KeyError: raise ValueError("unknown archive format '%s'" % format) func = format_info[0] for arg, val in format_info[1]: kwargs[arg] = val if format != 'zip': kwargs['owner'] = owner kwargs['group'] = group try: filename = func(base_name, base_dir, **kwargs) finally: if root_dir is not None: log.debug("changing back to '%s'", save_cwd) os.chdir(save_cwd) return filename python-deadlib-3.13.0/distutils/distutils/bcppcompiler.py000066400000000000000000000350561471045437300236230ustar00rootroot00000000000000"""distutils.bcppcompiler Contains BorlandCCompiler, an implementation of the abstract CCompiler class for the Borland C++ compiler. """ # This implementation by Lyle Johnson, based on the original msvccompiler.py # module and using the directions originally published by Gordon Williams. # XXX looks like there's a LOT of overlap between these two classes: # someone should sit down and factor out the common code as # WindowsCCompiler! --GPW import os from distutils.errors import \ DistutilsExecError, \ CompileError, LibError, LinkError, UnknownFileError from distutils.ccompiler import \ CCompiler, gen_preprocess_options from distutils.file_util import write_file from distutils.dep_util import newer from distutils import log class BCPPCompiler(CCompiler) : """Concrete class that implements an interface to the Borland C/C++ compiler, as defined by the CCompiler abstract class. """ compiler_type = 'bcpp' # Just set this so CCompiler's constructor doesn't barf. We currently # don't use the 'set_executables()' bureaucracy provided by CCompiler, # as it really isn't necessary for this sort of single-compiler class. # Would be nice to have a consistent interface with UnixCCompiler, # though, so it's worth thinking about. executables = {} # Private class data (need to distinguish C from C++ source for compiler) _c_extensions = ['.c'] _cpp_extensions = ['.cc', '.cpp', '.cxx'] # Needed for the filename generation methods provided by the # base class, CCompiler. src_extensions = _c_extensions + _cpp_extensions obj_extension = '.obj' static_lib_extension = '.lib' shared_lib_extension = '.dll' static_lib_format = shared_lib_format = '%s%s' exe_extension = '.exe' def __init__ (self, verbose=0, dry_run=0, force=0): CCompiler.__init__ (self, verbose, dry_run, force) # These executables are assumed to all be in the path. # Borland doesn't seem to use any special registry settings to # indicate their installation locations. self.cc = "bcc32.exe" self.linker = "ilink32.exe" self.lib = "tlib.exe" self.preprocess_options = None self.compile_options = ['/tWM', '/O2', '/q', '/g0'] self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0'] self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x'] self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x'] self.ldflags_static = [] self.ldflags_exe = ['/Gn', '/q', '/x'] self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r'] # -- Worker methods ------------------------------------------------ def compile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): macros, objects, extra_postargs, pp_opts, build = \ self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) compile_opts = extra_preargs or [] compile_opts.append ('-c') if debug: compile_opts.extend (self.compile_options_debug) else: compile_opts.extend (self.compile_options) for obj in objects: try: src, ext = build[obj] except KeyError: continue # XXX why do the normpath here? src = os.path.normpath(src) obj = os.path.normpath(obj) # XXX _setup_compile() did a mkpath() too but before the normpath. # Is it possible to skip the normpath? self.mkpath(os.path.dirname(obj)) if ext == '.res': # This is already a binary file -- skip it. continue # the 'for' loop if ext == '.rc': # This needs to be compiled to a .res file -- do it now. try: self.spawn (["brcc32", "-fo", obj, src]) except DistutilsExecError as msg: raise CompileError(msg) continue # the 'for' loop # The next two are both for the real compiler. if ext in self._c_extensions: input_opt = "" elif ext in self._cpp_extensions: input_opt = "-P" else: # Unknown file type -- no extra options. The compiler # will probably fail, but let it just in case this is a # file the compiler recognizes even if we don't. input_opt = "" output_opt = "-o" + obj # Compiler command line syntax is: "bcc32 [options] file(s)". # Note that the source file names must appear at the end of # the command line. try: self.spawn ([self.cc] + compile_opts + pp_opts + [input_opt, output_opt] + extra_postargs + [src]) except DistutilsExecError as msg: raise CompileError(msg) return objects # compile () def create_static_lib (self, objects, output_libname, output_dir=None, debug=0, target_lang=None): (objects, output_dir) = self._fix_object_args (objects, output_dir) output_filename = \ self.library_filename (output_libname, output_dir=output_dir) if self._need_link (objects, output_filename): lib_args = [output_filename, '/u'] + objects if debug: pass # XXX what goes here? try: self.spawn ([self.lib] + lib_args) except DistutilsExecError as msg: raise LibError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) # create_static_lib () def link (self, 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): # XXX this ignores 'build_temp'! should follow the lead of # msvccompiler.py (objects, output_dir) = self._fix_object_args (objects, output_dir) (libraries, library_dirs, runtime_library_dirs) = \ self._fix_lib_args (libraries, library_dirs, runtime_library_dirs) if runtime_library_dirs: log.warn("I don't know what to do with 'runtime_library_dirs': %s", str(runtime_library_dirs)) if output_dir is not None: output_filename = os.path.join (output_dir, output_filename) if self._need_link (objects, output_filename): # Figure out linker args based on type of target. if target_desc == CCompiler.EXECUTABLE: startup_obj = 'c0w32' if debug: ld_args = self.ldflags_exe_debug[:] else: ld_args = self.ldflags_exe[:] else: startup_obj = 'c0d32' if debug: ld_args = self.ldflags_shared_debug[:] else: ld_args = self.ldflags_shared[:] # Create a temporary exports file for use by the linker if export_symbols is None: def_file = '' else: head, tail = os.path.split (output_filename) modname, ext = os.path.splitext (tail) temp_dir = os.path.dirname(objects[0]) # preserve tree structure def_file = os.path.join (temp_dir, '%s.def' % modname) contents = ['EXPORTS'] for sym in (export_symbols or []): contents.append(' %s=_%s' % (sym, sym)) self.execute(write_file, (def_file, contents), "writing %s" % def_file) # Borland C++ has problems with '/' in paths objects2 = map(os.path.normpath, objects) # split objects in .obj and .res files # Borland C++ needs them at different positions in the command line objects = [startup_obj] resources = [] for file in objects2: (base, ext) = os.path.splitext(os.path.normcase(file)) if ext == '.res': resources.append(file) else: objects.append(file) for l in library_dirs: ld_args.append("/L%s" % os.path.normpath(l)) ld_args.append("/L.") # we sometimes use relative paths # list of object files ld_args.extend(objects) # XXX the command-line syntax for Borland C++ is a bit wonky; # certain filenames are jammed together in one big string, but # comma-delimited. This doesn't mesh too well with the # Unix-centric attitude (with a DOS/Windows quoting hack) of # 'spawn()', so constructing the argument list is a bit # awkward. Note that doing the obvious thing and jamming all # the filenames and commas into one argument would be wrong, # because 'spawn()' would quote any filenames with spaces in # them. Arghghh!. Apparently it works fine as coded... # name of dll/exe file ld_args.extend([',',output_filename]) # no map file and start libraries ld_args.append(',,') for lib in libraries: # see if we find it and if there is a bcpp specific lib # (xxx_bcpp.lib) libfile = self.find_library_file(library_dirs, lib, debug) if libfile is None: ld_args.append(lib) # probably a BCPP internal library -- don't warn else: # full name which prefers bcpp_xxx.lib over xxx.lib ld_args.append(libfile) # some default libraries ld_args.append ('import32') ld_args.append ('cw32mt') # def file for export symbols ld_args.extend([',',def_file]) # add resource files ld_args.append(',') ld_args.extend(resources) if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: ld_args.extend(extra_postargs) self.mkpath (os.path.dirname (output_filename)) try: self.spawn ([self.linker] + ld_args) except DistutilsExecError as msg: raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) # link () # -- Miscellaneous methods ----------------------------------------- def find_library_file (self, dirs, lib, debug=0): # List of effective library names to try, in order of preference: # xxx_bcpp.lib is better than xxx.lib # and xxx_d.lib is better than xxx.lib if debug is set # # The "_bcpp" suffix is to handle a Python installation for people # with multiple compilers (primarily Distutils hackers, I suspect # ;-). The idea is they'd have one static library for each # compiler they care about, since (almost?) every Windows compiler # seems to have a different format for static libraries. if debug: dlib = (lib + "_d") try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib) else: try_names = (lib + "_bcpp", lib) for dir in dirs: for name in try_names: libfile = os.path.join(dir, self.library_filename(name)) if os.path.exists(libfile): return libfile else: # Oops, didn't find it in *any* of 'dirs' return None # overwrite the one from CCompiler to support rc and res-files def object_filenames (self, source_filenames, strip_dir=0, output_dir=''): if output_dir is None: output_dir = '' obj_names = [] for src_name in source_filenames: # use normcase to make sure '.rc' is really '.rc' and not '.RC' (base, ext) = os.path.splitext (os.path.normcase(src_name)) if ext not in (self.src_extensions + ['.rc','.res']): raise UnknownFileError("unknown file type '%s' (from '%s')" % \ (ext, src_name)) if strip_dir: base = os.path.basename (base) if ext == '.res': # these can go unchanged obj_names.append (os.path.join (output_dir, base + ext)) elif ext == '.rc': # these need to be compiled to .res-files obj_names.append (os.path.join (output_dir, base + '.res')) else: obj_names.append (os.path.join (output_dir, base + self.obj_extension)) return obj_names # object_filenames () def preprocess (self, source, output_file=None, macros=None, include_dirs=None, extra_preargs=None, extra_postargs=None): (_, macros, include_dirs) = \ self._fix_compile_args(None, macros, include_dirs) pp_opts = gen_preprocess_options(macros, include_dirs) pp_args = ['cpp32.exe'] + pp_opts if output_file is not None: pp_args.append('-o' + output_file) if extra_preargs: pp_args[:0] = extra_preargs if extra_postargs: pp_args.extend(extra_postargs) pp_args.append(source) # We need to preprocess: either we're being forced to, or the # source file is newer than the target (or the target doesn't # exist). if self.force or output_file is None or newer(source, output_file): if output_file: self.mkpath(os.path.dirname(output_file)) try: self.spawn(pp_args) except DistutilsExecError as msg: print(msg) raise CompileError(msg) # preprocess() python-deadlib-3.13.0/distutils/distutils/ccompiler.py000066400000000000000000001344721471045437300231230ustar00rootroot00000000000000"""distutils.ccompiler Contains CCompiler, an abstract base class that defines the interface for the Distutils compiler abstraction model.""" import sys, os, re from distutils.errors import * from distutils.spawn import spawn from distutils.file_util import move_file from distutils.dir_util import mkpath from distutils.dep_util import newer_group from distutils.util import split_quoted, execute from distutils import log class CCompiler: """Abstract base class to define the interface that must be implemented by real compiler classes. 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. """ # 'compiler_type' is a class attribute that identifies this class. It # keeps code that wants to know what kind of compiler it's dealing with # from having to import all possible compiler classes just to do an # 'isinstance'. In concrete CCompiler subclasses, 'compiler_type' # should really, really be one of the keys of the 'compiler_class' # dictionary (see below -- used by the 'new_compiler()' factory # function) -- authors of new compiler interface classes are # responsible for updating 'compiler_class'! compiler_type = None # XXX things not handled by this compiler abstraction model: # * client can't provide additional options for a compiler, # e.g. warning, optimization, debugging flags. Perhaps this # should be the domain of concrete compiler abstraction classes # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base # class should have methods for the common ones. # * can't completely override the include or library searchg # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2". # I'm not sure how widely supported this is even by Unix # compilers, much less on other platforms. And I'm even less # sure how useful it is; maybe for cross-compiling, but # support for that is a ways off. (And anyways, cross # compilers probably have a dedicated binary with the # right paths compiled in. I hope.) # * can't do really freaky things with the library list/library # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against # different versions of libfoo.a in different locations. I # think this is useless without the ability to null out the # library search path anyways. # Subclasses that rely on the standard filename generation methods # implemented below should override these; see the comment near # those methods ('object_filenames()' et. al.) for details: src_extensions = None # list of strings obj_extension = None # string static_lib_extension = None shared_lib_extension = None # string static_lib_format = None # format string shared_lib_format = None # prob. same as static_lib_format exe_extension = None # string # Default language settings. language_map is used to detect a source # file or Extension target language, checking source filenames. # language_order is used to detect the language precedence, when deciding # what language to use when mixing source types. For example, if some # extension has two files with ".c" extension, and one with ".cpp", it # is still linked as c++. language_map = {".c" : "c", ".cc" : "c++", ".cpp" : "c++", ".cxx" : "c++", ".m" : "objc", } language_order = ["c++", "objc", "c"] def __init__(self, verbose=0, dry_run=0, force=0): self.dry_run = dry_run self.force = force self.verbose = verbose # 'output_dir': a common output directory for object, library, # shared object, and shared library files self.output_dir = None # 'macros': a list of macro definitions (or undefinitions). A # macro definition is a 2-tuple (name, value), where the value is # either a string or None (no explicit value). A macro # undefinition is a 1-tuple (name,). self.macros = [] # 'include_dirs': a list of directories to search for include files self.include_dirs = [] # 'libraries': a list of libraries to include in any link # (library names, not filenames: eg. "foo" not "libfoo.a") self.libraries = [] # 'library_dirs': a list of directories to search for libraries self.library_dirs = [] # 'runtime_library_dirs': a list of directories to search for # shared libraries/objects at runtime self.runtime_library_dirs = [] # 'objects': a list of object files (or similar, such as explicitly # named library files) to include on any link self.objects = [] for key in self.executables.keys(): self.set_executable(key, self.executables[key]) def set_executables(self, **kwargs): """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: 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 'distutils.util.split_quoted()'.) """ # Note that some CCompiler implementation classes will define class # attributes 'cpp', 'cc', etc. with hard-coded executable names; # this is appropriate when a compiler class is for exactly one # compiler/OS combination (eg. MSVCCompiler). Other compiler # classes (UnixCCompiler, in particular) are driven by information # discovered at run-time, since there are many different ways to do # basically the same things with Unix C compilers. for key in kwargs: if key not in self.executables: raise ValueError("unknown executable '%s' for class %s" % (key, self.__class__.__name__)) self.set_executable(key, kwargs[key]) def set_executable(self, key, value): if isinstance(value, str): setattr(self, key, split_quoted(value)) else: setattr(self, key, value) def _find_macro(self, name): i = 0 for defn in self.macros: if defn[0] == name: return i i += 1 return None def _check_macro_definitions(self, definitions): """Ensures that every element of 'definitions' is a valid macro definition, ie. either (name,value) 2-tuple or a (name,) tuple. Do nothing if all definitions are OK, raise TypeError otherwise. """ for defn in definitions: if not (isinstance(defn, tuple) and (len(defn) in (1, 2) and (isinstance (defn[1], str) or defn[1] is None)) and isinstance (defn[0], str)): raise TypeError(("invalid macro definition '%s': " % defn) + \ "must be tuple (string,), (string, string), or " + \ "(string, None)") # -- Bookkeeping methods ------------------------------------------- def define_macro(self, 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?) """ # Delete from the list of macro definitions/undefinitions if # already there (so that this one will take precedence). i = self._find_macro (name) if i is not None: del self.macros[i] self.macros.append((name, value)) def undefine_macro(self, name): """Undefine a preprocessor macro for all compilations driven by this compiler object. If the same macro is defined by 'define_macro()' and undefined by '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 'compile()'), then that takes precedence. """ # Delete from the list of macro definitions/undefinitions if # already there (so that this one will take precedence). i = self._find_macro (name) if i is not None: del self.macros[i] undefn = (name,) self.macros.append(undefn) def add_include_dir(self, 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 'add_include_dir()'. """ self.include_dirs.append(dir) def set_include_dirs(self, dirs): """Set the list of directories that will be searched to 'dirs' (a list of strings). Overrides any preceding calls to 'add_include_dir()'; subsequence calls to 'add_include_dir()' add to the list passed to 'set_include_dirs()'. This does not affect any list of standard include directories that the compiler may search by default. """ self.include_dirs = dirs[:] def add_library(self, 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 'add_library()' and/or '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. """ self.libraries.append(libname) def set_libraries(self, 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. """ self.libraries = libnames[:] def add_library_dir(self, dir): """Add 'dir' to the list of directories that will be searched for libraries specified to 'add_library()' and 'set_libraries()'. The linker will be instructed to search for libraries in the order they are supplied to 'add_library_dir()' and/or 'set_library_dirs()'. """ self.library_dirs.append(dir) def set_library_dirs(self, 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. """ self.library_dirs = dirs[:] def add_runtime_library_dir(self, dir): """Add 'dir' to the list of directories that will be searched for shared libraries at runtime. """ self.runtime_library_dirs.append(dir) def set_runtime_library_dirs(self, 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. """ self.runtime_library_dirs = dirs[:] def add_link_object(self, 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. """ self.objects.append(object) def set_link_objects(self, 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). """ self.objects = objects[:] # -- Private utility methods -------------------------------------- # (here for the convenience of subclasses) # Helper method to prep compiler in subclass compile() methods def _setup_compile(self, outdir, macros, incdirs, sources, depends, extra): """Process arguments and decide which source files to compile.""" if outdir is None: outdir = self.output_dir elif not isinstance(outdir, str): raise TypeError("'output_dir' must be a string or None") if macros is None: macros = self.macros elif isinstance(macros, list): macros = macros + (self.macros or []) else: raise TypeError("'macros' (if supplied) must be a list of tuples") if incdirs is None: incdirs = self.include_dirs elif isinstance(incdirs, (list, tuple)): incdirs = list(incdirs) + (self.include_dirs or []) else: raise TypeError( "'include_dirs' (if supplied) must be a list of strings") if extra is None: extra = [] # Get the list of expected output (object) files objects = self.object_filenames(sources, strip_dir=0, output_dir=outdir) assert len(objects) == len(sources) pp_opts = gen_preprocess_options(macros, incdirs) build = {} for i in range(len(sources)): src = sources[i] obj = objects[i] ext = os.path.splitext(src)[1] self.mkpath(os.path.dirname(obj)) build[obj] = (src, ext) return macros, objects, extra, pp_opts, build def _get_cc_args(self, pp_opts, debug, before): # works for unixccompiler, cygwinccompiler cc_args = pp_opts + ['-c'] if debug: cc_args[:0] = ['-g'] if before: cc_args[:0] = before return cc_args def _fix_compile_args(self, output_dir, macros, include_dirs): """Typecheck and fix-up some of the arguments to the 'compile()' method, and return fixed-up values. Specifically: if 'output_dir' is None, replaces it with 'self.output_dir'; ensures that 'macros' is a list, and augments it with 'self.macros'; ensures that 'include_dirs' is a list, and augments it with 'self.include_dirs'. Guarantees that the returned values are of the correct type, i.e. for 'output_dir' either string or None, and for 'macros' and 'include_dirs' either list or None. """ if output_dir is None: output_dir = self.output_dir elif not isinstance(output_dir, str): raise TypeError("'output_dir' must be a string or None") if macros is None: macros = self.macros elif isinstance(macros, list): macros = macros + (self.macros or []) else: raise TypeError("'macros' (if supplied) must be a list of tuples") if include_dirs is None: include_dirs = self.include_dirs elif isinstance(include_dirs, (list, tuple)): include_dirs = list(include_dirs) + (self.include_dirs or []) else: raise TypeError( "'include_dirs' (if supplied) must be a list of strings") return output_dir, macros, include_dirs def _prep_compile(self, sources, output_dir, depends=None): """Decide which source files must be recompiled. Determine the list of object files corresponding to 'sources', and figure out which ones really need to be recompiled. Return a list of all object files and a dictionary telling which source files can be skipped. """ # Get the list of expected output (object) files objects = self.object_filenames(sources, output_dir=output_dir) assert len(objects) == len(sources) # Return an empty dict for the "which source files can be skipped" # return value to preserve API compatibility. return objects, {} def _fix_object_args(self, objects, output_dir): """Typecheck and fix up some arguments supplied to various methods. Specifically: ensure that 'objects' is a list; if output_dir is None, replace with self.output_dir. Return fixed versions of 'objects' and 'output_dir'. """ if not isinstance(objects, (list, tuple)): raise TypeError("'objects' must be a list or tuple of strings") objects = list(objects) if output_dir is None: output_dir = self.output_dir elif not isinstance(output_dir, str): raise TypeError("'output_dir' must be a string or None") return (objects, output_dir) def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs): """Typecheck and fix up some of the arguments supplied to the 'link_*' methods. Specifically: ensure that all arguments are lists, and augment them with their permanent versions (eg. 'self.libraries' augments 'libraries'). Return a tuple with fixed versions of all arguments. """ if libraries is None: libraries = self.libraries elif isinstance(libraries, (list, tuple)): libraries = list (libraries) + (self.libraries or []) else: raise TypeError( "'libraries' (if supplied) must be a list of strings") if library_dirs is None: library_dirs = self.library_dirs elif isinstance(library_dirs, (list, tuple)): library_dirs = list (library_dirs) + (self.library_dirs or []) else: raise TypeError( "'library_dirs' (if supplied) must be a list of strings") if runtime_library_dirs is None: runtime_library_dirs = self.runtime_library_dirs elif isinstance(runtime_library_dirs, (list, tuple)): runtime_library_dirs = (list(runtime_library_dirs) + (self.runtime_library_dirs or [])) else: raise TypeError("'runtime_library_dirs' (if supplied) " "must be a list of strings") return (libraries, library_dirs, runtime_library_dirs) def _need_link(self, objects, output_file): """Return true if we need to relink the files listed in 'objects' to recreate 'output_file'. """ if self.force: return True else: if self.dry_run: newer = newer_group (objects, output_file, missing='newer') else: newer = newer_group (objects, output_file) return newer def detect_language(self, sources): """Detect the language of a given file, or list of files. Uses language_map, and language_order to do the job. """ if not isinstance(sources, list): sources = [sources] lang = None index = len(self.language_order) for source in sources: base, ext = os.path.splitext(source) extlang = self.language_map.get(ext) try: extindex = self.language_order.index(extlang) if extindex < index: lang = extlang index = extindex except ValueError: pass return lang # -- Worker methods ------------------------------------------------ # (must be implemented by subclasses) def preprocess(self, 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 'compile()', which will augment the macros set with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a list of directory names that will be added to the default list. Raises PreprocessError on failure. """ pass def compile(self, 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. '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. 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, "foo/bar.c" normally compiles to "foo/bar.o" (for a Unix implementation); if 'output_dir' is "build", then it would compile to "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 CompileError on failure. """ # A concrete compiler class can either override this method # entirely or implement _compile(). macros, objects, extra_postargs, pp_opts, build = \ self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) for obj in objects: try: src, ext = build[obj] except KeyError: continue self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts) # Return *all* object filenames, not just the ones we just built. return objects def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): """Compile 'src' to product 'obj'.""" # A concrete compiler class that does not override compile() # should implement _compile(). pass def create_static_lib(self, 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 'add_link_object()' and/or 'set_link_objects()', the libraries supplied to 'add_library()' and/or '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. '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 LibError on failure. """ pass # values for target_desc parameter in link() SHARED_OBJECT = "shared_object" SHARED_LIBRARY = "shared_library" EXECUTABLE = "executable" def link(self, 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 "libfoo.a" on Unix and "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 'add_library_dir()' and/or '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 'compile()' and 'create_static_lib()', with the slight distinction that it actually matters on most platforms (as opposed to 'create_static_lib()', which includes a 'debug' flag mostly for form's sake). 'extra_preargs' and 'extra_postargs' are as for '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 LinkError on failure. """ raise NotImplementedError # Old 'link_*()' methods, rewritten to use the new 'link()' method. def link_shared_lib(self, 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): self.link(CCompiler.SHARED_LIBRARY, objects, self.library_filename(output_libname, lib_type='shared'), output_dir, libraries, library_dirs, runtime_library_dirs, export_symbols, debug, extra_preargs, extra_postargs, build_temp, target_lang) def link_shared_object(self, 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): self.link(CCompiler.SHARED_OBJECT, objects, output_filename, output_dir, libraries, library_dirs, runtime_library_dirs, export_symbols, debug, extra_preargs, extra_postargs, build_temp, target_lang) def link_executable(self, 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): self.link(CCompiler.EXECUTABLE, objects, self.executable_filename(output_progname), output_dir, libraries, library_dirs, runtime_library_dirs, None, debug, extra_preargs, extra_postargs, None, target_lang) # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function; there is # no appropriate default implementation so subclasses should # implement all of these. def library_dir_option(self, dir): """Return the compiler option to add 'dir' to the list of directories searched for libraries. """ raise NotImplementedError def runtime_library_dir_option(self, dir): """Return the compiler option to add 'dir' to the list of directories searched for runtime libraries. """ raise NotImplementedError def library_option(self, lib): """Return the compiler option to add 'lib' to the list of libraries linked into the shared library or executable. """ raise NotImplementedError def has_function(self, 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. """ # this can't be included at module scope because it tries to # import math which might not be available at that point - maybe # the necessary logic should just be inlined? import tempfile if includes is None: includes = [] if include_dirs is None: include_dirs = [] if libraries is None: libraries = [] if library_dirs is None: library_dirs = [] fd, fname = tempfile.mkstemp(".c", funcname, text=True) f = os.fdopen(fd, "w") try: for incl in includes: f.write("""#include "%s"\n""" % incl) f.write("""\ int main (int argc, char **argv) { %s(); return 0; } """ % funcname) finally: f.close() try: objects = self.compile([fname], include_dirs=include_dirs) except CompileError: return False try: self.link_executable(objects, "a.out", libraries=libraries, library_dirs=library_dirs) except (LinkError, TypeError): return False return True def find_library_file (self, 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' 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. """ raise NotImplementedError # -- Filename generation methods ----------------------------------- # The default implementation of the filename generating methods are # prejudiced towards the Unix/DOS/Windows view of the world: # * object files are named by replacing the source file extension # (eg. .c/.cpp -> .o/.obj) # * library files (shared or static) are named by plugging the # library name and extension into a format string, eg. # "lib%s.%s" % (lib_name, ".a") for Unix static libraries # * executables are named by appending an extension (possibly # empty) to the program name: eg. progname + ".exe" for # Windows # # To reduce redundant code, these methods expect to find # several attributes in the current object (presumably defined # as class attributes): # * src_extensions - # list of C/C++ source file extensions, eg. ['.c', '.cpp'] # * obj_extension - # object file extension, eg. '.o' or '.obj' # * static_lib_extension - # extension for static library files, eg. '.a' or '.lib' # * shared_lib_extension - # extension for shared library/object files, eg. '.so', '.dll' # * static_lib_format - # format string for generating static library filenames, # eg. 'lib%s.%s' or '%s.%s' # * shared_lib_format # format string for generating shared library filenames # (probably same as static_lib_format, since the extension # is one of the intended parameters to the format string) # * exe_extension - # extension for executable files, eg. '' or '.exe' def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): if output_dir is None: output_dir = '' obj_names = [] for src_name in source_filenames: base, ext = os.path.splitext(src_name) base = os.path.splitdrive(base)[1] # Chop off the drive base = base[os.path.isabs(base):] # If abs, chop off leading / if ext not in self.src_extensions: raise UnknownFileError( "unknown file type '%s' (from '%s')" % (ext, src_name)) if strip_dir: base = os.path.basename(base) obj_names.append(os.path.join(output_dir, base + self.obj_extension)) return obj_names def shared_object_filename(self, basename, strip_dir=0, output_dir=''): assert output_dir is not None if strip_dir: basename = os.path.basename(basename) return os.path.join(output_dir, basename + self.shared_lib_extension) def executable_filename(self, basename, strip_dir=0, output_dir=''): assert output_dir is not None if strip_dir: basename = os.path.basename(basename) return os.path.join(output_dir, basename + (self.exe_extension or '')) def library_filename(self, libname, lib_type='static', # or 'shared' strip_dir=0, output_dir=''): assert output_dir is not None if lib_type not in ("static", "shared", "dylib", "xcode_stub"): raise ValueError( "'lib_type' must be \"static\", \"shared\", \"dylib\", or \"xcode_stub\"") fmt = getattr(self, lib_type + "_lib_format") ext = getattr(self, lib_type + "_lib_extension") dir, base = os.path.split(libname) filename = fmt % (base, ext) if strip_dir: dir = '' return os.path.join(output_dir, dir, filename) # -- Utility methods ----------------------------------------------- def announce(self, msg, level=1): log.debug(msg) def debug_print(self, msg): from distutils.debug import DEBUG if DEBUG: print(msg) def warn(self, msg): sys.stderr.write("warning: %s\n" % msg) def execute(self, func, args, msg=None, level=1): execute(func, args, msg, self.dry_run) def spawn(self, cmd): spawn(cmd, dry_run=self.dry_run) def move_file(self, src, dst): return move_file(src, dst, dry_run=self.dry_run) def mkpath (self, name, mode=0o777): mkpath(name, mode, dry_run=self.dry_run) # Map a sys.platform/os.name ('posix', 'nt') to the default compiler # type for that platform. Keys are interpreted as re match # patterns. Order is important; platform mappings are preferred over # OS names. _default_compilers = ( # Platform string mappings # on a cygwin built python we can use gcc like an ordinary UNIXish # compiler ('cygwin.*', 'unix'), # OS name mappings ('posix', 'unix'), ('nt', 'msvc'), ) def get_default_compiler(osname=None, platform=None): """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. """ if osname is None: osname = os.name if platform is None: platform = sys.platform for pattern, compiler in _default_compilers: if re.match(pattern, platform) is not None or \ re.match(pattern, osname) is not None: return compiler # Default to Unix compiler return 'unix' # Map compiler types to (module_name, class_name) pairs -- ie. where to # find the code that implements an interface to this compiler. (The module # is assumed to be in the 'distutils' package.) compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler', "standard UNIX-style compiler"), 'msvc': ('_msvccompiler', 'MSVCCompiler', "Microsoft Visual C++"), 'cygwin': ('cygwinccompiler', 'CygwinCCompiler', "Cygwin port of GNU C Compiler for Win32"), 'mingw32': ('cygwinccompiler', 'Mingw32CCompiler', "Mingw32 port of GNU C Compiler for Win32"), 'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"), } def show_compilers(): """Print list of available compilers (used by the "--help-compiler" options to "build", "build_ext", "build_clib"). """ # XXX this "knows" that the compiler option it's describing is # "--compiler", which just happens to be the case for the three # commands that use it. from distutils.fancy_getopt import FancyGetopt compilers = [] for compiler in compiler_class.keys(): compilers.append(("compiler="+compiler, None, compiler_class[compiler][2])) compilers.sort() pretty_printer = FancyGetopt(compilers) pretty_printer.print_help("List of available compilers:") def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0): """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" (UnixCCompiler class) and Visual C++ (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. """ if plat is None: plat = os.name try: if compiler is None: compiler = get_default_compiler(plat) (module_name, class_name, long_description) = compiler_class[compiler] except KeyError: msg = "don't know how to compile C/C++ code on platform '%s'" % plat if compiler is not None: msg = msg + " with '%s' compiler" % compiler raise DistutilsPlatformError(msg) try: module_name = "distutils." + module_name __import__ (module_name) module = sys.modules[module_name] klass = vars(module)[class_name] except ImportError: raise DistutilsModuleError( "can't compile C/C++ code: unable to load module '%s'" % \ module_name) except KeyError: raise DistutilsModuleError( "can't compile C/C++ code: unable to find class '%s' " "in module '%s'" % (class_name, module_name)) # XXX The None is necessary to preserve backwards compatibility # with classes that expect verbose to be the first positional # argument. return klass(None, dry_run, force) def gen_preprocess_options(macros, include_dirs): """Generate C pre-processor options (-D, -U, -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 (-U) macro 'name', and (name,value) means define (-D) macro 'name' to 'value'. 'include_dirs' is just a list of directory names to be added to the header file search path (-I). Returns a list of command-line options suitable for either Unix compilers or Visual C++. """ # XXX it would be nice (mainly aesthetic, and so we don't generate # stupid-looking command lines) to go over 'macros' and eliminate # redundant definitions/undefinitions (ie. ensure that only the # latest mention of a particular macro winds up on the command # line). I don't think it's essential, though, since most (all?) # Unix C compilers only pay attention to the latest -D or -U # mention of a macro on their command line. Similar situation for # 'include_dirs'. I'm punting on both for now. Anyways, weeding out # redundancies like this should probably be the province of # CCompiler, since the data structures used are inherited from it # and therefore common to all CCompiler classes. pp_opts = [] for macro in macros: if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2): raise TypeError( "bad macro definition '%s': " "each element of 'macros' list must be a 1- or 2-tuple" % macro) if len(macro) == 1: # undefine this macro pp_opts.append("-U%s" % macro[0]) elif len(macro) == 2: if macro[1] is None: # define with no explicit value pp_opts.append("-D%s" % macro[0]) else: # XXX *don't* need to be clever about quoting the # macro value here, because we're going to avoid the # shell at all costs when we spawn the command! pp_opts.append("-D%s=%s" % macro) for dir in include_dirs: pp_opts.append("-I%s" % dir) return pp_opts def 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). """ lib_opts = [] for dir in library_dirs: lib_opts.append(compiler.library_dir_option(dir)) for dir in runtime_library_dirs: opt = compiler.runtime_library_dir_option(dir) if isinstance(opt, list): lib_opts = lib_opts + opt else: lib_opts.append(opt) # XXX it's important that we *not* remove redundant library mentions! # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to # resolve all symbols. I just hope we never have to say "-lfoo obj.o # -lbar" to get things to work -- that's certainly a possibility, but a # pretty nasty way to arrange your C code. for lib in libraries: (lib_dir, lib_name) = os.path.split(lib) if lib_dir: lib_file = compiler.find_library_file([lib_dir], lib_name) if lib_file: lib_opts.append(lib_file) else: compiler.warn("no library file corresponding to " "'%s' found (skipping)" % lib) else: lib_opts.append(compiler.library_option (lib)) return lib_opts python-deadlib-3.13.0/distutils/distutils/cmd.py000066400000000000000000000432371471045437300217070ustar00rootroot00000000000000"""distutils.cmd Provides the Command class, the base class for the command classes in the distutils.command package. """ import sys, os, re from distutils.errors import DistutilsOptionError from distutils import util, dir_util, file_util, archive_util, dep_util from distutils import log class Command: """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 'initialize_options()' and "defined" (given their final values, aka "finalized") in '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 'finalize_options()'. The "body" of the subroutine, where it does all its work based on the values of its options, is the 'run()' method, which must also be implemented by every command class. """ # 'sub_commands' formalizes the notion of a "family" of commands, # eg. "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 # (command_name : string, predicate : unbound_method | string | None) # tuples, where 'predicate' is a method of the parent command that # determines whether the corresponding command is applicable in the # current situation. (Eg. we "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 unbound methods, so they must already have been # defined. The canonical example is the "install" command. sub_commands = [] # -- Creation/initialization methods ------------------------------- def __init__(self, dist): """Create and initialize a new Command object. Most importantly, invokes the 'initialize_options()' method, which is the real initializer and depends on the actual command being instantiated. """ # late import because of mutual dependence between these classes from distutils.dist import Distribution if not isinstance(dist, Distribution): raise TypeError("dist must be a Distribution instance") if self.__class__ is Command: raise RuntimeError("Command is an abstract class") self.distribution = dist self.initialize_options() # Per-command versions of the global flags, so that the user can # customize Distutils' behaviour command-by-command and let some # commands fall back on the Distribution's behaviour. None means # "not defined, check self.distribution's copy", while 0 or 1 mean # false and true (duh). Note that this means figuring out the real # value of each flag is a touch complicated -- hence "self._dry_run" # will be handled by __getattr__, below. # XXX This needs to be fixed. self._dry_run = None # verbose is largely ignored, but needs to be set for # backwards compatibility (I think)? self.verbose = dist.verbose # Some commands define a 'self.force' option to ignore file # timestamps, but methods defined *here* assume that # 'self.force' exists for all commands. So define it here # just to be safe. self.force = None # The 'help' flag is just used for command-line parsing, so # none of that complicated bureaucracy is needed. self.help = 0 # 'finalized' records whether or not 'finalize_options()' has been # called. 'finalize_options()' itself should not pay attention to # this flag: it is the business of 'ensure_finalized()', which # always calls 'finalize_options()', to respect/update it. self.finalized = 0 # XXX A more explicit way to customize dry_run would be better. def __getattr__(self, attr): if attr == 'dry_run': myval = getattr(self, "_" + attr) if myval is None: return getattr(self.distribution, attr) else: return myval else: raise AttributeError(attr) def ensure_finalized(self): if not self.finalized: self.finalize_options() self.finalized = 1 # Subclasses must define: # initialize_options() # provide default values for all options; may be customized by # setup script, by options from config file(s), or by command-line # options # finalize_options() # decide on the final values for all options; this is called # after all possible intervention from the outside world # (command-line, option file, etc.) has been processed # run() # run the command: do whatever it is we're here to do, # controlled by the command's various option values def initialize_options(self): """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, 'initialize_options()' implementations are just a bunch of "self.foo = None" assignments. This method must be implemented by all command classes. """ raise RuntimeError("abstract method -- subclass %s must override" % self.__class__) def finalize_options(self): """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 'initialize_options()'. This method must be implemented by all command classes. """ raise RuntimeError("abstract method -- subclass %s must override" % self.__class__) def dump_options(self, header=None, indent=""): from distutils.fancy_getopt import longopt_xlate if header is None: header = "command options for '%s':" % self.get_command_name() self.announce(indent + header, level=log.INFO) indent = indent + " " for (option, _, _) in self.user_options: option = option.translate(longopt_xlate) if option[-1] == "=": option = option[:-1] value = getattr(self, option) self.announce(indent + "%s = %s" % (option, value), level=log.INFO) def run(self): """A command's raison d'etre: carry out the action it exists to perform, controlled by the options initialized in 'initialize_options()', customized by other commands, the setup script, the command-line, and config files, and finalized in 'finalize_options()'. All terminal output and filesystem interaction should be done by 'run()'. This method must be implemented by all command classes. """ raise RuntimeError("abstract method -- subclass %s must override" % self.__class__) def announce(self, msg, level=1): """If the current verbosity level is of greater than or equal to 'level' print 'msg' to stdout. """ log.log(level, msg) def debug_print(self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the DISTUTILS_DEBUG environment variable) flag is true. """ from distutils.debug import DEBUG if DEBUG: print(msg) sys.stdout.flush() # -- Option validation methods ------------------------------------- # (these are very handy in writing the 'finalize_options()' method) # # NB. the general philosophy here is to ensure that a particular option # value meets certain type and value constraints. If not, we try to # force it into conformance (eg. if we expect a list but have a string, # split the string on comma and/or whitespace). If we can't force the # option into conformance, raise DistutilsOptionError. Thus, command # classes need do nothing more than (eg.) # self.ensure_string_list('foo') # and they can be guaranteed that thereafter, self.foo will be # a list of strings. def _ensure_stringlike(self, option, what, default=None): val = getattr(self, option) if val is None: setattr(self, option, default) return default elif not isinstance(val, str): raise DistutilsOptionError("'%s' must be a %s (got `%s`)" % (option, what, val)) return val def ensure_string(self, option, default=None): """Ensure that 'option' is a string; if not defined, set it to 'default'. """ self._ensure_stringlike(option, "string", default) def ensure_string_list(self, option): r"""Ensure that 'option' is a list of strings. If 'option' is currently a string, we split it either on /,\s*/ or /\s+/, so "foo bar baz", "foo,bar,baz", and "foo, bar baz" all become ["foo", "bar", "baz"]. """ val = getattr(self, option) if val is None: return elif isinstance(val, str): setattr(self, option, re.split(r',\s*|\s+', val)) else: if isinstance(val, list): ok = all(isinstance(v, str) for v in val) else: ok = False if not ok: raise DistutilsOptionError( "'%s' must be a list of strings (got %r)" % (option, val)) def _ensure_tested_string(self, option, tester, what, error_fmt, default=None): val = self._ensure_stringlike(option, what, default) if val is not None and not tester(val): raise DistutilsOptionError(("error in '%s' option: " + error_fmt) % (option, val)) def ensure_filename(self, option): """Ensure that 'option' is the name of an existing file.""" self._ensure_tested_string(option, os.path.isfile, "filename", "'%s' does not exist or is not a file") def ensure_dirname(self, option): self._ensure_tested_string(option, os.path.isdir, "directory name", "'%s' does not exist or is not a directory") # -- Convenience methods for commands ------------------------------ def get_command_name(self): if hasattr(self, 'command_name'): return self.command_name else: return self.__class__.__name__ def set_undefined_options(self, src_cmd, *option_pairs): """Set the values of any "undefined" options from corresponding option values in some other command object. "Undefined" here means "is None", which is the convention used to indicate that an option has not been changed between 'initialize_options()' and 'finalize_options()'. Usually called from 'finalize_options()' for options that depend on some other command rather than another option of the same command. 'src_cmd' is the other command from which option values will be taken (a command object will be created for it if necessary); the remaining arguments are '(src_option,dst_option)' tuples which mean "take the value of 'src_option' in the 'src_cmd' command object, and copy it to 'dst_option' in the current command object". """ # Option_pairs: list of (src_option, dst_option) tuples src_cmd_obj = self.distribution.get_command_obj(src_cmd) src_cmd_obj.ensure_finalized() for (src_option, dst_option) in option_pairs: if getattr(self, dst_option) is None: setattr(self, dst_option, getattr(src_cmd_obj, src_option)) def get_finalized_command(self, command, create=1): """Wrapper around Distribution's 'get_command_obj()' method: find (create if necessary and 'create' is true) the command object for 'command', call its 'ensure_finalized()' method, and return the finalized command object. """ cmd_obj = self.distribution.get_command_obj(command, create) cmd_obj.ensure_finalized() return cmd_obj # XXX rename to 'get_reinitialized_command()'? (should do the # same in dist.py, if so) def reinitialize_command(self, command, reinit_subcommands=0): return self.distribution.reinitialize_command(command, reinit_subcommands) def run_command(self, command): """Run some other command: uses the 'run_command()' method of Distribution, which creates and finalizes the command object if necessary and then invokes its 'run()' method. """ self.distribution.run_command(command) def get_sub_commands(self): """Determine the sub-commands that are relevant in the current distribution (ie., that need to be run). This is based on the 'sub_commands' class attribute: each tuple in that list may include a method that we call to determine if the subcommand needs to be run for the current distribution. Return a list of command names. """ commands = [] for (cmd_name, method) in self.sub_commands: if method is None or method(self): commands.append(cmd_name) return commands # -- External world manipulation ----------------------------------- def warn(self, msg): log.warn("warning: %s: %s\n", self.get_command_name(), msg) def execute(self, func, args, msg=None, level=1): util.execute(func, args, msg, dry_run=self.dry_run) def mkpath(self, name, mode=0o777): dir_util.mkpath(name, mode, dry_run=self.dry_run) def copy_file(self, infile, outfile, preserve_mode=1, preserve_times=1, link=None, level=1): """Copy a file respecting verbose, dry-run and force flags. (The former two default to whatever is in the Distribution object, and the latter defaults to false for commands that don't define it.)""" return file_util.copy_file(infile, outfile, preserve_mode, preserve_times, not self.force, link, dry_run=self.dry_run) def copy_tree(self, infile, outfile, preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1): """Copy an entire directory tree respecting verbose, dry-run, and force flags. """ return dir_util.copy_tree(infile, outfile, preserve_mode, preserve_times, preserve_symlinks, not self.force, dry_run=self.dry_run) def move_file (self, src, dst, level=1): """Move a file respecting dry-run flag.""" return file_util.move_file(src, dst, dry_run=self.dry_run) def spawn(self, cmd, search_path=1, level=1): """Spawn an external command respecting dry-run flag.""" from distutils.spawn import spawn spawn(cmd, search_path, dry_run=self.dry_run) def make_archive(self, base_name, format, root_dir=None, base_dir=None, owner=None, group=None): return archive_util.make_archive(base_name, format, root_dir, base_dir, dry_run=self.dry_run, owner=owner, group=group) def make_file(self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1): """Special case of 'execute()' for operations that process one or more input files and generate one output file. Works just like 'execute()', except the operation is skipped and a different message printed if 'outfile' already exists and is newer than all files listed in 'infiles'. If the command defined 'self.force', and it is true, then the command is unconditionally run -- does no timestamp checks. """ if skip_msg is None: skip_msg = "skipping %s (inputs unchanged)" % outfile # Allow 'infiles' to be a single string if isinstance(infiles, str): infiles = (infiles,) elif not isinstance(infiles, (list, tuple)): raise TypeError( "'infiles' must be a string, or a list or tuple of strings") if exec_msg is None: exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles)) # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it if self.force or dep_util.newer_group(infiles, outfile): self.execute(func, args, exec_msg, level) # Otherwise, print the "skip" message else: log.debug(skip_msg) python-deadlib-3.13.0/distutils/distutils/command/000077500000000000000000000000001471045437300221775ustar00rootroot00000000000000python-deadlib-3.13.0/distutils/distutils/command/__init__.py000066400000000000000000000014031471045437300243060ustar00rootroot00000000000000"""distutils.command Package containing implementation of all the standard Distutils commands.""" __all__ = ['build', 'build_py', 'build_ext', 'build_clib', 'build_scripts', 'clean', 'install', 'install_lib', 'install_headers', 'install_scripts', 'install_data', 'sdist', 'register', 'bdist', 'bdist_dumb', 'bdist_rpm', 'check', 'upload', # These two are reserved for future use: #'bdist_sdux', #'bdist_pkgtool', # Note: # bdist_packager is not included because it only provides # an abstract base class ] python-deadlib-3.13.0/distutils/distutils/command/bdist.py000066400000000000000000000123251471045437300236610ustar00rootroot00000000000000"""distutils.command.bdist Implements the Distutils 'bdist' command (create a built [binary] distribution).""" import os from distutils.core import Command from distutils.errors import * from distutils.util import get_platform def show_formats(): """Print list of available formats (arguments to "--format" option). """ from distutils.fancy_getopt import FancyGetopt formats = [] for format in bdist.format_commands: formats.append(("formats=" + format, None, bdist.format_command[format][1])) pretty_printer = FancyGetopt(formats) pretty_printer.print_help("List of available distribution formats:") class bdist(Command): description = "create a built (binary) distribution" user_options = [('bdist-base=', 'b', "temporary directory for creating built distributions"), ('plat-name=', 'p', "platform name to embed in generated filenames " "(default: %s)" % get_platform()), ('formats=', None, "formats for distribution (comma-separated list)"), ('dist-dir=', 'd', "directory to put final built distributions in " "[default: dist]"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), ('owner=', 'u', "Owner name used when creating a tar file" " [default: current user]"), ('group=', 'g', "Group name used when creating a tar file" " [default: current group]"), ] boolean_options = ['skip-build'] help_options = [ ('help-formats', None, "lists available distribution formats", show_formats), ] # The following commands do not take a format option from bdist no_format_option = ('bdist_rpm',) # This won't do in reality: will need to distinguish RPM-ish Linux, # Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS. default_format = {'posix': 'gztar', 'nt': 'zip'} # Establish the preferred order (for the --help-formats option). format_commands = ['rpm', 'gztar', 'bztar', 'xztar', 'ztar', 'tar', 'zip'] # And the real information. format_command = {'rpm': ('bdist_rpm', "RPM distribution"), 'gztar': ('bdist_dumb', "gzip'ed tar file"), 'bztar': ('bdist_dumb', "bzip2'ed tar file"), 'xztar': ('bdist_dumb', "xz'ed tar file"), 'ztar': ('bdist_dumb', "compressed tar file"), 'tar': ('bdist_dumb', "tar file"), 'zip': ('bdist_dumb', "ZIP file"), } def initialize_options(self): self.bdist_base = None self.plat_name = None self.formats = None self.dist_dir = None self.skip_build = 0 self.group = None self.owner = None def finalize_options(self): # have to finalize 'plat_name' before 'bdist_base' if self.plat_name is None: if self.skip_build: self.plat_name = get_platform() else: self.plat_name = self.get_finalized_command('build').plat_name # 'bdist_base' -- parent of per-built-distribution-format # temporary directories (eg. we'll probably have # "build/bdist./dumb", "build/bdist./rpm", etc.) if self.bdist_base is None: build_base = self.get_finalized_command('build').build_base self.bdist_base = os.path.join(build_base, 'bdist.' + self.plat_name) self.ensure_string_list('formats') if self.formats is None: try: self.formats = [self.default_format[os.name]] except KeyError: raise DistutilsPlatformError( "don't know how to create built distributions " "on platform %s" % os.name) if self.dist_dir is None: self.dist_dir = "dist" def run(self): # Figure out which sub-commands we need to run. commands = [] for format in self.formats: try: commands.append(self.format_command[format][0]) except KeyError: raise DistutilsOptionError("invalid format '%s'" % format) # Reinitialize and run each command. for i in range(len(self.formats)): cmd_name = commands[i] sub_cmd = self.reinitialize_command(cmd_name) if cmd_name not in self.no_format_option: sub_cmd.format = self.formats[i] # passing the owner and group names for tar archiving if cmd_name == 'bdist_dumb': sub_cmd.owner = self.owner sub_cmd.group = self.group # If we're going to need to run this command again, tell it to # keep its temporary files around so subsequent runs go faster. if cmd_name in commands[i+1:]: sub_cmd.keep_temp = 1 self.run_command(cmd_name) python-deadlib-3.13.0/distutils/distutils/command/bdist_dumb.py000066400000000000000000000114611471045437300246700ustar00rootroot00000000000000"""distutils.command.bdist_dumb Implements the Distutils 'bdist_dumb' command (create a "dumb" built distribution -- i.e., just an archive to be unpacked under $prefix or $exec_prefix).""" import os from distutils.core import Command from distutils.util import get_platform from distutils.dir_util import remove_tree, ensure_relative from distutils.errors import * from distutils.sysconfig import get_python_version from distutils import log class bdist_dumb(Command): description = "create a \"dumb\" built distribution" user_options = [('bdist-dir=', 'd', "temporary directory for creating the distribution"), ('plat-name=', 'p', "platform name to embed in generated filenames " "(default: %s)" % get_platform()), ('format=', 'f', "archive format to create (tar, gztar, bztar, xztar, " "ztar, zip)"), ('keep-temp', 'k', "keep the pseudo-installation tree around after " + "creating the distribution archive"), ('dist-dir=', 'd', "directory to put final built distributions in"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), ('relative', None, "build the archive using relative paths " "(default: false)"), ('owner=', 'u', "Owner name used when creating a tar file" " [default: current user]"), ('group=', 'g', "Group name used when creating a tar file" " [default: current group]"), ] boolean_options = ['keep-temp', 'skip-build', 'relative'] default_format = { 'posix': 'gztar', 'nt': 'zip' } def initialize_options(self): self.bdist_dir = None self.plat_name = None self.format = None self.keep_temp = 0 self.dist_dir = None self.skip_build = None self.relative = 0 self.owner = None self.group = None def finalize_options(self): if self.bdist_dir is None: bdist_base = self.get_finalized_command('bdist').bdist_base self.bdist_dir = os.path.join(bdist_base, 'dumb') if self.format is None: try: self.format = self.default_format[os.name] except KeyError: raise DistutilsPlatformError( "don't know how to create dumb built distributions " "on platform %s" % os.name) self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'), ('plat_name', 'plat_name'), ('skip_build', 'skip_build')) def run(self): if not self.skip_build: self.run_command('build') install = self.reinitialize_command('install', reinit_subcommands=1) install.root = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 log.info("installing to %s", self.bdist_dir) self.run_command('install') # And make an archive relative to the root of the # pseudo-installation tree. archive_basename = "%s.%s" % (self.distribution.get_fullname(), self.plat_name) pseudoinstall_root = os.path.join(self.dist_dir, archive_basename) if not self.relative: archive_root = self.bdist_dir else: if (self.distribution.has_ext_modules() and (install.install_base != install.install_platbase)): raise DistutilsPlatformError( "can't make a dumb built distribution where " "base and platbase are different (%s, %s)" % (repr(install.install_base), repr(install.install_platbase))) else: archive_root = os.path.join(self.bdist_dir, ensure_relative(install.install_base)) # Make the archive filename = self.make_archive(pseudoinstall_root, self.format, root_dir=archive_root, owner=self.owner, group=self.group) if self.distribution.has_ext_modules(): pyversion = get_python_version() else: pyversion = 'any' self.distribution.dist_files.append(('bdist_dumb', pyversion, filename)) if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) python-deadlib-3.13.0/distutils/distutils/command/bdist_rpm.py000066400000000000000000000520411471045437300245360ustar00rootroot00000000000000"""distutils.command.bdist_rpm Implements the Distutils 'bdist_rpm' command (create RPM source and binary distributions).""" import subprocess, sys, os from distutils.core import Command from distutils.debug import DEBUG from distutils.file_util import write_file from distutils.errors import * from distutils.sysconfig import get_python_version from distutils import log class bdist_rpm(Command): description = "create an RPM distribution" user_options = [ ('bdist-base=', None, "base directory for creating built distributions"), ('rpm-base=', None, "base directory for creating RPMs (defaults to \"rpm\" under " "--bdist-base; must be specified for RPM 2)"), ('dist-dir=', 'd', "directory to put final RPM files in " "(and .spec files if --spec-only)"), ('python=', None, "path to Python interpreter to hard-code in the .spec file " "(default: \"python\")"), ('fix-python', None, "hard-code the exact path to the current Python interpreter in " "the .spec file"), ('spec-only', None, "only regenerate spec file"), ('source-only', None, "only generate source RPM"), ('binary-only', None, "only generate binary RPM"), ('use-bzip2', None, "use bzip2 instead of gzip to create source distribution"), # More meta-data: too RPM-specific to put in the setup script, # but needs to go in the .spec file -- so we make these options # to "bdist_rpm". The idea is that packagers would put this # info in setup.cfg, although they are of course free to # supply it on the command line. ('distribution-name=', None, "name of the (Linux) distribution to which this " "RPM applies (*not* the name of the module distribution!)"), ('group=', None, "package classification [default: \"Development/Libraries\"]"), ('release=', None, "RPM release number"), ('serial=', None, "RPM serial number"), ('vendor=', None, "RPM \"vendor\" (eg. \"Joe Blow \") " "[default: maintainer or author from setup script]"), ('packager=', None, "RPM packager (eg. \"Jane Doe \") " "[default: vendor]"), ('doc-files=', None, "list of documentation files (space or comma-separated)"), ('changelog=', None, "RPM changelog"), ('icon=', None, "name of icon file"), ('provides=', None, "capabilities provided by this package"), ('requires=', None, "capabilities required by this package"), ('conflicts=', None, "capabilities which conflict with this package"), ('build-requires=', None, "capabilities required to build this package"), ('obsoletes=', None, "capabilities made obsolete by this package"), ('no-autoreq', None, "do not automatically calculate dependencies"), # Actions to take when building RPM ('keep-temp', 'k', "don't clean up RPM build directory"), ('no-keep-temp', None, "clean up RPM build directory [default]"), ('use-rpm-opt-flags', None, "compile with RPM_OPT_FLAGS when building from source RPM"), ('no-rpm-opt-flags', None, "do not pass any RPM CFLAGS to compiler"), ('rpm3-mode', None, "RPM 3 compatibility mode (default)"), ('rpm2-mode', None, "RPM 2 compatibility mode"), # Add the hooks necessary for specifying custom scripts ('prep-script=', None, "Specify a script for the PREP phase of RPM building"), ('build-script=', None, "Specify a script for the BUILD phase of RPM building"), ('pre-install=', None, "Specify a script for the pre-INSTALL phase of RPM building"), ('install-script=', None, "Specify a script for the INSTALL phase of RPM building"), ('post-install=', None, "Specify a script for the post-INSTALL phase of RPM building"), ('pre-uninstall=', None, "Specify a script for the pre-UNINSTALL phase of RPM building"), ('post-uninstall=', None, "Specify a script for the post-UNINSTALL phase of RPM building"), ('clean-script=', None, "Specify a script for the CLEAN phase of RPM building"), ('verify-script=', None, "Specify a script for the VERIFY phase of the RPM build"), # Allow a packager to explicitly force an architecture ('force-arch=', None, "Force an architecture onto the RPM build process"), ('quiet', 'q', "Run the INSTALL phase of RPM building in quiet mode"), ] boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', 'no-autoreq', 'quiet'] negative_opt = {'no-keep-temp': 'keep-temp', 'no-rpm-opt-flags': 'use-rpm-opt-flags', 'rpm2-mode': 'rpm3-mode'} def initialize_options(self): self.bdist_base = None self.rpm_base = None self.dist_dir = None self.python = None self.fix_python = None self.spec_only = None self.binary_only = None self.source_only = None self.use_bzip2 = None self.distribution_name = None self.group = None self.release = None self.serial = None self.vendor = None self.packager = None self.doc_files = None self.changelog = None self.icon = None self.prep_script = None self.build_script = None self.install_script = None self.clean_script = None self.verify_script = None self.pre_install = None self.post_install = None self.pre_uninstall = None self.post_uninstall = None self.prep = None self.provides = None self.requires = None self.conflicts = None self.build_requires = None self.obsoletes = None self.keep_temp = 0 self.use_rpm_opt_flags = 1 self.rpm3_mode = 1 self.no_autoreq = 0 self.force_arch = None self.quiet = 0 def finalize_options(self): self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) if self.rpm_base is None: if not self.rpm3_mode: raise DistutilsOptionError( "you must specify --rpm-base in RPM 2 mode") self.rpm_base = os.path.join(self.bdist_base, "rpm") if self.python is None: if self.fix_python: self.python = sys.executable else: self.python = "python3" elif self.fix_python: raise DistutilsOptionError( "--python and --fix-python are mutually exclusive options") if os.name != 'posix': raise DistutilsPlatformError("don't know how to create RPM " "distributions on platform %s" % os.name) if self.binary_only and self.source_only: raise DistutilsOptionError( "cannot supply both '--source-only' and '--binary-only'") # don't pass CFLAGS to pure python distributions if not self.distribution.has_ext_modules(): self.use_rpm_opt_flags = 0 self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) self.finalize_package_data() def finalize_package_data(self): self.ensure_string('group', "Development/Libraries") self.ensure_string('vendor', "%s <%s>" % (self.distribution.get_contact(), self.distribution.get_contact_email())) self.ensure_string('packager') self.ensure_string_list('doc_files') if isinstance(self.doc_files, list): for readme in ('README', 'README.txt'): if os.path.exists(readme) and readme not in self.doc_files: self.doc_files.append(readme) self.ensure_string('release', "1") self.ensure_string('serial') # should it be an int? self.ensure_string('distribution_name') self.ensure_string('changelog') # Format changelog correctly self.changelog = self._format_changelog(self.changelog) self.ensure_filename('icon') self.ensure_filename('prep_script') self.ensure_filename('build_script') self.ensure_filename('install_script') self.ensure_filename('clean_script') self.ensure_filename('verify_script') self.ensure_filename('pre_install') self.ensure_filename('post_install') self.ensure_filename('pre_uninstall') self.ensure_filename('post_uninstall') # XXX don't forget we punted on summaries and descriptions -- they # should be handled here eventually! # Now *this* is some meta-data that belongs in the setup script... self.ensure_string_list('provides') self.ensure_string_list('requires') self.ensure_string_list('conflicts') self.ensure_string_list('build_requires') self.ensure_string_list('obsoletes') self.ensure_string('force_arch') def run(self): if DEBUG: print("before _get_package_data():") print("vendor =", self.vendor) print("packager =", self.packager) print("doc_files =", self.doc_files) print("changelog =", self.changelog) # make directories if self.spec_only: spec_dir = self.dist_dir self.mkpath(spec_dir) else: rpm_dir = {} for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): rpm_dir[d] = os.path.join(self.rpm_base, d) self.mkpath(rpm_dir[d]) spec_dir = rpm_dir['SPECS'] # Spec file goes into 'dist_dir' if '--spec-only specified', # build/rpm. otherwise. spec_path = os.path.join(spec_dir, "%s.spec" % self.distribution.get_name()) self.execute(write_file, (spec_path, self._make_spec_file()), "writing '%s'" % spec_path) if self.spec_only: # stop if requested return # Make a source distribution and copy to SOURCES directory with # optional icon. saved_dist_files = self.distribution.dist_files[:] sdist = self.reinitialize_command('sdist') if self.use_bzip2: sdist.formats = ['bztar'] else: sdist.formats = ['gztar'] self.run_command('sdist') self.distribution.dist_files = saved_dist_files source = sdist.get_archive_files()[0] source_dir = rpm_dir['SOURCES'] self.copy_file(source, source_dir) if self.icon: if os.path.exists(self.icon): self.copy_file(self.icon, source_dir) else: raise DistutilsFileError( "icon file '%s' does not exist" % self.icon) # build package log.info("building RPMs") rpm_cmd = ['rpmbuild'] if self.source_only: # what kind of RPMs? rpm_cmd.append('-bs') elif self.binary_only: rpm_cmd.append('-bb') else: rpm_cmd.append('-ba') rpm_cmd.extend(['--define', '__python %s' % self.python]) if self.rpm3_mode: rpm_cmd.extend(['--define', '_topdir %s' % os.path.abspath(self.rpm_base)]) if not self.keep_temp: rpm_cmd.append('--clean') if self.quiet: rpm_cmd.append('--quiet') rpm_cmd.append(spec_path) # Determine the binary rpm names that should be built out of this spec # file # Note that some of these may not be really built (if the file # list is empty) nvr_string = "%{name}-%{version}-%{release}" src_rpm = nvr_string + ".src.rpm" non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm" q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % ( src_rpm, non_src_rpm, spec_path) out = os.popen(q_cmd) try: binary_rpms = [] source_rpm = None while True: line = out.readline() if not line: break l = line.strip().split() assert(len(l) == 2) binary_rpms.append(l[1]) # The source rpm is named after the first entry in the spec file if source_rpm is None: source_rpm = l[0] status = out.close() if status: raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) finally: out.close() self.spawn(rpm_cmd) if not self.dry_run: if self.distribution.has_ext_modules(): pyversion = get_python_version() else: pyversion = 'any' if not self.binary_only: srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) assert(os.path.exists(srpm)) self.move_file(srpm, self.dist_dir) filename = os.path.join(self.dist_dir, source_rpm) self.distribution.dist_files.append( ('bdist_rpm', pyversion, filename)) if not self.source_only: for rpm in binary_rpms: rpm = os.path.join(rpm_dir['RPMS'], rpm) if os.path.exists(rpm): self.move_file(rpm, self.dist_dir) filename = os.path.join(self.dist_dir, os.path.basename(rpm)) self.distribution.dist_files.append( ('bdist_rpm', pyversion, filename)) def _dist_path(self, path): return os.path.join(self.dist_dir, os.path.basename(path)) def _make_spec_file(self): """Generate the text of an RPM spec file and return it as a list of strings (one per line). """ # definitions and headers spec_file = [ '%define name ' + self.distribution.get_name(), '%define version ' + self.distribution.get_version().replace('-','_'), '%define unmangled_version ' + self.distribution.get_version(), '%define release ' + self.release.replace('-','_'), '', 'Summary: ' + self.distribution.get_description(), ] # Workaround for #14443 which affects some RPM based systems such as # RHEL6 (and probably derivatives) vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}') # Generate a potential replacement value for __os_install_post (whilst # normalizing the whitespace to simplify the test for whether the # invocation of brp-python-bytecompile passes in __python): vendor_hook = '\n'.join([' %s \\' % line.strip() for line in vendor_hook.splitlines()]) problem = "brp-python-bytecompile \\\n" fixed = "brp-python-bytecompile %{__python} \\\n" fixed_hook = vendor_hook.replace(problem, fixed) if fixed_hook != vendor_hook: spec_file.append('# Workaround for http://bugs.python.org/issue14443') spec_file.append('%define __os_install_post ' + fixed_hook + '\n') # put locale summaries into spec file # XXX not supported for now (hard to put a dictionary # in a config file -- arg!) #for locale in self.summaries.keys(): # spec_file.append('Summary(%s): %s' % (locale, # self.summaries[locale])) spec_file.extend([ 'Name: %{name}', 'Version: %{version}', 'Release: %{release}',]) # XXX yuck! this filename is available from the "sdist" command, # but only after it has run: and we create the spec file before # running "sdist", in case of --spec-only. if self.use_bzip2: spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2') else: spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') spec_file.extend([ 'License: ' + self.distribution.get_license(), 'Group: ' + self.group, 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot', 'Prefix: %{_prefix}', ]) if not self.force_arch: # noarch if no extension modules if not self.distribution.has_ext_modules(): spec_file.append('BuildArch: noarch') else: spec_file.append( 'BuildArch: %s' % self.force_arch ) for field in ('Vendor', 'Packager', 'Provides', 'Requires', 'Conflicts', 'Obsoletes', ): val = getattr(self, field.lower()) if isinstance(val, list): spec_file.append('%s: %s' % (field, ' '.join(val))) elif val is not None: spec_file.append('%s: %s' % (field, val)) if self.distribution.get_url() != 'UNKNOWN': spec_file.append('Url: ' + self.distribution.get_url()) if self.distribution_name: spec_file.append('Distribution: ' + self.distribution_name) if self.build_requires: spec_file.append('BuildRequires: ' + ' '.join(self.build_requires)) if self.icon: spec_file.append('Icon: ' + os.path.basename(self.icon)) if self.no_autoreq: spec_file.append('AutoReq: 0') spec_file.extend([ '', '%description', self.distribution.get_long_description() ]) # put locale descriptions into spec file # XXX again, suppressed because config file syntax doesn't # easily support this ;-( #for locale in self.descriptions.keys(): # spec_file.extend([ # '', # '%description -l ' + locale, # self.descriptions[locale], # ]) # rpm scripts # figure out default build script def_setup_call = "%s %s" % (self.python,os.path.basename(sys.argv[0])) def_build = "%s build" % def_setup_call if self.use_rpm_opt_flags: def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build # insert contents of files # XXX this is kind of misleading: user-supplied options are files # that we open and interpolate into the spec file, but the defaults # are just text that we drop in as-is. Hmmm. install_cmd = ('%s install -O1 --root=$RPM_BUILD_ROOT ' '--record=INSTALLED_FILES') % def_setup_call script_options = [ ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), ('build', 'build_script', def_build), ('install', 'install_script', install_cmd), ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), ('verifyscript', 'verify_script', None), ('pre', 'pre_install', None), ('post', 'post_install', None), ('preun', 'pre_uninstall', None), ('postun', 'post_uninstall', None), ] for (rpm_opt, attr, default) in script_options: # Insert contents of file referred to, if no file is referred to # use 'default' as contents of script val = getattr(self, attr) if val or default: spec_file.extend([ '', '%' + rpm_opt,]) if val: with open(val) as f: spec_file.extend(f.read().split('\n')) else: spec_file.append(default) # files section spec_file.extend([ '', '%files -f INSTALLED_FILES', '%defattr(-,root,root)', ]) if self.doc_files: spec_file.append('%doc ' + ' '.join(self.doc_files)) if self.changelog: spec_file.extend([ '', '%changelog',]) spec_file.extend(self.changelog) return spec_file def _format_changelog(self, changelog): """Format the changelog correctly and convert it to a list of strings """ if not changelog: return changelog new_changelog = [] for line in changelog.strip().split('\n'): line = line.strip() if line[0] == '*': new_changelog.extend(['', line]) elif line[0] == '-': new_changelog.append(line) else: new_changelog.append(' ' + line) # strip trailing newline inserted by first changelog entry if not new_changelog[0]: del new_changelog[0] return new_changelog python-deadlib-3.13.0/distutils/distutils/command/build.py000066400000000000000000000132071471045437300236530ustar00rootroot00000000000000"""distutils.command.build Implements the Distutils 'build' command.""" import sys, os from distutils.core import Command from distutils.errors import DistutilsOptionError from distutils.util import get_platform def show_compilers(): from distutils.ccompiler import show_compilers show_compilers() class build(Command): description = "build everything needed to install" user_options = [ ('build-base=', 'b', "base directory for build library"), ('build-purelib=', None, "build directory for platform-neutral distributions"), ('build-platlib=', None, "build directory for platform-specific distributions"), ('build-lib=', None, "build directory for all distribution (defaults to either " + "build-purelib or build-platlib"), ('build-scripts=', None, "build directory for scripts"), ('build-temp=', 't', "temporary build directory"), ('plat-name=', 'p', "platform name to build for, if supported " "(default: %s)" % get_platform()), ('compiler=', 'c', "specify the compiler type"), ('parallel=', 'j', "number of parallel build jobs"), ('debug', 'g', "compile extensions and libraries with debugging information"), ('force', 'f', "forcibly build everything (ignore file timestamps)"), ('executable=', 'e', "specify final destination interpreter path (build.py)"), ] boolean_options = ['debug', 'force'] help_options = [ ('help-compiler', None, "list available compilers", show_compilers), ] def initialize_options(self): self.build_base = 'build' # these are decided only after 'build_base' has its final value # (unless overridden by the user or client) self.build_purelib = None self.build_platlib = None self.build_lib = None self.build_temp = None self.build_scripts = None self.compiler = None self.plat_name = None self.debug = None self.force = 0 self.executable = None self.parallel = None def finalize_options(self): if self.plat_name is None: self.plat_name = get_platform() else: # plat-name only supported for windows (other platforms are # supported via ./configure flags, if at all). Avoid misleading # other platforms. if os.name != 'nt': raise DistutilsOptionError( "--plat-name only supported on Windows (try " "using './configure --help' on your platform)") plat_specifier = ".%s-%d.%d" % (self.plat_name, *sys.version_info[:2]) # Make it so Python 2.x and Python 2.x with --with-pydebug don't # share the same build directories. Doing so confuses the build # process for C modules if hasattr(sys, 'gettotalrefcount'): plat_specifier += '-pydebug' # 'build_purelib' and 'build_platlib' just default to 'lib' and # 'lib.' under the base build directory. We only use one of # them for a given distribution, though -- if self.build_purelib is None: self.build_purelib = os.path.join(self.build_base, 'lib') if self.build_platlib is None: self.build_platlib = os.path.join(self.build_base, 'lib' + plat_specifier) # 'build_lib' is the actual directory that we will use for this # particular module distribution -- if user didn't supply it, pick # one of 'build_purelib' or 'build_platlib'. if self.build_lib is None: if self.distribution.ext_modules: self.build_lib = self.build_platlib else: self.build_lib = self.build_purelib # 'build_temp' -- temporary directory for compiler turds, # "build/temp." if self.build_temp is None: self.build_temp = os.path.join(self.build_base, 'temp' + plat_specifier) if self.build_scripts is None: self.build_scripts = os.path.join(self.build_base, 'scripts-%d.%d' % sys.version_info[:2]) if self.executable is None and sys.executable: self.executable = os.path.normpath(sys.executable) if isinstance(self.parallel, str): try: self.parallel = int(self.parallel) except ValueError: raise DistutilsOptionError("parallel should be an integer") def run(self): # Run all relevant sub-commands. This will be some subset of: # - build_py - pure Python modules # - build_clib - standalone C libraries # - build_ext - Python extensions # - build_scripts - (Python) scripts for cmd_name in self.get_sub_commands(): self.run_command(cmd_name) # -- Predicates for the sub-command list --------------------------- def has_pure_modules(self): return self.distribution.has_pure_modules() def has_c_libraries(self): return self.distribution.has_c_libraries() def has_ext_modules(self): return self.distribution.has_ext_modules() def has_scripts(self): return self.distribution.has_scripts() sub_commands = [('build_py', has_pure_modules), ('build_clib', has_c_libraries), ('build_ext', has_ext_modules), ('build_scripts', has_scripts), ] python-deadlib-3.13.0/distutils/distutils/command/build_clib.py000066400000000000000000000175261471045437300246540ustar00rootroot00000000000000"""distutils.command.build_clib Implements the Distutils 'build_clib' command, to build a C/C++ library that is included in the module distribution and needed by an extension module.""" # XXX this module has *lots* of code ripped-off quite transparently from # build_ext.py -- not surprisingly really, as the work required to build # a static library from a collection of C source files is not really all # that different from what's required to build a shared object file from # a collection of C source files. Nevertheless, I haven't done the # necessary refactoring to account for the overlap in code between the # two modules, mainly because a number of subtle details changed in the # cut 'n paste. Sigh. import os from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler from distutils import log def show_compilers(): from distutils.ccompiler import show_compilers show_compilers() class build_clib(Command): description = "build C/C++ libraries used by Python extensions" user_options = [ ('build-clib=', 'b', "directory to build C/C++ libraries to"), ('build-temp=', 't', "directory to put temporary build by-products"), ('debug', 'g', "compile with debugging information"), ('force', 'f', "forcibly build everything (ignore file timestamps)"), ('compiler=', 'c', "specify the compiler type"), ] boolean_options = ['debug', 'force'] help_options = [ ('help-compiler', None, "list available compilers", show_compilers), ] def initialize_options(self): self.build_clib = None self.build_temp = None # List of libraries to build self.libraries = None # Compilation options for all libraries self.include_dirs = None self.define = None self.undef = None self.debug = None self.force = 0 self.compiler = None def finalize_options(self): # This might be confusing: both build-clib and build-temp default # to build-temp as defined by the "build" command. This is because # I think that C libraries are really just temporary build # by-products, at least from the point of view of building Python # extensions -- but I want to keep my options open. self.set_undefined_options('build', ('build_temp', 'build_clib'), ('build_temp', 'build_temp'), ('compiler', 'compiler'), ('debug', 'debug'), ('force', 'force')) self.libraries = self.distribution.libraries if self.libraries: self.check_library_list(self.libraries) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if isinstance(self.include_dirs, str): self.include_dirs = self.include_dirs.split(os.pathsep) # XXX same as for build_ext -- what about 'self.define' and # 'self.undef' ? def run(self): if not self.libraries: return # Yech -- this is cut 'n pasted from build_ext.py! from distutils.ccompiler import new_compiler self.compiler = new_compiler(compiler=self.compiler, dry_run=self.dry_run, force=self.force) customize_compiler(self.compiler) if self.include_dirs is not None: self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name,value) in self.define: self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: self.compiler.undefine_macro(macro) self.build_libraries(self.libraries) def check_library_list(self, libraries): """Ensure that the list of libraries is valid. `library` is presumably provided as a command option 'libraries'. This method checks that it is a list of 2-tuples, where the tuples are (library_name, build_info_dict). Raise DistutilsSetupError if the structure is invalid anywhere; just returns otherwise. """ if not isinstance(libraries, list): raise DistutilsSetupError( "'libraries' option must be a list of tuples") for lib in libraries: if not isinstance(lib, tuple) and len(lib) != 2: raise DistutilsSetupError( "each element of 'libraries' must a 2-tuple") name, build_info = lib if not isinstance(name, str): raise DistutilsSetupError( "first element of each tuple in 'libraries' " "must be a string (the library name)") if '/' in name or (os.sep != '/' and os.sep in name): raise DistutilsSetupError("bad library name '%s': " "may not contain directory separators" % lib[0]) if not isinstance(build_info, dict): raise DistutilsSetupError( "second element of each tuple in 'libraries' " "must be a dictionary (build info)") def get_library_names(self): # Assume the library list is valid -- 'check_library_list()' is # called from 'finalize_options()', so it should be! if not self.libraries: return None lib_names = [] for (lib_name, build_info) in self.libraries: lib_names.append(lib_name) return lib_names def get_source_files(self): self.check_library_list(self.libraries) filenames = [] for (lib_name, build_info) in self.libraries: sources = build_info.get('sources') if sources is None or not isinstance(sources, (list, tuple)): raise DistutilsSetupError( "in 'libraries' option (library '%s'), " "'sources' must be present and must be " "a list of source filenames" % lib_name) filenames.extend(sources) return filenames def build_libraries(self, libraries): for (lib_name, build_info) in libraries: sources = build_info.get('sources') if sources is None or not isinstance(sources, (list, tuple)): raise DistutilsSetupError( "in 'libraries' option (library '%s'), " "'sources' must be present and must be " "a list of source filenames" % lib_name) sources = list(sources) log.info("building '%s' library", lib_name) # First, compile the source code to object files in the library # directory. (This should probably change to putting object # files in a temporary build directory.) macros = build_info.get('macros') include_dirs = build_info.get('include_dirs') objects = self.compiler.compile(sources, output_dir=self.build_temp, macros=macros, include_dirs=include_dirs, debug=self.debug) # Now "link" the object files together into a static library. # (On Unix at least, this isn't really linking -- it just # builds an archive. Whatever.) self.compiler.create_static_lib(objects, lib_name, output_dir=self.build_clib, debug=self.debug) python-deadlib-3.13.0/distutils/distutils/command/build_ext.py000066400000000000000000000756131471045437300245440ustar00rootroot00000000000000"""distutils.command.build_ext Implements the Distutils 'build_ext' command, for building extension modules (currently limited to C extensions, should accommodate C++ extensions ASAP).""" import contextlib import os import re import sys from distutils.core import Command from distutils.errors import * from distutils.sysconfig import customize_compiler, get_python_version from distutils.sysconfig import get_config_h_filename from distutils.dep_util import newer_group from distutils.extension import Extension from distutils.util import get_platform from distutils import log from site import USER_BASE # An extension name is just a dot-separated list of Python NAMEs (ie. # the same as a fully-qualified module name). extension_name_re = re.compile \ (r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$') def show_compilers (): from distutils.ccompiler import show_compilers show_compilers() class build_ext(Command): description = "build C/C++ extensions (compile/link to build directory)" # XXX thoughts on how to deal with complex command-line options like # these, i.e. how to make it so fancy_getopt can suck them off the # command line and make it look like setup.py defined the appropriate # lists of tuples of what-have-you. # - each command needs a callback to process its command-line options # - Command.__init__() needs access to its share of the whole # command line (must ultimately come from # Distribution.parse_command_line()) # - it then calls the current command class' option-parsing # callback to deal with weird options like -D, which have to # parse the option text and churn out some custom data # structure # - that data structure (in this case, a list of 2-tuples) # will then be present in the command object by the time # we get to finalize_options() (i.e. the constructor # takes care of both command-line and client options # in between initialize_options() and finalize_options()) sep_by = " (separated by '%s')" % os.pathsep user_options = [ ('build-lib=', 'b', "directory for compiled extension modules"), ('build-temp=', 't', "directory for temporary files (build by-products)"), ('plat-name=', 'p', "platform name to cross-compile for, if supported " "(default: %s)" % get_platform()), ('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" + sep_by), ('define=', 'D', "C preprocessor macros to define"), ('undef=', 'U', "C preprocessor macros to undefine"), ('libraries=', 'l', "external C libraries to link with"), ('library-dirs=', 'L', "directories to search for external C libraries" + sep_by), ('rpath=', 'R', "directories to search for shared C libraries at runtime"), ('link-objects=', 'O', "extra explicit link objects to include in the link"), ('debug', 'g', "compile/link with debugging information"), ('force', 'f', "forcibly build everything (ignore file timestamps)"), ('compiler=', 'c', "specify the compiler type"), ('parallel=', 'j', "number of parallel build jobs"), ('swig-cpp', None, "make SWIG create C++ files (default is C)"), ('swig-opts=', None, "list of SWIG command line options"), ('swig=', None, "path to the SWIG executable"), ('user', None, "add user include, library and rpath") ] boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user'] help_options = [ ('help-compiler', None, "list available compilers", show_compilers), ] def initialize_options(self): self.extensions = None self.build_lib = None self.plat_name = None self.build_temp = None self.inplace = 0 self.package = None self.include_dirs = None self.define = None self.undef = None self.libraries = None self.library_dirs = None self.rpath = None self.link_objects = None self.debug = None self.force = None self.compiler = None self.swig = None self.swig_cpp = None self.swig_opts = None self.user = None self.parallel = None def finalize_options(self): from distutils import sysconfig self.set_undefined_options('build', ('build_lib', 'build_lib'), ('build_temp', 'build_temp'), ('compiler', 'compiler'), ('debug', 'debug'), ('force', 'force'), ('parallel', 'parallel'), ('plat_name', 'plat_name'), ) if self.package is None: self.package = self.distribution.ext_package self.extensions = self.distribution.ext_modules # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. py_include = sysconfig.get_python_inc() plat_py_include = sysconfig.get_python_inc(plat_specific=1) if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] if isinstance(self.include_dirs, str): self.include_dirs = self.include_dirs.split(os.pathsep) # If in a virtualenv, add its include directory # Issue 16116 if sys.exec_prefix != sys.base_exec_prefix: self.include_dirs.append(os.path.join(sys.exec_prefix, 'include')) # Put the Python "system" include dir at the end, so that # any local include dirs take precedence. self.include_dirs.extend(py_include.split(os.path.pathsep)) if plat_py_include != py_include: self.include_dirs.extend( plat_py_include.split(os.path.pathsep)) self.ensure_string_list('libraries') self.ensure_string_list('link_objects') # Life is easier if we're not forever checking for None, so # simplify these options to empty lists if unset if self.libraries is None: self.libraries = [] if self.library_dirs is None: self.library_dirs = [] elif isinstance(self.library_dirs, str): self.library_dirs = self.library_dirs.split(os.pathsep) if self.rpath is None: self.rpath = [] elif isinstance(self.rpath, str): self.rpath = self.rpath.split(os.pathsep) # for extensions under windows use different directories # for Release and Debug builds. # also Python's library directory must be appended to library_dirs if os.name == 'nt': # the 'libs' directory is for binary installs - we assume that # must be the *native* platform. But we don't really support # cross-compiling via a binary install anyway, so we let it go. self.library_dirs.append(os.path.join(sys.exec_prefix, 'libs')) if sys.base_exec_prefix != sys.prefix: # Issue 16116 self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs')) if self.debug: self.build_temp = os.path.join(self.build_temp, "Debug") else: self.build_temp = os.path.join(self.build_temp, "Release") # Append the source distribution include and library directories, # this allows distutils on windows to work in the source tree self.include_dirs.append(os.path.dirname(get_config_h_filename())) _sys_home = getattr(sys, '_home', None) if _sys_home: self.library_dirs.append(_sys_home) # Use the .lib files for the correct architecture if self.plat_name == 'win32': suffix = 'win32' else: # win-amd64 suffix = self.plat_name[4:] new_lib = os.path.join(sys.exec_prefix, 'PCbuild') if suffix: new_lib = os.path.join(new_lib, suffix) self.library_dirs.append(new_lib) # For extensions under Cygwin, Python's library directory must be # appended to library_dirs if sys.platform[:6] == 'cygwin': if sys.executable.startswith(os.path.join(sys.exec_prefix, "bin")): # building third party extensions self.library_dirs.append(os.path.join(sys.prefix, "lib", "python" + get_python_version(), "config")) else: # building python standard extensions self.library_dirs.append('.') # For building extensions with a shared Python library, # Python's library directory must be appended to library_dirs # See Issues: #1600860, #4366 if (sysconfig.get_config_var('Py_ENABLE_SHARED')): if not sysconfig.python_build: # building third party extensions self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: # building python standard extensions self.library_dirs.append('.') # The argument parsing will result in self.define being a string, but # it has to be a list of 2-tuples. All the preprocessor symbols # specified by the 'define' option will be set to '1'. Multiple # symbols can be separated with commas. if self.define: defines = self.define.split(',') self.define = [(symbol, '1') for symbol in defines] # The option for macros to undefine is also a string from the # option parsing, but has to be a list. Multiple symbols can also # be separated with commas here. if self.undef: self.undef = self.undef.split(',') if self.swig_opts is None: self.swig_opts = [] else: self.swig_opts = self.swig_opts.split(' ') # Finally add the user include and library directories if requested if self.user: user_include = os.path.join(USER_BASE, "include") user_lib = os.path.join(USER_BASE, "lib") if os.path.isdir(user_include): self.include_dirs.append(user_include) if os.path.isdir(user_lib): self.library_dirs.append(user_lib) self.rpath.append(user_lib) if isinstance(self.parallel, str): try: self.parallel = int(self.parallel) except ValueError: raise DistutilsOptionError("parallel should be an integer") def run(self): from distutils.ccompiler import new_compiler # 'self.extensions', as supplied by setup.py, is a list of # Extension instances. See the documentation for Extension (in # distutils.extension) for details. # # For backwards compatibility with Distutils 0.8.2 and earlier, we # also allow the 'extensions' list to be a list of tuples: # (ext_name, build_info) # where build_info is a dictionary containing everything that # Extension instances do except the name, with a few things being # differently named. We convert these 2-tuples to Extension # instances as needed. if not self.extensions: return # If we were asked to build any C/C++ libraries, make sure that the # directory where we put them is in the library search path for # linking extensions. if self.distribution.has_c_libraries(): build_clib = self.get_finalized_command('build_clib') self.libraries.extend(build_clib.get_library_names() or []) self.library_dirs.append(build_clib.build_clib) # Setup the CCompiler object that we'll use to do all the # compiling and linking self.compiler = new_compiler(compiler=self.compiler, verbose=self.verbose, dry_run=self.dry_run, force=self.force) customize_compiler(self.compiler) # If we are cross-compiling, init the compiler now (if we are not # cross-compiling, init would not hurt, but people may rely on # late initialization of compiler even if they shouldn't...) if os.name == 'nt' and self.plat_name != get_platform(): self.compiler.initialize(self.plat_name) # And make sure that any compile/link-related options (which might # come from the command-line or from the setup script) are set in # that CCompiler object -- that way, they automatically apply to # all compiling and linking done here. if self.include_dirs is not None: self.compiler.set_include_dirs(self.include_dirs) if self.define is not None: # 'define' option is a list of (name,value) tuples for (name, value) in self.define: self.compiler.define_macro(name, value) if self.undef is not None: for macro in self.undef: self.compiler.undefine_macro(macro) if self.libraries is not None: self.compiler.set_libraries(self.libraries) if self.library_dirs is not None: self.compiler.set_library_dirs(self.library_dirs) if self.rpath is not None: self.compiler.set_runtime_library_dirs(self.rpath) if self.link_objects is not None: self.compiler.set_link_objects(self.link_objects) # Now actually compile and link everything. self.build_extensions() def check_extensions_list(self, extensions): """Ensure that the list of extensions (presumably provided as a command option 'extensions') is valid, i.e. it is a list of Extension objects. We also support the old-style list of 2-tuples, where the tuples are (ext_name, build_info), which are converted to Extension instances here. Raise DistutilsSetupError if the structure is invalid anywhere; just returns otherwise. """ if not isinstance(extensions, list): raise DistutilsSetupError( "'ext_modules' option must be a list of Extension instances") for i, ext in enumerate(extensions): if isinstance(ext, Extension): continue # OK! (assume type-checking done # by Extension constructor) if not isinstance(ext, tuple) or len(ext) != 2: raise DistutilsSetupError( "each element of 'ext_modules' option must be an " "Extension instance or 2-tuple") ext_name, build_info = ext log.warn("old-style (ext_name, build_info) tuple found in " "ext_modules for extension '%s' " "-- please convert to Extension instance", ext_name) if not (isinstance(ext_name, str) and extension_name_re.match(ext_name)): raise DistutilsSetupError( "first element of each tuple in 'ext_modules' " "must be the extension name (a string)") if not isinstance(build_info, dict): raise DistutilsSetupError( "second element of each tuple in 'ext_modules' " "must be a dictionary (build info)") # OK, the (ext_name, build_info) dict is type-safe: convert it # to an Extension instance. ext = Extension(ext_name, build_info['sources']) # Easy stuff: one-to-one mapping from dict elements to # instance attributes. for key in ('include_dirs', 'library_dirs', 'libraries', 'extra_objects', 'extra_compile_args', 'extra_link_args'): val = build_info.get(key) if val is not None: setattr(ext, key, val) # Medium-easy stuff: same syntax/semantics, different names. ext.runtime_library_dirs = build_info.get('rpath') if 'def_file' in build_info: log.warn("'def_file' element of build info dict " "no longer supported") # Non-trivial stuff: 'macros' split into 'define_macros' # and 'undef_macros'. macros = build_info.get('macros') if macros: ext.define_macros = [] ext.undef_macros = [] for macro in macros: if not (isinstance(macro, tuple) and len(macro) in (1, 2)): raise DistutilsSetupError( "'macros' element of build info dict " "must be 1- or 2-tuple") if len(macro) == 1: ext.undef_macros.append(macro[0]) elif len(macro) == 2: ext.define_macros.append(macro) extensions[i] = ext def get_source_files(self): self.check_extensions_list(self.extensions) filenames = [] # Wouldn't it be neat if we knew the names of header files too... for ext in self.extensions: filenames.extend(ext.sources) return filenames def get_outputs(self): # Sanity check the 'extensions' list -- can't assume this is being # done in the same run as a 'build_extensions()' call (in fact, we # can probably assume that it *isn't*!). self.check_extensions_list(self.extensions) # And build the list of output (built) filenames. Note that this # ignores the 'inplace' flag, and assumes everything goes in the # "build" tree. outputs = [] for ext in self.extensions: outputs.append(self.get_ext_fullpath(ext.name)) return outputs def build_extensions(self): # First, sanity-check the 'extensions' list self.check_extensions_list(self.extensions) if self.parallel: self._build_extensions_parallel() else: self._build_extensions_serial() def _build_extensions_parallel(self): workers = self.parallel if self.parallel is True: workers = os.cpu_count() # may return None try: from concurrent.futures import ThreadPoolExecutor except ImportError: workers = None if workers is None: self._build_extensions_serial() return with ThreadPoolExecutor(max_workers=workers) as executor: futures = [executor.submit(self.build_extension, ext) for ext in self.extensions] for ext, fut in zip(self.extensions, futures): with self._filter_build_errors(ext): fut.result() def _build_extensions_serial(self): for ext in self.extensions: with self._filter_build_errors(ext): self.build_extension(ext) @contextlib.contextmanager def _filter_build_errors(self, ext): try: yield except (CCompilerError, DistutilsError, CompileError) as e: if not ext.optional: raise self.warn('building extension "%s" failed: %s' % (ext.name, e)) def build_extension(self, ext): sources = ext.sources if sources is None or not isinstance(sources, (list, tuple)): raise DistutilsSetupError( "in 'ext_modules' option (extension '%s'), " "'sources' must be present and must be " "a list of source filenames" % ext.name) # sort to make the resulting .so file build reproducible sources = sorted(sources) ext_path = self.get_ext_fullpath(ext.name) depends = sources + ext.depends if not (self.force or newer_group(depends, ext_path, 'newer')): log.debug("skipping '%s' extension (up-to-date)", ext.name) return else: log.info("building '%s' extension", ext.name) # First, scan the sources for SWIG definition files (.i), run # SWIG on 'em to create .c files, and modify the sources list # accordingly. sources = self.swig_sources(sources, ext) # Next, compile the source code to object files. # XXX not honouring 'define_macros' or 'undef_macros' -- the # CCompiler API needs to change to accommodate this, and I # want to do one thing at a time! # Two possible sources for extra compiler arguments: # - 'extra_compile_args' in Extension object # - CFLAGS environment variable (not particularly # elegant, but people seem to expect it and I # guess it's useful) # The environment variable should take precedence, and # any sensible compiler will give precedence to later # command line args. Hence we combine them in order: extra_args = ext.extra_compile_args or [] macros = ext.define_macros[:] for undef in ext.undef_macros: macros.append((undef,)) objects = self.compiler.compile(sources, output_dir=self.build_temp, macros=macros, include_dirs=ext.include_dirs, debug=self.debug, extra_postargs=extra_args, depends=ext.depends) # XXX outdated variable, kept here in case third-part code # needs it. self._built_objects = objects[:] # Now link the object files together into a "shared object" -- # of course, first we have to figure out all the other things # that go into the mix. if ext.extra_objects: objects.extend(ext.extra_objects) extra_args = ext.extra_link_args or [] # Detect target language, if not provided language = ext.language or self.compiler.detect_language(sources) self.compiler.link_shared_object( objects, ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, runtime_library_dirs=ext.runtime_library_dirs, extra_postargs=extra_args, export_symbols=self.get_export_symbols(ext), debug=self.debug, build_temp=self.build_temp, target_lang=language) def swig_sources(self, sources, extension): """Walk the list of source files in 'sources', looking for SWIG interface (.i) files. Run SWIG on all that are found, and return a modified 'sources' list with SWIG source files replaced by the generated C (or C++) files. """ new_sources = [] swig_sources = [] swig_targets = {} # XXX this drops generated C/C++ files into the source tree, which # is fine for developers who want to distribute the generated # source -- but there should be an option to put SWIG output in # the temp dir. if self.swig_cpp: log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") if self.swig_cpp or ('-c++' in self.swig_opts) or \ ('-c++' in extension.swig_opts): target_ext = '.cpp' else: target_ext = '.c' for source in sources: (base, ext) = os.path.splitext(source) if ext == ".i": # SWIG interface file new_sources.append(base + '_wrap' + target_ext) swig_sources.append(source) swig_targets[source] = new_sources[-1] else: new_sources.append(source) if not swig_sources: return new_sources swig = self.swig or self.find_swig() swig_cmd = [swig, "-python"] swig_cmd.extend(self.swig_opts) if self.swig_cpp: swig_cmd.append("-c++") # Do not override commandline arguments if not self.swig_opts: for o in extension.swig_opts: swig_cmd.append(o) for source in swig_sources: target = swig_targets[source] log.info("swigging %s to %s", source, target) self.spawn(swig_cmd + ["-o", target, source]) return new_sources def find_swig(self): """Return the name of the SWIG executable. On Unix, this is just "swig" -- it should be in the PATH. Tries a bit harder on Windows. """ if os.name == "posix": return "swig" elif os.name == "nt": # Look for SWIG in its standard installation directory on # Windows (or so I presume!). If we find it there, great; # if not, act like Unix and assume it's in the PATH. for vers in ("1.3", "1.2", "1.1"): fn = os.path.join("c:\\swig%s" % vers, "swig.exe") if os.path.isfile(fn): return fn else: return "swig.exe" else: raise DistutilsPlatformError( "I don't know how to find (much less run) SWIG " "on platform '%s'" % os.name) # -- Name generators ----------------------------------------------- # (extension names, filenames, whatever) def get_ext_fullpath(self, ext_name): """Returns the path of the filename for a given extension. The file is located in `build_lib` or directly in the package (inplace option). """ fullname = self.get_ext_fullname(ext_name) modpath = fullname.split('.') filename = self.get_ext_filename(modpath[-1]) if not self.inplace: # no further work needed # returning : # build_dir/package/path/filename filename = os.path.join(*modpath[:-1]+[filename]) return os.path.join(self.build_lib, filename) # the inplace option requires to find the package directory # using the build_py command for that package = '.'.join(modpath[0:-1]) build_py = self.get_finalized_command('build_py') package_dir = os.path.abspath(build_py.get_package_dir(package)) # returning # package_dir/filename return os.path.join(package_dir, filename) def get_ext_fullname(self, ext_name): """Returns the fullname of a given extension name. Adds the `package.` prefix""" if self.package is None: return ext_name else: return self.package + '.' + ext_name def get_ext_filename(self, ext_name): r"""Convert the name of an extension (eg. "foo.bar") into the name of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ from distutils.sysconfig import get_config_var ext_path = ext_name.split('.') ext_suffix = get_config_var('EXT_SUFFIX') return os.path.join(*ext_path) + ext_suffix def get_export_symbols(self, ext): """Return the list of symbols that a shared extension has to export. This either uses 'ext.export_symbols' or, if it's not provided, "PyInit_" + module_name. Only relevant on Windows, where the .pyd file (DLL) must export the module "PyInit_" function. """ suffix = '_' + ext.name.split('.')[-1] try: # Unicode module name support as defined in PEP-489 # https://peps.python.org/pep-0489/#export-hook-name suffix.encode('ascii') except UnicodeEncodeError: suffix = 'U' + suffix.encode('punycode').replace(b'-', b'_').decode('ascii') initfunc_name = "PyInit" + suffix if initfunc_name not in ext.export_symbols: ext.export_symbols.append(initfunc_name) return ext.export_symbols def get_libraries(self, ext): """Return the list of libraries to link against when building a shared extension. On most platforms, this is just 'ext.libraries'; on Windows, we add the Python library (eg. python20.dll). """ # The python library is always needed on Windows. For MSVC, this # is redundant, since the library is mentioned in a pragma in # pyconfig.h that MSVC groks. The other Windows compilers all seem # to need it mentioned explicitly, though, so that's what we do. # Append '_d' to the python import library on debug builds. if sys.platform == "win32": from distutils._msvccompiler import MSVCCompiler if not isinstance(self.compiler, MSVCCompiler): template = "python%d%d" if self.debug: template = template + '_d' pythonlib = (template % (sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)) # don't extend ext.libraries, it may be shared with other # extensions, it is a reference to the original list return ext.libraries + [pythonlib] else: # On Android only the main executable and LD_PRELOADs are considered # to be RTLD_GLOBAL, all the dependencies of the main executable # remain RTLD_LOCAL and so the shared libraries must be linked with # libpython when python is built with a shared python library (issue # bpo-21536). # On Cygwin (and if required, other POSIX-like platforms based on # Windows like MinGW) it is simply necessary that all symbols in # shared libraries are resolved at link time. from distutils.sysconfig import get_config_var link_libpython = False if get_config_var('Py_ENABLE_SHARED'): # A native build on an Android device or on Cygwin if hasattr(sys, 'getandroidapilevel'): link_libpython = True elif sys.platform == 'cygwin': link_libpython = True elif '_PYTHON_HOST_PLATFORM' in os.environ: # We are cross-compiling for one of the relevant platforms if get_config_var('ANDROID_API_LEVEL') != 0: link_libpython = True elif get_config_var('MACHDEP') == 'cygwin': link_libpython = True if link_libpython: ldversion = get_config_var('LDVERSION') return ext.libraries + ['python' + ldversion] return ext.libraries python-deadlib-3.13.0/distutils/distutils/command/build_py.py000066400000000000000000000414461471045437300243710ustar00rootroot00000000000000"""distutils.command.build_py Implements the Distutils 'build_py' command.""" import os import importlib.util import sys import glob from distutils.core import Command from distutils.errors import * from distutils.util import convert_path, Mixin2to3 from distutils import log class build_py (Command): description = "\"build\" pure Python modules (copy to build directory)" user_options = [ ('build-lib=', 'd', "directory to \"build\" (copy) to"), ('compile', 'c', "compile .py to .pyc"), ('no-compile', None, "don't compile .py files [default]"), ('optimize=', 'O', "also compile with optimization: -O1 for \"python -O\", " "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), ('force', 'f', "forcibly build everything (ignore file timestamps)"), ] boolean_options = ['compile', 'force'] negative_opt = {'no-compile' : 'compile'} def initialize_options(self): self.build_lib = None self.py_modules = None self.package = None self.package_data = None self.package_dir = None self.compile = 0 self.optimize = 0 self.force = None def finalize_options(self): self.set_undefined_options('build', ('build_lib', 'build_lib'), ('force', 'force')) # Get the distribution options that are aliases for build_py # options -- list of packages and list of modules. self.packages = self.distribution.packages self.py_modules = self.distribution.py_modules self.package_data = self.distribution.package_data self.package_dir = {} if self.distribution.package_dir: for name, path in self.distribution.package_dir.items(): self.package_dir[name] = convert_path(path) self.data_files = self.get_data_files() # Ick, copied straight from install_lib.py (fancy_getopt needs a # type system! Hell, *everything* needs a type system!!!) if not isinstance(self.optimize, int): try: self.optimize = int(self.optimize) assert 0 <= self.optimize <= 2 except (ValueError, AssertionError): raise DistutilsOptionError("optimize must be 0, 1, or 2") def run(self): # XXX copy_file by default preserves atime and mtime. IMHO this is # the right thing to do, but perhaps it should be an option -- in # particular, a site administrator might want installed files to # reflect the time of installation rather than the last # modification time before the installed release. # XXX copy_file by default preserves mode, which appears to be the # wrong thing to do: if a file is read-only in the working # directory, we want it to be installed read/write so that the next # installation of the same module distribution can overwrite it # without problems. (This might be a Unix-specific issue.) Thus # we turn off 'preserve_mode' when copying to the build directory, # since the build directory is supposed to be exactly what the # installation will look like (ie. we preserve mode when # installing). # Two options control which modules will be installed: 'packages' # and 'py_modules'. The former lets us work with whole packages, not # specifying individual modules at all; the latter is for # specifying modules one-at-a-time. if self.py_modules: self.build_modules() if self.packages: self.build_packages() self.build_package_data() self.byte_compile(self.get_outputs(include_bytecode=0)) def get_data_files(self): """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" data = [] if not self.packages: return data for package in self.packages: # Locate package source directory src_dir = self.get_package_dir(package) # Compute package build directory build_dir = os.path.join(*([self.build_lib] + package.split('.'))) # Length of path to strip from found files plen = 0 if src_dir: plen = len(src_dir)+1 # Strip directory from globbed filenames filenames = [ file[plen:] for file in self.find_data_files(package, src_dir) ] data.append((package, src_dir, build_dir, filenames)) return data def find_data_files(self, package, src_dir): """Return filenames for package's data files in 'src_dir'""" globs = (self.package_data.get('', []) + self.package_data.get(package, [])) files = [] for pattern in globs: # Each pattern has to be converted to a platform-specific path filelist = glob.glob(os.path.join(glob.escape(src_dir), convert_path(pattern))) # Files that match more than one pattern are only added once files.extend([fn for fn in filelist if fn not in files and os.path.isfile(fn)]) return files def build_package_data(self): """Copy data files into build directory""" lastdir = None for package, src_dir, build_dir, filenames in self.data_files: for filename in filenames: target = os.path.join(build_dir, filename) self.mkpath(os.path.dirname(target)) self.copy_file(os.path.join(src_dir, filename), target, preserve_mode=False) def get_package_dir(self, package): """Return the directory, relative to the top of the source distribution, where package 'package' should be found (at least according to the 'package_dir' option, if any).""" path = package.split('.') if not self.package_dir: if path: return os.path.join(*path) else: return '' else: tail = [] while path: try: pdir = self.package_dir['.'.join(path)] except KeyError: tail.insert(0, path[-1]) del path[-1] else: tail.insert(0, pdir) return os.path.join(*tail) else: # Oops, got all the way through 'path' without finding a # match in package_dir. If package_dir defines a directory # for the root (nameless) package, then fallback on it; # otherwise, we might as well have not consulted # package_dir at all, as we just use the directory implied # by 'tail' (which should be the same as the original value # of 'path' at this point). pdir = self.package_dir.get('') if pdir is not None: tail.insert(0, pdir) if tail: return os.path.join(*tail) else: return '' def check_package(self, package, package_dir): # Empty dir name means current directory, which we can probably # assume exists. Also, os.path.exists and isdir don't know about # my "empty string means current dir" convention, so we have to # circumvent them. if package_dir != "": if not os.path.exists(package_dir): raise DistutilsFileError( "package directory '%s' does not exist" % package_dir) if not os.path.isdir(package_dir): raise DistutilsFileError( "supposed package directory '%s' exists, " "but is not a directory" % package_dir) # Require __init__.py for all but the "root package" if package: init_py = os.path.join(package_dir, "__init__.py") if os.path.isfile(init_py): return init_py else: log.warn(("package init file '%s' not found " + "(or not a regular file)"), init_py) # Either not in a package at all (__init__.py not expected), or # __init__.py doesn't exist -- so don't return the filename. return None def check_module(self, module, module_file): if not os.path.isfile(module_file): log.warn("file %s (for module %s) not found", module_file, module) return False else: return True def find_package_modules(self, package, package_dir): self.check_package(package, package_dir) module_files = glob.glob(os.path.join(glob.escape(package_dir), "*.py")) modules = [] setup_script = os.path.abspath(self.distribution.script_name) for f in module_files: abs_f = os.path.abspath(f) if abs_f != setup_script: module = os.path.splitext(os.path.basename(f))[0] modules.append((package, module, f)) else: self.debug_print("excluding %s" % setup_script) return modules def find_modules(self): """Finds individually-specified Python modules, ie. those listed by module name in 'self.py_modules'. Returns a list of tuples (package, module_base, filename): 'package' is a tuple of the path through package-space to the module; 'module_base' is the bare (no packages, no dots) module name, and 'filename' is the path to the ".py" file (relative to the distribution root) that implements the module. """ # Map package names to tuples of useful info about the package: # (package_dir, checked) # package_dir - the directory where we'll find source files for # this package # checked - true if we have checked that the package directory # is valid (exists, contains __init__.py, ... ?) packages = {} # List of (package, module, filename) tuples to return modules = [] # We treat modules-in-packages almost the same as toplevel modules, # just the "package" for a toplevel is empty (either an empty # string or empty list, depending on context). Differences: # - don't check for __init__.py in directory for empty package for module in self.py_modules: path = module.split('.') package = '.'.join(path[0:-1]) module_base = path[-1] try: (package_dir, checked) = packages[package] except KeyError: package_dir = self.get_package_dir(package) checked = 0 if not checked: init_py = self.check_package(package, package_dir) packages[package] = (package_dir, 1) if init_py: modules.append((package, "__init__", init_py)) # XXX perhaps we should also check for just .pyc files # (so greedy closed-source bastards can distribute Python # modules too) module_file = os.path.join(package_dir, module_base + ".py") if not self.check_module(module, module_file): continue modules.append((package, module_base, module_file)) return modules def find_all_modules(self): """Compute the list of all modules that will be built, whether they are specified one-module-at-a-time ('self.py_modules') or by whole packages ('self.packages'). Return a list of tuples (package, module, module_file), just like 'find_modules()' and 'find_package_modules()' do.""" modules = [] if self.py_modules: modules.extend(self.find_modules()) if self.packages: for package in self.packages: package_dir = self.get_package_dir(package) m = self.find_package_modules(package, package_dir) modules.extend(m) return modules def get_source_files(self): return [module[-1] for module in self.find_all_modules()] def get_module_outfile(self, build_dir, package, module): outfile_path = [build_dir] + list(package) + [module + ".py"] return os.path.join(*outfile_path) def get_outputs(self, include_bytecode=1): modules = self.find_all_modules() outputs = [] for (package, module, module_file) in modules: package = package.split('.') filename = self.get_module_outfile(self.build_lib, package, module) outputs.append(filename) if include_bytecode: if self.compile: outputs.append(importlib.util.cache_from_source( filename, optimization='')) if self.optimize > 0: outputs.append(importlib.util.cache_from_source( filename, optimization=self.optimize)) outputs += [ os.path.join(build_dir, filename) for package, src_dir, build_dir, filenames in self.data_files for filename in filenames ] return outputs def build_module(self, module, module_file, package): if isinstance(package, str): package = package.split('.') elif not isinstance(package, (list, tuple)): raise TypeError( "'package' must be a string (dot-separated), list, or tuple") # Now put the module source file into the "build" area -- this is # easy, we just copy it somewhere under self.build_lib (the build # directory for Python source). outfile = self.get_module_outfile(self.build_lib, package, module) dir = os.path.dirname(outfile) self.mkpath(dir) return self.copy_file(module_file, outfile, preserve_mode=0) def build_modules(self): modules = self.find_modules() for (package, module, module_file) in modules: # Now "build" the module -- ie. copy the source file to # self.build_lib (the build directory for Python source). # (Actually, it gets copied to the directory for this package # under self.build_lib.) self.build_module(module, module_file, package) def build_packages(self): for package in self.packages: # Get list of (package, module, module_file) tuples based on # scanning the package directory. 'package' is only included # in the tuple so that 'find_modules()' and # 'find_package_tuples()' have a consistent interface; it's # ignored here (apart from a sanity check). Also, 'module' is # the *unqualified* module name (ie. no dots, no package -- we # already know its package!), and 'module_file' is the path to # the .py file, relative to the current directory # (ie. including 'package_dir'). package_dir = self.get_package_dir(package) modules = self.find_package_modules(package, package_dir) # Now loop over the modules we found, "building" each one (just # copy it to self.build_lib). for (package_, module, module_file) in modules: assert package == package_ self.build_module(module, module_file, package) def byte_compile(self, files): if sys.dont_write_bytecode: self.warn('byte-compiling is disabled, skipping.') return from distutils.util import byte_compile prefix = self.build_lib if prefix[-1] != os.sep: prefix = prefix + os.sep # XXX this code is essentially the same as the 'byte_compile() # method of the "install_lib" command, except for the determination # of the 'prefix' string. Hmmm. if self.compile: byte_compile(files, optimize=0, force=self.force, prefix=prefix, dry_run=self.dry_run) if self.optimize > 0: byte_compile(files, optimize=self.optimize, force=self.force, prefix=prefix, dry_run=self.dry_run) class build_py_2to3(build_py, Mixin2to3): def run(self): self.updated_files = [] # Base class code if self.py_modules: self.build_modules() if self.packages: self.build_packages() self.build_package_data() # 2to3 self.run_2to3(self.updated_files) # Remaining base class code self.byte_compile(self.get_outputs(include_bytecode=0)) def build_module(self, module, module_file, package): res = build_py.build_module(self, module, module_file, package) if res[1]: # file was copied self.updated_files.append(res[0]) return res python-deadlib-3.13.0/distutils/distutils/command/build_scripts.py000066400000000000000000000141301471045437300254160ustar00rootroot00000000000000"""distutils.command.build_scripts Implements the Distutils 'build_scripts' command.""" import os, re from stat import ST_MODE from distutils import sysconfig from distutils.core import Command from distutils.dep_util import newer from distutils.util import convert_path, Mixin2to3 from distutils import log import tokenize # check if Python is called on the first line with this expression first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$') class build_scripts(Command): description = "\"build\" scripts (copy and fixup #! line)" user_options = [ ('build-dir=', 'd', "directory to \"build\" (copy) to"), ('force', 'f', "forcibly build everything (ignore file timestamps"), ('executable=', 'e', "specify final destination interpreter path"), ] boolean_options = ['force'] def initialize_options(self): self.build_dir = None self.scripts = None self.force = None self.executable = None self.outfiles = None def finalize_options(self): self.set_undefined_options('build', ('build_scripts', 'build_dir'), ('force', 'force'), ('executable', 'executable')) self.scripts = self.distribution.scripts def get_source_files(self): return self.scripts def run(self): if not self.scripts: return self.copy_scripts() def copy_scripts(self): r"""Copy each script listed in 'self.scripts'; if it's marked as a Python script in the Unix way (first line matches 'first_line_re', ie. starts with "\#!" and contains "python"), then adjust the first line to refer to the current Python interpreter as we copy. """ self.mkpath(self.build_dir) outfiles = [] updated_files = [] for script in self.scripts: adjust = False script = convert_path(script) outfile = os.path.join(self.build_dir, os.path.basename(script)) outfiles.append(outfile) if not self.force and not newer(script, outfile): log.debug("not copying %s (up-to-date)", script) continue # Always open the file, but ignore failures in dry-run mode -- # that way, we'll get accurate feedback if we can read the # script. try: f = open(script, "rb") except OSError: if not self.dry_run: raise f = None else: encoding, lines = tokenize.detect_encoding(f.readline) f.seek(0) first_line = f.readline() if not first_line: self.warn("%s is an empty file (skipping)" % script) continue match = first_line_re.match(first_line) if match: adjust = True post_interp = match.group(1) or b'' if adjust: log.info("copying and adjusting %s -> %s", script, self.build_dir) updated_files.append(outfile) if not self.dry_run: if not sysconfig.python_build: executable = self.executable else: executable = os.path.join( sysconfig.get_config_var("BINDIR"), "python%s%s" % (sysconfig.get_config_var("VERSION"), sysconfig.get_config_var("EXE"))) executable = os.fsencode(executable) shebang = b"#!" + executable + post_interp + b"\n" # Python parser starts to read a script using UTF-8 until # it gets a #coding:xxx cookie. The shebang has to be the # first line of a file, the #coding:xxx cookie cannot be # written before. So the shebang has to be decodable from # UTF-8. try: shebang.decode('utf-8') except UnicodeDecodeError: raise ValueError( "The shebang ({!r}) is not decodable " "from utf-8".format(shebang)) # If the script is encoded to a custom encoding (use a # #coding:xxx cookie), the shebang has to be decodable from # the script encoding too. try: shebang.decode(encoding) except UnicodeDecodeError: raise ValueError( "The shebang ({!r}) is not decodable " "from the script encoding ({})" .format(shebang, encoding)) with open(outfile, "wb") as outf: outf.write(shebang) outf.writelines(f.readlines()) if f: f.close() else: if f: f.close() updated_files.append(outfile) self.copy_file(script, outfile) if os.name == 'posix': for file in outfiles: if self.dry_run: log.info("changing mode of %s", file) else: oldmode = os.stat(file)[ST_MODE] & 0o7777 newmode = (oldmode | 0o555) & 0o7777 if newmode != oldmode: log.info("changing mode of %s from %o to %o", file, oldmode, newmode) os.chmod(file, newmode) # XXX should we modify self.outfiles? return outfiles, updated_files class build_scripts_2to3(build_scripts, Mixin2to3): def copy_scripts(self): outfiles, updated_files = build_scripts.copy_scripts(self) if not self.dry_run: self.run_2to3(updated_files) return outfiles, updated_files python-deadlib-3.13.0/distutils/distutils/command/check.py000066400000000000000000000134021471045437300236260ustar00rootroot00000000000000"""distutils.command.check Implements the Distutils 'check' command. """ from distutils.core import Command from distutils.errors import DistutilsSetupError try: # docutils is installed from docutils.utils import Reporter from docutils.parsers.rst import Parser from docutils import frontend from docutils import nodes class SilentReporter(Reporter): def __init__(self, source, report_level, halt_level, stream=None, debug=0, encoding='ascii', error_handler='replace'): self.messages = [] Reporter.__init__(self, source, report_level, halt_level, stream, debug, encoding, error_handler) def system_message(self, level, message, *children, **kwargs): self.messages.append((level, message, children, kwargs)) return nodes.system_message(message, level=level, type=self.levels[level], *children, **kwargs) HAS_DOCUTILS = True except Exception: # Catch all exceptions because exceptions besides ImportError probably # indicate that docutils is not ported to Py3k. HAS_DOCUTILS = False class check(Command): """This command checks the meta-data of the package. """ description = ("perform some checks on the package") user_options = [('metadata', 'm', 'Verify meta-data'), ('restructuredtext', 'r', ('Checks if long string meta-data syntax ' 'are reStructuredText-compliant')), ('strict', 's', 'Will exit with an error if a check fails')] boolean_options = ['metadata', 'restructuredtext', 'strict'] def initialize_options(self): """Sets default values for options.""" self.restructuredtext = 0 self.metadata = 1 self.strict = 0 self._warnings = 0 def finalize_options(self): pass def warn(self, msg): """Counts the number of warnings that occurs.""" self._warnings += 1 return Command.warn(self, msg) def run(self): """Runs the command.""" # perform the various tests if self.metadata: self.check_metadata() if self.restructuredtext: if HAS_DOCUTILS: self.check_restructuredtext() elif self.strict: raise DistutilsSetupError('The docutils package is needed.') # let's raise an error in strict mode, if we have at least # one warning if self.strict and self._warnings > 0: raise DistutilsSetupError('Please correct your package.') def check_metadata(self): """Ensures that all required elements of meta-data are supplied. Required fields: name, version, URL Recommended fields: (author and author_email) or (maintainer and maintainer_email) Warns if any are missing. """ metadata = self.distribution.metadata missing = [] for attr in ('name', 'version', 'url'): if not (hasattr(metadata, attr) and getattr(metadata, attr)): missing.append(attr) if missing: self.warn("missing required meta-data: %s" % ', '.join(missing)) if metadata.author: if not metadata.author_email: self.warn("missing meta-data: if 'author' supplied, " + "'author_email' should be supplied too") elif metadata.maintainer: if not metadata.maintainer_email: self.warn("missing meta-data: if 'maintainer' supplied, " + "'maintainer_email' should be supplied too") else: self.warn("missing meta-data: either (author and author_email) " + "or (maintainer and maintainer_email) " + "should be supplied") def check_restructuredtext(self): """Checks if the long string fields are reST-compliant.""" data = self.distribution.get_long_description() for warning in self._check_rst_data(data): line = warning[-1].get('line') if line is None: warning = warning[1] else: warning = '%s (line %s)' % (warning[1], line) self.warn(warning) def _check_rst_data(self, data): """Returns warnings when the provided data doesn't compile.""" # the include and csv_table directives need this to be a path source_path = self.distribution.script_name or 'setup.py' parser = Parser() try: get_default_settings = frontend.get_default_settings except AttributeError: # Deprecated in Docutils 0.19, may be broken in Docutils 0.21. settings = frontend.OptionParser(components=(Parser,)).get_default_values() else: settings = get_default_settings(Parser) settings.tab_width = 4 settings.pep_references = None settings.rfc_references = None reporter = SilentReporter(source_path, settings.report_level, settings.halt_level, stream=settings.warning_stream, debug=settings.debug, encoding=settings.error_encoding, error_handler=settings.error_encoding_error_handler) document = nodes.document(settings, reporter, source=source_path) document.note_source(source_path, -1) try: parser.parse(data, document) except AttributeError as e: reporter.messages.append( (-1, 'Could not finish the parsing: %s.' % e, '', {})) return reporter.messages python-deadlib-3.13.0/distutils/distutils/command/clean.py000066400000000000000000000053301471045437300236340ustar00rootroot00000000000000"""distutils.command.clean Implements the Distutils 'clean' command.""" # contributed by Bastian Kleineidam , added 2000-03-18 import os from distutils.core import Command from distutils.dir_util import remove_tree from distutils import log class clean(Command): description = "clean up temporary files from 'build' command" user_options = [ ('build-base=', 'b', "base build directory (default: 'build.build-base')"), ('build-lib=', None, "build directory for all modules (default: 'build.build-lib')"), ('build-temp=', 't', "temporary build directory (default: 'build.build-temp')"), ('build-scripts=', None, "build directory for scripts (default: 'build.build-scripts')"), ('bdist-base=', None, "temporary directory for built distributions"), ('all', 'a', "remove all build output, not just temporary by-products") ] boolean_options = ['all'] def initialize_options(self): self.build_base = None self.build_lib = None self.build_temp = None self.build_scripts = None self.bdist_base = None self.all = None def finalize_options(self): self.set_undefined_options('build', ('build_base', 'build_base'), ('build_lib', 'build_lib'), ('build_scripts', 'build_scripts'), ('build_temp', 'build_temp')) self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) def run(self): # remove the build/temp. directory (unless it's already # gone) if os.path.exists(self.build_temp): remove_tree(self.build_temp, dry_run=self.dry_run) else: log.debug("'%s' does not exist -- can't clean it", self.build_temp) if self.all: # remove build directories for directory in (self.build_lib, self.bdist_base, self.build_scripts): if os.path.exists(directory): remove_tree(directory, dry_run=self.dry_run) else: log.warn("'%s' does not exist -- can't clean it", directory) # just for the heck of it, try to remove the base build directory: # we might have emptied it right now, but if not we don't care if not self.dry_run: try: os.rmdir(self.build_base) log.info("removing '%s'", self.build_base) except OSError: pass python-deadlib-3.13.0/distutils/distutils/command/command_template000066400000000000000000000011711471045437300254330ustar00rootroot00000000000000"""distutils.command.x Implements the Distutils 'x' command. """ # created 2000/mm/dd, John Doe __revision__ = "$Id$" from distutils.core import Command class x(Command): # Brief (40-50 characters) description of the command description = "" # List of option tuples: long name, short name (None if no short # name), and help string. user_options = [('', '', ""), ] def initialize_options(self): self. = None self. = None self. = None def finalize_options(self): if self.x is None: self.x = def run(self): python-deadlib-3.13.0/distutils/distutils/command/config.py000066400000000000000000000314751471045437300240300ustar00rootroot00000000000000"""distutils.command.config Implements the Distutils 'config' command, a (mostly) empty command class that exists mainly to be sub-classed by specific module distributions and applications. The idea is that while every "config" command is different, at least they're all named the same, and users always see "config" in the list of standard commands. Also, this is a good place to put common configure-like tasks: "try to compile this C code", or "figure out where this header file lives". """ import os, re from distutils.core import Command from distutils.errors import DistutilsExecError from distutils.sysconfig import customize_compiler from distutils import log LANG_EXT = {"c": ".c", "c++": ".cxx"} class config(Command): description = "prepare to build" user_options = [ ('compiler=', None, "specify the compiler type"), ('cc=', None, "specify the compiler executable"), ('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"), ('libraries=', 'l', "external C libraries to link with"), ('library-dirs=', 'L', "directories to search for external C libraries"), ('noisy', None, "show every action (compile, link, run, ...) taken"), ('dump-source', None, "dump generated source files before attempting to compile them"), ] # The three standard command methods: since the "config" command # does nothing by default, these are empty. def initialize_options(self): self.compiler = None self.cc = None self.include_dirs = None self.libraries = None self.library_dirs = None # maximal output for now self.noisy = 1 self.dump_source = 1 # list of temporary files generated along-the-way that we have # to clean at some point self.temp_files = [] def finalize_options(self): if self.include_dirs is None: self.include_dirs = self.distribution.include_dirs or [] elif isinstance(self.include_dirs, str): self.include_dirs = self.include_dirs.split(os.pathsep) if self.libraries is None: self.libraries = [] elif isinstance(self.libraries, str): self.libraries = [self.libraries] if self.library_dirs is None: self.library_dirs = [] elif isinstance(self.library_dirs, str): self.library_dirs = self.library_dirs.split(os.pathsep) def run(self): pass # Utility methods for actual "config" commands. The interfaces are # loosely based on Autoconf macros of similar names. Sub-classes # may use these freely. def _check_compiler(self): """Check that 'self.compiler' really is a CCompiler object; if not, make it one. """ # We do this late, and only on-demand, because this is an expensive # import. from distutils.ccompiler import CCompiler, new_compiler if not isinstance(self.compiler, CCompiler): self.compiler = new_compiler(compiler=self.compiler, dry_run=self.dry_run, force=1) customize_compiler(self.compiler) if self.include_dirs: self.compiler.set_include_dirs(self.include_dirs) if self.libraries: self.compiler.set_libraries(self.libraries) if self.library_dirs: self.compiler.set_library_dirs(self.library_dirs) def _gen_temp_sourcefile(self, body, headers, lang): filename = "_configtest" + LANG_EXT[lang] with open(filename, "w") as file: if headers: for header in headers: file.write("#include <%s>\n" % header) file.write("\n") file.write(body) if body[-1] != "\n": file.write("\n") return filename def _preprocess(self, body, headers, include_dirs, lang): src = self._gen_temp_sourcefile(body, headers, lang) out = "_configtest.i" self.temp_files.extend([src, out]) self.compiler.preprocess(src, out, include_dirs=include_dirs) return (src, out) def _compile(self, body, headers, include_dirs, lang): src = self._gen_temp_sourcefile(body, headers, lang) if self.dump_source: dump_file(src, "compiling '%s':" % src) (obj,) = self.compiler.object_filenames([src]) self.temp_files.extend([src, obj]) self.compiler.compile([src], include_dirs=include_dirs) return (src, obj) def _link(self, body, headers, include_dirs, libraries, library_dirs, lang): (src, obj) = self._compile(body, headers, include_dirs, lang) prog = os.path.splitext(os.path.basename(src))[0] self.compiler.link_executable([obj], prog, libraries=libraries, library_dirs=library_dirs, target_lang=lang) if self.compiler.exe_extension is not None: prog = prog + self.compiler.exe_extension self.temp_files.append(prog) return (src, obj, prog) def _clean(self, *filenames): if not filenames: filenames = self.temp_files self.temp_files = [] log.info("removing: %s", ' '.join(filenames)) for filename in filenames: try: os.remove(filename) except OSError: pass # XXX these ignore the dry-run flag: what to do, what to do? even if # you want a dry-run build, you still need some sort of configuration # info. My inclination is to make it up to the real config command to # consult 'dry_run', and assume a default (minimal) configuration if # true. The problem with trying to do it here is that you'd have to # return either true or false from all the 'try' methods, neither of # which is correct. # XXX need access to the header search path and maybe default macros. def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): """Construct a source file from 'body' (a string containing lines of C/C++ code) and 'headers' (a list of header files to include) and run it through the preprocessor. Return true if the preprocessor succeeded, false if there were any errors. ('body' probably isn't of much use, but what the heck.) """ from distutils.ccompiler import CompileError self._check_compiler() ok = True try: self._preprocess(body, headers, include_dirs, lang) except CompileError: ok = False self._clean() return ok def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, lang="c"): """Construct a source file (just like 'try_cpp()'), run it through the preprocessor, and return true if any line of the output matches 'pattern'. 'pattern' should either be a compiled regex object or a string containing a regex. If both 'body' and 'headers' are None, preprocesses an empty file -- which can be useful to determine the symbols the preprocessor and compiler set by default. """ self._check_compiler() src, out = self._preprocess(body, headers, include_dirs, lang) if isinstance(pattern, str): pattern = re.compile(pattern) with open(out) as file: match = False while True: line = file.readline() if line == '': break if pattern.search(line): match = True break self._clean() return match def try_compile(self, body, headers=None, include_dirs=None, lang="c"): """Try to compile a source file built from 'body' and 'headers'. Return true on success, false otherwise. """ from distutils.ccompiler import CompileError self._check_compiler() try: self._compile(body, headers, include_dirs, lang) ok = True except CompileError: ok = False log.info(ok and "success!" or "failure.") self._clean() return ok def try_link(self, body, headers=None, include_dirs=None, libraries=None, library_dirs=None, lang="c"): """Try to compile and link a source file, built from 'body' and 'headers', to executable form. Return true on success, false otherwise. """ from distutils.ccompiler import CompileError, LinkError self._check_compiler() try: self._link(body, headers, include_dirs, libraries, library_dirs, lang) ok = True except (CompileError, LinkError): ok = False log.info(ok and "success!" or "failure.") self._clean() return ok def try_run(self, body, headers=None, include_dirs=None, libraries=None, library_dirs=None, lang="c"): """Try to compile, link to an executable, and run a program built from 'body' and 'headers'. Return true on success, false otherwise. """ from distutils.ccompiler import CompileError, LinkError self._check_compiler() try: src, obj, exe = self._link(body, headers, include_dirs, libraries, library_dirs, lang) self.spawn([exe]) ok = True except (CompileError, LinkError, DistutilsExecError): ok = False log.info(ok and "success!" or "failure.") self._clean() return ok # -- High-level methods -------------------------------------------- # (these are the ones that are actually likely to be useful # when implementing a real-world config command!) def check_func(self, func, headers=None, include_dirs=None, libraries=None, library_dirs=None, decl=0, call=0): """Determine if function 'func' is available by constructing a source file that refers to 'func', and compiles and links it. If everything succeeds, returns true; otherwise returns false. The constructed source file starts out by including the header files listed in 'headers'. If 'decl' is true, it then declares 'func' (as "int func()"); you probably shouldn't supply 'headers' and set 'decl' true in the same call, or you might get errors about a conflicting declarations for 'func'. Finally, the constructed 'main()' function either references 'func' or (if 'call' is true) calls it. 'libraries' and 'library_dirs' are used when linking. """ self._check_compiler() body = [] if decl: body.append("int %s ();" % func) body.append("int main () {") if call: body.append(" %s();" % func) else: body.append(" %s;" % func) body.append("}") body = "\n".join(body) + "\n" return self.try_link(body, headers, include_dirs, libraries, library_dirs) def check_lib(self, library, library_dirs=None, headers=None, include_dirs=None, other_libraries=[]): """Determine if 'library' is available to be linked against, without actually checking that any particular symbols are provided by it. 'headers' will be used in constructing the source file to be compiled, but the only effect of this is to check if all the header files listed are available. Any libraries listed in 'other_libraries' will be included in the link, in case 'library' has symbols that depend on other libraries. """ self._check_compiler() return self.try_link("int main (void) { }", headers, include_dirs, [library] + other_libraries, library_dirs) def check_header(self, header, include_dirs=None, library_dirs=None, lang="c"): """Determine if the system header file named by 'header_file' exists and can be found by the preprocessor; return true if so, false otherwise. """ return self.try_cpp(body="/* No body */", headers=[header], include_dirs=include_dirs) def dump_file(filename, head=None): """Dumps a file content into log.info. If head is not None, will be dumped before the file content. """ if head is None: log.info('%s', filename) else: log.info(head) file = open(filename) try: log.info(file.read()) finally: file.close() python-deadlib-3.13.0/distutils/distutils/command/install.py000066400000000000000000000671221471045437300242270ustar00rootroot00000000000000"""distutils.command.install Implements the Distutils 'install' command.""" import sys import sysconfig import os import re from distutils import log from distutils.core import Command from distutils.debug import DEBUG from distutils.sysconfig import get_config_vars from distutils.errors import DistutilsPlatformError from distutils.file_util import write_file from distutils.util import convert_path, subst_vars, change_root from distutils.util import get_platform from distutils.errors import DistutilsOptionError from site import USER_BASE from site import USER_SITE HAS_USER_SITE = (USER_SITE is not None) # The keys to an installation scheme; if any new types of files are to be # installed, be sure to add an entry to every scheme in # sysconfig._INSTALL_SCHEMES, and to SCHEME_KEYS here. SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data') # The following code provides backward-compatible INSTALL_SCHEMES # while making the sysconfig module the single point of truth. # This makes it easier for OS distributions where they need to # alter locations for packages installations in a single place. # Note that this module is deprecated (PEP 632); all consumers # of this information should switch to using sysconfig directly. INSTALL_SCHEMES = {"unix_prefix": {}, "unix_home": {}, "nt": {}} # Copy from sysconfig._INSTALL_SCHEMES for key in SCHEME_KEYS: for distutils_scheme_name, sys_scheme_name in ( ("unix_prefix", "posix_prefix"), ("unix_home", "posix_home"), ("nt", "nt")): sys_key = key sys_scheme = sysconfig._INSTALL_SCHEMES[sys_scheme_name] if key == "headers" and key not in sys_scheme: # On POSIX-y platforms, Python will: # - Build from .h files in 'headers' (only there when # building CPython) # - Install .h files to 'include' # When 'headers' is missing, fall back to 'include' sys_key = 'include' INSTALL_SCHEMES[distutils_scheme_name][key] = sys_scheme[sys_key] # Transformation to different template format for main_key in INSTALL_SCHEMES: for key, value in INSTALL_SCHEMES[main_key].items(): # Change all ocurences of {variable} to $variable value = re.sub(r"\{(.+?)\}", r"$\g<1>", value) value = value.replace("$installed_base", "$base") value = value.replace("$py_version_nodot_plat", "$py_version_nodot") if key == "headers": value += "/$dist_name" if sys.version_info >= (3, 9) and key == "platlib": # platlibdir is available since 3.9: bpo-1294959 value = value.replace("/lib/", "/$platlibdir/") INSTALL_SCHEMES[main_key][key] = value # The following part of INSTALL_SCHEMES has a different definition # than the one in sysconfig, but because both depend on the site module, # the outcomes should be the same. if HAS_USER_SITE: INSTALL_SCHEMES['nt_user'] = { 'purelib': '$usersite', 'platlib': '$usersite', 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name', 'scripts': '$userbase/Python$py_version_nodot/Scripts', 'data' : '$userbase', } INSTALL_SCHEMES['unix_user'] = { 'purelib': '$usersite', 'platlib': '$usersite', 'headers': '$userbase/include/python$py_version_short$abiflags/$dist_name', 'scripts': '$userbase/bin', 'data' : '$userbase', } class install(Command): description = "install everything from build directory" user_options = [ # Select installation scheme and set base director(y|ies) ('prefix=', None, "installation prefix"), ('exec-prefix=', None, "(Unix only) prefix for platform-specific files"), ('home=', None, "(Unix only) home directory to install under"), # Or, just set the base director(y|ies) ('install-base=', None, "base installation directory (instead of --prefix or --home)"), ('install-platbase=', None, "base installation directory for platform-specific files " + "(instead of --exec-prefix or --home)"), ('root=', None, "install everything relative to this alternate root directory"), # Or, explicitly set the installation scheme ('install-purelib=', None, "installation directory for pure Python module distributions"), ('install-platlib=', None, "installation directory for non-pure module distributions"), ('install-lib=', None, "installation directory for all module distributions " + "(overrides --install-purelib and --install-platlib)"), ('install-headers=', None, "installation directory for C/C++ headers"), ('install-scripts=', None, "installation directory for Python scripts"), ('install-data=', None, "installation directory for data files"), # Byte-compilation options -- see install_lib.py for details, as # these are duplicated from there (but only install_lib does # anything with them). ('compile', 'c', "compile .py to .pyc [default]"), ('no-compile', None, "don't compile .py files"), ('optimize=', 'O', "also compile with optimization: -O1 for \"python -O\", " "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), # Miscellaneous control options ('force', 'f', "force installation (overwrite any existing files)"), ('skip-build', None, "skip rebuilding everything (for testing/debugging)"), # Where to install documentation (eventually!) #('doc-format=', None, "format of documentation to generate"), #('install-man=', None, "directory for Unix man pages"), #('install-html=', None, "directory for HTML documentation"), #('install-info=', None, "directory for GNU info files"), ('record=', None, "filename in which to record list of installed files"), ] boolean_options = ['compile', 'force', 'skip-build'] if HAS_USER_SITE: user_options.append(('user', None, "install in user site-package '%s'" % USER_SITE)) boolean_options.append('user') negative_opt = {'no-compile' : 'compile'} def initialize_options(self): """Initializes options.""" # High-level options: these select both an installation base # and scheme. self.prefix = None self.exec_prefix = None self.home = None self.user = 0 # These select only the installation base; it's up to the user to # specify the installation scheme (currently, that means supplying # the --install-{platlib,purelib,scripts,data} options). self.install_base = None self.install_platbase = None self.root = None # These options are the actual installation directories; if not # supplied by the user, they are filled in using the installation # scheme implied by prefix/exec-prefix/home and the contents of # that installation scheme. self.install_purelib = None # for pure module distributions self.install_platlib = None # non-pure (dists w/ extensions) self.install_headers = None # for C/C++ headers self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None if HAS_USER_SITE: self.install_userbase = USER_BASE self.install_usersite = USER_SITE self.compile = None self.optimize = None # Deprecated # These two are for putting non-packagized distributions into their # own directory and creating a .pth file if it makes sense. # 'extra_path' comes from the setup file; 'install_path_file' can # be turned off if it makes no sense to install a .pth file. (But # better to install it uselessly than to guess wrong and not # install it when it's necessary and would be used!) Currently, # 'install_path_file' is always true unless some outsider meddles # with it. self.extra_path = None self.install_path_file = 1 # 'force' forces installation, even if target files are not # out-of-date. 'skip_build' skips running the "build" command, # handy if you know it's not necessary. 'warn_dir' (which is *not* # a user option, it's just there so the bdist_* commands can turn # it off) determines whether we warn about installing to a # directory not in sys.path. self.force = 0 self.skip_build = 0 self.warn_dir = 1 # These are only here as a conduit from the 'build' command to the # 'install_*' commands that do the real work. ('build_base' isn't # actually used anywhere, but it might be useful in future.) They # are not user options, because if the user told the install # command where the build directory is, that wouldn't affect the # build command. self.build_base = None self.build_lib = None # Not defined yet because we don't know anything about # documentation yet. #self.install_man = None #self.install_html = None #self.install_info = None self.record = None # -- Option finalizing methods ------------------------------------- # (This is rather more involved than for most commands, # because this is where the policy for installing third- # party Python modules on various platforms given a wide # array of user input is decided. Yes, it's quite complex!) def finalize_options(self): """Finalizes options.""" # This method (and its helpers, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default # installation directories for modules, extension modules, and # anything else we care to install from a Python module # distribution. Thus, this code makes a pretty important policy # statement about how third-party stuff is added to a Python # installation! Note that the actual work of installation is done # by the relatively simple 'install_*' commands; they just take # their orders from the installation directory options determined # here. # Check for errors/inconsistencies in the options; first, stuff # that's wrong on any platform. if ((self.prefix or self.exec_prefix or self.home) and (self.install_base or self.install_platbase)): raise DistutilsOptionError( "must supply either prefix/exec-prefix/home or " + "install-base/install-platbase -- not both") if self.home and (self.prefix or self.exec_prefix): raise DistutilsOptionError( "must supply either home or prefix/exec-prefix -- not both") if self.user and (self.prefix or self.exec_prefix or self.home or self.install_base or self.install_platbase): raise DistutilsOptionError("can't combine user with prefix, " "exec_prefix/home, or install_(plat)base") # Next, stuff that's wrong (or dubious) only on certain platforms. if os.name != "posix": if self.exec_prefix: self.warn("exec-prefix option ignored on this platform") self.exec_prefix = None # Now the interesting logic -- so interesting that we farm it out # to other methods. The goal of these methods is to set the final # values for the install_{lib,scripts,data,...} options, using as # input a heady brew of prefix, exec_prefix, home, install_base, # install_platbase, user-supplied versions of # install_{purelib,platlib,lib,scripts,data,...}, and the # INSTALL_SCHEME dictionary above. Phew! self.dump_dirs("pre-finalize_{unix,other}") if os.name == 'posix': self.finalize_unix() else: self.finalize_other() self.dump_dirs("post-finalize_{unix,other}()") # Expand configuration variables, tilde, etc. in self.install_base # and self.install_platbase -- that way, we can use $base or # $platbase in the other installation directories and not worry # about needing recursive variable expansion (shudder). py_version = sys.version.split()[0] (prefix, exec_prefix) = get_config_vars('prefix', 'exec_prefix') try: abiflags = sys.abiflags except AttributeError: # sys.abiflags may not be defined on all platforms. abiflags = '' self.config_vars = {'dist_name': self.distribution.get_name(), 'dist_version': self.distribution.get_version(), 'dist_fullname': self.distribution.get_fullname(), 'py_version': py_version, 'py_version_short': '%d.%d' % sys.version_info[:2], 'py_version_nodot': '%d%d' % sys.version_info[:2], 'sys_prefix': prefix, 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, 'abiflags': abiflags, 'platlibdir': sys.platlibdir, } if HAS_USER_SITE: self.config_vars['userbase'] = self.install_userbase self.config_vars['usersite'] = self.install_usersite if sysconfig.is_python_build(True): self.config_vars['srcdir'] = sysconfig.get_config_var('srcdir') self.expand_basedirs() self.dump_dirs("post-expand_basedirs()") # Now define config vars for the base directories so we can expand # everything else. self.config_vars['base'] = self.install_base self.config_vars['platbase'] = self.install_platbase if DEBUG: from pprint import pprint print("config vars:") pprint(self.config_vars) # Expand "~" and configuration variables in the installation # directories. self.expand_dirs() self.dump_dirs("post-expand_dirs()") # Create directories in the home dir: if self.user: self.create_home_path() # Pick the actual directory to install all modules to: either # install_purelib or install_platlib, depending on whether this # module distribution is pure or not. Of course, if the user # already specified install_lib, use their selection. if self.install_lib is None: if self.distribution.ext_modules: # has extensions: non-pure self.install_lib = self.install_platlib else: self.install_lib = self.install_purelib # Convert directories from Unix /-separated syntax to the local # convention. self.convert_paths('lib', 'purelib', 'platlib', 'scripts', 'data', 'headers') if HAS_USER_SITE: self.convert_paths('userbase', 'usersite') # Deprecated # Well, we're not actually fully completely finalized yet: we still # have to deal with 'extra_path', which is the hack for allowing # non-packagized module distributions (hello, Numerical Python!) to # get their own directories. self.handle_extra_path() self.install_libbase = self.install_lib # needed for .pth file self.install_lib = os.path.join(self.install_lib, self.extra_dirs) # If a new root directory was supplied, make all the installation # dirs relative to it. if self.root is not None: self.change_roots('libbase', 'lib', 'purelib', 'platlib', 'scripts', 'data', 'headers') self.dump_dirs("after prepending root") # Find out the build directories, ie. where to install from. self.set_undefined_options('build', ('build_base', 'build_base'), ('build_lib', 'build_lib')) # Punt on doc directories for now -- after all, we're punting on # documentation completely! def dump_dirs(self, msg): """Dumps the list of user options.""" if not DEBUG: return from distutils.fancy_getopt import longopt_xlate log.debug(msg + ":") for opt in self.user_options: opt_name = opt[0] if opt_name[-1] == "=": opt_name = opt_name[0:-1] if opt_name in self.negative_opt: opt_name = self.negative_opt[opt_name] opt_name = opt_name.translate(longopt_xlate) val = not getattr(self, opt_name) else: opt_name = opt_name.translate(longopt_xlate) val = getattr(self, opt_name) log.debug(" %s: %s", opt_name, val) def finalize_unix(self): """Finalizes options for posix platforms.""" if self.install_base is not None or self.install_platbase is not None: if ((self.install_lib is None and self.install_purelib is None and self.install_platlib is None) or self.install_headers is None or self.install_scripts is None or self.install_data is None): raise DistutilsOptionError( "install-base or install-platbase supplied, but " "installation scheme is incomplete") return if self.user: if self.install_userbase is None: raise DistutilsPlatformError( "User base directory is not specified") self.install_base = self.install_platbase = self.install_userbase self.select_scheme("unix_user") elif self.home is not None: self.install_base = self.install_platbase = self.home self.select_scheme("unix_home") else: if self.prefix is None: if self.exec_prefix is not None: raise DistutilsOptionError( "must not supply exec-prefix without prefix") self.prefix = os.path.normpath(sys.prefix) self.exec_prefix = os.path.normpath(sys.exec_prefix) else: if self.exec_prefix is None: self.exec_prefix = self.prefix self.install_base = self.prefix self.install_platbase = self.exec_prefix self.select_scheme("unix_prefix") def finalize_other(self): """Finalizes options for non-posix platforms""" if self.user: if self.install_userbase is None: raise DistutilsPlatformError( "User base directory is not specified") self.install_base = self.install_platbase = self.install_userbase self.select_scheme(os.name + "_user") elif self.home is not None: self.install_base = self.install_platbase = self.home self.select_scheme("unix_home") else: if self.prefix is None: self.prefix = os.path.normpath(sys.prefix) self.install_base = self.install_platbase = self.prefix try: self.select_scheme(os.name) except KeyError: raise DistutilsPlatformError( "I don't know how to install stuff on '%s'" % os.name) def select_scheme(self, name): """Sets the install directories by applying the install schemes.""" # it's the caller's problem if they supply a bad name! scheme = INSTALL_SCHEMES[name] for key in SCHEME_KEYS: attrname = 'install_' + key if getattr(self, attrname) is None: setattr(self, attrname, scheme[key]) def _expand_attrs(self, attrs): for attr in attrs: val = getattr(self, attr) if val is not None: if os.name == 'posix' or os.name == 'nt': val = os.path.expanduser(val) val = subst_vars(val, self.config_vars) setattr(self, attr, val) def expand_basedirs(self): """Calls `os.path.expanduser` on install_base, install_platbase and root.""" self._expand_attrs(['install_base', 'install_platbase', 'root']) def expand_dirs(self): """Calls `os.path.expanduser` on install dirs.""" self._expand_attrs(['install_purelib', 'install_platlib', 'install_lib', 'install_headers', 'install_scripts', 'install_data',]) def convert_paths(self, *names): """Call `convert_path` over `names`.""" for name in names: attr = "install_" + name setattr(self, attr, convert_path(getattr(self, attr))) def handle_extra_path(self): """Set `path_file` and `extra_dirs` using `extra_path`.""" if self.extra_path is None: self.extra_path = self.distribution.extra_path if self.extra_path is not None: log.warn( "Distribution option extra_path is deprecated. " "See issue27919 for details." ) if isinstance(self.extra_path, str): self.extra_path = self.extra_path.split(',') if len(self.extra_path) == 1: path_file = extra_dirs = self.extra_path[0] elif len(self.extra_path) == 2: path_file, extra_dirs = self.extra_path else: raise DistutilsOptionError( "'extra_path' option must be a list, tuple, or " "comma-separated string with 1 or 2 elements") # convert to local form in case Unix notation used (as it # should be in setup scripts) extra_dirs = convert_path(extra_dirs) else: path_file = None extra_dirs = '' # XXX should we warn if path_file and not extra_dirs? (in which # case the path file would be harmless but pointless) self.path_file = path_file self.extra_dirs = extra_dirs def change_roots(self, *names): """Change the install directories pointed by name using root.""" for name in names: attr = "install_" + name setattr(self, attr, change_root(self.root, getattr(self, attr))) def create_home_path(self): """Create directories under ~.""" if not self.user: return home = convert_path(os.path.expanduser("~")) for name, path in self.config_vars.items(): if path.startswith(home) and not os.path.isdir(path): self.debug_print("os.makedirs('%s', 0o700)" % path) os.makedirs(path, 0o700) # -- Command execution methods ------------------------------------- def run(self): """Runs the command.""" # Obviously have to build before we can install if not self.skip_build: self.run_command('build') # If we built for any other platform, we can't install. build_plat = self.distribution.get_command_obj('build').plat_name # check warn_dir - it is a clue that the 'install' is happening # internally, and not to sys.path, so we don't check the platform # matches what we are running. if self.warn_dir and build_plat != get_platform(): raise DistutilsPlatformError("Can't install when " "cross-compiling") # Run all sub-commands (at least those that need to be run) for cmd_name in self.get_sub_commands(): self.run_command(cmd_name) if self.path_file: self.create_path_file() # write list of installed files, if requested. if self.record: outputs = self.get_outputs() if self.root: # strip any package prefix root_len = len(self.root) for counter in range(len(outputs)): outputs[counter] = outputs[counter][root_len:] self.execute(write_file, (self.record, outputs), "writing list of installed files to '%s'" % self.record) sys_path = map(os.path.normpath, sys.path) sys_path = map(os.path.normcase, sys_path) install_lib = os.path.normcase(os.path.normpath(self.install_lib)) if (self.warn_dir and not (self.path_file and self.install_path_file) and install_lib not in sys_path): log.debug(("modules installed to '%s', which is not in " "Python's module search path (sys.path) -- " "you'll have to change the search path yourself"), self.install_lib) def create_path_file(self): """Creates the .pth file""" filename = os.path.join(self.install_libbase, self.path_file + ".pth") if self.install_path_file: self.execute(write_file, (filename, [self.extra_dirs]), "creating %s" % filename) else: self.warn("path file '%s' not created" % filename) # -- Reporting methods --------------------------------------------- def get_outputs(self): """Assembles the outputs of all the sub-commands.""" outputs = [] for cmd_name in self.get_sub_commands(): cmd = self.get_finalized_command(cmd_name) # Add the contents of cmd.get_outputs(), ensuring # that outputs doesn't contain duplicate entries for filename in cmd.get_outputs(): if filename not in outputs: outputs.append(filename) if self.path_file and self.install_path_file: outputs.append(os.path.join(self.install_libbase, self.path_file + ".pth")) return outputs def get_inputs(self): """Returns the inputs of all the sub-commands""" # XXX gee, this looks familiar ;-( inputs = [] for cmd_name in self.get_sub_commands(): cmd = self.get_finalized_command(cmd_name) inputs.extend(cmd.get_inputs()) return inputs # -- Predicates for sub-command list ------------------------------- def has_lib(self): """Returns true if the current distribution has any Python modules to install.""" return (self.distribution.has_pure_modules() or self.distribution.has_ext_modules()) def has_headers(self): """Returns true if the current distribution has any headers to install.""" return self.distribution.has_headers() def has_scripts(self): """Returns true if the current distribution has any scripts to. install.""" return self.distribution.has_scripts() def has_data(self): """Returns true if the current distribution has any data to. install.""" return self.distribution.has_data_files() # 'sub_commands': a list of commands this command might have to run to # get its work done. See cmd.py for more info. sub_commands = [('install_lib', has_lib), ('install_headers', has_headers), ('install_scripts', has_scripts), ('install_data', has_data), ('install_egg_info', lambda self:True), ] python-deadlib-3.13.0/distutils/distutils/command/install_data.py000066400000000000000000000054061471045437300252150ustar00rootroot00000000000000"""distutils.command.install_data Implements the Distutils 'install_data' command, for installing platform-independent data files.""" # contributed by Bastian Kleineidam import os from distutils.core import Command from distutils.util import change_root, convert_path class install_data(Command): description = "install data files" user_options = [ ('install-dir=', 'd', "base directory for installing data files " "(default: installation base dir)"), ('root=', None, "install everything relative to this alternate root directory"), ('force', 'f', "force installation (overwrite existing files)"), ] boolean_options = ['force'] def initialize_options(self): self.install_dir = None self.outfiles = [] self.root = None self.force = 0 self.data_files = self.distribution.data_files self.warn_dir = 1 def finalize_options(self): self.set_undefined_options('install', ('install_data', 'install_dir'), ('root', 'root'), ('force', 'force'), ) def run(self): self.mkpath(self.install_dir) for f in self.data_files: if isinstance(f, str): # it's a simple file, so copy it f = convert_path(f) if self.warn_dir: self.warn("setup script did not provide a directory for " "'%s' -- installing right in '%s'" % (f, self.install_dir)) (out, _) = self.copy_file(f, self.install_dir) self.outfiles.append(out) else: # it's a tuple with path to install to and a list of files dir = convert_path(f[0]) if not os.path.isabs(dir): dir = os.path.join(self.install_dir, dir) elif self.root: dir = change_root(self.root, dir) self.mkpath(dir) if f[1] == []: # If there are no files listed, the user must be # trying to create an empty directory, so add the # directory to the list of output files. self.outfiles.append(dir) else: # Copy files, adding them to the list of output files. for data in f[1]: data = convert_path(data) (out, _) = self.copy_file(data, dir) self.outfiles.append(out) def get_inputs(self): return self.data_files or [] def get_outputs(self): return self.outfiles python-deadlib-3.13.0/distutils/distutils/command/install_egg_info.py000066400000000000000000000050531471045437300260570ustar00rootroot00000000000000"""distutils.command.install_egg_info Implements the Distutils 'install_egg_info' command, for installing a package's PKG-INFO metadata.""" from distutils.cmd import Command from distutils import log, dir_util import os, sys, re class install_egg_info(Command): """Install an .egg-info file for the package""" description = "Install package's PKG-INFO metadata as an .egg-info file" user_options = [ ('install-dir=', 'd', "directory to install to"), ] def initialize_options(self): self.install_dir = None def finalize_options(self): self.set_undefined_options('install_lib',('install_dir','install_dir')) basename = "%s-%s-py%d.%d.egg-info" % ( to_filename(safe_name(self.distribution.get_name())), to_filename(safe_version(self.distribution.get_version())), *sys.version_info[:2] ) self.target = os.path.join(self.install_dir, basename) self.outputs = [self.target] def run(self): target = self.target if os.path.isdir(target) and not os.path.islink(target): dir_util.remove_tree(target, dry_run=self.dry_run) elif os.path.exists(target): self.execute(os.unlink,(self.target,),"Removing "+target) elif not os.path.isdir(self.install_dir): self.execute(os.makedirs, (self.install_dir,), "Creating "+self.install_dir) log.info("Writing %s", target) if not self.dry_run: with open(target, 'w', encoding='UTF-8') as f: self.distribution.metadata.write_pkg_file(f) def get_outputs(self): return self.outputs # The following routines are taken from setuptools' pkg_resources module and # can be replaced by importing them from pkg_resources once it is included # in the stdlib. def safe_name(name): """Convert an arbitrary string to a standard distribution name Any runs of non-alphanumeric/. characters are replaced with a single '-'. """ return re.sub('[^A-Za-z0-9.]+', '-', name) def safe_version(version): """Convert an arbitrary string to a standard version string Spaces become dots, and all other non-alphanumeric characters become dashes, with runs of multiple dashes condensed to a single dash. """ version = version.replace(' ','.') return re.sub('[^A-Za-z0-9.]+', '-', version) def to_filename(name): """Convert a project or version name to its filename-escaped form Any '-' characters are currently replaced with '_'. """ return name.replace('-','_') python-deadlib-3.13.0/distutils/distutils/command/install_headers.py000066400000000000000000000024221471045437300257120ustar00rootroot00000000000000"""distutils.command.install_headers Implements the Distutils 'install_headers' command, to install C/C++ header files to the Python include directory.""" from distutils.core import Command # XXX force is never used class install_headers(Command): description = "install C/C++ header files" user_options = [('install-dir=', 'd', "directory to install header files to"), ('force', 'f', "force installation (overwrite existing files)"), ] boolean_options = ['force'] def initialize_options(self): self.install_dir = None self.force = 0 self.outfiles = [] def finalize_options(self): self.set_undefined_options('install', ('install_headers', 'install_dir'), ('force', 'force')) def run(self): headers = self.distribution.headers if not headers: return self.mkpath(self.install_dir) for header in headers: (out, _) = self.copy_file(header, self.install_dir) self.outfiles.append(out) def get_inputs(self): return self.distribution.headers or [] def get_outputs(self): return self.outfiles python-deadlib-3.13.0/distutils/distutils/command/install_lib.py000066400000000000000000000203151471045437300250460ustar00rootroot00000000000000"""distutils.command.install_lib Implements the Distutils 'install_lib' command (install all Python modules).""" import os import importlib.util import sys from distutils.core import Command from distutils.errors import DistutilsOptionError # Extension for Python source files. PYTHON_SOURCE_EXTENSION = ".py" class install_lib(Command): description = "install all Python modules (extensions and pure Python)" # The byte-compilation options are a tad confusing. Here are the # possible scenarios: # 1) no compilation at all (--no-compile --no-optimize) # 2) compile .pyc only (--compile --no-optimize; default) # 3) compile .pyc and "opt-1" .pyc (--compile --optimize) # 4) compile "opt-1" .pyc only (--no-compile --optimize) # 5) compile .pyc and "opt-2" .pyc (--compile --optimize-more) # 6) compile "opt-2" .pyc only (--no-compile --optimize-more) # # The UI for this is two options, 'compile' and 'optimize'. # 'compile' is strictly boolean, and only decides whether to # generate .pyc files. 'optimize' is three-way (0, 1, or 2), and # decides both whether to generate .pyc files and what level of # optimization to use. user_options = [ ('install-dir=', 'd', "directory to install to"), ('build-dir=','b', "build directory (where to install from)"), ('force', 'f', "force installation (overwrite existing files)"), ('compile', 'c', "compile .py to .pyc [default]"), ('no-compile', None, "don't compile .py files"), ('optimize=', 'O', "also compile with optimization: -O1 for \"python -O\", " "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), ('skip-build', None, "skip the build steps"), ] boolean_options = ['force', 'compile', 'skip-build'] negative_opt = {'no-compile' : 'compile'} def initialize_options(self): # let the 'install' command dictate our installation directory self.install_dir = None self.build_dir = None self.force = 0 self.compile = None self.optimize = None self.skip_build = None def finalize_options(self): # Get all the information we need to install pure Python modules # from the umbrella 'install' command -- build (source) directory, # install (target) directory, and whether to compile .py files. self.set_undefined_options('install', ('build_lib', 'build_dir'), ('install_lib', 'install_dir'), ('force', 'force'), ('compile', 'compile'), ('optimize', 'optimize'), ('skip_build', 'skip_build'), ) if self.compile is None: self.compile = True if self.optimize is None: self.optimize = False if not isinstance(self.optimize, int): try: self.optimize = int(self.optimize) if self.optimize not in (0, 1, 2): raise AssertionError except (ValueError, AssertionError): raise DistutilsOptionError("optimize must be 0, 1, or 2") def run(self): # Make sure we have built everything we need first self.build() # Install everything: simply dump the entire contents of the build # directory to the installation directory (that's the beauty of # having a build directory!) outfiles = self.install() # (Optionally) compile .py to .pyc if outfiles is not None and self.distribution.has_pure_modules(): self.byte_compile(outfiles) # -- Top-level worker functions ------------------------------------ # (called from 'run()') def build(self): if not self.skip_build: if self.distribution.has_pure_modules(): self.run_command('build_py') if self.distribution.has_ext_modules(): self.run_command('build_ext') def install(self): if os.path.isdir(self.build_dir): outfiles = self.copy_tree(self.build_dir, self.install_dir) else: self.warn("'%s' does not exist -- no Python modules to install" % self.build_dir) return return outfiles def byte_compile(self, files): if sys.dont_write_bytecode: self.warn('byte-compiling is disabled, skipping.') return from distutils.util import byte_compile # Get the "--root" directory supplied to the "install" command, # and use it as a prefix to strip off the purported filename # encoded in bytecode files. This is far from complete, but it # should at least generate usable bytecode in RPM distributions. install_root = self.get_finalized_command('install').root if self.compile: byte_compile(files, optimize=0, force=self.force, prefix=install_root, dry_run=self.dry_run) if self.optimize > 0: byte_compile(files, optimize=self.optimize, force=self.force, prefix=install_root, verbose=self.verbose, dry_run=self.dry_run) # -- Utility methods ----------------------------------------------- def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir): if not has_any: return [] build_cmd = self.get_finalized_command(build_cmd) build_files = build_cmd.get_outputs() build_dir = getattr(build_cmd, cmd_option) prefix_len = len(build_dir) + len(os.sep) outputs = [] for file in build_files: outputs.append(os.path.join(output_dir, file[prefix_len:])) return outputs def _bytecode_filenames(self, py_filenames): bytecode_files = [] for py_file in py_filenames: # Since build_py handles package data installation, the # list of outputs can contain more than just .py files. # Make sure we only report bytecode for the .py files. ext = os.path.splitext(os.path.normcase(py_file))[1] if ext != PYTHON_SOURCE_EXTENSION: continue if self.compile: bytecode_files.append(importlib.util.cache_from_source( py_file, optimization='')) if self.optimize > 0: bytecode_files.append(importlib.util.cache_from_source( py_file, optimization=self.optimize)) return bytecode_files # -- External interface -------------------------------------------- # (called by outsiders) def get_outputs(self): """Return the list of files that would be installed if this command were actually run. Not affected by the "dry-run" flag or whether modules have actually been built yet. """ pure_outputs = \ self._mutate_outputs(self.distribution.has_pure_modules(), 'build_py', 'build_lib', self.install_dir) if self.compile: bytecode_outputs = self._bytecode_filenames(pure_outputs) else: bytecode_outputs = [] ext_outputs = \ self._mutate_outputs(self.distribution.has_ext_modules(), 'build_ext', 'build_lib', self.install_dir) return pure_outputs + bytecode_outputs + ext_outputs def get_inputs(self): """Get the list of files that are input to this command, ie. the files that get installed as they are named in the build tree. The files in this list correspond one-to-one to the output filenames returned by 'get_outputs()'. """ inputs = [] if self.distribution.has_pure_modules(): build_py = self.get_finalized_command('build_py') inputs.extend(build_py.get_outputs()) if self.distribution.has_ext_modules(): build_ext = self.get_finalized_command('build_ext') inputs.extend(build_ext.get_outputs()) return inputs python-deadlib-3.13.0/distutils/distutils/command/install_scripts.py000066400000000000000000000037411471045437300257730ustar00rootroot00000000000000"""distutils.command.install_scripts Implements the Distutils 'install_scripts' command, for installing Python scripts.""" # contributed by Bastian Kleineidam import os from distutils.core import Command from distutils import log from stat import ST_MODE class install_scripts(Command): description = "install scripts (Python or otherwise)" user_options = [ ('install-dir=', 'd', "directory to install scripts to"), ('build-dir=','b', "build directory (where to install from)"), ('force', 'f', "force installation (overwrite existing files)"), ('skip-build', None, "skip the build steps"), ] boolean_options = ['force', 'skip-build'] def initialize_options(self): self.install_dir = None self.force = 0 self.build_dir = None self.skip_build = None def finalize_options(self): self.set_undefined_options('build', ('build_scripts', 'build_dir')) self.set_undefined_options('install', ('install_scripts', 'install_dir'), ('force', 'force'), ('skip_build', 'skip_build'), ) def run(self): if not self.skip_build: self.run_command('build_scripts') self.outfiles = self.copy_tree(self.build_dir, self.install_dir) if os.name == 'posix': # Set the executable bits (owner, group, and world) on # all the scripts we just installed. for file in self.get_outputs(): if self.dry_run: log.info("changing mode of %s", file) else: mode = ((os.stat(file)[ST_MODE]) | 0o555) & 0o7777 log.info("changing mode of %s to %o", file, mode) os.chmod(file, mode) def get_inputs(self): return self.distribution.scripts or [] def get_outputs(self): return self.outfiles or [] python-deadlib-3.13.0/distutils/distutils/command/register.py000066400000000000000000000267001471045437300244020ustar00rootroot00000000000000"""distutils.command.register Implements the Distutils 'register' command (register with the repository). """ # created 2002/10/21, Richard Jones import getpass import io import urllib.parse, urllib.request from warnings import warn from distutils.core import PyPIRCCommand from distutils.errors import * from distutils import log class register(PyPIRCCommand): description = ("register the distribution with the Python package index") user_options = PyPIRCCommand.user_options + [ ('list-classifiers', None, 'list the valid Trove classifiers'), ('strict', None , 'Will stop the registering if the meta-data are not fully compliant') ] boolean_options = PyPIRCCommand.boolean_options + [ 'verify', 'list-classifiers', 'strict'] sub_commands = [('check', lambda self: True)] def initialize_options(self): PyPIRCCommand.initialize_options(self) self.list_classifiers = 0 self.strict = 0 def finalize_options(self): PyPIRCCommand.finalize_options(self) # setting options for the `check` subcommand check_options = {'strict': ('register', self.strict), 'restructuredtext': ('register', 1)} self.distribution.command_options['check'] = check_options def run(self): self.finalize_options() self._set_config() # Run sub commands for cmd_name in self.get_sub_commands(): self.run_command(cmd_name) if self.dry_run: self.verify_metadata() elif self.list_classifiers: self.classifiers() else: self.send_metadata() def check_metadata(self): """Deprecated API.""" warn("distutils.command.register.check_metadata is deprecated, \ use the check command instead", PendingDeprecationWarning) check = self.distribution.get_command_obj('check') check.ensure_finalized() check.strict = self.strict check.restructuredtext = 1 check.run() def _set_config(self): ''' Reads the configuration file and set attributes. ''' config = self._read_pypirc() if config != {}: self.username = config['username'] self.password = config['password'] self.repository = config['repository'] self.realm = config['realm'] self.has_config = True else: if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): raise ValueError('%s not found in .pypirc' % self.repository) if self.repository == 'pypi': self.repository = self.DEFAULT_REPOSITORY self.has_config = False def classifiers(self): ''' Fetch the list of classifiers from the server. ''' url = self.repository+'?:action=list_classifiers' response = urllib.request.urlopen(url) log.info(self._read_pypi_response(response)) def verify_metadata(self): ''' Send the metadata to the package index server to be checked. ''' # send the info to the server and report the result (code, result) = self.post_to_server(self.build_post_data('verify')) log.info('Server response (%s): %s', code, result) def send_metadata(self): ''' Send the metadata to the package index server. Well, do the following: 1. figure who the user is, and then 2. send the data as a Basic auth'ed POST. First we try to read the username/password from $HOME/.pypirc, which is a ConfigParser-formatted file with a section [distutils] containing username and password entries (both in clear text). Eg: [distutils] index-servers = pypi [pypi] username: fred password: sekrit Otherwise, to figure who the user is, we offer the user three choices: 1. use existing login, 2. register as a new user, or 3. set the password to a random string and email the user. ''' # see if we can short-cut and get the username/password from the # config if self.has_config: choice = '1' username = self.username password = self.password else: choice = 'x' username = password = '' # get the user's login info choices = '1 2 3 4'.split() while choice not in choices: self.announce('''\ We need to know who you are, so please choose either: 1. use your existing login, 2. register as a new user, 3. have the server generate a new password for you (and email it to you), or 4. quit Your selection [default 1]: ''', log.INFO) choice = input() if not choice: choice = '1' elif choice not in choices: print('Please choose one of the four options!') if choice == '1': # get the username and password while not username: username = input('Username: ') while not password: password = getpass.getpass('Password: ') # set up the authentication auth = urllib.request.HTTPPasswordMgr() host = urllib.parse.urlparse(self.repository)[1] auth.add_password(self.realm, host, username, password) # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), auth) self.announce('Server response (%s): %s' % (code, result), log.INFO) # possibly save the login if code == 200: if self.has_config: # sharing the password in the distribution instance # so the upload command can reuse it self.distribution.password = password else: self.announce(('I can store your PyPI login so future ' 'submissions will be faster.'), log.INFO) self.announce('(the login will be stored in %s)' % \ self._get_rc_file(), log.INFO) choice = 'X' while choice.lower() not in 'yn': choice = input('Save your login (y/N)?') if not choice: choice = 'n' if choice.lower() == 'y': self._store_pypirc(username, password) elif choice == '2': data = {':action': 'user'} data['name'] = data['password'] = data['email'] = '' data['confirm'] = None while not data['name']: data['name'] = input('Username: ') while data['password'] != data['confirm']: while not data['password']: data['password'] = getpass.getpass('Password: ') while not data['confirm']: data['confirm'] = getpass.getpass(' Confirm: ') if data['password'] != data['confirm']: data['password'] = '' data['confirm'] = None print("Password and confirm don't match!") while not data['email']: data['email'] = input(' EMail: ') code, result = self.post_to_server(data) if code != 200: log.info('Server response (%s): %s', code, result) else: log.info('You will receive an email shortly.') log.info(('Follow the instructions in it to ' 'complete registration.')) elif choice == '3': data = {':action': 'password_reset'} data['email'] = '' while not data['email']: data['email'] = input('Your email address: ') code, result = self.post_to_server(data) log.info('Server response (%s): %s', code, result) def build_post_data(self, action): # figure the data to send - the metadata plus some additional # information used by the package server meta = self.distribution.metadata data = { ':action': action, 'metadata_version' : '1.0', 'name': meta.get_name(), 'version': meta.get_version(), 'summary': meta.get_description(), 'home_page': meta.get_url(), 'author': meta.get_contact(), 'author_email': meta.get_contact_email(), 'license': meta.get_licence(), 'description': meta.get_long_description(), 'keywords': meta.get_keywords(), 'platform': meta.get_platforms(), 'classifiers': meta.get_classifiers(), 'download_url': meta.get_download_url(), # PEP 314 'provides': meta.get_provides(), 'requires': meta.get_requires(), 'obsoletes': meta.get_obsoletes(), } if data['provides'] or data['requires'] or data['obsoletes']: data['metadata_version'] = '1.1' return data def post_to_server(self, data, auth=None): ''' Post a query to the server, and return a string response. ''' if 'name' in data: self.announce('Registering %s to %s' % (data['name'], self.repository), log.INFO) # Build up the MIME payload for the urllib2 POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary end_boundary = sep_boundary + '--' body = io.StringIO() for key, value in data.items(): # handle multiple entries for the same name if type(value) not in (type([]), type( () )): value = [value] for value in value: value = str(value) body.write(sep_boundary) body.write('\nContent-Disposition: form-data; name="%s"'%key) body.write("\n\n") body.write(value) if value and value[-1] == '\r': body.write('\n') # write an extra newline (lurve Macs) body.write(end_boundary) body.write("\n") body = body.getvalue().encode("utf-8") # build the Request headers = { 'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary, 'Content-length': str(len(body)) } req = urllib.request.Request(self.repository, body, headers) # handle HTTP and include the Basic Auth handler opener = urllib.request.build_opener( urllib.request.HTTPBasicAuthHandler(password_mgr=auth) ) data = '' try: result = opener.open(req) except urllib.error.HTTPError as e: if self.show_response: data = e.fp.read() result = e.code, e.msg except urllib.error.URLError as e: result = 500, str(e) else: if self.show_response: data = self._read_pypi_response(result) result = 200, 'OK' if self.show_response: msg = '\n'.join(('-' * 75, data, '-' * 75)) self.announce(msg, log.INFO) return result python-deadlib-3.13.0/distutils/distutils/command/sdist.py000066400000000000000000000450751471045437300237120ustar00rootroot00000000000000"""distutils.command.sdist Implements the Distutils 'sdist' command (create a source distribution).""" import os import sys from glob import glob from warnings import warn from distutils.core import Command from distutils import dir_util from distutils import file_util from distutils import archive_util from distutils.text_file import TextFile from distutils.filelist import FileList from distutils import log from distutils.util import convert_path from distutils.errors import DistutilsTemplateError, DistutilsOptionError def show_formats(): """Print all possible values for the 'formats' option (used by the "--help-formats" command-line option). """ from distutils.fancy_getopt import FancyGetopt from distutils.archive_util import ARCHIVE_FORMATS formats = [] for format in ARCHIVE_FORMATS.keys(): formats.append(("formats=" + format, None, ARCHIVE_FORMATS[format][2])) formats.sort() FancyGetopt(formats).print_help( "List of available source distribution formats:") class sdist(Command): description = "create a source distribution (tarball, zip file, etc.)" def checking_metadata(self): """Callable used for the check sub-command. Placed here so user_options can view it""" return self.metadata_check user_options = [ ('template=', 't', "name of manifest template file [default: MANIFEST.in]"), ('manifest=', 'm', "name of manifest file [default: MANIFEST]"), ('use-defaults', None, "include the default file set in the manifest " "[default; disable with --no-defaults]"), ('no-defaults', None, "don't include the default file set"), ('prune', None, "specifically exclude files/directories that should not be " "distributed (build tree, RCS/CVS dirs, etc.) " "[default; disable with --no-prune]"), ('no-prune', None, "don't automatically exclude anything"), ('manifest-only', 'o', "just regenerate the manifest and then stop " "(implies --force-manifest)"), ('force-manifest', 'f', "forcibly regenerate the manifest and carry on as usual. " "Deprecated: now the manifest is always regenerated."), ('formats=', None, "formats for source distribution (comma-separated list)"), ('keep-temp', 'k', "keep the distribution tree around after creating " + "archive file(s)"), ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), ('metadata-check', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), ('owner=', 'u', "Owner name used when creating a tar file [default: current user]"), ('group=', 'g', "Group name used when creating a tar file [default: current group]"), ] boolean_options = ['use-defaults', 'prune', 'manifest-only', 'force-manifest', 'keep-temp', 'metadata-check'] help_options = [ ('help-formats', None, "list available distribution formats", show_formats), ] negative_opt = {'no-defaults': 'use-defaults', 'no-prune': 'prune' } sub_commands = [('check', checking_metadata)] READMES = ('README', 'README.txt', 'README.rst') def initialize_options(self): # 'template' and 'manifest' are, respectively, the names of # the manifest template and manifest file. self.template = None self.manifest = None # 'use_defaults': if true, we will include the default file set # in the manifest self.use_defaults = 1 self.prune = 1 self.manifest_only = 0 self.force_manifest = 0 self.formats = ['gztar'] self.keep_temp = 0 self.dist_dir = None self.archive_files = None self.metadata_check = 1 self.owner = None self.group = None def finalize_options(self): if self.manifest is None: self.manifest = "MANIFEST" if self.template is None: self.template = "MANIFEST.in" self.ensure_string_list('formats') bad_format = archive_util.check_archive_formats(self.formats) if bad_format: raise DistutilsOptionError( "unknown archive format '%s'" % bad_format) if self.dist_dir is None: self.dist_dir = "dist" def run(self): # 'filelist' contains the list of files that will make up the # manifest self.filelist = FileList() # Run sub commands for cmd_name in self.get_sub_commands(): self.run_command(cmd_name) # Do whatever it takes to get the list of files to process # (process the manifest template, read an existing manifest, # whatever). File list is accumulated in 'self.filelist'. self.get_file_list() # If user just wanted us to regenerate the manifest, stop now. if self.manifest_only: return # Otherwise, go ahead and create the source distribution tarball, # or zipfile, or whatever. self.make_distribution() def check_metadata(self): """Deprecated API.""" warn("distutils.command.sdist.check_metadata is deprecated, \ use the check command instead", PendingDeprecationWarning) check = self.distribution.get_command_obj('check') check.ensure_finalized() check.run() def get_file_list(self): """Figure out the list of files to include in the source distribution, and put it in 'self.filelist'. This might involve reading the manifest template (and writing the manifest), or just reading the manifest, or just using the default file set -- it all depends on the user's options. """ # new behavior when using a template: # the file list is recalculated every time because # even if MANIFEST.in or setup.py are not changed # the user might have added some files in the tree that # need to be included. # # This makes --force the default and only behavior with templates. template_exists = os.path.isfile(self.template) if not template_exists and self._manifest_is_not_generated(): self.read_manifest() self.filelist.sort() self.filelist.remove_duplicates() return if not template_exists: self.warn(("manifest template '%s' does not exist " + "(using default file list)") % self.template) self.filelist.findall() if self.use_defaults: self.add_defaults() if template_exists: self.read_template() if self.prune: self.prune_file_list() self.filelist.sort() self.filelist.remove_duplicates() self.write_manifest() def add_defaults(self): """Add all the default files to self.filelist: - README or README.txt - setup.py - test/test*.py - all pure Python modules mentioned in setup script - all files pointed by package_data (build_py) - all files defined in data_files. - all files defined as scripts. - all C sources listed as part of extensions or C libraries in the setup script (doesn't catch C headers!) Warns if (README or README.txt) or setup.py are missing; everything else is optional. """ self._add_defaults_standards() self._add_defaults_optional() self._add_defaults_python() self._add_defaults_data_files() self._add_defaults_ext() self._add_defaults_c_libs() self._add_defaults_scripts() @staticmethod def _cs_path_exists(fspath): """ Case-sensitive path existence check >>> sdist._cs_path_exists(__file__) True >>> sdist._cs_path_exists(__file__.upper()) False """ if not os.path.exists(fspath): return False # make absolute so we always have a directory abspath = os.path.abspath(fspath) directory, filename = os.path.split(abspath) return filename in os.listdir(directory) def _add_defaults_standards(self): standards = [self.READMES, self.distribution.script_name] for fn in standards: if isinstance(fn, tuple): alts = fn got_it = False for fn in alts: if self._cs_path_exists(fn): got_it = True self.filelist.append(fn) break if not got_it: self.warn("standard file not found: should have one of " + ', '.join(alts)) else: if self._cs_path_exists(fn): self.filelist.append(fn) else: self.warn("standard file '%s' not found" % fn) def _add_defaults_optional(self): optional = ['test/test*.py', 'setup.cfg'] for pattern in optional: files = filter(os.path.isfile, glob(pattern)) self.filelist.extend(files) def _add_defaults_python(self): # build_py is used to get: # - python modules # - files defined in package_data build_py = self.get_finalized_command('build_py') # getting python files if self.distribution.has_pure_modules(): self.filelist.extend(build_py.get_source_files()) # getting package_data files # (computed in build_py.data_files by build_py.finalize_options) for pkg, src_dir, build_dir, filenames in build_py.data_files: for filename in filenames: self.filelist.append(os.path.join(src_dir, filename)) def _add_defaults_data_files(self): # getting distribution.data_files if self.distribution.has_data_files(): for item in self.distribution.data_files: if isinstance(item, str): # plain file item = convert_path(item) if os.path.isfile(item): self.filelist.append(item) else: # a (dirname, filenames) tuple dirname, filenames = item for f in filenames: f = convert_path(f) if os.path.isfile(f): self.filelist.append(f) def _add_defaults_ext(self): if self.distribution.has_ext_modules(): build_ext = self.get_finalized_command('build_ext') self.filelist.extend(build_ext.get_source_files()) def _add_defaults_c_libs(self): if self.distribution.has_c_libraries(): build_clib = self.get_finalized_command('build_clib') self.filelist.extend(build_clib.get_source_files()) def _add_defaults_scripts(self): if self.distribution.has_scripts(): build_scripts = self.get_finalized_command('build_scripts') self.filelist.extend(build_scripts.get_source_files()) def read_template(self): """Read and parse manifest template file named by self.template. (usually "MANIFEST.in") The parsing and processing is done by 'self.filelist', which updates itself accordingly. """ log.info("reading manifest template '%s'", self.template) template = TextFile(self.template, strip_comments=1, skip_blanks=1, join_lines=1, lstrip_ws=1, rstrip_ws=1, collapse_join=1) try: while True: line = template.readline() if line is None: # end of file break try: self.filelist.process_template_line(line) # the call above can raise a DistutilsTemplateError for # malformed lines, or a ValueError from the lower-level # convert_path function except (DistutilsTemplateError, ValueError) as msg: self.warn("%s, line %d: %s" % (template.filename, template.current_line, msg)) finally: template.close() def prune_file_list(self): """Prune off branches that might slip into the file list as created by 'read_template()', but really don't belong there: * the build tree (typically "build") * the release tree itself (only an issue if we ran "sdist" previously with --keep-temp, or it aborted) * any RCS, CVS, .svn, .hg, .git, .bzr, _darcs directories """ build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() self.filelist.exclude_pattern(None, prefix=build.build_base) self.filelist.exclude_pattern(None, prefix=base_dir) if sys.platform == 'win32': seps = r'/|\\' else: seps = '/' vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', '_darcs'] vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps) self.filelist.exclude_pattern(vcs_ptrn, is_regex=1) def write_manifest(self): """Write the file list in 'self.filelist' (presumably as filled in by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. """ if self._manifest_is_not_generated(): log.info("not writing to manually maintained " "manifest file '%s'" % self.manifest) return content = self.filelist.files[:] content.insert(0, '# file GENERATED by distutils, do NOT edit') self.execute(file_util.write_file, (self.manifest, content), "writing manifest file '%s'" % self.manifest) def _manifest_is_not_generated(self): # check for special comment used in 3.1.3 and higher if not os.path.isfile(self.manifest): return False fp = open(self.manifest) try: first_line = fp.readline() finally: fp.close() return first_line != '# file GENERATED by distutils, do NOT edit\n' def read_manifest(self): """Read the manifest file (named by 'self.manifest') and use it to fill in 'self.filelist', the list of files to include in the source distribution. """ log.info("reading manifest file '%s'", self.manifest) with open(self.manifest) as manifest: for line in manifest: # ignore comments and blank lines line = line.strip() if line.startswith('#') or not line: continue self.filelist.append(line) def make_release_tree(self, base_dir, files): """Create the directory tree that will become the source distribution archive. All directories implied by the filenames in 'files' are created under 'base_dir', and then we hard link or copy (if hard linking is unavailable) those files into place. Essentially, this duplicates the developer's source tree, but in a directory named after the distribution, containing only the files to be distributed. """ # Create all the directories under 'base_dir' necessary to # put 'files' there; the 'mkpath()' is just so we don't die # if the manifest happens to be empty. self.mkpath(base_dir) dir_util.create_tree(base_dir, files, dry_run=self.dry_run) # And walk over the list of files, either making a hard link (if # os.link exists) to each one that doesn't already exist in its # corresponding location under 'base_dir', or copying each file # that's out-of-date in 'base_dir'. (Usually, all files will be # out-of-date, because by default we blow away 'base_dir' when # we're done making the distribution archives.) if hasattr(os, 'link'): # can make hard links on this system link = 'hard' msg = "making hard links in %s..." % base_dir else: # nope, have to copy link = None msg = "copying files to %s..." % base_dir if not files: log.warn("no files to distribute -- empty manifest?") else: log.info(msg) for file in files: if not os.path.isfile(file): log.warn("'%s' not a regular file -- skipping", file) else: dest = os.path.join(base_dir, file) self.copy_file(file, dest, link=link) self.distribution.metadata.write_pkg_info(base_dir) def make_distribution(self): """Create the source distribution(s). First, we create the release tree with 'make_release_tree()'; then, we create all required archive files (according to 'self.formats') from the release tree. Finally, we clean up by blowing away the release tree (unless 'self.keep_temp' is true). The list of archive files created is stored so it can be retrieved later by 'get_archive_files()'. """ # Don't warn about missing meta-data here -- should be (and is!) # done elsewhere. base_dir = self.distribution.get_fullname() base_name = os.path.join(self.dist_dir, base_dir) self.make_release_tree(base_dir, self.filelist.files) archive_files = [] # remember names of files we create # tar archive must be created last to avoid overwrite and remove if 'tar' in self.formats: self.formats.append(self.formats.pop(self.formats.index('tar'))) for fmt in self.formats: file = self.make_archive(base_name, fmt, base_dir=base_dir, owner=self.owner, group=self.group) archive_files.append(file) self.distribution.dist_files.append(('sdist', '', file)) self.archive_files = archive_files if not self.keep_temp: dir_util.remove_tree(base_dir, dry_run=self.dry_run) def get_archive_files(self): """Return the list of archive files created when the command was run, or None if the command hasn't run yet. """ return self.archive_files python-deadlib-3.13.0/distutils/distutils/command/upload.py000066400000000000000000000167051471045437300240460ustar00rootroot00000000000000""" distutils.command.upload Implements the Distutils 'upload' subcommand (upload package to a package index). """ import os import io import hashlib from base64 import standard_b64encode from urllib.error import HTTPError from urllib.request import urlopen, Request from urllib.parse import urlparse from distutils.errors import DistutilsError, DistutilsOptionError from distutils.core import PyPIRCCommand from distutils.spawn import spawn from distutils import log # PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256) # https://bugs.python.org/issue40698 _FILE_CONTENT_DIGESTS = { "md5_digest": getattr(hashlib, "md5", None), "sha256_digest": getattr(hashlib, "sha256", None), "blake2_256_digest": getattr(hashlib, "blake2b", None), } class upload(PyPIRCCommand): description = "upload binary package to PyPI" user_options = PyPIRCCommand.user_options + [ ('sign', 's', 'sign files to upload using gpg'), ('identity=', 'i', 'GPG identity used to sign files'), ] boolean_options = PyPIRCCommand.boolean_options + ['sign'] def initialize_options(self): PyPIRCCommand.initialize_options(self) self.username = '' self.password = '' self.show_response = 0 self.sign = False self.identity = None def finalize_options(self): PyPIRCCommand.finalize_options(self) if self.identity and not self.sign: raise DistutilsOptionError( "Must use --sign for --identity to have meaning" ) config = self._read_pypirc() if config != {}: self.username = config['username'] self.password = config['password'] self.repository = config['repository'] self.realm = config['realm'] # getting the password from the distribution # if previously set by the register command if not self.password and self.distribution.password: self.password = self.distribution.password def run(self): if not self.distribution.dist_files: msg = ("Must create and upload files in one command " "(e.g. setup.py sdist upload)") raise DistutilsOptionError(msg) for command, pyversion, filename in self.distribution.dist_files: self.upload_file(command, pyversion, filename) def upload_file(self, command, pyversion, filename): # Makes sure the repository URL is compliant schema, netloc, url, params, query, fragments = \ urlparse(self.repository) if params or query or fragments: raise AssertionError("Incompatible url %s" % self.repository) if schema not in ('http', 'https'): raise AssertionError("unsupported schema " + schema) # Sign if requested if self.sign: gpg_args = ["gpg", "--detach-sign", "-a", filename] if self.identity: gpg_args[2:2] = ["--local-user", self.identity] spawn(gpg_args, dry_run=self.dry_run) # Fill in the data - send all the meta-data in case we need to # register a new release f = open(filename,'rb') try: content = f.read() finally: f.close() meta = self.distribution.metadata data = { # action ':action': 'file_upload', 'protocol_version': '1', # identify release 'name': meta.get_name(), 'version': meta.get_version(), # file content 'content': (os.path.basename(filename),content), 'filetype': command, 'pyversion': pyversion, # additional meta-data 'metadata_version': '1.0', 'summary': meta.get_description(), 'home_page': meta.get_url(), 'author': meta.get_contact(), 'author_email': meta.get_contact_email(), 'license': meta.get_licence(), 'description': meta.get_long_description(), 'keywords': meta.get_keywords(), 'platform': meta.get_platforms(), 'classifiers': meta.get_classifiers(), 'download_url': meta.get_download_url(), # PEP 314 'provides': meta.get_provides(), 'requires': meta.get_requires(), 'obsoletes': meta.get_obsoletes(), } data['comment'] = '' # file content digests for digest_name, digest_cons in _FILE_CONTENT_DIGESTS.items(): if digest_cons is None: continue try: data[digest_name] = digest_cons(content).hexdigest() except ValueError: # hash digest not available or blocked by security policy pass if self.sign: with open(filename + ".asc", "rb") as f: data['gpg_signature'] = (os.path.basename(filename) + ".asc", f.read()) # set up the authentication user_pass = (self.username + ":" + self.password).encode('ascii') # The exact encoding of the authentication string is debated. # Anyway PyPI only accepts ascii for both username or password. auth = "Basic " + standard_b64encode(user_pass).decode('ascii') # Build up the MIME payload for the POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = b'\r\n--' + boundary.encode('ascii') end_boundary = sep_boundary + b'--\r\n' body = io.BytesIO() for key, value in data.items(): title = '\r\nContent-Disposition: form-data; name="%s"' % key # handle multiple entries for the same name if not isinstance(value, list): value = [value] for value in value: if type(value) is tuple: title += '; filename="%s"' % value[0] value = value[1] else: value = str(value).encode('utf-8') body.write(sep_boundary) body.write(title.encode('utf-8')) body.write(b"\r\n\r\n") body.write(value) body.write(end_boundary) body = body.getvalue() msg = "Submitting %s to %s" % (filename, self.repository) self.announce(msg, log.INFO) # build the Request headers = { 'Content-type': 'multipart/form-data; boundary=%s' % boundary, 'Content-length': str(len(body)), 'Authorization': auth, } request = Request(self.repository, data=body, headers=headers) # send the data try: result = urlopen(request) status = result.getcode() reason = result.msg except HTTPError as e: status = e.code reason = e.msg except OSError as e: self.announce(str(e), log.ERROR) raise if status == 200: self.announce('Server response (%s): %s' % (status, reason), log.INFO) if self.show_response: text = self._read_pypi_response(result) msg = '\n'.join(('-' * 75, text, '-' * 75)) self.announce(msg, log.INFO) else: msg = 'Upload failed (%s): %s' % (status, reason) self.announce(msg, log.ERROR) raise DistutilsError(msg) python-deadlib-3.13.0/distutils/distutils/config.py000066400000000000000000000115271471045437300224060ustar00rootroot00000000000000"""distutils.pypirc Provides the PyPIRCCommand class, the base class for the command classes that uses .pypirc in the distutils.command package. """ import os from configparser import RawConfigParser import warnings from distutils.cmd import Command DEFAULT_PYPIRC = """\ [distutils] index-servers = pypi [pypi] username:%s password:%s """ class PyPIRCCommand(Command): """Base command that knows how to handle the .pypirc file """ DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/' DEFAULT_REALM = 'pypi' repository = None realm = None user_options = [ ('repository=', 'r', "url of repository [default: %s]" % \ DEFAULT_REPOSITORY), ('show-response', None, 'display full response text from server')] boolean_options = ['show-response'] def _get_rc_file(self): """Returns rc file path.""" return os.path.join(os.path.expanduser('~'), '.pypirc') def _store_pypirc(self, username, password): """Creates a default .pypirc file.""" rc = self._get_rc_file() with os.fdopen(os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600), 'w') as f: f.write(DEFAULT_PYPIRC % (username, password)) def _read_pypirc(self): """Reads the .pypirc file.""" rc = self._get_rc_file() if os.path.exists(rc): self.announce('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY config = RawConfigParser() config.read(rc) sections = config.sections() if 'distutils' in sections: # let's get the list of servers index_servers = config.get('distutils', 'index-servers') _servers = [server.strip() for server in index_servers.split('\n') if server.strip() != ''] if _servers == []: # nothing set, let's try to get the default pypi if 'pypi' in sections: _servers = ['pypi'] else: # the file is not properly defined, returning # an empty dict return {} for server in _servers: current = {'server': server} current['username'] = config.get(server, 'username') # optional params for key, default in (('repository', self.DEFAULT_REPOSITORY), ('realm', self.DEFAULT_REALM), ('password', None)): if config.has_option(server, key): current[key] = config.get(server, key) else: current[key] = default # work around people having "repository" for the "pypi" # section of their config set to the HTTP (rather than # HTTPS) URL if (server == 'pypi' and repository in (self.DEFAULT_REPOSITORY, 'pypi')): current['repository'] = self.DEFAULT_REPOSITORY return current if (current['server'] == repository or current['repository'] == repository): return current elif 'server-login' in sections: # old format server = 'server-login' if config.has_option(server, 'repository'): repository = config.get(server, 'repository') else: repository = self.DEFAULT_REPOSITORY return {'username': config.get(server, 'username'), 'password': config.get(server, 'password'), 'repository': repository, 'server': server, 'realm': self.DEFAULT_REALM} return {} def _read_pypi_response(self, response): """Read and decode a PyPI HTTP response.""" with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) import cgi content_type = response.getheader('content-type', 'text/plain') encoding = cgi.parse_header(content_type)[1].get('charset', 'ascii') return response.read().decode(encoding) def initialize_options(self): """Initialize options.""" self.repository = None self.realm = None self.show_response = 0 def finalize_options(self): """Finalizes options.""" if self.repository is None: self.repository = self.DEFAULT_REPOSITORY if self.realm is None: self.realm = self.DEFAULT_REALM python-deadlib-3.13.0/distutils/distutils/core.py000066400000000000000000000212541471045437300220670ustar00rootroot00000000000000"""distutils.core The only module that needs to be imported to use the Distutils; provides the 'setup' function (which is to be called from the setup script). Also indirectly provides the Distribution and Command classes, although they are really defined in distutils.dist and distutils.cmd. """ import os import sys from distutils.debug import DEBUG from distutils.errors import * # Mainly import these so setup scripts can "from distutils.core import" them. from distutils.dist import Distribution from distutils.cmd import Command from distutils.config import PyPIRCCommand from distutils.extension import Extension # This is a barebones help message generated displayed when the user # runs the setup script with no arguments at all. More useful help # is generated with various --help options: global help, list commands, # and per-command help. USAGE = """\ usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] or: %(script)s --help [cmd1 cmd2 ...] or: %(script)s --help-commands or: %(script)s cmd --help """ def gen_usage (script_name): script = os.path.basename(script_name) return USAGE % vars() # Some mild magic to control the behaviour of 'setup()' from 'run_setup()'. _setup_stop_after = None _setup_distribution = None # Legal keyword arguments for the setup() function setup_keywords = ('distclass', 'script_name', 'script_args', 'options', 'name', 'version', 'author', 'author_email', 'maintainer', 'maintainer_email', 'url', 'license', 'description', 'long_description', 'keywords', 'platforms', 'classifiers', 'download_url', 'requires', 'provides', 'obsoletes', ) # Legal keyword arguments for the Extension constructor extension_keywords = ('name', 'sources', 'include_dirs', 'define_macros', 'undef_macros', 'library_dirs', 'libraries', 'runtime_library_dirs', 'extra_objects', 'extra_compile_args', 'extra_link_args', 'swig_opts', 'export_symbols', 'depends', 'language') def setup (**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: create a Distribution instance; find and parse config files; parse the command line; run each Distutils command found there, customized by the options supplied to 'setup()' (as keyword arguments), in config files, and on the command line. The Distribution instance might be an instance of a class supplied via the 'distclass' keyword argument to 'setup'; if no such class is supplied, then the Distribution class (in dist.py) is instantiated. All other arguments to 'setup' (except for 'cmdclass') are used to set attributes of the Distribution instance. The 'cmdclass' argument, if supplied, is a dictionary mapping command names to command classes. Each command encountered on the command line will be turned into a command class, which is in turn instantiated; any class found in 'cmdclass' is used in place of the default, which is (for command 'foo_bar') class 'foo_bar' in module 'distutils.command.foo_bar'. The command class must provide a 'user_options' attribute which is a list of option specifiers for 'distutils.fancy_getopt'. Any command-line options between the current and the next command are used to set attributes of the current command object. When the entire command-line has been successfully parsed, calls the 'run()' method on each command object in turn. This method will be driven entirely by the Distribution object (which each command object has a reference to, thanks to its constructor), and the command-specific options that became attributes of each command object. """ global _setup_stop_after, _setup_distribution # Determine the distribution class -- either caller-supplied or # our Distribution (see below). klass = attrs.get('distclass') if klass: del attrs['distclass'] else: klass = Distribution if 'script_name' not in attrs: attrs['script_name'] = os.path.basename(sys.argv[0]) if 'script_args' not in attrs: attrs['script_args'] = sys.argv[1:] # Create the Distribution instance, using the remaining arguments # (ie. everything except distclass) to initialize it try: _setup_distribution = dist = klass(attrs) except DistutilsSetupError as msg: if 'name' not in attrs: raise SystemExit("error in setup command: %s" % msg) else: raise SystemExit("error in %s setup command: %s" % \ (attrs['name'], msg)) if _setup_stop_after == "init": return dist # Find and parse the config file(s): they will override options from # the setup script, but be overridden by the command line. dist.parse_config_files() if DEBUG: print("options (after parsing config files):") dist.dump_option_dicts() if _setup_stop_after == "config": return dist # Parse the command line and override config files; any # command-line errors are the end user's fault, so turn them into # SystemExit to suppress tracebacks. try: ok = dist.parse_command_line() except DistutilsArgError as msg: raise SystemExit(gen_usage(dist.script_name) + "\nerror: %s" % msg) if DEBUG: print("options (after parsing command line):") dist.dump_option_dicts() if _setup_stop_after == "commandline": return dist # And finally, run all the commands found on the command line. if ok: try: dist.run_commands() except KeyboardInterrupt: raise SystemExit("interrupted") except OSError as exc: if DEBUG: sys.stderr.write("error: %s\n" % (exc,)) raise else: raise SystemExit("error: %s" % (exc,)) except (DistutilsError, CCompilerError) as msg: if DEBUG: raise else: raise SystemExit("error: " + str(msg)) return dist # setup () def run_setup (script_name, script_args=None, stop_after="run"): """Run a setup script in a somewhat controlled environment, and return the 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 'setup()', or the contents of the config files or command-line. 'script_name' is a file that will be read and run with '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 'setup()' when to stop processing; possible values: init stop after the Distribution instance has been created and populated with the keyword arguments to 'setup()' config stop after config files have been parsed (and their data stored in the Distribution instance) commandline stop after the command-line ('sys.argv[1:]' or 'script_args') have been parsed (and the data stored in the Distribution) run [default] stop after all commands have been run (the same as if 'setup()' had been called in the usual way Returns the Distribution instance, which provides all information used to drive the Distutils. """ if stop_after not in ('init', 'config', 'commandline', 'run'): raise ValueError("invalid value for 'stop_after': %r" % (stop_after,)) global _setup_stop_after, _setup_distribution _setup_stop_after = stop_after save_argv = sys.argv.copy() g = {'__file__': script_name} try: try: sys.argv[0] = script_name if script_args is not None: sys.argv[1:] = script_args with open(script_name, 'rb') as f: exec(f.read(), g) finally: sys.argv = save_argv _setup_stop_after = None except SystemExit: # Hmm, should we do something if exiting with a non-zero code # (ie. error)? pass if _setup_distribution is None: raise RuntimeError(("'distutils.core.setup()' was never called -- " "perhaps '%s' is not a Distutils setup script?") % \ script_name) # I wonder if the setup script's namespace -- g and l -- would be of # any interest to callers? #print "_setup_distribution:", _setup_distribution return _setup_distribution # run_setup () python-deadlib-3.13.0/distutils/distutils/cygwinccompiler.py000066400000000000000000000377741471045437300243530ustar00rootroot00000000000000"""distutils.cygwinccompiler Provides the CygwinCCompiler class, a subclass of 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). """ # problems: # # * if you use a msvc compiled python version (1.5.2) # 1. you have to insert a __GNUC__ section in its config.h # 2. you have to generate an import library for its dll # - create a def-file for python??.dll # - create an import library using # dlltool --dllname python15.dll --def python15.def \ # --output-lib libpython15.a # # see also http://starship.python.net/crew/kernr/mingw32/Notes.html # # * We put export_symbols in a def-file, and don't use # --export-all-symbols because it doesn't worked reliable in some # tested configurations. And because other windows compilers also # need their symbols specified this no serious problem. # # tested configurations: # # * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works # (after patching python's config.h and for C++ some other include files) # see also http://starship.python.net/crew/kernr/mingw32/Notes.html # * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works # (ld doesn't support -shared, so we use dllwrap) # * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now # - its dllwrap doesn't work, there is a bug in binutils 2.10.90 # see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html # - using gcc -mdll instead dllwrap doesn't work without -static because # it tries to link against dlls instead their import libraries. (If # it finds the dll first.) # By specifying -static we force ld to link against the import libraries, # this is windows standard and there are normally not the necessary symbols # in the dlls. # *** only the version of June 2000 shows these problems # * cygwin gcc 3.2/ld 2.13.90 works # (ld supports -shared) # * mingw gcc 3.2/ld 2.13 works # (ld supports -shared) import os import sys import copy from subprocess import Popen, PIPE, check_output import re from distutils.unixccompiler import UnixCCompiler from distutils.file_util import write_file from distutils.errors import (DistutilsExecError, CCompilerError, CompileError, UnknownFileError) from distutils.version import LooseVersion from distutils.spawn import find_executable def get_msvcr(): """Include the appropriate MSVC runtime library if Python was built with MSVC 7.0 or later. """ msc_pos = sys.version.find('MSC v.') if msc_pos != -1: msc_ver = sys.version[msc_pos+6:msc_pos+10] if msc_ver == '1300': # MSVC 7.0 return ['msvcr70'] elif msc_ver == '1310': # MSVC 7.1 return ['msvcr71'] elif msc_ver == '1400': # VS2005 / MSVC 8.0 return ['msvcr80'] elif msc_ver == '1500': # VS2008 / MSVC 9.0 return ['msvcr90'] elif msc_ver == '1600': # VS2010 / MSVC 10.0 return ['msvcr100'] else: raise ValueError("Unknown MS Compiler version %s " % msc_ver) class CygwinCCompiler(UnixCCompiler): """ Handles the Cygwin port of the GNU C compiler to Windows. """ compiler_type = 'cygwin' obj_extension = ".o" static_lib_extension = ".a" shared_lib_extension = ".dll" static_lib_format = "lib%s%s" shared_lib_format = "%s%s" exe_extension = ".exe" def __init__(self, verbose=0, dry_run=0, force=0): UnixCCompiler.__init__(self, verbose, dry_run, force) status, details = check_config_h() self.debug_print("Python's GCC status: %s (details: %s)" % (status, details)) if status is not CONFIG_H_OK: self.warn( "Python's pyconfig.h doesn't seem to support your compiler. " "Reason: %s. " "Compiling may fail because of undefined preprocessor macros." % details) self.gcc_version, self.ld_version, self.dllwrap_version = \ get_versions() self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" % (self.gcc_version, self.ld_version, self.dllwrap_version) ) # ld_version >= "2.10.90" and < "2.13" should also be able to use # gcc -mdll instead of dllwrap # Older dllwraps had own version numbers, newer ones use the # same as the rest of binutils ( also ld ) # dllwrap 2.10.90 is buggy if self.ld_version >= "2.10.90": self.linker_dll = "gcc" else: self.linker_dll = "dllwrap" # ld_version >= "2.13" support -shared so use it instead of # -mdll -static if self.ld_version >= "2.13": shared_option = "-shared" else: shared_option = "-mdll -static" # Hard-code GCC because that's what this is all about. # XXX optimization, warnings etc. should be customizable. self.set_executables(compiler='gcc -mcygwin -O -Wall', compiler_so='gcc -mcygwin -mdll -O -Wall', compiler_cxx='g++ -mcygwin -O -Wall', linker_exe='gcc -mcygwin', linker_so=('%s -mcygwin %s' % (self.linker_dll, shared_option))) # cygwin and mingw32 need different sets of libraries if self.gcc_version == "2.91.57": # cygwin shouldn't need msvcrt, but without the dlls will crash # (gcc version 2.91.57) -- perhaps something about initialization self.dll_libraries=["msvcrt"] self.warn( "Consider upgrading to a newer version of gcc") else: # Include the appropriate MSVC runtime library if Python was built # with MSVC 7.0 or later. self.dll_libraries = get_msvcr() def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): """Compiles the source by spawning GCC and windres if needed.""" if ext == '.rc' or ext == '.res': # gcc needs '.res' and '.rc' compiled to object files !!! try: self.spawn(["windres", "-i", src, "-o", obj]) except DistutilsExecError as msg: raise CompileError(msg) else: # for other files use the C-compiler try: self.spawn(self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs) except DistutilsExecError as msg: raise CompileError(msg) def link(self, 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 the objects.""" # use separate copies, so we can modify the lists extra_preargs = copy.copy(extra_preargs or []) libraries = copy.copy(libraries or []) objects = copy.copy(objects or []) # Additional libraries libraries.extend(self.dll_libraries) # handle export symbols by creating a def-file # with executables this only works with gcc/ld as linker if ((export_symbols is not None) and (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): # (The linker doesn't do anything if output is up-to-date. # So it would probably better to check if we really need this, # but for this we had to insert some unchanged parts of # UnixCCompiler, and this is not what we want.) # we want to put some files in the same directory as the # object files are, build_temp doesn't help much # where are the object files temp_dir = os.path.dirname(objects[0]) # name of dll to give the helper files the same base name (dll_name, dll_extension) = os.path.splitext( os.path.basename(output_filename)) # generate the filenames for these files def_file = os.path.join(temp_dir, dll_name + ".def") lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a") # Generate .def file contents = [ "LIBRARY %s" % os.path.basename(output_filename), "EXPORTS"] for sym in export_symbols: contents.append(sym) self.execute(write_file, (def_file, contents), "writing %s" % def_file) # next add options for def-file and to creating import libraries # dllwrap uses different options than gcc/ld if self.linker_dll == "dllwrap": extra_preargs.extend(["--output-lib", lib_file]) # for dllwrap we have to use a special option extra_preargs.extend(["--def", def_file]) # we use gcc/ld here and can be sure ld is >= 2.9.10 else: # doesn't work: bfd_close build\...\libfoo.a: Invalid operation #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file]) # for gcc/ld the def-file is specified as any object files objects.append(def_file) #end: if ((export_symbols is not None) and # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")): # who wants symbols and a many times larger output file # should explicitly switch the debug mode on # otherwise we let dllwrap/ld strip the output file # (On my machine: 10KiB < stripped_file < ??100KiB # unstripped_file = stripped_file + XXX KiB # ( XXX=254 for a typical python extension)) if not debug: extra_preargs.append("-s") UnixCCompiler.link(self, target_desc, objects, output_filename, output_dir, libraries, library_dirs, runtime_library_dirs, None, # export_symbols, we do this in our def-file debug, extra_preargs, extra_postargs, build_temp, target_lang) # -- Miscellaneous methods ----------------------------------------- def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): """Adds supports for rc and res files.""" if output_dir is None: output_dir = '' obj_names = [] for src_name in source_filenames: # use normcase to make sure '.rc' is really '.rc' and not '.RC' base, ext = os.path.splitext(os.path.normcase(src_name)) if ext not in (self.src_extensions + ['.rc','.res']): raise UnknownFileError("unknown file type '%s' (from '%s')" % \ (ext, src_name)) if strip_dir: base = os.path.basename (base) if ext in ('.res', '.rc'): # these need to be compiled to object files obj_names.append (os.path.join(output_dir, base + ext + self.obj_extension)) else: obj_names.append (os.path.join(output_dir, base + self.obj_extension)) return obj_names # the same as cygwin plus some additional parameters class Mingw32CCompiler(CygwinCCompiler): """ Handles the Mingw32 port of the GNU C compiler to Windows. """ compiler_type = 'mingw32' def __init__(self, verbose=0, dry_run=0, force=0): CygwinCCompiler.__init__ (self, verbose, dry_run, force) # ld_version >= "2.13" support -shared so use it instead of # -mdll -static if self.ld_version >= "2.13": shared_option = "-shared" else: shared_option = "-mdll -static" # A real mingw32 doesn't need to specify a different entry point, # but cygwin 2.91.57 in no-cygwin-mode needs it. if self.gcc_version <= "2.91.57": entry_point = '--entry _DllMain@12' else: entry_point = '' if is_cygwingcc(): raise CCompilerError( 'Cygwin gcc cannot be used with --compiler=mingw32') self.set_executables(compiler='gcc -O -Wall', compiler_so='gcc -mdll -O -Wall', compiler_cxx='g++ -O -Wall', linker_exe='gcc', linker_so='%s %s %s' % (self.linker_dll, shared_option, entry_point)) # Maybe we should also append -mthreads, but then the finished # dlls need another dll (mingwm10.dll see Mingw32 docs) # (-mthreads: Support thread-safe exception handling on `Mingw32') # no additional libraries needed self.dll_libraries=[] # Include the appropriate MSVC runtime library if Python was built # with MSVC 7.0 or later. self.dll_libraries = get_msvcr() # Because these compilers aren't configured in Python's pyconfig.h file by # default, we should at least warn the user if he is using an unmodified # version. CONFIG_H_OK = "ok" CONFIG_H_NOTOK = "not ok" CONFIG_H_UNCERTAIN = "uncertain" def check_config_h(): """Check if the current Python installation appears amenable to building extensions with GCC. Returns a tuple (status, details), where 'status' is one of the following constants: - CONFIG_H_OK: all is well, go ahead and compile - CONFIG_H_NOTOK: doesn't look good - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h 'details' is a human-readable string explaining the situation. Note there are two ways to conclude "OK": either 'sys.version' contains the string "GCC" (implying that this Python was built with GCC), or the installed "pyconfig.h" contains the string "__GNUC__". """ # XXX since this function also checks sys.version, it's not strictly a # "pyconfig.h" check -- should probably be renamed... from distutils import sysconfig # if sys.version contains GCC then python was compiled with GCC, and the # pyconfig.h file should be OK if "GCC" in sys.version: return CONFIG_H_OK, "sys.version mentions 'GCC'" # let's see if __GNUC__ is mentioned in python.h fn = sysconfig.get_config_h_filename() try: config_h = open(fn) try: if "__GNUC__" in config_h.read(): return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn else: return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn finally: config_h.close() except OSError as exc: return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror)) RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)') def _find_exe_version(cmd): """Find the version of an executable by running `cmd` in the shell. If the command is not found, or the output does not match `RE_VERSION`, returns None. """ executable = cmd.split()[0] if find_executable(executable) is None: return None out = Popen(cmd, shell=True, stdout=PIPE).stdout try: out_string = out.read() finally: out.close() result = RE_VERSION.search(out_string) if result is None: return None # LooseVersion works with strings # so we need to decode our bytes return LooseVersion(result.group(1).decode()) def get_versions(): """ Try to find out the versions of gcc, ld and dllwrap. If not possible it returns None for it. """ commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version'] return tuple([_find_exe_version(cmd) for cmd in commands]) def is_cygwingcc(): '''Try to determine if the gcc that would be used is from cygwin.''' out_string = check_output(['gcc', '-dumpmachine']) return out_string.strip().endswith(b'cygwin') python-deadlib-3.13.0/distutils/distutils/debug.py000066400000000000000000000002131471045437300222150ustar00rootroot00000000000000import os # If DISTUTILS_DEBUG is anything other than the empty string, we run in # debug mode. DEBUG = os.environ.get('DISTUTILS_DEBUG') python-deadlib-3.13.0/distutils/distutils/dep_util.py000066400000000000000000000066431471045437300227510ustar00rootroot00000000000000"""distutils.dep_util Utility functions for simple, timestamp-based dependency of files and groups of files; also, function based entirely on such timestamp dependency analysis.""" import os from distutils.errors import DistutilsFileError def 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 younger than 'source'. Raise DistutilsFileError if 'source' does not exist. """ if not os.path.exists(source): raise DistutilsFileError("file '%s' does not exist" % os.path.abspath(source)) if not os.path.exists(target): return 1 from stat import ST_MTIME mtime1 = os.stat(source)[ST_MTIME] mtime2 = os.stat(target)[ST_MTIME] return mtime1 > mtime2 # newer () def 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 'newer()'. """ if len(sources) != len(targets): raise ValueError("'sources' and 'targets' must be same length") # build a pair of lists (sources, targets) where source is newer n_sources = [] n_targets = [] for i in range(len(sources)): if newer(sources[i], targets[i]): n_sources.append(sources[i]) n_targets.append(targets[i]) return (n_sources, n_targets) # newer_pairwise () def 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 OSError from inside '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). """ # If the target doesn't even exist, then it's definitely out-of-date. if not os.path.exists(target): return 1 # Otherwise we have to find out the hard way: if *any* source file # is more recent than 'target', then 'target' is out-of-date and # we can immediately return true. If we fall through to the end # of the loop, then 'target' is up-to-date and we return false. from stat import ST_MTIME target_mtime = os.stat(target)[ST_MTIME] for source in sources: if not os.path.exists(source): if missing == 'error': # blow up when we stat() the file pass elif missing == 'ignore': # missing source dropped from continue # target's dependency list elif missing == 'newer': # missing source means target is return 1 # out-of-date source_mtime = os.stat(source)[ST_MTIME] if source_mtime > target_mtime: return 1 else: return 0 # newer_group () python-deadlib-3.13.0/distutils/distutils/dir_util.py000066400000000000000000000171421471045437300227530ustar00rootroot00000000000000"""distutils.dir_util Utility functions for manipulating directories and directory trees.""" import os import errno from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log # cache for by mkpath() -- in addition to cheapening redundant calls, # eliminates redundant "creating /foo/bar/baz" messages in dry-run mode _path_created = {} # I don't use os.makedirs because a) it's new to Python 1.5.2, and # b) it blows up if the directory already exists (I want to silently # succeed in that case). def mkpath(name, mode=0o777, verbose=1, 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 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. """ global _path_created # Detect a common bug -- name is None if not isinstance(name, str): raise DistutilsInternalError( "mkpath: 'name' must be a string (got %r)" % (name,)) # XXX what's the better way to handle verbosity? print as we create # each directory in the path (the current behaviour), or only announce # the creation of the whole path? (quite easy to do the latter since # we're not using a recursive algorithm) name = os.path.normpath(name) created_dirs = [] if os.path.isdir(name) or name == '': return created_dirs if _path_created.get(os.path.abspath(name)): return created_dirs (head, tail) = os.path.split(name) tails = [tail] # stack of lone dirs to create while head and tail and not os.path.isdir(head): (head, tail) = os.path.split(head) tails.insert(0, tail) # push next higher dir onto stack # now 'head' contains the deepest directory that already exists # (that is, the child of 'head' in 'name' is the highest directory # that does *not* exist) for d in tails: #print "head = %s, d = %s: " % (head, d), head = os.path.join(head, d) abs_head = os.path.abspath(head) if _path_created.get(abs_head): continue if verbose >= 1: log.info("creating %s", head) if not dry_run: try: os.mkdir(head, mode) except OSError as exc: if not (exc.errno == errno.EEXIST and os.path.isdir(head)): raise DistutilsFileError( "could not create '%s': %s" % (head, exc.args[-1])) created_dirs.append(head) _path_created[abs_head] = 1 return created_dirs def create_tree(base_dir, files, mode=0o777, verbose=1, 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 'mkpath()'. """ # First get the list of directories to create need_dir = set() for file in files: need_dir.add(os.path.join(base_dir, os.path.dirname(file))) # Now create them for dir in sorted(need_dir): mkpath(dir, mode, verbose=verbose, dry_run=dry_run) def copy_tree(src, dst, preserve_mode=1, preserve_times=1, preserve_symlinks=0, update=0, verbose=1, 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 DistutilsFileError. If 'dst' does not exist, it is created with '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 '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 'copy_file'. """ from distutils.file_util import copy_file if not dry_run and not os.path.isdir(src): raise DistutilsFileError( "cannot copy tree '%s': not a directory" % src) try: names = os.listdir(src) except OSError as e: if dry_run: names = [] else: raise DistutilsFileError( "error listing files in '%s': %s" % (src, e.strerror)) if not dry_run: mkpath(dst, verbose=verbose) outputs = [] for n in names: src_name = os.path.join(src, n) dst_name = os.path.join(dst, n) if n.startswith('.nfs'): # skip NFS rename files continue if preserve_symlinks and os.path.islink(src_name): link_dest = os.readlink(src_name) if verbose >= 1: log.info("linking %s -> %s", dst_name, link_dest) if not dry_run: os.symlink(link_dest, dst_name) outputs.append(dst_name) elif os.path.isdir(src_name): outputs.extend( copy_tree(src_name, dst_name, preserve_mode, preserve_times, preserve_symlinks, update, verbose=verbose, dry_run=dry_run)) else: copy_file(src_name, dst_name, preserve_mode, preserve_times, update, verbose=verbose, dry_run=dry_run) outputs.append(dst_name) return outputs def _build_cmdtuple(path, cmdtuples): """Helper for remove_tree().""" for f in os.listdir(path): real_f = os.path.join(path,f) if os.path.isdir(real_f) and not os.path.islink(real_f): _build_cmdtuple(real_f, cmdtuples) else: cmdtuples.append((os.remove, real_f)) cmdtuples.append((os.rmdir, path)) def remove_tree(directory, verbose=1, dry_run=0): """Recursively remove an entire directory tree. Any errors are ignored (apart from being reported to stdout if 'verbose' is true). """ global _path_created if verbose >= 1: log.info("removing '%s' (and everything under it)", directory) if dry_run: return cmdtuples = [] _build_cmdtuple(directory, cmdtuples) for cmd in cmdtuples: try: cmd[0](cmd[1]) # remove dir from cache if it's already there abspath = os.path.abspath(cmd[1]) if abspath in _path_created: del _path_created[abspath] except OSError as exc: log.warn("error removing %s: %s", directory, exc) def ensure_relative(path): """Take the full path 'path', and make it a relative path. This is useful to make 'path' the second argument to os.path.join(). """ drive, path = os.path.splitdrive(path) if path[0:1] == os.sep: path = drive + path[1:] return path python-deadlib-3.13.0/distutils/distutils/dist.py000066400000000000000000001423211471045437300221010ustar00rootroot00000000000000"""distutils.dist Provides the Distribution class, which represents the module distribution being built/installed/distributed. """ import sys import os import re from email import message_from_file try: import warnings except ImportError: warnings = None from distutils.errors import * from distutils.fancy_getopt import FancyGetopt, translate_longopt from distutils.util import check_environ, strtobool, rfc822_escape from distutils import log from distutils.debug import DEBUG # Regex to define acceptable Distutils command names. This is not *quite* # the same as a Python NAME -- I don't allow leading underscores. The fact # that they're very similar is no coincidence; the default naming scheme is # to look for a Python module named after the command. command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') def _ensure_list(value, fieldname): if isinstance(value, str): # a string containing comma separated values is okay. It will # be converted to a list by Distribution.finalize_options(). pass elif not isinstance(value, list): # passing a tuple or an iterator perhaps, warn and convert typename = type(value).__name__ msg = f"Warning: '{fieldname}' should be a list, got type '{typename}'" log.log(log.WARN, msg) value = list(value) return value class Distribution: """The core of the Distutils. Most of the work hiding behind 'setup' is really done within a Distribution instance, which farms the work out to the Distutils commands specified on the command line. Setup scripts will almost never instantiate Distribution directly, unless the 'setup()' function is totally inadequate to their needs. However, it is conceivable that a setup script might wish to subclass Distribution for some specialized purpose, and then pass the subclass to 'setup()' as the 'distclass' keyword argument. If so, it is necessary to respect the expectations that 'setup' has of Distribution. See the code for 'setup()', in core.py, for details. """ # 'global_options' describes the command-line options that may be # supplied to the setup script prior to any actual commands. # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of # these global options. This list should be kept to a bare minimum, # since every global option is also valid as a command option -- and we # don't want to pollute the commands with too many options that they # have minimal control over. # The fourth entry for verbose means that it can be repeated. global_options = [ ('verbose', 'v', "run verbosely (default)", 1), ('quiet', 'q', "run quietly (turns verbosity off)"), ('dry-run', 'n', "don't actually do anything"), ('help', 'h', "show detailed help message"), ('no-user-cfg', None, 'ignore pydistutils.cfg in your home directory'), ] # 'common_usage' is a short (2-3 line) string describing the common # usage of the setup script. common_usage = """\ Common commands: (see '--help-commands' for more) setup.py build will build the package underneath 'build/' setup.py install will install the package """ # options that are not propagated to the commands display_options = [ ('help-commands', None, "list all available commands"), ('name', None, "print package name"), ('version', 'V', "print package version"), ('fullname', None, "print -"), ('author', None, "print the author's name"), ('author-email', None, "print the author's email address"), ('maintainer', None, "print the maintainer's name"), ('maintainer-email', None, "print the maintainer's email address"), ('contact', None, "print the maintainer's name if known, else the author's"), ('contact-email', None, "print the maintainer's email address if known, else the author's"), ('url', None, "print the URL for this package"), ('license', None, "print the license of the package"), ('licence', None, "alias for --license"), ('description', None, "print the package description"), ('long-description', None, "print the long package description"), ('platforms', None, "print the list of platforms"), ('classifiers', None, "print the list of classifiers"), ('keywords', None, "print the list of keywords"), ('provides', None, "print the list of packages/modules provided"), ('requires', None, "print the list of packages/modules required"), ('obsoletes', None, "print the list of packages/modules made obsolete") ] display_option_names = [translate_longopt(x[0]) for x in display_options] # negative options are options that exclude other options negative_opt = {'quiet': 'verbose'} # -- Creation/initialization methods ------------------------------- def __init__(self, attrs=None): """Construct a new Distribution instance: initialize all the attributes of a Distribution, and then use 'attrs' (a dictionary mapping attribute names to values) to assign some of those attributes their "real" values. (Any attributes not mentioned in 'attrs' will be assigned to some null value: 0, None, an empty list or dictionary, etc.) Most importantly, initialize the 'command_obj' attribute to the empty dictionary; this will be filled in with real command objects by 'parse_command_line()'. """ # Default values for our command-line options self.verbose = 1 self.dry_run = 0 self.help = 0 for attr in self.display_option_names: setattr(self, attr, 0) # Store the distribution meta-data (name, version, author, and so # forth) in a separate object -- we're getting to have enough # information here (and enough command-line options) that it's # worth it. Also delegate 'get_XXX()' methods to the 'metadata' # object in a sneaky and underhanded (but efficient!) way. self.metadata = DistributionMetadata() for basename in self.metadata._METHOD_BASENAMES: method_name = "get_" + basename setattr(self, method_name, getattr(self.metadata, method_name)) # 'cmdclass' maps command names to class objects, so we # can 1) quickly figure out which class to instantiate when # we need to create a new command object, and 2) have a way # for the setup script to override command classes self.cmdclass = {} # 'command_packages' is a list of packages in which commands # are searched for. The factory for command 'foo' is expected # to be named 'foo' in the module 'foo' in one of the packages # named here. This list is searched from the left; an error # is raised if no named package provides the command being # searched for. (Always access using get_command_packages().) self.command_packages = None # 'script_name' and 'script_args' are usually set to sys.argv[0] # and sys.argv[1:], but they can be overridden when the caller is # not necessarily a setup script run from the command-line. self.script_name = None self.script_args = None # 'command_options' is where we store command options between # parsing them (from config files, the command-line, etc.) and when # they are actually needed -- ie. when the command in question is # instantiated. It is a dictionary of dictionaries of 2-tuples: # command_options = { command_name : { option : (source, value) } } self.command_options = {} # 'dist_files' is the list of (command, pyversion, file) that # have been created by any dist commands run so far. This is # filled regardless of whether the run is dry or not. pyversion # gives sysconfig.get_python_version() if the dist file is # specific to a Python version, 'any' if it is good for all # Python versions on the target platform, and '' for a source # file. pyversion should not be used to specify minimum or # maximum required Python versions; use the metainfo for that # instead. self.dist_files = [] # These options are really the business of various commands, rather # than of the Distribution itself. We provide aliases for them in # Distribution as a convenience to the developer. self.packages = None self.package_data = {} self.package_dir = None self.py_modules = None self.libraries = None self.headers = None self.ext_modules = None self.ext_package = None self.include_dirs = None self.extra_path = None self.scripts = None self.data_files = None self.password = '' # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to # Command instances -- that's how we enforce that every command # class is a singleton. self.command_obj = {} # 'have_run' maps command names to boolean values; it keeps track # of whether we have actually run a particular command, to make it # cheap to "run" a command whenever we think we might need to -- if # it's already been done, no need for expensive filesystem # operations, we just check the 'have_run' dictionary and carry on. # It's only safe to query 'have_run' for a command class that has # been instantiated -- a false value will be inserted when the # command object is created, and replaced with a true value when # the command is successfully run. Thus it's probably best to use # '.get()' rather than a straight lookup. self.have_run = {} # Now we'll use the attrs dictionary (ultimately, keyword args from # the setup script) to possibly override any or all of these # distribution options. if attrs: # Pull out the set of command options and work on them # specifically. Note that this order guarantees that aliased # command options will override any supplied redundantly # through the general options dictionary. options = attrs.get('options') if options is not None: del attrs['options'] for (command, cmd_options) in options.items(): opt_dict = self.get_option_dict(command) for (opt, val) in cmd_options.items(): opt_dict[opt] = ("setup script", val) if 'licence' in attrs: attrs['license'] = attrs['licence'] del attrs['licence'] msg = "'licence' distribution option is deprecated; use 'license'" if warnings is not None: warnings.warn(msg) else: sys.stderr.write(msg + "\n") # Now work on the rest of the attributes. Any attribute that's # not already defined is invalid! for (key, val) in attrs.items(): if hasattr(self.metadata, "set_" + key): getattr(self.metadata, "set_" + key)(val) elif hasattr(self.metadata, key): setattr(self.metadata, key, val) elif hasattr(self, key): setattr(self, key, val) else: msg = "Unknown distribution option: %s" % repr(key) warnings.warn(msg) # no-user-cfg is handled before other command line args # because other args override the config files, and this # one is needed before we can load the config files. # If attrs['script_args'] wasn't passed, assume false. # # This also make sure we just look at the global options self.want_user_cfg = True if self.script_args is not None: for arg in self.script_args: if not arg.startswith('-'): break if arg == '--no-user-cfg': self.want_user_cfg = False break self.finalize_options() def get_option_dict(self, command): """Get the option dictionary for a given command. If that command's option dictionary hasn't been created yet, then create it and return the new dictionary; otherwise, return the existing option dictionary. """ dict = self.command_options.get(command) if dict is None: dict = self.command_options[command] = {} return dict def dump_option_dicts(self, header=None, commands=None, indent=""): from pprint import pformat if commands is None: # dump all command option dicts commands = sorted(self.command_options.keys()) if header is not None: self.announce(indent + header) indent = indent + " " if not commands: self.announce(indent + "no commands known yet") return for cmd_name in commands: opt_dict = self.command_options.get(cmd_name) if opt_dict is None: self.announce(indent + "no option dict for '%s' command" % cmd_name) else: self.announce(indent + "option dict for '%s' command:" % cmd_name) out = pformat(opt_dict) for line in out.split('\n'): self.announce(indent + " " + line) # -- Config file finding/parsing methods --------------------------- def find_config_files(self): """Find as many configuration files as should be processed for this platform, and return a list of filenames in the order in which they should be parsed. The filenames returned are guaranteed to exist (modulo nasty race conditions). There are three possible config files: distutils.cfg in the Distutils installation directory (ie. where the top-level Distutils __inst__.py file lives), a file in the user's home directory named .pydistutils.cfg on Unix and pydistutils.cfg on Windows/Mac; and setup.cfg in the current directory. The file in the user's home directory can be disabled with the --no-user-cfg option. """ files = [] check_environ() # Where to look for the system-wide Distutils config file sys_dir = os.path.dirname(sys.modules['distutils'].__file__) # Look for the system config file sys_file = os.path.join(sys_dir, "distutils.cfg") if os.path.isfile(sys_file): files.append(sys_file) # What to call the per-user config file if os.name == 'posix': user_filename = ".pydistutils.cfg" else: user_filename = "pydistutils.cfg" # And look for the user config file if self.want_user_cfg: user_file = os.path.join(os.path.expanduser('~'), user_filename) if os.path.isfile(user_file): files.append(user_file) # All platforms support local setup.cfg local_file = "setup.cfg" if os.path.isfile(local_file): files.append(local_file) if DEBUG: self.announce("using config files: %s" % ', '.join(files)) return files def parse_config_files(self, filenames=None): from configparser import ConfigParser # Ignore install directory options if we have a venv if sys.prefix != sys.base_prefix: ignore_options = [ 'install-base', 'install-platbase', 'install-lib', 'install-platlib', 'install-purelib', 'install-headers', 'install-scripts', 'install-data', 'prefix', 'exec-prefix', 'home', 'user', 'root'] else: ignore_options = [] ignore_options = frozenset(ignore_options) if filenames is None: filenames = self.find_config_files() if DEBUG: self.announce("Distribution.parse_config_files():") parser = ConfigParser() for filename in filenames: if DEBUG: self.announce(" reading %s" % filename) parser.read(filename) for section in parser.sections(): options = parser.options(section) opt_dict = self.get_option_dict(section) for opt in options: if opt != '__name__' and opt not in ignore_options: val = parser.get(section,opt) opt = opt.replace('-', '_') opt_dict[opt] = (filename, val) # Make the ConfigParser forget everything (so we retain # the original filenames that options come from) parser.__init__() # If there was a "global" section in the config file, use it # to set Distribution options. if 'global' in self.command_options: for (opt, (src, val)) in self.command_options['global'].items(): alias = self.negative_opt.get(opt) try: if alias: setattr(self, alias, not strtobool(val)) elif opt in ('verbose', 'dry_run'): # ugh! setattr(self, opt, strtobool(val)) else: setattr(self, opt, val) except ValueError as msg: raise DistutilsOptionError(msg) # -- Command-line parsing methods ---------------------------------- def parse_command_line(self): """Parse the setup script's command line, taken from the 'script_args' instance attribute (which defaults to 'sys.argv[1:]' -- see 'setup()' in core.py). This list is first processed for "global options" -- options that set attributes of the Distribution instance. Then, it is alternately scanned for Distutils commands and options for that command. Each new command terminates the options for the previous command. The allowed options for a command are determined by the 'user_options' attribute of the command class -- thus, we have to be able to load command classes in order to parse the command line. Any error in that 'options' attribute raises DistutilsGetoptError; any error on the command-line raises DistutilsArgError. If no Distutils commands were found on the command line, raises DistutilsArgError. Return true if command-line was successfully parsed and we should carry on with executing commands; false if no errors but we shouldn't execute commands (currently, this only happens if user asks for help). """ # # We now have enough information to show the Macintosh dialog # that allows the user to interactively specify the "command line". # toplevel_options = self._get_toplevel_options() # We have to parse the command line a bit at a time -- global # options, then the first command, then its options, and so on -- # because each command will be handled by a different class, and # the options that are valid for a particular class aren't known # until we have loaded the command class, which doesn't happen # until we know what the command is. self.commands = [] parser = FancyGetopt(toplevel_options + self.display_options) parser.set_negative_aliases(self.negative_opt) parser.set_aliases({'licence': 'license'}) args = parser.getopt(args=self.script_args, object=self) option_order = parser.get_option_order() log.set_verbosity(self.verbose) # for display options we return immediately if self.handle_display_options(option_order): return while args: args = self._parse_command_opts(parser, args) if args is None: # user asked for help (and got it) return # Handle the cases of --help as a "global" option, ie. # "setup.py --help" and "setup.py --help command ...". For the # former, we show global options (--verbose, --dry-run, etc.) # and display-only options (--name, --version, etc.); for the # latter, we omit the display-only options and show help for # each command listed on the command line. if self.help: self._show_help(parser, display_options=len(self.commands) == 0, commands=self.commands) return # Oops, no commands found -- an end-user error if not self.commands: raise DistutilsArgError("no commands supplied") # All is well: return true return True def _get_toplevel_options(self): """Return the non-display options recognized at the top level. This includes options that are recognized *only* at the top level as well as options recognized for commands. """ return self.global_options + [ ("command-packages=", None, "list of packages that provide distutils commands"), ] def _parse_command_opts(self, parser, args): """Parse the command-line options for a single command. 'parser' must be a FancyGetopt instance; 'args' must be the list of arguments, starting with the current command (whose options we are about to parse). Returns a new version of 'args' with the next command at the front of the list; will be the empty list if there are no more commands on the command line. Returns None if the user asked for help on this command. """ # late import because of mutual dependence between these modules from distutils.cmd import Command # Pull the current command from the head of the command line command = args[0] if not command_re.match(command): raise SystemExit("invalid command name '%s'" % command) self.commands.append(command) # Dig up the command class that implements this command, so we # 1) know that it's a valid command, and 2) know which options # it takes. try: cmd_class = self.get_command_class(command) except DistutilsModuleError as msg: raise DistutilsArgError(msg) # Require that the command class be derived from Command -- want # to be sure that the basic "command" interface is implemented. if not issubclass(cmd_class, Command): raise DistutilsClassError( "command class %s must subclass Command" % cmd_class) # Also make sure that the command object provides a list of its # known options. if not (hasattr(cmd_class, 'user_options') and isinstance(cmd_class.user_options, list)): msg = ("command class %s must provide " "'user_options' attribute (a list of tuples)") raise DistutilsClassError(msg % cmd_class) # If the command class has a list of negative alias options, # merge it in with the global negative aliases. negative_opt = self.negative_opt if hasattr(cmd_class, 'negative_opt'): negative_opt = negative_opt.copy() negative_opt.update(cmd_class.negative_opt) # Check for help_options in command class. They have a different # format (tuple of four) so we need to preprocess them here. if (hasattr(cmd_class, 'help_options') and isinstance(cmd_class.help_options, list)): help_options = fix_help_options(cmd_class.help_options) else: help_options = [] # All commands support the global options too, just by adding # in 'global_options'. parser.set_option_table(self.global_options + cmd_class.user_options + help_options) parser.set_negative_aliases(negative_opt) (args, opts) = parser.getopt(args[1:]) if hasattr(opts, 'help') and opts.help: self._show_help(parser, display_options=0, commands=[cmd_class]) return if (hasattr(cmd_class, 'help_options') and isinstance(cmd_class.help_options, list)): help_option_found=0 for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): help_option_found=1 if callable(func): func() else: raise DistutilsClassError( "invalid help function %r for help option '%s': " "must be a callable object (function, etc.)" % (func, help_option)) if help_option_found: return # Put the options from the command-line into their official # holding pen, the 'command_options' dictionary. opt_dict = self.get_option_dict(command) for (name, value) in vars(opts).items(): opt_dict[name] = ("command line", value) return args def finalize_options(self): """Set final values for all the options on the Distribution instance, analogous to the .finalize_options() method of Command objects. """ for attr in ('keywords', 'platforms'): value = getattr(self.metadata, attr) if value is None: continue if isinstance(value, str): value = [elm.strip() for elm in value.split(',')] setattr(self.metadata, attr, value) def _show_help(self, parser, global_options=1, display_options=1, commands=[]): """Show help for the setup script command-line in the form of several lists of command-line options. 'parser' should be a FancyGetopt instance; do not expect it to be returned in the same state, as its option table will be reset to make it generate the correct help text. If 'global_options' is true, lists the global options: --verbose, --dry-run, etc. If 'display_options' is true, lists the "display-only" options: --name, --version, etc. Finally, lists per-command help for every command name or command class in 'commands'. """ # late import because of mutual dependence between these modules from distutils.core import gen_usage from distutils.cmd import Command if global_options: if display_options: options = self._get_toplevel_options() else: options = self.global_options parser.set_option_table(options) parser.print_help(self.common_usage + "\nGlobal options:") print('') if display_options: parser.set_option_table(self.display_options) parser.print_help( "Information display options (just display " + "information, ignore any commands)") print('') for command in self.commands: if isinstance(command, type) and issubclass(command, Command): klass = command else: klass = self.get_command_class(command) if (hasattr(klass, 'help_options') and isinstance(klass.help_options, list)): parser.set_option_table(klass.user_options + fix_help_options(klass.help_options)) else: parser.set_option_table(klass.user_options) parser.print_help("Options for '%s' command:" % klass.__name__) print('') print(gen_usage(self.script_name)) def handle_display_options(self, option_order): """If there were any non-global "display-only" options (--help-commands or the metadata display options) on the command line, display the requested info and return true; else return false. """ from distutils.core import gen_usage # User just wants a list of commands -- we'll print it out and stop # processing now (ie. if they ran "setup --help-commands foo bar", # we ignore "foo bar"). if self.help_commands: self.print_commands() print('') print(gen_usage(self.script_name)) return 1 # If user supplied any of the "display metadata" options, then # display that metadata in the order in which the user supplied the # metadata options. any_display_options = 0 is_display_option = {} for option in self.display_options: is_display_option[option[0]] = 1 for (opt, val) in option_order: if val and is_display_option.get(opt): opt = translate_longopt(opt) value = getattr(self.metadata, "get_"+opt)() if opt in ['keywords', 'platforms']: print(','.join(value)) elif opt in ('classifiers', 'provides', 'requires', 'obsoletes'): print('\n'.join(value)) else: print(value) any_display_options = 1 return any_display_options def print_command_list(self, commands, header, max_length): """Print a subset of the list of all commands -- used by 'print_commands()'. """ print(header + ":") for cmd in commands: klass = self.cmdclass.get(cmd) if not klass: klass = self.get_command_class(cmd) try: description = klass.description except AttributeError: description = "(no description available)" print(" %-*s %s" % (max_length, cmd, description)) def print_commands(self): """Print out a help message listing all available commands with a description of each. The list is divided into "standard commands" (listed in distutils.command.__all__) and "extra commands" (mentioned in self.cmdclass, but not a standard command). The descriptions come from the command class attribute 'description'. """ import distutils.command std_commands = distutils.command.__all__ is_std = {} for cmd in std_commands: is_std[cmd] = 1 extra_commands = [] for cmd in self.cmdclass.keys(): if not is_std.get(cmd): extra_commands.append(cmd) max_length = 0 for cmd in (std_commands + extra_commands): if len(cmd) > max_length: max_length = len(cmd) self.print_command_list(std_commands, "Standard commands", max_length) if extra_commands: print() self.print_command_list(extra_commands, "Extra commands", max_length) def get_command_list(self): """Get a list of (command, description) tuples. The list is divided into "standard commands" (listed in distutils.command.__all__) and "extra commands" (mentioned in self.cmdclass, but not a standard command). The descriptions come from the command class attribute 'description'. """ # Currently this is only used on Mac OS, for the Mac-only GUI # Distutils interface (by Jack Jansen) import distutils.command std_commands = distutils.command.__all__ is_std = {} for cmd in std_commands: is_std[cmd] = 1 extra_commands = [] for cmd in self.cmdclass.keys(): if not is_std.get(cmd): extra_commands.append(cmd) rv = [] for cmd in (std_commands + extra_commands): klass = self.cmdclass.get(cmd) if not klass: klass = self.get_command_class(cmd) try: description = klass.description except AttributeError: description = "(no description available)" rv.append((cmd, description)) return rv # -- Command class/object methods ---------------------------------- def get_command_packages(self): """Return a list of packages from which commands are loaded.""" pkgs = self.command_packages if not isinstance(pkgs, list): if pkgs is None: pkgs = '' pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != ''] if "distutils.command" not in pkgs: pkgs.insert(0, "distutils.command") self.command_packages = pkgs return pkgs def get_command_class(self, command): """Return the class that implements the Distutils command named by 'command'. First we check the 'cmdclass' dictionary; if the command is mentioned there, we fetch the class object from the dictionary and return it. Otherwise we load the command module ("distutils.command." + command) and fetch the command class from the module. The loaded class is also stored in 'cmdclass' to speed future calls to 'get_command_class()'. Raises DistutilsModuleError if the expected module could not be found, or if that module does not define the expected class. """ klass = self.cmdclass.get(command) if klass: return klass for pkgname in self.get_command_packages(): module_name = "%s.%s" % (pkgname, command) klass_name = command try: __import__(module_name) module = sys.modules[module_name] except ImportError: continue try: klass = getattr(module, klass_name) except AttributeError: raise DistutilsModuleError( "invalid command '%s' (no class '%s' in module '%s')" % (command, klass_name, module_name)) self.cmdclass[command] = klass return klass raise DistutilsModuleError("invalid command '%s'" % command) def get_command_obj(self, command, create=1): """Return the command object for 'command'. Normally this object is cached on a previous call to 'get_command_obj()'; if no command object for 'command' is in the cache, then we either create and return it (if 'create' is true) or return None. """ cmd_obj = self.command_obj.get(command) if not cmd_obj and create: if DEBUG: self.announce("Distribution.get_command_obj(): " "creating '%s' command object" % command) klass = self.get_command_class(command) cmd_obj = self.command_obj[command] = klass(self) self.have_run[command] = 0 # Set any options that were supplied in config files # or on the command line. (NB. support for error # reporting is lame here: any errors aren't reported # until 'finalize_options()' is called, which means # we won't report the source of the error.) options = self.command_options.get(command) if options: self._set_command_options(cmd_obj, options) return cmd_obj def _set_command_options(self, command_obj, option_dict=None): """Set the options for 'command_obj' from 'option_dict'. Basically this means copying elements of a dictionary ('option_dict') to attributes of an instance ('command'). 'command_obj' must be a Command instance. If 'option_dict' is not supplied, uses the standard option dictionary for this command (from 'self.command_options'). """ command_name = command_obj.get_command_name() if option_dict is None: option_dict = self.get_option_dict(command_name) if DEBUG: self.announce(" setting options for '%s' command:" % command_name) for (option, (source, value)) in option_dict.items(): if DEBUG: self.announce(" %s = %s (from %s)" % (option, value, source)) try: bool_opts = [translate_longopt(o) for o in command_obj.boolean_options] except AttributeError: bool_opts = [] try: neg_opt = command_obj.negative_opt except AttributeError: neg_opt = {} try: is_string = isinstance(value, str) if option in neg_opt and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) elif option in bool_opts and is_string: setattr(command_obj, option, strtobool(value)) elif hasattr(command_obj, option): setattr(command_obj, option, value) else: raise DistutilsOptionError( "error in %s: command '%s' has no such option '%s'" % (source, command_name, option)) except ValueError as msg: raise DistutilsOptionError(msg) def reinitialize_command(self, command, reinit_subcommands=0): """Reinitializes a command to the state it was in when first returned by 'get_command_obj()': ie., initialized but not yet finalized. This provides the opportunity to sneak option values in programmatically, overriding or supplementing user-supplied values from the config files and command line. You'll have to re-finalize the command object (by calling 'finalize_options()' or 'ensure_finalized()') before using it for real. 'command' should be a command name (string) or command object. If 'reinit_subcommands' is true, also reinitializes the command's sub-commands, as declared by the 'sub_commands' class attribute (if it has one). See the "install" command for an example. Only reinitializes the sub-commands that actually matter, ie. those whose test predicates return true. Returns the reinitialized command object. """ from distutils.cmd import Command if not isinstance(command, Command): command_name = command command = self.get_command_obj(command_name) else: command_name = command.get_command_name() if not command.finalized: return command command.initialize_options() command.finalized = 0 self.have_run[command_name] = 0 self._set_command_options(command) if reinit_subcommands: for sub in command.get_sub_commands(): self.reinitialize_command(sub, reinit_subcommands) return command # -- Methods that operate on the Distribution ---------------------- def announce(self, msg, level=log.INFO): log.log(level, msg) def run_commands(self): """Run each command that was seen on the setup script command line. Uses the list of commands found and cache of command objects created by 'get_command_obj()'. """ for cmd in self.commands: self.run_command(cmd) # -- Methods that operate on its Commands -------------------------- def run_command(self, command): """Do whatever it takes to run a command (including nothing at all, if the command has already been run). Specifically: if we have already created and run the command named by 'command', return silently without doing anything. If the command named by 'command' doesn't even have a command object yet, create one. Then invoke 'run()' on that command object (or an existing one). """ # Already been here, done that? then return silently. if self.have_run.get(command): return log.info("running %s", command) cmd_obj = self.get_command_obj(command) cmd_obj.ensure_finalized() cmd_obj.run() self.have_run[command] = 1 # -- Distribution query methods ------------------------------------ def has_pure_modules(self): return len(self.packages or self.py_modules or []) > 0 def has_ext_modules(self): return self.ext_modules and len(self.ext_modules) > 0 def has_c_libraries(self): return self.libraries and len(self.libraries) > 0 def has_modules(self): return self.has_pure_modules() or self.has_ext_modules() def has_headers(self): return self.headers and len(self.headers) > 0 def has_scripts(self): return self.scripts and len(self.scripts) > 0 def has_data_files(self): return self.data_files and len(self.data_files) > 0 def is_pure(self): return (self.has_pure_modules() and not self.has_ext_modules() and not self.has_c_libraries()) # -- Metadata query methods ---------------------------------------- # If you're looking for 'get_name()', 'get_version()', and so forth, # they are defined in a sneaky way: the constructor binds self.get_XXX # to self.metadata.get_XXX. The actual code is in the # DistributionMetadata class, below. class DistributionMetadata: """Dummy class to hold the distribution meta-data: name, version, author, and so forth. """ _METHOD_BASENAMES = ("name", "version", "author", "author_email", "maintainer", "maintainer_email", "url", "license", "description", "long_description", "keywords", "platforms", "fullname", "contact", "contact_email", "classifiers", "download_url", # PEP 314 "provides", "requires", "obsoletes", ) def __init__(self, path=None): if path is not None: self.read_pkg_file(open(path)) else: self.name = None self.version = None self.author = None self.author_email = None self.maintainer = None self.maintainer_email = None self.url = None self.license = None self.description = None self.long_description = None self.keywords = None self.platforms = None self.classifiers = None self.download_url = None # PEP 314 self.provides = None self.requires = None self.obsoletes = None def read_pkg_file(self, file): """Reads the metadata values from a file object.""" msg = message_from_file(file) def _read_field(name): value = msg[name] if value == 'UNKNOWN': return None return value def _read_list(name): values = msg.get_all(name, None) if values == []: return None return values metadata_version = msg['metadata-version'] self.name = _read_field('name') self.version = _read_field('version') self.description = _read_field('summary') # we are filling author only. self.author = _read_field('author') self.maintainer = None self.author_email = _read_field('author-email') self.maintainer_email = None self.url = _read_field('home-page') self.license = _read_field('license') if 'download-url' in msg: self.download_url = _read_field('download-url') else: self.download_url = None self.long_description = _read_field('description') self.description = _read_field('summary') if 'keywords' in msg: self.keywords = _read_field('keywords').split(',') self.platforms = _read_list('platform') self.classifiers = _read_list('classifier') # PEP 314 - these fields only exist in 1.1 if metadata_version == '1.1': self.requires = _read_list('requires') self.provides = _read_list('provides') self.obsoletes = _read_list('obsoletes') else: self.requires = None self.provides = None self.obsoletes = None def write_pkg_info(self, base_dir): """Write the PKG-INFO file into the release tree. """ with open(os.path.join(base_dir, 'PKG-INFO'), 'w', encoding='UTF-8') as pkg_info: self.write_pkg_file(pkg_info) def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. """ version = '1.0' if (self.provides or self.requires or self.obsoletes or self.classifiers or self.download_url): version = '1.1' file.write('Metadata-Version: %s\n' % version) file.write('Name: %s\n' % self.get_name()) file.write('Version: %s\n' % self.get_version()) file.write('Summary: %s\n' % self.get_description()) file.write('Home-page: %s\n' % self.get_url()) file.write('Author: %s\n' % self.get_contact()) file.write('Author-email: %s\n' % self.get_contact_email()) file.write('License: %s\n' % self.get_license()) if self.download_url: file.write('Download-URL: %s\n' % self.download_url) long_desc = rfc822_escape(self.get_long_description()) file.write('Description: %s\n' % long_desc) keywords = ','.join(self.get_keywords()) if keywords: file.write('Keywords: %s\n' % keywords) self._write_list(file, 'Platform', self.get_platforms()) self._write_list(file, 'Classifier', self.get_classifiers()) # PEP 314 self._write_list(file, 'Requires', self.get_requires()) self._write_list(file, 'Provides', self.get_provides()) self._write_list(file, 'Obsoletes', self.get_obsoletes()) def _write_list(self, file, name, values): for value in values: file.write('%s: %s\n' % (name, value)) # -- Metadata query methods ---------------------------------------- def get_name(self): return self.name or "UNKNOWN" def get_version(self): return self.version or "0.0.0" def get_fullname(self): return "%s-%s" % (self.get_name(), self.get_version()) def get_author(self): return self.author or "UNKNOWN" def get_author_email(self): return self.author_email or "UNKNOWN" def get_maintainer(self): return self.maintainer or "UNKNOWN" def get_maintainer_email(self): return self.maintainer_email or "UNKNOWN" def get_contact(self): return self.maintainer or self.author or "UNKNOWN" def get_contact_email(self): return self.maintainer_email or self.author_email or "UNKNOWN" def get_url(self): return self.url or "UNKNOWN" def get_license(self): return self.license or "UNKNOWN" get_licence = get_license def get_description(self): return self.description or "UNKNOWN" def get_long_description(self): return self.long_description or "UNKNOWN" def get_keywords(self): return self.keywords or [] def set_keywords(self, value): self.keywords = _ensure_list(value, 'keywords') def get_platforms(self): return self.platforms or ["UNKNOWN"] def set_platforms(self, value): self.platforms = _ensure_list(value, 'platforms') def get_classifiers(self): return self.classifiers or [] def set_classifiers(self, value): self.classifiers = _ensure_list(value, 'classifiers') def get_download_url(self): return self.download_url or "UNKNOWN" # PEP 314 def get_requires(self): return self.requires or [] def set_requires(self, value): import distutils.versionpredicate for v in value: distutils.versionpredicate.VersionPredicate(v) self.requires = list(value) def get_provides(self): return self.provides or [] def set_provides(self, value): value = [v.strip() for v in value] for v in value: import distutils.versionpredicate distutils.versionpredicate.split_provision(v) self.provides = value def get_obsoletes(self): return self.obsoletes or [] def set_obsoletes(self, value): import distutils.versionpredicate for v in value: distutils.versionpredicate.VersionPredicate(v) self.obsoletes = list(value) def fix_help_options(options): """Convert a 4-tuple 'help_options' list as found in various command classes to the 3-tuple form required by FancyGetopt. """ new_options = [] for help_tuple in options: new_options.append(help_tuple[0:3]) return new_options python-deadlib-3.13.0/distutils/distutils/errors.py000066400000000000000000000067711471045437300224620ustar00rootroot00000000000000"""distutils.errors 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".""" class DistutilsError (Exception): """The root of all Distutils evil.""" pass class DistutilsModuleError (DistutilsError): """Unable to load an expected module, or to find an expected class within some module (in particular, command modules and classes).""" pass class DistutilsClassError (DistutilsError): """Some command class (or possibly distribution class, if anyone feels a need to subclass Distribution) is found not to be holding up its end of the bargain, ie. implementing some part of the "command "interface.""" pass class DistutilsGetoptError (DistutilsError): """The option table provided to 'fancy_getopt()' is bogus.""" pass class DistutilsArgError (DistutilsError): """Raised by fancy_getopt in response to getopt.error -- ie. an error in the command line usage.""" pass class DistutilsFileError (DistutilsError): """Any problems in the filesystem: expected file not found, etc. Typically this is for problems that we detect before OSError could be raised.""" pass class DistutilsOptionError (DistutilsError): """Syntactic/semantic errors in command options, such as use of mutually conflicting options, or inconsistent options, badly-spelled values, etc. No distinction is made between option values originating in the setup script, the command line, config files, or what-have-you -- but if we *know* something originated in the setup script, we'll raise DistutilsSetupError instead.""" pass class DistutilsSetupError (DistutilsError): """For errors that can be definitely blamed on the setup script, such as invalid keyword arguments to 'setup()'.""" pass class DistutilsPlatformError (DistutilsError): """We don't know how to do something on the current platform (but we do know how to do it on some platform) -- eg. trying to compile C files on a platform not supported by a CCompiler subclass.""" pass class DistutilsExecError (DistutilsError): """Any problems executing an external program (such as the C compiler, when compiling C files).""" pass class DistutilsInternalError (DistutilsError): """Internal inconsistencies or impossibilities (obviously, this should never be seen if the code is working!).""" pass class DistutilsTemplateError (DistutilsError): """Syntax error in a file list template.""" class DistutilsByteCompileError(DistutilsError): """Byte compile error.""" # Exception classes used by the CCompiler implementation classes class CCompilerError (Exception): """Some compile/link operation failed.""" class PreprocessError (CCompilerError): """Failure to preprocess one or more C/C++ files.""" class CompileError (CCompilerError): """Failure to compile one or more C/C++ source files.""" class LibError (CCompilerError): """Failure to create a static library from one or more C/C++ object files.""" class LinkError (CCompilerError): """Failure to link one or more C/C++ object files into an executable or shared library file.""" class UnknownFileError (CCompilerError): """Attempt to process an unknown file type.""" python-deadlib-3.13.0/distutils/distutils/extension.py000066400000000000000000000244411471045437300231540ustar00rootroot00000000000000"""distutils.extension Provides the Extension class, used to describe C/C++ extension modules in setup scripts.""" import os import re import warnings # This class is really only used by the "build_ext" command, so it might # make sense to put it in distutils.command.build_ext. However, that # module is already big enough, and I want to make this class a bit more # complex to simplify some common cases ("foo" module in "foo.c") and do # better error-checking ("foo.c" actually exists). # # Also, putting this in build_ext.py means every setup script would have to # import that large-ish module (indirectly, through distutils.core) in # order to do anything. class Extension: """Just a collection of attributes that describes an extension module and everything needed to build it (hopefully in a portable way, but there are hooks that let you be as unportable as you need). Instance attributes: name : string the full name of the extension, including any packages -- ie. *not* a filename or pathname, but Python dotted name sources : [string] list of source filenames, 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 "build_ext" command as source for a Python extension. include_dirs : [string] list of directories to search for C/C++ header files (in Unix form for portability) define_macros : [(name : string, value : string|None)] list of macros to define; each macro is defined using a 2-tuple, 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 -DFOO on Unix C compiler command line) undef_macros : [string] list of macros to undefine explicitly library_dirs : [string] list of directories to search for C/C++ libraries at link time libraries : [string] list of library names (not filenames or paths) to link against runtime_library_dirs : [string] list of directories to search for C/C++ libraries at run time (for shared extensions, this is when the extension is loaded) extra_objects : [string] list of extra files to link with (eg. object files not implied by 'sources', static library that must be explicitly specified, binary resource files, etc.) extra_compile_args : [string] any extra platform- and compiler-specific information to use when compiling the source files in 'sources'. For platforms and compilers where "command line" makes sense, this is typically a list of command-line arguments, but for other platforms it could be anything. extra_link_args : [string] any extra platform- and 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 : [string] list of symbols to be exported 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. swig_opts : [string] any extra options to pass to SWIG if a source file has the .i extension. depends : [string] list of files that the extension depends on language : string extension language (i.e. "c", "c++", "objc"). Will be detected from the source extensions if not provided. optional : boolean specifies that a build failure in the extension should not abort the build process, but simply not install the failing extension. """ # When adding arguments to this constructor, be sure to update # setup_keywords in core.py. def __init__(self, name, sources, include_dirs=None, define_macros=None, undef_macros=None, library_dirs=None, libraries=None, runtime_library_dirs=None, extra_objects=None, extra_compile_args=None, extra_link_args=None, export_symbols=None, swig_opts = None, depends=None, language=None, optional=None, **kw # To catch unknown keywords ): if not isinstance(name, str): raise AssertionError("'name' must be a string") if not (isinstance(sources, list) and all(isinstance(v, str) for v in sources)): raise AssertionError("'sources' must be a list of strings") self.name = name self.sources = sources self.include_dirs = include_dirs or [] self.define_macros = define_macros or [] self.undef_macros = undef_macros or [] self.library_dirs = library_dirs or [] self.libraries = libraries or [] self.runtime_library_dirs = runtime_library_dirs or [] self.extra_objects = extra_objects or [] self.extra_compile_args = extra_compile_args or [] self.extra_link_args = extra_link_args or [] self.export_symbols = export_symbols or [] self.swig_opts = swig_opts or [] self.depends = depends or [] self.language = language self.optional = optional # If there are unknown keyword options, warn about them if len(kw) > 0: options = [repr(option) for option in kw] options = ', '.join(sorted(options)) msg = "Unknown Extension options: %s" % options warnings.warn(msg) def __repr__(self): return '<%s.%s(%r) at %#x>' % ( self.__class__.__module__, self.__class__.__qualname__, self.name, id(self)) def read_setup_file(filename): """Reads a Setup file and returns Extension instances.""" from distutils.sysconfig import (parse_makefile, expand_makefile_vars, _variable_rx) from distutils.text_file import TextFile from distutils.util import split_quoted # First pass over the file to gather "VAR = VALUE" assignments. vars = parse_makefile(filename) # Second pass to gobble up the real content: lines of the form # ... [ ...] [ ...] [ ...] file = TextFile(filename, strip_comments=1, skip_blanks=1, join_lines=1, lstrip_ws=1, rstrip_ws=1) try: extensions = [] while True: line = file.readline() if line is None: # eof break if re.match(_variable_rx, line): # VAR=VALUE, handled in first pass continue if line[0] == line[-1] == "*": file.warn("'%s' lines not handled yet" % line) continue line = expand_makefile_vars(line, vars) words = split_quoted(line) # NB. this parses a slightly different syntax than the old # makesetup script: here, there must be exactly one extension per # line, and it must be the first word of the line. I have no idea # why the old syntax supported multiple extensions per line, as # they all wind up being the same. module = words[0] ext = Extension(module, []) append_next_word = None for word in words[1:]: if append_next_word is not None: append_next_word.append(word) append_next_word = None continue suffix = os.path.splitext(word)[1] switch = word[0:2] ; value = word[2:] if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"): # hmm, should we do something about C vs. C++ sources? # or leave it up to the CCompiler implementation to # worry about? ext.sources.append(word) elif switch == "-I": ext.include_dirs.append(value) elif switch == "-D": equals = value.find("=") if equals == -1: # bare "-DFOO" -- no value ext.define_macros.append((value, None)) else: # "-DFOO=blah" ext.define_macros.append((value[0:equals], value[equals+2:])) elif switch == "-U": ext.undef_macros.append(value) elif switch == "-C": # only here 'cause makesetup has it! ext.extra_compile_args.append(word) elif switch == "-l": ext.libraries.append(value) elif switch == "-L": ext.library_dirs.append(value) elif switch == "-R": ext.runtime_library_dirs.append(value) elif word == "-rpath": append_next_word = ext.runtime_library_dirs elif word == "-Xlinker": append_next_word = ext.extra_link_args elif word == "-Xcompiler": append_next_word = ext.extra_compile_args elif switch == "-u": ext.extra_link_args.append(word) if not value: append_next_word = ext.extra_link_args elif suffix in (".a", ".so", ".sl", ".o", ".dylib"): # NB. a really faithful emulation of makesetup would # append a .o file to extra_objects only if it # had a slash in it; otherwise, it would s/.o/.c/ # and append it to sources. Hmmmm. ext.extra_objects.append(word) else: file.warn("unrecognized argument '%s'" % word) extensions.append(ext) finally: file.close() return extensions python-deadlib-3.13.0/distutils/distutils/fancy_getopt.py000066400000000000000000000425701471045437300236250ustar00rootroot00000000000000"""distutils.fancy_getopt Wrapper around the standard getopt module that provides the following additional features: * short and long options are tied together * options have help strings, so fancy_getopt could potentially create a complete usage summary * options set attributes of a passed-in object """ import sys, string, re import getopt from distutils.errors import * # Much like command_re in distutils.core, this is close to but not quite # the same as a Python NAME -- except, in the spirit of most GNU # utilities, we use '-' in place of '_'. (The spirit of LISP lives on!) # The similarities to NAME are again not a coincidence... longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)' longopt_re = re.compile(r'^%s$' % longopt_pat) # For recognizing "negative alias" options, eg. "quiet=!verbose" neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) # This is used to translate long options to legitimate Python identifiers # (for use as attributes of some object). longopt_xlate = str.maketrans('-', '_') class FancyGetopt: """Wrapper around the standard 'getopt()' module that provides some handy extra functionality: * short and long options are tied together * options have help strings, and help text can be assembled from them * options set attributes of a passed-in object * boolean options can have "negative aliases" -- eg. if --quiet is the "negative alias" of --verbose, then "--quiet" on the command line sets 'verbose' to false """ def __init__(self, option_table=None): # The option table is (currently) a list of tuples. The # tuples may have 3 or four values: # (long_option, short_option, help_string [, repeatable]) # if an option takes an argument, its long_option should have '=' # appended; short_option should just be a single character, no ':' # in any case. If a long_option doesn't have a corresponding # short_option, short_option should be None. All option tuples # must have long options. self.option_table = option_table # 'option_index' maps long option names to entries in the option # table (ie. those 3-tuples). self.option_index = {} if self.option_table: self._build_index() # 'alias' records (duh) alias options; {'foo': 'bar'} means # --foo is an alias for --bar self.alias = {} # 'negative_alias' keeps track of options that are the boolean # opposite of some other option self.negative_alias = {} # These keep track of the information in the option table. We # don't actually populate these structures until we're ready to # parse the command-line, since the 'option_table' passed in here # isn't necessarily the final word. self.short_opts = [] self.long_opts = [] self.short2long = {} self.attr_name = {} self.takes_arg = {} # And 'option_order' is filled up in 'getopt()'; it records the # original order of options (and their values) on the command-line, # but expands short options, converts aliases, etc. self.option_order = [] def _build_index(self): self.option_index.clear() for option in self.option_table: self.option_index[option[0]] = option def set_option_table(self, option_table): self.option_table = option_table self._build_index() def add_option(self, long_option, short_option=None, help_string=None): if long_option in self.option_index: raise DistutilsGetoptError( "option conflict: already an option '%s'" % long_option) else: option = (long_option, short_option, help_string) self.option_table.append(option) self.option_index[long_option] = option def has_option(self, long_option): """Return true if the option table for this parser has an option with long name 'long_option'.""" return long_option in self.option_index def get_attr_name(self, long_option): """Translate long option name 'long_option' to the form it has as an attribute of some object: ie., translate hyphens to underscores.""" return long_option.translate(longopt_xlate) def _check_alias_dict(self, aliases, what): assert isinstance(aliases, dict) for (alias, opt) in aliases.items(): if alias not in self.option_index: raise DistutilsGetoptError(("invalid %s '%s': " "option '%s' not defined") % (what, alias, alias)) if opt not in self.option_index: raise DistutilsGetoptError(("invalid %s '%s': " "aliased option '%s' not defined") % (what, alias, opt)) def set_aliases(self, alias): """Set the aliases for this option parser.""" self._check_alias_dict(alias, "alias") self.alias = alias def set_negative_aliases(self, negative_alias): """Set the negative aliases for this option parser. 'negative_alias' should be a dictionary mapping option names to option names, both the key and value must already be defined in the option table.""" self._check_alias_dict(negative_alias, "negative alias") self.negative_alias = negative_alias def _grok_option_table(self): """Populate the various data structures that keep tabs on the option table. Called by 'getopt()' before it can do anything worthwhile. """ self.long_opts = [] self.short_opts = [] self.short2long.clear() self.repeat = {} for option in self.option_table: if len(option) == 3: long, short, help = option repeat = 0 elif len(option) == 4: long, short, help, repeat = option else: # the option table is part of the code, so simply # assert that it is correct raise ValueError("invalid option tuple: %r" % (option,)) # Type- and value-check the option names if not isinstance(long, str) or len(long) < 2: raise DistutilsGetoptError(("invalid long option '%s': " "must be a string of length >= 2") % long) if (not ((short is None) or (isinstance(short, str) and len(short) == 1))): raise DistutilsGetoptError("invalid short option '%s': " "must a single character or None" % short) self.repeat[long] = repeat self.long_opts.append(long) if long[-1] == '=': # option takes an argument? if short: short = short + ':' long = long[0:-1] self.takes_arg[long] = 1 else: # Is option is a "negative alias" for some other option (eg. # "quiet" == "!verbose")? alias_to = self.negative_alias.get(long) if alias_to is not None: if self.takes_arg[alias_to]: raise DistutilsGetoptError( "invalid negative alias '%s': " "aliased option '%s' takes a value" % (long, alias_to)) self.long_opts[-1] = long # XXX redundant?! self.takes_arg[long] = 0 # If this is an alias option, make sure its "takes arg" flag is # the same as the option it's aliased to. alias_to = self.alias.get(long) if alias_to is not None: if self.takes_arg[long] != self.takes_arg[alias_to]: raise DistutilsGetoptError( "invalid alias '%s': inconsistent with " "aliased option '%s' (one of them takes a value, " "the other doesn't" % (long, alias_to)) # Now enforce some bondage on the long option name, so we can # later translate it to an attribute name on some object. Have # to do this a bit late to make sure we've removed any trailing # '='. if not longopt_re.match(long): raise DistutilsGetoptError( "invalid long option name '%s' " "(must be letters, numbers, hyphens only" % long) self.attr_name[long] = self.get_attr_name(long) if short: self.short_opts.append(short) self.short2long[short[0]] = long def getopt(self, 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 OptionDummy object, stores option values there, and returns a tuple (args, object). If 'object' is supplied, it is modified in place and 'getopt()' just returns 'args'; in both cases, the returned 'args' is a modified copy of the passed-in 'args' list, which is left untouched. """ if args is None: args = sys.argv[1:] if object is None: object = OptionDummy() created_object = True else: created_object = False self._grok_option_table() short_opts = ' '.join(self.short_opts) try: opts, args = getopt.getopt(args, short_opts, self.long_opts) except getopt.error as msg: raise DistutilsArgError(msg) for opt, val in opts: if len(opt) == 2 and opt[0] == '-': # it's a short option opt = self.short2long[opt[1]] else: assert len(opt) > 2 and opt[:2] == '--' opt = opt[2:] alias = self.alias.get(opt) if alias: opt = alias if not self.takes_arg[opt]: # boolean option? assert val == '', "boolean option can't have value" alias = self.negative_alias.get(opt) if alias: opt = alias val = 0 else: val = 1 attr = self.attr_name[opt] # The only repeating option at the moment is 'verbose'. # It has a negative option -q quiet, which should set verbose = 0. if val and self.repeat.get(attr) is not None: val = getattr(object, attr, 0) + 1 setattr(object, attr, val) self.option_order.append((opt, val)) # for opts if created_object: return args, object else: return args def get_option_order(self): """Returns the list of (option, value) tuples processed by the previous run of 'getopt()'. Raises RuntimeError if 'getopt()' hasn't been called yet. """ if self.option_order is None: raise RuntimeError("'getopt()' hasn't been called yet") else: return self.option_order def generate_help(self, header=None): """Generate help text (a list of strings, one per suggested line of output) from the option table for this FancyGetopt object. """ # Blithely assume the option table is good: probably wouldn't call # 'generate_help()' unless you've already called 'getopt()'. # First pass: determine maximum length of long option names max_opt = 0 for option in self.option_table: long = option[0] short = option[1] l = len(long) if long[-1] == '=': l = l - 1 if short is not None: l = l + 5 # " (-x)" where short == 'x' if l > max_opt: max_opt = l opt_width = max_opt + 2 + 2 + 2 # room for indent + dashes + gutter # Typical help block looks like this: # --foo controls foonabulation # Help block for longest option looks like this: # --flimflam set the flim-flam level # and with wrapped text: # --flimflam set the flim-flam level (must be between # 0 and 100, except on Tuesdays) # Options with short names will have the short name shown (but # it doesn't contribute to max_opt): # --foo (-f) controls foonabulation # If adding the short option would make the left column too wide, # we push the explanation off to the next line # --flimflam (-l) # set the flim-flam level # Important parameters: # - 2 spaces before option block start lines # - 2 dashes for each long option name # - min. 2 spaces between option and explanation (gutter) # - 5 characters (incl. space) for short option name # Now generate lines of help text. (If 80 columns were good enough # for Jesus, then 78 columns are good enough for me!) line_width = 78 text_width = line_width - opt_width big_indent = ' ' * opt_width if header: lines = [header] else: lines = ['Option summary:'] for option in self.option_table: long, short, help = option[:3] text = wrap_text(help, text_width) if long[-1] == '=': long = long[0:-1] # Case 1: no short option at all (makes life easy) if short is None: if text: lines.append(" --%-*s %s" % (max_opt, long, text[0])) else: lines.append(" --%-*s " % (max_opt, long)) # Case 2: we have a short option, so we have to include it # just after the long option else: opt_names = "%s (-%s)" % (long, short) if text: lines.append(" --%-*s %s" % (max_opt, opt_names, text[0])) else: lines.append(" --%-*s" % opt_names) for l in text[1:]: lines.append(big_indent + l) return lines def print_help(self, header=None, file=None): if file is None: file = sys.stdout for line in self.generate_help(header): file.write(line + "\n") def fancy_getopt(options, negative_opt, object, args): parser = FancyGetopt(options) parser.set_negative_aliases(negative_opt) return parser.getopt(args, object) WS_TRANS = {ord(_wschar) : ' ' for _wschar in string.whitespace} def wrap_text(text, width): """wrap_text(text : string, width : int) -> [string] Split 'text' into multiple lines of no more than 'width' characters each, and return the list of strings that results. """ if text is None: return [] if len(text) <= width: return [text] text = text.expandtabs() text = text.translate(WS_TRANS) chunks = re.split(r'( +|-+)', text) chunks = [ch for ch in chunks if ch] # ' - ' results in empty strings lines = [] while chunks: cur_line = [] # list of chunks (to-be-joined) cur_len = 0 # length of current line while chunks: l = len(chunks[0]) if cur_len + l <= width: # can squeeze (at least) this chunk in cur_line.append(chunks[0]) del chunks[0] cur_len = cur_len + l else: # this line is full # drop last chunk if all space if cur_line and cur_line[-1][0] == ' ': del cur_line[-1] break if chunks: # any chunks left to process? # if the current line is still empty, then we had a single # chunk that's too big too fit on a line -- so we break # down and break it up at the line width if cur_len == 0: cur_line.append(chunks[0][0:width]) chunks[0] = chunks[0][width:] # all-whitespace chunks at the end of a line can be discarded # (and we know from the re.split above that if a chunk has # *any* whitespace, it is *all* whitespace) if chunks[0][0] == ' ': del chunks[0] # and store this line in the list-of-all-lines -- as a single # string, of course! lines.append(''.join(cur_line)) return lines def translate_longopt(opt): """Convert a long option name to a valid Python identifier by changing "-" to "_". """ return opt.translate(longopt_xlate) class OptionDummy: """Dummy class just used as a place to hold command-line option values as instance attributes.""" def __init__(self, options=[]): """Create a new OptionDummy instance. The attributes listed in 'options' will be initialized to None.""" for opt in options: setattr(self, opt, None) if __name__ == "__main__": text = """\ Tra-la-la, supercalifragilisticexpialidocious. How *do* you spell that odd word, anyways? (Someone ask Mary -- she'll know [or she'll say, "How should I know?"].)""" for w in (10, 20, 30, 40): print("width: %d" % w) print("\n".join(wrap_text(text, w))) print() python-deadlib-3.13.0/distutils/distutils/file_util.py000066400000000000000000000177241471045437300231220ustar00rootroot00000000000000"""distutils.file_util Utility functions for operating on single files. """ import os from distutils.errors import DistutilsFileError from distutils import log # for generating verbose output in 'copy_file()' _copy_action = { None: 'copying', 'hard': 'hard linking', 'sym': 'symbolically linking' } def _copy_file_contents(src, dst, buffer_size=16*1024): """Copy the file 'src' to 'dst'; both must be filenames. Any error opening either file, reading from 'src', or writing to 'dst', raises DistutilsFileError. Data is read/written in chunks of 'buffer_size' bytes (default 16k). No attempt is made to handle anything apart from regular files. """ # Stolen from shutil module in the standard library, but with # custom error-handling added. fsrc = None fdst = None try: try: fsrc = open(src, 'rb') except OSError as e: raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror)) if os.path.exists(dst): try: os.unlink(dst) except OSError as e: raise DistutilsFileError( "could not delete '%s': %s" % (dst, e.strerror)) try: fdst = open(dst, 'wb') except OSError as e: raise DistutilsFileError( "could not create '%s': %s" % (dst, e.strerror)) while True: try: buf = fsrc.read(buffer_size) except OSError as e: raise DistutilsFileError( "could not read from '%s': %s" % (src, e.strerror)) if not buf: break try: fdst.write(buf) except OSError as e: raise DistutilsFileError( "could not write to '%s': %s" % (dst, e.strerror)) finally: if fdst: fdst.close() if fsrc: fsrc.close() def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0, link=None, verbose=1, dry_run=0): """Copy a 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 (os.link) or symbolic links (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: 'copy_file()' doesn't check if hard or symbolic linking is available. If hardlink fails, falls back to _copy_file_contents(). Under Mac OS, uses the native file copy function in macostools; on other systems, uses '_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). from distutils.dep_util import newer from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE if not os.path.isfile(src): raise DistutilsFileError( "can't copy '%s': doesn't exist or not a regular file" % src) if os.path.isdir(dst): dir = dst dst = os.path.join(dst, os.path.basename(src)) else: dir = os.path.dirname(dst) if update and not newer(src, dst): if verbose >= 1: log.debug("not copying %s (output up-to-date)", src) return (dst, 0) try: action = _copy_action[link] except KeyError: raise ValueError("invalid value '%s' for 'link' argument" % link) if verbose >= 1: if os.path.basename(dst) == os.path.basename(src): log.info("%s %s -> %s", action, src, dir) else: log.info("%s %s -> %s", action, src, dst) if dry_run: return (dst, 1) # If linking (hard or symbolic), use the appropriate system call # (Unix only, of course, but that's the caller's responsibility) elif link == 'hard': if not (os.path.exists(dst) and os.path.samefile(src, dst)): try: os.link(src, dst) return (dst, 1) except OSError: # If hard linking fails, fall back on copying file # (some special filesystems don't support hard linking # even under Unix, see issue #8876). pass elif link == 'sym': if not (os.path.exists(dst) and os.path.samefile(src, dst)): os.symlink(src, dst) return (dst, 1) # Otherwise (non-Mac, not linking), copy the file contents and # (optionally) copy the times and mode. _copy_file_contents(src, dst) if preserve_mode or preserve_times: st = os.stat(src) # According to David Ascher , utime() should be done # before chmod() (at least under NT). if preserve_times: os.utime(dst, (st[ST_ATIME], st[ST_MTIME])) if preserve_mode: os.chmod(dst, S_IMODE(st[ST_MODE])) return (dst, 1) # XXX I suspect this is Unix-specific -- need porting help! def move_file (src, dst, verbose=1, dry_run=0): """Move a 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'. Return the new full name of the file. Handles cross-device moves on Unix using 'copy_file()'. What about other systems??? """ from os.path import exists, isfile, isdir, basename, dirname import errno if verbose >= 1: log.info("moving %s -> %s", src, dst) if dry_run: return dst if not isfile(src): raise DistutilsFileError("can't move '%s': not a regular file" % src) if isdir(dst): dst = os.path.join(dst, basename(src)) elif exists(dst): raise DistutilsFileError( "can't move '%s': destination '%s' already exists" % (src, dst)) if not isdir(dirname(dst)): raise DistutilsFileError( "can't move '%s': destination '%s' not a valid path" % (src, dst)) copy_it = False try: os.rename(src, dst) except OSError as e: (num, msg) = e.args if num == errno.EXDEV: copy_it = True else: raise DistutilsFileError( "couldn't move '%s' to '%s': %s" % (src, dst, msg)) if copy_it: copy_file(src, dst, verbose=verbose) try: os.unlink(src) except OSError as e: (num, msg) = e.args try: os.unlink(dst) except OSError: pass raise DistutilsFileError( "couldn't move '%s' to '%s' by copy/delete: " "delete '%s' failed: %s" % (src, dst, src, msg)) return dst def write_file (filename, contents): """Create a file with the specified name and write 'contents' (a sequence of strings without line terminators) to it. """ f = open(filename, "w") try: for line in contents: f.write(line + "\n") finally: f.close() python-deadlib-3.13.0/distutils/distutils/filelist.py000066400000000000000000000310401471045437300227440ustar00rootroot00000000000000"""distutils.filelist Provides the FileList class, used for poking about the filesystem and building lists of files. """ import os, re import fnmatch import functools from distutils.util import convert_path from distutils.errors import DistutilsTemplateError, DistutilsInternalError from distutils import log class FileList: """A list of files built by on exploring the filesystem and filtered by applying various patterns to what we find there. Instance attributes: dir directory from which files will be taken -- only used if 'allfiles' not supplied to constructor files list of filenames currently being built/filtered/manipulated allfiles complete list of files under consideration (ie. without any filtering applied) """ def __init__(self, warn=None, debug_print=None): # ignore argument to FileList, but keep them for backwards # compatibility self.allfiles = None self.files = [] def set_allfiles(self, allfiles): self.allfiles = allfiles def findall(self, dir=os.curdir): self.allfiles = findall(dir) def debug_print(self, msg): """Print 'msg' to stdout if the global DEBUG (taken from the DISTUTILS_DEBUG environment variable) flag is true. """ from distutils.debug import DEBUG if DEBUG: print(msg) # -- List-like methods --------------------------------------------- def append(self, item): self.files.append(item) def extend(self, items): self.files.extend(items) def sort(self): # Not a strict lexical sort! sortable_files = sorted(map(os.path.split, self.files)) self.files = [] for sort_tuple in sortable_files: self.files.append(os.path.join(*sort_tuple)) # -- Other miscellaneous utility methods --------------------------- def remove_duplicates(self): # Assumes list has been sorted! for i in range(len(self.files) - 1, 0, -1): if self.files[i] == self.files[i - 1]: del self.files[i] # -- "File template" methods --------------------------------------- def _parse_template_line(self, line): words = line.split() action = words[0] patterns = dir = dir_pattern = None if action in ('include', 'exclude', 'global-include', 'global-exclude'): if len(words) < 2: raise DistutilsTemplateError( "'%s' expects ..." % action) patterns = [convert_path(w) for w in words[1:]] elif action in ('recursive-include', 'recursive-exclude'): if len(words) < 3: raise DistutilsTemplateError( "'%s' expects ..." % action) dir = convert_path(words[1]) patterns = [convert_path(w) for w in words[2:]] elif action in ('graft', 'prune'): if len(words) != 2: raise DistutilsTemplateError( "'%s' expects a single " % action) dir_pattern = convert_path(words[1]) else: raise DistutilsTemplateError("unknown action '%s'" % action) return (action, patterns, dir, dir_pattern) def process_template_line(self, line): # Parse the line: split it up, make sure the right number of words # is there, and return the relevant words. 'action' is always # defined: it's the first word of the line. Which of the other # three are defined depends on the action; it'll be either # patterns, (dir and patterns), or (dir_pattern). (action, patterns, dir, dir_pattern) = self._parse_template_line(line) # OK, now we know that the action is valid and we have the # right number of words on the line for that action -- so we # can proceed with minimal error-checking. if action == 'include': self.debug_print("include " + ' '.join(patterns)) for pattern in patterns: if not self.include_pattern(pattern, anchor=1): log.warn("warning: no files found matching '%s'", pattern) elif action == 'exclude': self.debug_print("exclude " + ' '.join(patterns)) for pattern in patterns: if not self.exclude_pattern(pattern, anchor=1): log.warn(("warning: no previously-included files " "found matching '%s'"), pattern) elif action == 'global-include': self.debug_print("global-include " + ' '.join(patterns)) for pattern in patterns: if not self.include_pattern(pattern, anchor=0): log.warn(("warning: no files found matching '%s' " "anywhere in distribution"), pattern) elif action == 'global-exclude': self.debug_print("global-exclude " + ' '.join(patterns)) for pattern in patterns: if not self.exclude_pattern(pattern, anchor=0): log.warn(("warning: no previously-included files matching " "'%s' found anywhere in distribution"), pattern) elif action == 'recursive-include': self.debug_print("recursive-include %s %s" % (dir, ' '.join(patterns))) for pattern in patterns: if not self.include_pattern(pattern, prefix=dir): log.warn(("warning: no files found matching '%s' " "under directory '%s'"), pattern, dir) elif action == 'recursive-exclude': self.debug_print("recursive-exclude %s %s" % (dir, ' '.join(patterns))) for pattern in patterns: if not self.exclude_pattern(pattern, prefix=dir): log.warn(("warning: no previously-included files matching " "'%s' found under directory '%s'"), pattern, dir) elif action == 'graft': self.debug_print("graft " + dir_pattern) if not self.include_pattern(None, prefix=dir_pattern): log.warn("warning: no directories found matching '%s'", dir_pattern) elif action == 'prune': self.debug_print("prune " + dir_pattern) if not self.exclude_pattern(None, prefix=dir_pattern): log.warn(("no previously-included directories found " "matching '%s'"), dir_pattern) else: raise DistutilsInternalError( "this cannot happen: invalid action '%s'" % action) # -- Filtering/selection methods ----------------------------------- def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): """Select strings (presumably filenames) from 'self.files' that match 'pattern', a Unix-style wildcard (glob) pattern. Patterns are not quite the same as implemented by the 'fnmatch' module: '*' and '?' match non-special characters, where "special" is platform- dependent: slash on Unix; colon, slash, and backslash on DOS/Windows; and colon on Mac OS. If 'anchor' is true (the default), then the pattern match is more stringent: "*.py" will match "foo.py" but not "foo/bar.py". If 'anchor' is false, both of these will match. If 'prefix' is supplied, then only filenames starting with 'prefix' (itself a pattern) and ending with 'pattern', with anything in between them, will match. 'anchor' is ignored in this case. If 'is_regex' is true, 'anchor' and 'prefix' are ignored, and 'pattern' is assumed to be either a string containing a regex or a regex object -- no translation is done, the regex is just compiled and used as-is. Selected strings will be added to self.files. Return True if files are found, False otherwise. """ # XXX docstring lying about what the special chars are? files_found = False pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) self.debug_print("include_pattern: applying regex r'%s'" % pattern_re.pattern) # delayed loading of allfiles list if self.allfiles is None: self.findall() for name in self.allfiles: if pattern_re.search(name): self.debug_print(" adding " + name) self.files.append(name) files_found = True return files_found def exclude_pattern (self, pattern, anchor=1, prefix=None, is_regex=0): """Remove strings (presumably filenames) from 'files' that match 'pattern'. Other parameters are the same as for 'include_pattern()', above. The list 'self.files' is modified in place. Return True if files are found, False otherwise. """ files_found = False pattern_re = translate_pattern(pattern, anchor, prefix, is_regex) self.debug_print("exclude_pattern: applying regex r'%s'" % pattern_re.pattern) for i in range(len(self.files)-1, -1, -1): if pattern_re.search(self.files[i]): self.debug_print(" removing " + self.files[i]) del self.files[i] files_found = True return files_found # ---------------------------------------------------------------------- # Utility functions def _find_all_simple(path): """ Find all files under 'path' """ results = ( os.path.join(base, file) for base, dirs, files in os.walk(path, followlinks=True) for file in files ) return filter(os.path.isfile, results) def findall(dir=os.curdir): """ Find all files under 'dir' and return the list of full filenames. Unless dir is '.', return full filenames with dir prepended. """ files = _find_all_simple(dir) if dir == os.curdir: make_rel = functools.partial(os.path.relpath, start=dir) files = map(make_rel, files) return list(files) def glob_to_re(pattern): """Translate a shell-like glob pattern to a regular expression; return a string containing the regex. Differs from 'fnmatch.translate()' in that '*' does not match "special characters" (which are platform-specific). """ pattern_re = fnmatch.translate(pattern) # '?' and '*' in the glob pattern become '.' and '.*' in the RE, which # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, # and by extension they shouldn't match such "special characters" under # any OS. So change all non-escaped dots in the RE to match any # character except the special characters (currently: just os.sep). sep = os.sep if os.sep == '\\': # we're using a regex to manipulate a regex, so we need # to escape the backslash twice sep = r'\\\\' escaped = r'\1[^%s]' % sep pattern_re = re.sub(r'((?= self.threshold: if args: msg = msg % args if level in (WARN, ERROR, FATAL): stream = sys.stderr else: stream = sys.stdout try: stream.write('%s\n' % msg) except UnicodeEncodeError: # emulate backslashreplace error handler encoding = stream.encoding msg = msg.encode(encoding, "backslashreplace").decode(encoding) stream.write('%s\n' % msg) stream.flush() def log(self, level, msg, *args): self._log(level, msg, args) def debug(self, msg, *args): self._log(DEBUG, msg, args) def info(self, msg, *args): self._log(INFO, msg, args) def warn(self, msg, *args): self._log(WARN, msg, args) def error(self, msg, *args): self._log(ERROR, msg, args) def fatal(self, msg, *args): self._log(FATAL, msg, args) _global_log = Log() log = _global_log.log debug = _global_log.debug info = _global_log.info warn = _global_log.warn error = _global_log.error fatal = _global_log.fatal def set_threshold(level): # return the old threshold for use from tests old = _global_log.threshold _global_log.threshold = level return old def set_verbosity(v): if v <= 0: set_threshold(WARN) elif v == 1: set_threshold(INFO) elif v >= 2: set_threshold(DEBUG) python-deadlib-3.13.0/distutils/distutils/msvc9compiler.py000066400000000000000000000733651471045437300237450ustar00rootroot00000000000000"""distutils.msvc9compiler Contains MSVCCompiler, an implementation of the abstract CCompiler class for the Microsoft Visual Studio 2008. The module is compatible with VS 2005 and VS 2008. You can find legacy support for older versions of VS in distutils.msvccompiler. """ # Written by Perry Stoll # hacked by Robin Becker and Thomas Heller to do a better job of # finding DevStudio (through the registry) # ported to VS2005 and VS 2008 by Christian Heimes import os import subprocess import sys import re from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError from distutils.ccompiler import CCompiler, gen_lib_options from distutils import log from distutils.util import get_platform import winreg RegOpenKeyEx = winreg.OpenKeyEx RegEnumKey = winreg.EnumKey RegEnumValue = winreg.EnumValue RegError = winreg.error HKEYS = (winreg.HKEY_USERS, winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CLASSES_ROOT) NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32) if NATIVE_WIN64: # Visual C++ is a 32-bit application, so we need to look in # the corresponding registry branch, if we're running a # 64-bit Python on Win64 VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f" WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows" NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework" else: VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f" WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows" NET_BASE = r"Software\Microsoft\.NETFramework" # A map keyed by get_platform() return values to values accepted by # 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is # the param to cross-compile on x86 targeting amd64.) PLAT_TO_VCVARS = { 'win32' : 'x86', 'win-amd64' : 'amd64', } class Reg: """Helper class to read values from the registry """ def get_value(cls, path, key): for base in HKEYS: d = cls.read_values(base, path) if d and key in d: return d[key] raise KeyError(key) get_value = classmethod(get_value) def read_keys(cls, base, key): """Return list of registry keys.""" try: handle = RegOpenKeyEx(base, key) except RegError: return None L = [] i = 0 while True: try: k = RegEnumKey(handle, i) except RegError: break L.append(k) i += 1 return L read_keys = classmethod(read_keys) def read_values(cls, base, key): """Return dict of registry keys and values. All names are converted to lowercase. """ try: handle = RegOpenKeyEx(base, key) except RegError: return None d = {} i = 0 while True: try: name, value, type = RegEnumValue(handle, i) except RegError: break name = name.lower() d[cls.convert_mbcs(name)] = cls.convert_mbcs(value) i += 1 return d read_values = classmethod(read_values) def convert_mbcs(s): dec = getattr(s, "decode", None) if dec is not None: try: s = dec("mbcs") except UnicodeError: pass return s convert_mbcs = staticmethod(convert_mbcs) class MacroExpander: def __init__(self, version): self.macros = {} self.vsbase = VS_BASE % version self.load_macros(version) def set_macro(self, macro, path, key): self.macros["$(%s)" % macro] = Reg.get_value(path, key) def load_macros(self, version): self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir") self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir") self.set_macro("FrameworkDir", NET_BASE, "installroot") try: if version >= 8.0: self.set_macro("FrameworkSDKDir", NET_BASE, "sdkinstallrootv2.0") else: raise KeyError("sdkinstallrootv2.0") except KeyError: raise DistutilsPlatformError( """Python was built with Visual Studio 2008; extensions must be built with a compiler than can generate compatible binaries. Visual Studio 2008 was not found on this system. If you have Cygwin installed, you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") if version >= 9.0: self.set_macro("FrameworkVersion", self.vsbase, "clr version") self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder") else: p = r"Software\Microsoft\NET Framework Setup\Product" for base in HKEYS: try: h = RegOpenKeyEx(base, p) except RegError: continue key = RegEnumKey(h, 0) d = Reg.get_value(base, r"%s\%s" % (p, key)) self.macros["$(FrameworkVersion)"] = d["version"] def sub(self, s): for k, v in self.macros.items(): s = s.replace(k, v) return s def get_build_version(): """Return the version of MSVC that was used to build Python. For Python 2.3 and up, the version number is included in sys.version. For earlier versions, assume the compiler is MSVC 6. """ prefix = "MSC v." i = sys.version.find(prefix) if i == -1: return 6 i = i + len(prefix) s, rest = sys.version[i:].split(" ", 1) majorVersion = int(s[:-2]) - 6 if majorVersion >= 13: # v13 was skipped and should be v14 majorVersion += 1 minorVersion = int(s[2:3]) / 10.0 # I don't think paths are affected by minor version in version 6 if majorVersion == 6: minorVersion = 0 if majorVersion >= 6: return majorVersion + minorVersion # else we don't know what version of the compiler this is return None def normalize_and_reduce_paths(paths): """Return a list of normalized paths with duplicates removed. The current order of paths is maintained. """ # Paths are normalized so things like: /a and /a/ aren't both preserved. reduced_paths = [] for p in paths: np = os.path.normpath(p) # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. if np not in reduced_paths: reduced_paths.append(np) return reduced_paths def removeDuplicates(variable): """Remove duplicate values of an environment variable. """ oldList = variable.split(os.pathsep) newList = [] for i in oldList: if i not in newList: newList.append(i) newVariable = os.pathsep.join(newList) return newVariable def find_vcvarsall(version): """Find the vcvarsall.bat file At first it tries to find the productdir of VS 2008 in the registry. If that fails it falls back to the VS90COMNTOOLS env var. """ vsbase = VS_BASE % version try: productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, "productdir") except KeyError: log.debug("Unable to find productdir in registry") productdir = None if not productdir or not os.path.isdir(productdir): toolskey = "VS%0.f0COMNTOOLS" % version toolsdir = os.environ.get(toolskey, None) if toolsdir and os.path.isdir(toolsdir): productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC") productdir = os.path.abspath(productdir) if not os.path.isdir(productdir): log.debug("%s is not a valid directory" % productdir) return None else: log.debug("Env var %s is not set or invalid" % toolskey) if not productdir: log.debug("No productdir found") return None vcvarsall = os.path.join(productdir, "vcvarsall.bat") if os.path.isfile(vcvarsall): return vcvarsall log.debug("Unable to find vcvarsall.bat") return None def query_vcvarsall(version, arch="x86"): """Launch vcvarsall.bat and read the settings from its environment """ vcvarsall = find_vcvarsall(version) interesting = {"include", "lib", "libpath", "path"} result = {} if vcvarsall is None: raise DistutilsPlatformError("Unable to find vcvarsall.bat") log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version) popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch), stdout=subprocess.PIPE, stderr=subprocess.PIPE) try: stdout, stderr = popen.communicate() if popen.wait() != 0: raise DistutilsPlatformError(stderr.decode("mbcs")) stdout = stdout.decode("mbcs") for line in stdout.split("\n"): line = Reg.convert_mbcs(line) if '=' not in line: continue line = line.strip() key, value = line.split('=', 1) key = key.lower() if key in interesting: if value.endswith(os.pathsep): value = value[:-1] result[key] = removeDuplicates(value) finally: popen.stdout.close() popen.stderr.close() if len(result) != len(interesting): raise ValueError(str(list(result.keys()))) return result # More globals VERSION = get_build_version() if VERSION < 8.0: raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION) # MACROS = MacroExpander(VERSION) class MSVCCompiler(CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, as defined by the CCompiler abstract class.""" compiler_type = 'msvc' # Just set this so CCompiler's constructor doesn't barf. We currently # don't use the 'set_executables()' bureaucracy provided by CCompiler, # as it really isn't necessary for this sort of single-compiler class. # Would be nice to have a consistent interface with UnixCCompiler, # though, so it's worth thinking about. executables = {} # Private class data (need to distinguish C from C++ source for compiler) _c_extensions = ['.c'] _cpp_extensions = ['.cc', '.cpp', '.cxx'] _rc_extensions = ['.rc'] _mc_extensions = ['.mc'] # Needed for the filename generation methods provided by the # base class, CCompiler. src_extensions = (_c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions) res_extension = '.res' obj_extension = '.obj' static_lib_extension = '.lib' shared_lib_extension = '.dll' static_lib_format = shared_lib_format = '%s%s' exe_extension = '.exe' def __init__(self, verbose=0, dry_run=0, force=0): CCompiler.__init__ (self, verbose, dry_run, force) self.__version = VERSION self.__root = r"Software\Microsoft\VisualStudio" # self.__macros = MACROS self.__paths = [] # target platform (.plat_name is consistent with 'bdist') self.plat_name = None self.__arch = None # deprecated name self.initialized = False def initialize(self, plat_name=None): # multi-init means we would need to check platform same each time... assert not self.initialized, "don't init multiple times" if plat_name is None: plat_name = get_platform() # sanity check for platforms to prevent obscure errors later. ok_plats = 'win32', 'win-amd64' if plat_name not in ok_plats: raise DistutilsPlatformError("--plat-name must be one of %s" % (ok_plats,)) if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): # Assume that the SDK set up everything alright; don't try to be # smarter self.cc = "cl.exe" self.linker = "link.exe" self.lib = "lib.exe" self.rc = "rc.exe" self.mc = "mc.exe" else: # On x86, 'vcvars32.bat amd64' creates an env that doesn't work; # to cross compile, you use 'x86_amd64'. # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross # compile use 'x86' (ie, it runs the x86 compiler directly) if plat_name == get_platform() or plat_name == 'win32': # native build or cross-compile to win32 plat_spec = PLAT_TO_VCVARS[plat_name] else: # cross compile from win32 -> some 64bit plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \ PLAT_TO_VCVARS[plat_name] vc_env = query_vcvarsall(VERSION, plat_spec) self.__paths = vc_env['path'].split(os.pathsep) os.environ['lib'] = vc_env['lib'] os.environ['include'] = vc_env['include'] if len(self.__paths) == 0: raise DistutilsPlatformError("Python was built with %s, " "and extensions need to be built with the same " "version of the compiler, but it isn't installed." % self.__product) self.cc = self.find_exe("cl.exe") self.linker = self.find_exe("link.exe") self.lib = self.find_exe("lib.exe") self.rc = self.find_exe("rc.exe") # resource compiler self.mc = self.find_exe("mc.exe") # message compiler #self.set_path_env_var('lib') #self.set_path_env_var('include') # extend the MSVC path with the current path try: for p in os.environ['path'].split(';'): self.__paths.append(p) except KeyError: pass self.__paths = normalize_and_reduce_paths(self.__paths) os.environ['path'] = ";".join(self.__paths) self.preprocess_options = None if self.__arch == "x86": self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/DNDEBUG'] self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/Z7', '/D_DEBUG'] else: # Win64 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , '/DNDEBUG'] self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', '/Z7', '/D_DEBUG'] self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] if self.__version >= 7: self.ldflags_shared_debug = [ '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' ] self.ldflags_static = [ '/nologo'] self.initialized = True # -- Worker methods ------------------------------------------------ def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): # Copied from ccompiler.py, extended to return .res as 'object'-file # for .rc input file if output_dir is None: output_dir = '' obj_names = [] for src_name in source_filenames: (base, ext) = os.path.splitext (src_name) base = os.path.splitdrive(base)[1] # Chop off the drive base = base[os.path.isabs(base):] # If abs, chop off leading / if ext not in self.src_extensions: # Better to raise an exception instead of silently continuing # and later complain about sources and targets having # different lengths raise CompileError ("Don't know how to compile %s" % src_name) if strip_dir: base = os.path.basename (base) if ext in self._rc_extensions: obj_names.append (os.path.join (output_dir, base + self.res_extension)) elif ext in self._mc_extensions: obj_names.append (os.path.join (output_dir, base + self.res_extension)) else: obj_names.append (os.path.join (output_dir, base + self.obj_extension)) return obj_names def compile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): if not self.initialized: self.initialize() compile_info = self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) macros, objects, extra_postargs, pp_opts, build = compile_info compile_opts = extra_preargs or [] compile_opts.append ('/c') if debug: compile_opts.extend(self.compile_options_debug) else: compile_opts.extend(self.compile_options) for obj in objects: try: src, ext = build[obj] except KeyError: continue if debug: # pass the full pathname to MSVC in debug mode, # this allows the debugger to find the source file # without asking the user to browse for it src = os.path.abspath(src) if ext in self._c_extensions: input_opt = "/Tc" + src elif ext in self._cpp_extensions: input_opt = "/Tp" + src elif ext in self._rc_extensions: # compile .RC to .RES file input_opt = src output_opt = "/fo" + obj try: self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt]) except DistutilsExecError as msg: raise CompileError(msg) continue elif ext in self._mc_extensions: # Compile .MC to .RC file to .RES file. # * '-h dir' specifies the directory for the # generated include file # * '-r dir' specifies the target directory of the # generated RC file and the binary message resource # it includes # # For now (since there are no options to change this), # we use the source-directory for the include file and # the build directory for the RC file and message # resources. This works at least for win32all. h_dir = os.path.dirname(src) rc_dir = os.path.dirname(obj) try: # first compile .MC to .RC and .H file self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src]) base, _ = os.path.splitext (os.path.basename (src)) rc_file = os.path.join (rc_dir, base + '.rc') # then compile .RC to .RES file self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) except DistutilsExecError as msg: raise CompileError(msg) continue else: # how to handle this file? raise CompileError("Don't know how to compile %s to %s" % (src, obj)) output_opt = "/Fo" + obj try: self.spawn([self.cc] + compile_opts + pp_opts + [input_opt, output_opt] + extra_postargs) except DistutilsExecError as msg: raise CompileError(msg) return objects def create_static_lib(self, objects, output_libname, output_dir=None, debug=0, target_lang=None): if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args(objects, output_dir) output_filename = self.library_filename(output_libname, output_dir=output_dir) if self._need_link(objects, output_filename): lib_args = objects + ['/OUT:' + output_filename] if debug: pass # XXX what goes here? try: self.spawn([self.lib] + lib_args) except DistutilsExecError as msg: raise LibError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) def link(self, 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): if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args(objects, output_dir) fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) (libraries, library_dirs, runtime_library_dirs) = fixed_args if runtime_library_dirs: self.warn ("I don't know what to do with 'runtime_library_dirs': " + str (runtime_library_dirs)) lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) if output_dir is not None: output_filename = os.path.join(output_dir, output_filename) if self._need_link(objects, output_filename): if target_desc == CCompiler.EXECUTABLE: if debug: ldflags = self.ldflags_shared_debug[1:] else: ldflags = self.ldflags_shared[1:] else: if debug: ldflags = self.ldflags_shared_debug else: ldflags = self.ldflags_shared export_opts = [] for sym in (export_symbols or []): export_opts.append("/EXPORT:" + sym) ld_args = (ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]) # The MSVC linker generates .lib and .exp files, which cannot be # suppressed by any linker switches. The .lib files may even be # needed! Make sure they are generated in the temporary build # directory. Since they have different names for debug and release # builds, they can go into the same directory. build_temp = os.path.dirname(objects[0]) if export_symbols is not None: (dll_name, dll_ext) = os.path.splitext( os.path.basename(output_filename)) implib_file = os.path.join( build_temp, self.library_filename(dll_name)) ld_args.append ('/IMPLIB:' + implib_file) self.manifest_setup_ldargs(output_filename, build_temp, ld_args) if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: ld_args.extend(extra_postargs) self.mkpath(os.path.dirname(output_filename)) try: self.spawn([self.linker] + ld_args) except DistutilsExecError as msg: raise LinkError(msg) # embed the manifest # XXX - this is somewhat fragile - if mt.exe fails, distutils # will still consider the DLL up-to-date, but it will not have a # manifest. Maybe we should link to a temp file? OTOH, that # implies a build environment error that shouldn't go undetected. mfinfo = self.manifest_get_embed_info(target_desc, ld_args) if mfinfo is not None: mffilename, mfid = mfinfo out_arg = '-outputresource:%s;%s' % (output_filename, mfid) try: self.spawn(['mt.exe', '-nologo', '-manifest', mffilename, out_arg]) except DistutilsExecError as msg: raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): # If we need a manifest at all, an embedded manifest is recommended. # See MSDN article titled # "How to: Embed a Manifest Inside a C/C++ Application" # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx) # Ask the linker to generate the manifest in the temp dir, so # we can check it, and possibly embed it, later. temp_manifest = os.path.join( build_temp, os.path.basename(output_filename) + ".manifest") ld_args.append('/MANIFESTFILE:' + temp_manifest) def manifest_get_embed_info(self, target_desc, ld_args): # If a manifest should be embedded, return a tuple of # (manifest_filename, resource_id). Returns None if no manifest # should be embedded. See http://bugs.python.org/issue7833 for why # we want to avoid any manifest for extension modules if we can. for arg in ld_args: if arg.startswith("/MANIFESTFILE:"): temp_manifest = arg.split(":", 1)[1] break else: # no /MANIFESTFILE so nothing to do. return None if target_desc == CCompiler.EXECUTABLE: # by default, executables always get the manifest with the # CRT referenced. mfid = 1 else: # Extension modules try and avoid any manifest if possible. mfid = 2 temp_manifest = self._remove_visual_c_ref(temp_manifest) if temp_manifest is None: return None return temp_manifest, mfid def _remove_visual_c_ref(self, manifest_file): try: # Remove references to the Visual C runtime, so they will # fall through to the Visual C dependency of Python.exe. # This way, when installed for a restricted user (e.g. # runtimes are not in WinSxS folder, but in Python's own # folder), the runtimes do not need to be in every folder # with .pyd's. # Returns either the filename of the modified manifest or # None if no manifest should be embedded. manifest_f = open(manifest_file) try: manifest_buf = manifest_f.read() finally: manifest_f.close() pattern = re.compile( r"""|)""", re.DOTALL) manifest_buf = re.sub(pattern, "", manifest_buf) pattern = r"\s*" manifest_buf = re.sub(pattern, "", manifest_buf) # Now see if any other assemblies are referenced - if not, we # don't want a manifest embedded. pattern = re.compile( r"""|)""", re.DOTALL) if re.search(pattern, manifest_buf) is None: return None manifest_f = open(manifest_file, 'w') try: manifest_f.write(manifest_buf) return manifest_file finally: manifest_f.close() except OSError: pass # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in # ccompiler.py. def library_dir_option(self, dir): return "/LIBPATH:" + dir def runtime_library_dir_option(self, dir): raise DistutilsPlatformError( "don't know how to set runtime library search path for MSVC++") def library_option(self, lib): return self.library_filename(lib) def find_library_file(self, dirs, lib, debug=0): # Prefer a debugging library if found (and requested), but deal # with it if we don't have one. if debug: try_names = [lib + "_d", lib] else: try_names = [lib] for dir in dirs: for name in try_names: libfile = os.path.join(dir, self.library_filename (name)) if os.path.exists(libfile): return libfile else: # Oops, didn't find it in *any* of 'dirs' return None # Helper methods for using the MSVC registry settings def find_exe(self, exe): """Return path to an MSVC executable program. Tries to find the program in several places: first, one of the MSVC program search paths from the registry; next, the directories in the PATH environment variable. If any of those work, return an absolute path that is known to exist. If none of them work, just return the original program name, 'exe'. """ for p in self.__paths: fn = os.path.join(os.path.abspath(p), exe) if os.path.isfile(fn): return fn # didn't find it; try existing path for p in os.environ['Path'].split(';'): fn = os.path.join(os.path.abspath(p),exe) if os.path.isfile(fn): return fn return exe python-deadlib-3.13.0/distutils/distutils/msvccompiler.py000066400000000000000000000557471471045437300236600ustar00rootroot00000000000000"""distutils.msvccompiler Contains MSVCCompiler, an implementation of the abstract CCompiler class for the Microsoft Visual Studio. """ # Written by Perry Stoll # hacked by Robin Becker and Thomas Heller to do a better job of # finding DevStudio (through the registry) import sys, os from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError from distutils.ccompiler import \ CCompiler, gen_lib_options from distutils import log _can_read_reg = False try: import winreg _can_read_reg = True hkey_mod = winreg RegOpenKeyEx = winreg.OpenKeyEx RegEnumKey = winreg.EnumKey RegEnumValue = winreg.EnumValue RegError = winreg.error except ImportError: try: import win32api import win32con _can_read_reg = True hkey_mod = win32con RegOpenKeyEx = win32api.RegOpenKeyEx RegEnumKey = win32api.RegEnumKey RegEnumValue = win32api.RegEnumValue RegError = win32api.error except ImportError: log.info("Warning: Can't read registry to find the " "necessary compiler setting\n" "Make sure that Python modules winreg, " "win32api or win32con are installed.") if _can_read_reg: HKEYS = (hkey_mod.HKEY_USERS, hkey_mod.HKEY_CURRENT_USER, hkey_mod.HKEY_LOCAL_MACHINE, hkey_mod.HKEY_CLASSES_ROOT) def read_keys(base, key): """Return list of registry keys.""" try: handle = RegOpenKeyEx(base, key) except RegError: return None L = [] i = 0 while True: try: k = RegEnumKey(handle, i) except RegError: break L.append(k) i += 1 return L def read_values(base, key): """Return dict of registry keys and values. All names are converted to lowercase. """ try: handle = RegOpenKeyEx(base, key) except RegError: return None d = {} i = 0 while True: try: name, value, type = RegEnumValue(handle, i) except RegError: break name = name.lower() d[convert_mbcs(name)] = convert_mbcs(value) i += 1 return d def convert_mbcs(s): dec = getattr(s, "decode", None) if dec is not None: try: s = dec("mbcs") except UnicodeError: pass return s class MacroExpander: def __init__(self, version): self.macros = {} self.load_macros(version) def set_macro(self, macro, path, key): for base in HKEYS: d = read_values(base, path) if d: self.macros["$(%s)" % macro] = d[key] break def load_macros(self, version): vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir") self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir") net = r"Software\Microsoft\.NETFramework" self.set_macro("FrameworkDir", net, "installroot") try: if version > 7.0: self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1") else: self.set_macro("FrameworkSDKDir", net, "sdkinstallroot") except KeyError as exc: # raise DistutilsPlatformError( """Python was built with Visual Studio 2003; extensions must be built with a compiler than can generate compatible binaries. Visual Studio 2003 was not found on this system. If you have Cygwin installed, you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""") p = r"Software\Microsoft\NET Framework Setup\Product" for base in HKEYS: try: h = RegOpenKeyEx(base, p) except RegError: continue key = RegEnumKey(h, 0) d = read_values(base, r"%s\%s" % (p, key)) self.macros["$(FrameworkVersion)"] = d["version"] def sub(self, s): for k, v in self.macros.items(): s = s.replace(k, v) return s def get_build_version(): """Return the version of MSVC that was used to build Python. For Python 2.3 and up, the version number is included in sys.version. For earlier versions, assume the compiler is MSVC 6. """ prefix = "MSC v." i = sys.version.find(prefix) if i == -1: return 6 i = i + len(prefix) s, rest = sys.version[i:].split(" ", 1) majorVersion = int(s[:-2]) - 6 if majorVersion >= 13: # v13 was skipped and should be v14 majorVersion += 1 minorVersion = int(s[2:3]) / 10.0 # I don't think paths are affected by minor version in version 6 if majorVersion == 6: minorVersion = 0 if majorVersion >= 6: return majorVersion + minorVersion # else we don't know what version of the compiler this is return None def get_build_architecture(): """Return the processor architecture. Possible results are "Intel" or "AMD64". """ prefix = " bit (" i = sys.version.find(prefix) if i == -1: return "Intel" j = sys.version.find(")", i) return sys.version[i+len(prefix):j] def normalize_and_reduce_paths(paths): """Return a list of normalized paths with duplicates removed. The current order of paths is maintained. """ # Paths are normalized so things like: /a and /a/ aren't both preserved. reduced_paths = [] for p in paths: np = os.path.normpath(p) # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set. if np not in reduced_paths: reduced_paths.append(np) return reduced_paths class MSVCCompiler(CCompiler) : """Concrete class that implements an interface to Microsoft Visual C++, as defined by the CCompiler abstract class.""" compiler_type = 'msvc' # Just set this so CCompiler's constructor doesn't barf. We currently # don't use the 'set_executables()' bureaucracy provided by CCompiler, # as it really isn't necessary for this sort of single-compiler class. # Would be nice to have a consistent interface with UnixCCompiler, # though, so it's worth thinking about. executables = {} # Private class data (need to distinguish C from C++ source for compiler) _c_extensions = ['.c'] _cpp_extensions = ['.cc', '.cpp', '.cxx'] _rc_extensions = ['.rc'] _mc_extensions = ['.mc'] # Needed for the filename generation methods provided by the # base class, CCompiler. src_extensions = (_c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions) res_extension = '.res' obj_extension = '.obj' static_lib_extension = '.lib' shared_lib_extension = '.dll' static_lib_format = shared_lib_format = '%s%s' exe_extension = '.exe' def __init__(self, verbose=0, dry_run=0, force=0): CCompiler.__init__ (self, verbose, dry_run, force) self.__version = get_build_version() self.__arch = get_build_architecture() if self.__arch == "Intel": # x86 if self.__version >= 7: self.__root = r"Software\Microsoft\VisualStudio" self.__macros = MacroExpander(self.__version) else: self.__root = r"Software\Microsoft\Devstudio" self.__product = "Visual Studio version %s" % self.__version else: # Win64. Assume this was built with the platform SDK self.__product = "Microsoft SDK compiler %s" % (self.__version + 6) self.initialized = False def initialize(self): self.__paths = [] if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"): # Assume that the SDK set up everything alright; don't try to be # smarter self.cc = "cl.exe" self.linker = "link.exe" self.lib = "lib.exe" self.rc = "rc.exe" self.mc = "mc.exe" else: self.__paths = self.get_msvc_paths("path") if len(self.__paths) == 0: raise DistutilsPlatformError("Python was built with %s, " "and extensions need to be built with the same " "version of the compiler, but it isn't installed." % self.__product) self.cc = self.find_exe("cl.exe") self.linker = self.find_exe("link.exe") self.lib = self.find_exe("lib.exe") self.rc = self.find_exe("rc.exe") # resource compiler self.mc = self.find_exe("mc.exe") # message compiler self.set_path_env_var('lib') self.set_path_env_var('include') # extend the MSVC path with the current path try: for p in os.environ['path'].split(';'): self.__paths.append(p) except KeyError: pass self.__paths = normalize_and_reduce_paths(self.__paths) os.environ['path'] = ";".join(self.__paths) self.preprocess_options = None if self.__arch == "Intel": self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GX' , '/DNDEBUG'] self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX', '/Z7', '/D_DEBUG'] else: # Win64 self.compile_options = [ '/nologo', '/Ox', '/MD', '/W3', '/GS-' , '/DNDEBUG'] self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-', '/Z7', '/D_DEBUG'] self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO'] if self.__version >= 7: self.ldflags_shared_debug = [ '/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG' ] else: self.ldflags_shared_debug = [ '/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG' ] self.ldflags_static = [ '/nologo'] self.initialized = True # -- Worker methods ------------------------------------------------ def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): # Copied from ccompiler.py, extended to return .res as 'object'-file # for .rc input file if output_dir is None: output_dir = '' obj_names = [] for src_name in source_filenames: (base, ext) = os.path.splitext (src_name) base = os.path.splitdrive(base)[1] # Chop off the drive base = base[os.path.isabs(base):] # If abs, chop off leading / if ext not in self.src_extensions: # Better to raise an exception instead of silently continuing # and later complain about sources and targets having # different lengths raise CompileError ("Don't know how to compile %s" % src_name) if strip_dir: base = os.path.basename (base) if ext in self._rc_extensions: obj_names.append (os.path.join (output_dir, base + self.res_extension)) elif ext in self._mc_extensions: obj_names.append (os.path.join (output_dir, base + self.res_extension)) else: obj_names.append (os.path.join (output_dir, base + self.obj_extension)) return obj_names def compile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): if not self.initialized: self.initialize() compile_info = self._setup_compile(output_dir, macros, include_dirs, sources, depends, extra_postargs) macros, objects, extra_postargs, pp_opts, build = compile_info compile_opts = extra_preargs or [] compile_opts.append ('/c') if debug: compile_opts.extend(self.compile_options_debug) else: compile_opts.extend(self.compile_options) for obj in objects: try: src, ext = build[obj] except KeyError: continue if debug: # pass the full pathname to MSVC in debug mode, # this allows the debugger to find the source file # without asking the user to browse for it src = os.path.abspath(src) if ext in self._c_extensions: input_opt = "/Tc" + src elif ext in self._cpp_extensions: input_opt = "/Tp" + src elif ext in self._rc_extensions: # compile .RC to .RES file input_opt = src output_opt = "/fo" + obj try: self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt]) except DistutilsExecError as msg: raise CompileError(msg) continue elif ext in self._mc_extensions: # Compile .MC to .RC file to .RES file. # * '-h dir' specifies the directory for the # generated include file # * '-r dir' specifies the target directory of the # generated RC file and the binary message resource # it includes # # For now (since there are no options to change this), # we use the source-directory for the include file and # the build directory for the RC file and message # resources. This works at least for win32all. h_dir = os.path.dirname(src) rc_dir = os.path.dirname(obj) try: # first compile .MC to .RC and .H file self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src]) base, _ = os.path.splitext (os.path.basename (src)) rc_file = os.path.join (rc_dir, base + '.rc') # then compile .RC to .RES file self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) except DistutilsExecError as msg: raise CompileError(msg) continue else: # how to handle this file? raise CompileError("Don't know how to compile %s to %s" % (src, obj)) output_opt = "/Fo" + obj try: self.spawn([self.cc] + compile_opts + pp_opts + [input_opt, output_opt] + extra_postargs) except DistutilsExecError as msg: raise CompileError(msg) return objects def create_static_lib(self, objects, output_libname, output_dir=None, debug=0, target_lang=None): if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args(objects, output_dir) output_filename = self.library_filename(output_libname, output_dir=output_dir) if self._need_link(objects, output_filename): lib_args = objects + ['/OUT:' + output_filename] if debug: pass # XXX what goes here? try: self.spawn([self.lib] + lib_args) except DistutilsExecError as msg: raise LibError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) def link(self, 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): if not self.initialized: self.initialize() (objects, output_dir) = self._fix_object_args(objects, output_dir) fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) (libraries, library_dirs, runtime_library_dirs) = fixed_args if runtime_library_dirs: self.warn ("I don't know what to do with 'runtime_library_dirs': " + str (runtime_library_dirs)) lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) if output_dir is not None: output_filename = os.path.join(output_dir, output_filename) if self._need_link(objects, output_filename): if target_desc == CCompiler.EXECUTABLE: if debug: ldflags = self.ldflags_shared_debug[1:] else: ldflags = self.ldflags_shared[1:] else: if debug: ldflags = self.ldflags_shared_debug else: ldflags = self.ldflags_shared export_opts = [] for sym in (export_symbols or []): export_opts.append("/EXPORT:" + sym) ld_args = (ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]) # The MSVC linker generates .lib and .exp files, which cannot be # suppressed by any linker switches. The .lib files may even be # needed! Make sure they are generated in the temporary build # directory. Since they have different names for debug and release # builds, they can go into the same directory. if export_symbols is not None: (dll_name, dll_ext) = os.path.splitext( os.path.basename(output_filename)) implib_file = os.path.join( os.path.dirname(objects[0]), self.library_filename(dll_name)) ld_args.append ('/IMPLIB:' + implib_file) if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: ld_args.extend(extra_postargs) self.mkpath(os.path.dirname(output_filename)) try: self.spawn([self.linker] + ld_args) except DistutilsExecError as msg: raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in # ccompiler.py. def library_dir_option(self, dir): return "/LIBPATH:" + dir def runtime_library_dir_option(self, dir): raise DistutilsPlatformError( "don't know how to set runtime library search path for MSVC++") def library_option(self, lib): return self.library_filename(lib) def find_library_file(self, dirs, lib, debug=0): # Prefer a debugging library if found (and requested), but deal # with it if we don't have one. if debug: try_names = [lib + "_d", lib] else: try_names = [lib] for dir in dirs: for name in try_names: libfile = os.path.join(dir, self.library_filename (name)) if os.path.exists(libfile): return libfile else: # Oops, didn't find it in *any* of 'dirs' return None # Helper methods for using the MSVC registry settings def find_exe(self, exe): """Return path to an MSVC executable program. Tries to find the program in several places: first, one of the MSVC program search paths from the registry; next, the directories in the PATH environment variable. If any of those work, return an absolute path that is known to exist. If none of them work, just return the original program name, 'exe'. """ for p in self.__paths: fn = os.path.join(os.path.abspath(p), exe) if os.path.isfile(fn): return fn # didn't find it; try existing path for p in os.environ['Path'].split(';'): fn = os.path.join(os.path.abspath(p),exe) if os.path.isfile(fn): return fn return exe def get_msvc_paths(self, path, platform='x86'): """Get a list of devstudio directories (include, lib or path). Return a list of strings. The list will be empty if unable to access the registry or appropriate registry keys not found. """ if not _can_read_reg: return [] path = path + " dirs" if self.__version >= 7: key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" % (self.__root, self.__version)) else: key = (r"%s\6.0\Build System\Components\Platforms" r"\Win32 (%s)\Directories" % (self.__root, platform)) for base in HKEYS: d = read_values(base, key) if d: if self.__version >= 7: return self.__macros.sub(d[path]).split(";") else: return d[path].split(";") # MSVC 6 seems to create the registry entries we need only when # the GUI is run. if self.__version == 6: for base in HKEYS: if read_values(base, r"%s\6.0" % self.__root) is not None: self.warn("It seems you have Visual Studio 6 installed, " "but the expected registry settings are not present.\n" "You must at least run the Visual Studio GUI once " "so that these entries are created.") break return [] def set_path_env_var(self, name): """Set environment variable 'name' to an MSVC path type value. This is equivalent to a SET command prior to execution of spawned commands. """ if name == "lib": p = self.get_msvc_paths("library") else: p = self.get_msvc_paths(name) if p: os.environ[name] = ';'.join(p) if get_build_version() >= 8.0: log.debug("Importing new compiler from distutils.msvc9compiler") OldMSVCCompiler = MSVCCompiler from distutils.msvc9compiler import MSVCCompiler # get_build_architecture not really relevant now we support cross-compile from distutils.msvc9compiler import MacroExpander python-deadlib-3.13.0/distutils/distutils/spawn.py000066400000000000000000000110641471045437300222650ustar00rootroot00000000000000"""distutils.spawn Provides the 'spawn()' function, a front-end to various platform- specific functions for launching another program in a sub-process. Also provides the 'find_executable()' to search the path for a given executable name. """ import sys import os import subprocess from distutils.errors import DistutilsPlatformError, DistutilsExecError from distutils.debug import DEBUG from distutils import log if sys.platform == 'darwin': _cfg_target = None _cfg_target_split = None def spawn(cmd, search_path=1, verbose=0, dry_run=0): """Run another program, specified as a command list 'cmd', in a new process. 'cmd' is just the argument list for the new process, ie. cmd[0] is the program to run and cmd[1:] are the rest of its arguments. There is no way to run a program with a name different from that of its executable. If 'search_path' is true (the default), the system's executable search path will be used to find the program; otherwise, cmd[0] must be the exact path to the executable. If 'dry_run' is true, the command will not actually be run. Raise DistutilsExecError if running the program fails in any way; just return on success. """ # cmd is documented as a list, but just in case some code passes a tuple # in, protect our %-formatting code against horrible death cmd = list(cmd) log.info(' '.join(cmd)) if dry_run: return if search_path: executable = find_executable(cmd[0]) if executable is not None: cmd[0] = executable env = None if sys.platform == 'darwin': global _cfg_target, _cfg_target_split if _cfg_target is None: from distutils import sysconfig _cfg_target = sysconfig.get_config_var( 'MACOSX_DEPLOYMENT_TARGET') or '' if _cfg_target: _cfg_target_split = [int(x) for x in _cfg_target.split('.')] if _cfg_target: # Ensure that the deployment target of the build process is not # less than 10.3 if the interpreter was built for 10.3 or later. # This ensures extension modules are built with correct # compatibility values, specifically LDSHARED which can use # '-undefined dynamic_lookup' which only works on >= 10.3. cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target) cur_target_split = [int(x) for x in cur_target.split('.')] if _cfg_target_split[:2] >= [10, 3] and cur_target_split[:2] < [10, 3]: my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: ' 'now "%s" but "%s" during configure;' 'must use 10.3 or later' % (cur_target, _cfg_target)) raise DistutilsPlatformError(my_msg) env = dict(os.environ, MACOSX_DEPLOYMENT_TARGET=cur_target) try: proc = subprocess.Popen(cmd, env=env) proc.wait() exitcode = proc.returncode except OSError as exc: if not DEBUG: cmd = cmd[0] raise DistutilsExecError( "command %r failed: %s" % (cmd, exc.args[-1])) from exc if exitcode: if not DEBUG: cmd = cmd[0] raise DistutilsExecError( "command %r failed with exit code %s" % (cmd, exitcode)) def find_executable(executable, path=None): """Tries to find 'executable' in the directories listed in 'path'. A string listing directories separated by 'os.pathsep'; defaults to os.environ['PATH']. Returns the complete filename or None if not found. """ _, ext = os.path.splitext(executable) if (sys.platform == 'win32') and (ext != '.exe'): executable = executable + '.exe' if os.path.isfile(executable): return executable if path is None: path = os.environ.get('PATH', None) if path is None: try: path = os.confstr("CS_PATH") except (AttributeError, ValueError): # os.confstr() or CS_PATH is not available path = os.defpath # bpo-35755: Don't use os.defpath if the PATH environment variable is # set to an empty string # PATH='' doesn't match, whereas PATH=':' looks in the current directory if not path: return None paths = path.split(os.pathsep) for p in paths: f = os.path.join(p, executable) if os.path.isfile(f): # the file exists, we have a shot at spawn working return f return None python-deadlib-3.13.0/distutils/distutils/sysconfig.py000066400000000000000000000302231471045437300231370ustar00rootroot00000000000000"""Provide access to Python's configuration information. The specific configuration variables available depend heavily on the platform and configuration. The values may be retrieved using get_config_var(name), and the list of variables is available via get_config_vars().keys(). Additional convenience functions are also available. Written by: Fred L. Drake, Jr. Email: """ import _imp import os import re import sys import warnings from functools import partial from .errors import DistutilsPlatformError from sysconfig import ( _PREFIX as PREFIX, _BASE_PREFIX as BASE_PREFIX, _EXEC_PREFIX as EXEC_PREFIX, _BASE_EXEC_PREFIX as BASE_EXEC_PREFIX, _PROJECT_BASE as project_base, _PYTHON_BUILD as python_build, _init_posix as sysconfig_init_posix, parse_config_h as sysconfig_parse_config_h, _init_non_posix, _variable_rx, _findvar1_rx, _findvar2_rx, expand_makefile_vars, is_python_build, get_config_h_filename, get_config_var, get_config_vars, get_makefile_filename, get_python_version, ) # This is better than # from sysconfig import _CONFIG_VARS as _config_vars # because it makes sure that the global dictionary is initialized # which might not be true in the time of import. _config_vars = get_config_vars() warnings.warn( 'The distutils.sysconfig module is deprecated, use sysconfig instead', DeprecationWarning, stacklevel=2 ) # Following functions are the same as in sysconfig but with different API def parse_config_h(fp, g=None): return sysconfig_parse_config_h(fp, vars=g) _python_build = partial(is_python_build, check_home=True) _init_posix = partial(sysconfig_init_posix, _config_vars) _init_nt = partial(_init_non_posix, _config_vars) # Similar function is also implemented in sysconfig as _parse_makefile # but without the parsing capabilities of distutils.text_file.TextFile. def parse_makefile(fn, g=None): """Parse a Makefile-style file. A dictionary containing name/value pairs is returned. If an optional dictionary is passed in as the second argument, it is used instead of a new dictionary. """ from distutils.text_file import TextFile fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape") if g is None: g = {} done = {} notdone = {} while True: line = fp.readline() if line is None: # eof break m = re.match(_variable_rx, line) if m: n, v = m.group(1, 2) v = v.strip() # `$$' is a literal `$' in make tmpv = v.replace('$$', '') if "$" in tmpv: notdone[n] = v else: try: v = int(v) except ValueError: # insert literal `$' done[n] = v.replace('$$', '$') else: done[n] = v # Variables with a 'PY_' prefix in the makefile. These need to # be made available without that prefix through sysconfig. # Special care is needed to ensure that variable expansion works, even # if the expansion uses the name without a prefix. renamed_variables = ('CFLAGS', 'LDFLAGS', 'CPPFLAGS') # do variable interpolation here while notdone: for name in list(notdone): value = notdone[name] m = re.search(_findvar1_rx, value) or re.search(_findvar2_rx, value) if m: n = m.group(1) found = True if n in done: item = str(done[n]) elif n in notdone: # get it on a subsequent round found = False elif n in os.environ: # do it like make: fall back to environment item = os.environ[n] elif n in renamed_variables: if name.startswith('PY_') and name[3:] in renamed_variables: item = "" elif 'PY_' + n in notdone: found = False else: item = str(done['PY_' + n]) else: done[n] = item = "" if found: after = value[m.end():] value = value[:m.start()] + item + after if "$" in after: notdone[name] = value else: try: value = int(value) except ValueError: done[name] = value.strip() else: done[name] = value del notdone[name] if name.startswith('PY_') \ and name[3:] in renamed_variables: name = name[3:] if name not in done: done[name] = value else: # bogus variable reference; just drop it since we can't deal del notdone[name] fp.close() # strip spurious spaces for k, v in done.items(): if isinstance(v, str): done[k] = v.strip() # save the results in the global dictionary g.update(done) return g # Following functions are deprecated together with this module and they # have no direct replacement # Calculate the build qualifier flags if they are defined. Adding the flags # to the include and lib directories only makes sense for an installation, not # an in-source build. build_flags = '' try: if not python_build: build_flags = sys.abiflags except AttributeError: # It's not a configure-based build, so the sys module doesn't have # this attribute, which is fine. pass def customize_compiler(compiler): """Do any platform-specific customization of a CCompiler instance. Mainly needed on Unix, so we can plug in the information that varies across Unices and is stored in Python's Makefile. """ if compiler.compiler_type == "unix": if sys.platform == "darwin": # Perform first-time customization of compiler-related # config vars on OS X now that we know we need a compiler. # This is primarily to support Pythons from binary # installers. The kind and paths to build tools on # the user system may vary significantly from the system # that Python itself was built on. Also the user OS # version and build tools may not support the same set # of CPU architectures for universal builds. if not _config_vars.get('CUSTOMIZED_OSX_COMPILER'): import _osx_support _osx_support.customize_compiler(_config_vars) _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True' (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \ get_config_vars('CC', 'CXX', 'CFLAGS', 'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS') if 'CC' in os.environ: newcc = os.environ['CC'] if (sys.platform == 'darwin' and 'LDSHARED' not in os.environ and ldshared.startswith(cc)): # On OS X, if CC is overridden, use that as the default # command for LDSHARED as well ldshared = newcc + ldshared[len(cc):] cc = newcc if 'CXX' in os.environ: cxx = os.environ['CXX'] if 'LDSHARED' in os.environ: ldshared = os.environ['LDSHARED'] if 'CPP' in os.environ: cpp = os.environ['CPP'] else: cpp = cc + " -E" # not always if 'LDFLAGS' in os.environ: ldshared = ldshared + ' ' + os.environ['LDFLAGS'] if 'CFLAGS' in os.environ: cflags = cflags + ' ' + os.environ['CFLAGS'] ldshared = ldshared + ' ' + os.environ['CFLAGS'] if 'CPPFLAGS' in os.environ: cpp = cpp + ' ' + os.environ['CPPFLAGS'] cflags = cflags + ' ' + os.environ['CPPFLAGS'] ldshared = ldshared + ' ' + os.environ['CPPFLAGS'] if 'AR' in os.environ: ar = os.environ['AR'] if 'ARFLAGS' in os.environ: archiver = ar + ' ' + os.environ['ARFLAGS'] else: archiver = ar + ' ' + ar_flags cc_cmd = cc + ' ' + cflags compiler.set_executables( preprocessor=cpp, compiler=cc_cmd, compiler_so=cc_cmd + ' ' + ccshared, compiler_cxx=cxx, linker_so=ldshared, linker_exe=cc, archiver=archiver) compiler.shared_lib_extension = shlib_suffix def get_python_inc(plat_specific=0, prefix=None): """Return the directory containing installed Python header files. If 'plat_specific' is false (the default), this is the path to the non-platform-specific header files, i.e. Python.h and so on; otherwise, this is the path to platform-specific header files (namely pyconfig.h). If 'prefix' is supplied, use it instead of sys.base_prefix or sys.base_exec_prefix -- i.e., ignore 'plat_specific'. """ if prefix is None: prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX if os.name == "posix": if python_build: # Assume the executable is in the build directory. The # pyconfig.h file should be in the same directory. Since # the build directory may not be the source directory, we # must use "srcdir" from the makefile to find the "Include" # directory. if plat_specific: return project_base else: incdir = os.path.join(get_config_var('srcdir'), 'Include') return os.path.normpath(incdir) python_dir = 'python' + get_python_version() + build_flags return os.path.join(prefix, "include", python_dir) elif os.name == "nt": if python_build: # Include both the include and PC dir to ensure we can find # pyconfig.h return (os.path.join(prefix, "include") + os.path.pathsep + os.path.join(prefix, "PC")) return os.path.join(prefix, "include") else: raise DistutilsPlatformError( "I don't know where Python installs its C header files " "on platform '%s'" % os.name) def get_python_lib(plat_specific=0, standard_lib=0, prefix=None): """Return the directory containing the Python library (standard or site additions). If 'plat_specific' is true, return the directory containing platform-specific modules, i.e. any module from a non-pure-Python module distribution; otherwise, return the platform-shared library directory. If 'standard_lib' is true, return the directory containing standard Python library modules; otherwise, return the directory for site-specific modules. If 'prefix' is supplied, use it instead of sys.base_prefix or sys.base_exec_prefix -- i.e., ignore 'plat_specific'. """ if prefix is None: if standard_lib: prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX else: prefix = plat_specific and EXEC_PREFIX or PREFIX if os.name == "posix": if plat_specific or standard_lib: # Platform-specific modules (any module from a non-pure-Python # module distribution) or standard Python library modules. libdir = sys.platlibdir else: # Pure Python libdir = "lib" libpython = os.path.join(prefix, libdir, "python" + get_python_version()) if standard_lib: return libpython else: return os.path.join(libpython, "site-packages") elif os.name == "nt": if standard_lib: return os.path.join(prefix, "Lib") else: return os.path.join(prefix, "Lib", "site-packages") else: raise DistutilsPlatformError( "I don't know where Python installs its library " "on platform '%s'" % os.name) python-deadlib-3.13.0/distutils/distutils/tests/000077500000000000000000000000001471045437300217235ustar00rootroot00000000000000python-deadlib-3.13.0/distutils/distutils/tests/Setup.sample000066400000000000000000000043111471045437300242250ustar00rootroot00000000000000# Setup file from the pygame project #--StartConfig SDL = -I/usr/include/SDL -D_REENTRANT -lSDL FONT = -lSDL_ttf IMAGE = -lSDL_image MIXER = -lSDL_mixer SMPEG = -lsmpeg PNG = -lpng JPEG = -ljpeg SCRAP = -lX11 PORTMIDI = -lportmidi PORTTIME = -lporttime #--EndConfig #DEBUG = -C-W -C-Wall DEBUG = #the following modules are optional. you will want to compile #everything you can, but you can ignore ones you don't have #dependencies for, just comment them out imageext src/imageext.c $(SDL) $(IMAGE) $(PNG) $(JPEG) $(DEBUG) font src/font.c $(SDL) $(FONT) $(DEBUG) mixer src/mixer.c $(SDL) $(MIXER) $(DEBUG) mixer_music src/music.c $(SDL) $(MIXER) $(DEBUG) _numericsurfarray src/_numericsurfarray.c $(SDL) $(DEBUG) _numericsndarray src/_numericsndarray.c $(SDL) $(MIXER) $(DEBUG) movie src/movie.c $(SDL) $(SMPEG) $(DEBUG) scrap src/scrap.c $(SDL) $(SCRAP) $(DEBUG) _camera src/_camera.c src/camera_v4l2.c src/camera_v4l.c $(SDL) $(DEBUG) pypm src/pypm.c $(SDL) $(PORTMIDI) $(PORTTIME) $(DEBUG) GFX = src/SDL_gfx/SDL_gfxPrimitives.c #GFX = src/SDL_gfx/SDL_gfxBlitFunc.c src/SDL_gfx/SDL_gfxPrimitives.c gfxdraw src/gfxdraw.c $(SDL) $(GFX) $(DEBUG) #these modules are required for pygame to run. they only require #SDL as a dependency. these should not be altered base src/base.c $(SDL) $(DEBUG) cdrom src/cdrom.c $(SDL) $(DEBUG) color src/color.c $(SDL) $(DEBUG) constants src/constants.c $(SDL) $(DEBUG) display src/display.c $(SDL) $(DEBUG) event src/event.c $(SDL) $(DEBUG) fastevent src/fastevent.c src/fastevents.c $(SDL) $(DEBUG) key src/key.c $(SDL) $(DEBUG) mouse src/mouse.c $(SDL) $(DEBUG) rect src/rect.c $(SDL) $(DEBUG) rwobject src/rwobject.c $(SDL) $(DEBUG) surface src/surface.c src/alphablit.c src/surface_fill.c $(SDL) $(DEBUG) surflock src/surflock.c $(SDL) $(DEBUG) time src/time.c $(SDL) $(DEBUG) joystick src/joystick.c $(SDL) $(DEBUG) draw src/draw.c $(SDL) $(DEBUG) image src/image.c $(SDL) $(DEBUG) overlay src/overlay.c $(SDL) $(DEBUG) transform src/transform.c src/rotozoom.c src/scale2x.c src/scale_mmx.c $(SDL) $(DEBUG) mask src/mask.c src/bitmask.c $(SDL) $(DEBUG) bufferproxy src/bufferproxy.c $(SDL) $(DEBUG) pixelarray src/pixelarray.c $(SDL) $(DEBUG) _arraysurfarray src/_arraysurfarray.c $(SDL) $(DEBUG) python-deadlib-3.13.0/distutils/distutils/tests/__init__.py000066400000000000000000000020551471045437300240360ustar00rootroot00000000000000"""Test suite for distutils. This test suite consists of a collection of test modules in the distutils.tests package. Tests for the command classes in the distutils.command package are included in distutils.tests as well, instead of using a separate distutils.command.tests package, since command identification is done by import rather than matching pre-defined names. """ import os import unittest from test.support.warnings_helper import save_restore_warnings_filters from test.support import warnings_helper from test.support import load_package_tests def load_tests(*args): # bpo-40055: Save/restore warnings filters to leave them unchanged. # Importing tests imports docutils which imports pkg_resources # which adds a warnings filter. with (save_restore_warnings_filters(), warnings_helper.check_warnings( ("The distutils.sysconfig module is deprecated", DeprecationWarning), quiet=True)): return load_package_tests(os.path.dirname(__file__), *args) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/includetest.rst000066400000000000000000000000311471045437300247720ustar00rootroot00000000000000This should be included. python-deadlib-3.13.0/distutils/distutils/tests/support.py000066400000000000000000000145321471045437300240160ustar00rootroot00000000000000"""Support code for distutils test cases.""" import os import sys import shutil import tempfile import unittest import sysconfig from copy import deepcopy from test.support import os_helper from distutils import log from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution class LoggingSilencer(object): def setUp(self): super().setUp() self.threshold = log.set_threshold(log.FATAL) # catching warnings # when log will be replaced by logging # we won't need such monkey-patch anymore self._old_log = log.Log._log log.Log._log = self._log self.logs = [] def tearDown(self): log.set_threshold(self.threshold) log.Log._log = self._old_log super().tearDown() def _log(self, level, msg, args): if level not in (DEBUG, INFO, WARN, ERROR, FATAL): raise ValueError('%s wrong log level' % str(level)) if not isinstance(msg, str): raise TypeError("msg should be str, not '%.200s'" % (type(msg).__name__)) self.logs.append((level, msg, args)) def get_logs(self, *levels): return [msg % args for level, msg, args in self.logs if level in levels] def clear_logs(self): self.logs = [] class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. This is intended to be used with unittest.TestCase. """ def setUp(self): super().setUp() self.old_cwd = os.getcwd() self.tempdirs = [] def tearDown(self): # Restore working dir, for Solaris and derivatives, where rmdir() # on the current directory fails. os.chdir(self.old_cwd) super().tearDown() while self.tempdirs: tmpdir = self.tempdirs.pop() os_helper.rmtree(tmpdir) def mkdtemp(self): """Create a temporary directory that will be cleaned up. Returns the path of the directory. """ d = tempfile.mkdtemp() self.tempdirs.append(d) return d def write_file(self, path, content='xxx'): """Writes a file in the given path. path can be a string or a sequence. """ if isinstance(path, (list, tuple)): path = os.path.join(*path) f = open(path, 'w') try: f.write(content) finally: f.close() def create_dist(self, pkg_name='foo', **kw): """Will generate a test environment. This function creates: - a Distribution instance using keywords - a temporary directory with a package structure It returns the package directory and the distribution instance. """ tmp_dir = self.mkdtemp() pkg_dir = os.path.join(tmp_dir, pkg_name) os.mkdir(pkg_dir) dist = Distribution(attrs=kw) return pkg_dir, dist class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" def __init__(self, **kwargs): for kw, val in kwargs.items(): setattr(self, kw, val) def ensure_finalized(self): pass class EnvironGuard(object): def setUp(self): super(EnvironGuard, self).setUp() self.old_environ = deepcopy(os.environ) def tearDown(self): for key, value in self.old_environ.items(): if os.environ.get(key) != value: os.environ[key] = value for key in tuple(os.environ.keys()): if key not in self.old_environ: del os.environ[key] super(EnvironGuard, self).tearDown() def copy_xxmodule_c(directory): """Helper for tests that need the xxmodule.c source file. Example use: def test_compile(self): copy_xxmodule_c(self.tmpdir) self.assertIn('xxmodule.c', os.listdir(self.tmpdir)) If the source file can be found, it will be copied to *directory*. If not, the test will be skipped. Errors during copy are not caught. """ filename = _get_xxmodule_path() if filename is None: raise unittest.SkipTest('cannot find xxmodule.c (test must run in ' 'the python build dir)') shutil.copy(filename, directory) def _get_xxmodule_path(): srcdir = sysconfig.get_config_var('srcdir') candidates = [ # use installed copy if available os.path.join(os.path.dirname(__file__), 'xxmodule.c'), # otherwise try using copy from build directory os.path.join(srcdir, 'Modules', 'xxmodule.c'), # srcdir mysteriously can be $srcdir/Lib/distutils/tests when # this file is run from its parent directory, so walk up the # tree to find the real srcdir os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'), ] for path in candidates: if os.path.exists(path): return path def fixup_build_ext(cmd): """Function needed to make build_ext tests pass. When Python was built with --enable-shared on Unix, -L. is not enough to find libpython.so, because regrtest runs in a tempdir, not in the source directory where the .so lives. When Python was built with in debug mode on Windows, build_ext commands need their debug attribute set, and it is not done automatically for some reason. This function handles both of these things. Example use: cmd = build_ext(dist) support.fixup_build_ext(cmd) cmd.ensure_finalized() Unlike most other Unix platforms, Mac OS X embeds absolute paths to shared libraries into executables, so the fixup is not needed there. """ if os.name == 'nt': cmd.debug = sys.executable.endswith('_d.exe') elif sysconfig.get_config_var('Py_ENABLE_SHARED'): # To further add to the shared builds fun on Unix, we can't just add # library_dirs to the Extension() instance because that doesn't get # plumbed through to the final compiler command. runshared = sysconfig.get_config_var('RUNSHARED') if runshared is None: cmd.library_dirs = ['.'] else: if sys.platform == 'darwin': cmd.library_dirs = [] else: name, equals, value = runshared.partition('=') cmd.library_dirs = [d for d in value.split(os.pathsep) if d] python-deadlib-3.13.0/distutils/distutils/tests/test_archive_util.py000066400000000000000000000337101471045437300260160ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Tests for distutils.archive_util.""" import unittest import os import sys import tarfile from os.path import splitdrive import warnings from distutils import archive_util from distutils.archive_util import (check_archive_formats, make_tarball, make_zipfile, make_archive, ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support from test.support import patch from test.support.os_helper import change_cwd from test.support.warnings_helper import check_warnings try: import grp import pwd UID_GID_SUPPORT = True except ImportError: UID_GID_SUPPORT = False try: import zipfile ZIP_SUPPORT = True except ImportError: ZIP_SUPPORT = find_executable('zip') try: import zlib ZLIB_SUPPORT = True except ImportError: ZLIB_SUPPORT = False try: import bz2 except ImportError: bz2 = None try: import lzma except ImportError: lzma = None def can_fs_encode(filename): """ Return True if the filename can be saved in the file system. """ if os.path.supports_unicode_filenames: return True try: filename.encode(sys.getfilesystemencoding()) except UnicodeEncodeError: return False return True class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_make_tarball(self, name='archive'): # creating something to tar tmpdir = self._create_files() self._make_tarball(tmpdir, name, '.tar.gz') # trying an uncompressed one self._make_tarball(tmpdir, name, '.tar', compress=None) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_make_tarball_gzip(self): tmpdir = self._create_files() self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip') @unittest.skipUnless(bz2, 'Need bz2 support to run') def test_make_tarball_bzip2(self): tmpdir = self._create_files() self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2') @unittest.skipUnless(lzma, 'Need lzma support to run') def test_make_tarball_xz(self): tmpdir = self._create_files() self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz') @unittest.skipUnless(can_fs_encode('årchiv'), 'File system cannot handle this filename') def test_make_tarball_latin1(self): """ Mirror test_make_tarball, except filename contains latin characters. """ self.test_make_tarball('årchiv') # note this isn't a real word @unittest.skipUnless(can_fs_encode('のアーカイブ'), 'File system cannot handle this filename') def test_make_tarball_extended(self): """ Mirror test_make_tarball, except filename contains extended characters outside the latin charset. """ self.test_make_tarball('のアーカイブ') # japanese for archive def _make_tarball(self, tmpdir, target_name, suffix, **kwargs): tmpdir2 = self.mkdtemp() unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], "source and target should be on same drive") base_name = os.path.join(tmpdir2, target_name) # working with relative paths to avoid tar warnings with change_cwd(tmpdir): make_tarball(splitdrive(base_name)[1], 'dist', **kwargs) # check if the compressed tarball was created tarball = base_name + suffix self.assertTrue(os.path.exists(tarball)) self.assertEqual(self._tarinfo(tarball), self._created_files) def _tarinfo(self, path): tar = tarfile.open(path) try: names = tar.getnames() names.sort() return names finally: tar.close() _zip_created_files = ['dist/', 'dist/file1', 'dist/file2', 'dist/sub/', 'dist/sub/file3', 'dist/sub2/'] _created_files = [p.rstrip('/') for p in _zip_created_files] def _create_files(self): # creating something to tar tmpdir = self.mkdtemp() dist = os.path.join(tmpdir, 'dist') os.mkdir(dist) self.write_file([dist, 'file1'], 'xxx') self.write_file([dist, 'file2'], 'xxx') os.mkdir(os.path.join(dist, 'sub')) self.write_file([dist, 'sub', 'file3'], 'xxx') os.mkdir(os.path.join(dist, 'sub2')) return tmpdir @unittest.skipUnless(find_executable('tar') and find_executable('gzip') and ZLIB_SUPPORT, 'Need the tar, gzip and zlib command to run') def test_tarfile_vs_tar(self): tmpdir = self._create_files() tmpdir2 = self.mkdtemp() base_name = os.path.join(tmpdir2, 'archive') old_dir = os.getcwd() os.chdir(tmpdir) try: make_tarball(base_name, 'dist') finally: os.chdir(old_dir) # check if the compressed tarball was created tarball = base_name + '.tar.gz' self.assertTrue(os.path.exists(tarball)) # now create another tarball using `tar` tarball2 = os.path.join(tmpdir, 'archive2.tar.gz') tar_cmd = ['tar', '-cf', 'archive2.tar', 'dist'] gzip_cmd = ['gzip', '-f', '-9', 'archive2.tar'] old_dir = os.getcwd() os.chdir(tmpdir) try: spawn(tar_cmd) spawn(gzip_cmd) finally: os.chdir(old_dir) self.assertTrue(os.path.exists(tarball2)) # let's compare both tarballs self.assertEqual(self._tarinfo(tarball), self._created_files) self.assertEqual(self._tarinfo(tarball2), self._created_files) # trying an uncompressed one base_name = os.path.join(tmpdir2, 'archive') old_dir = os.getcwd() os.chdir(tmpdir) try: make_tarball(base_name, 'dist', compress=None) finally: os.chdir(old_dir) tarball = base_name + '.tar' self.assertTrue(os.path.exists(tarball)) # now for a dry_run base_name = os.path.join(tmpdir2, 'archive') old_dir = os.getcwd() os.chdir(tmpdir) try: make_tarball(base_name, 'dist', compress=None, dry_run=True) finally: os.chdir(old_dir) tarball = base_name + '.tar' self.assertTrue(os.path.exists(tarball)) @unittest.skipUnless(find_executable('compress'), 'The compress program is required') def test_compress_deprecated(self): tmpdir = self._create_files() base_name = os.path.join(self.mkdtemp(), 'archive') # using compress and testing the PendingDeprecationWarning old_dir = os.getcwd() os.chdir(tmpdir) try: with check_warnings() as w: warnings.simplefilter("always") make_tarball(base_name, 'dist', compress='compress') finally: os.chdir(old_dir) tarball = base_name + '.tar.Z' self.assertTrue(os.path.exists(tarball)) self.assertEqual(len(w.warnings), 1) # same test with dry_run os.remove(tarball) old_dir = os.getcwd() os.chdir(tmpdir) try: with check_warnings() as w: warnings.simplefilter("always") make_tarball(base_name, 'dist', compress='compress', dry_run=True) finally: os.chdir(old_dir) self.assertFalse(os.path.exists(tarball)) self.assertEqual(len(w.warnings), 1) @unittest.skipUnless(ZIP_SUPPORT and ZLIB_SUPPORT, 'Need zip and zlib support to run') def test_make_zipfile(self): # creating something to tar tmpdir = self._create_files() base_name = os.path.join(self.mkdtemp(), 'archive') with change_cwd(tmpdir): make_zipfile(base_name, 'dist') # check if the compressed tarball was created tarball = base_name + '.zip' self.assertTrue(os.path.exists(tarball)) with zipfile.ZipFile(tarball) as zf: self.assertEqual(sorted(zf.namelist()), self._zip_created_files) @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') def test_make_zipfile_no_zlib(self): patch(self, archive_util.zipfile, 'zlib', None) # force zlib ImportError called = [] zipfile_class = zipfile.ZipFile def fake_zipfile(*a, **kw): if kw.get('compression', None) == zipfile.ZIP_STORED: called.append((a, kw)) return zipfile_class(*a, **kw) patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile) # create something to tar and compress tmpdir = self._create_files() base_name = os.path.join(self.mkdtemp(), 'archive') with change_cwd(tmpdir): make_zipfile(base_name, 'dist') tarball = base_name + '.zip' self.assertEqual(called, [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) self.assertTrue(os.path.exists(tarball)) with zipfile.ZipFile(tarball) as zf: self.assertEqual(sorted(zf.namelist()), self._zip_created_files) def test_check_archive_formats(self): self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), 'xxx') self.assertIsNone(check_archive_formats(['gztar', 'bztar', 'xztar', 'ztar', 'tar', 'zip'])) def test_make_archive(self): tmpdir = self.mkdtemp() base_name = os.path.join(tmpdir, 'archive') self.assertRaises(ValueError, make_archive, base_name, 'xxx') def test_make_archive_cwd(self): current_dir = os.getcwd() def _breaks(*args, **kw): raise RuntimeError() ARCHIVE_FORMATS['xxx'] = (_breaks, [], 'xxx file') try: try: make_archive('xxx', 'xxx', root_dir=self.mkdtemp()) except: pass self.assertEqual(os.getcwd(), current_dir) finally: del ARCHIVE_FORMATS['xxx'] def test_make_archive_tar(self): base_dir = self._create_files() base_name = os.path.join(self.mkdtemp() , 'archive') res = make_archive(base_name, 'tar', base_dir, 'dist') self.assertTrue(os.path.exists(res)) self.assertEqual(os.path.basename(res), 'archive.tar') self.assertEqual(self._tarinfo(res), self._created_files) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_make_archive_gztar(self): base_dir = self._create_files() base_name = os.path.join(self.mkdtemp() , 'archive') res = make_archive(base_name, 'gztar', base_dir, 'dist') self.assertTrue(os.path.exists(res)) self.assertEqual(os.path.basename(res), 'archive.tar.gz') self.assertEqual(self._tarinfo(res), self._created_files) @unittest.skipUnless(bz2, 'Need bz2 support to run') def test_make_archive_bztar(self): base_dir = self._create_files() base_name = os.path.join(self.mkdtemp() , 'archive') res = make_archive(base_name, 'bztar', base_dir, 'dist') self.assertTrue(os.path.exists(res)) self.assertEqual(os.path.basename(res), 'archive.tar.bz2') self.assertEqual(self._tarinfo(res), self._created_files) @unittest.skipUnless(lzma, 'Need xz support to run') def test_make_archive_xztar(self): base_dir = self._create_files() base_name = os.path.join(self.mkdtemp() , 'archive') res = make_archive(base_name, 'xztar', base_dir, 'dist') self.assertTrue(os.path.exists(res)) self.assertEqual(os.path.basename(res), 'archive.tar.xz') self.assertEqual(self._tarinfo(res), self._created_files) def test_make_archive_owner_group(self): # testing make_archive with owner and group, with various combinations # this works even if there's not gid/uid support if UID_GID_SUPPORT: group = grp.getgrgid(0)[0] owner = pwd.getpwuid(0)[0] else: group = owner = 'root' base_dir = self._create_files() root_dir = self.mkdtemp() base_name = os.path.join(self.mkdtemp() , 'archive') res = make_archive(base_name, 'zip', root_dir, base_dir, owner=owner, group=group) self.assertTrue(os.path.exists(res)) res = make_archive(base_name, 'zip', root_dir, base_dir) self.assertTrue(os.path.exists(res)) res = make_archive(base_name, 'tar', root_dir, base_dir, owner=owner, group=group) self.assertTrue(os.path.exists(res)) res = make_archive(base_name, 'tar', root_dir, base_dir, owner='kjhkjhkjg', group='oihohoh') self.assertTrue(os.path.exists(res)) @unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib") @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") def test_tarfile_root_owner(self): tmpdir = self._create_files() base_name = os.path.join(self.mkdtemp(), 'archive') old_dir = os.getcwd() os.chdir(tmpdir) group = grp.getgrgid(0)[0] owner = pwd.getpwuid(0)[0] try: archive_name = make_tarball(base_name, 'dist', compress=None, owner=owner, group=group) finally: os.chdir(old_dir) # check if the compressed tarball was created self.assertTrue(os.path.exists(archive_name)) # now checks the rights archive = tarfile.open(archive_name) try: for member in archive.getmembers(): self.assertEqual(member.uid, 0) self.assertEqual(member.gid, 0) finally: archive.close() if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_bdist.py000066400000000000000000000030001471045437300244320ustar00rootroot00000000000000"""Tests for distutils.command.bdist.""" import os import unittest import warnings with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) from distutils.command.bdist import bdist from distutils.tests import support class BuildTestCase(support.TempdirManager, unittest.TestCase): def test_formats(self): # let's create a command and make sure # we can set the format dist = self.create_dist()[1] cmd = bdist(dist) cmd.formats = ['tar'] cmd.ensure_finalized() self.assertEqual(cmd.formats, ['tar']) # what formats does bdist offer? formats = ['bztar', 'gztar', 'rpm', 'tar', 'xztar', 'zip', 'ztar'] found = sorted(cmd.format_command) self.assertEqual(found, formats) def test_skip_build(self): # bug #10946: bdist --skip-build should trickle down to subcommands dist = self.create_dist()[1] cmd = bdist(dist) cmd.skip_build = 1 cmd.ensure_finalized() dist.command_obj['bdist'] = cmd for name in ['bdist_dumb']: # bdist_rpm does not support --skip-build subcmd = cmd.get_finalized_command(name) if getattr(subcmd, '_unsupported', False): # command is not supported on this build continue self.assertTrue(subcmd.skip_build, '%s should take --skip-build from bdist' % name) if __name__ == '__main__': unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_bdist_dumb.py000066400000000000000000000053441471045437300254560ustar00rootroot00000000000000"""Tests for distutils.command.bdist_dumb.""" import os import sys import zipfile import unittest from distutils.core import Distribution from distutils.command.bdist_dumb import bdist_dumb from distutils.tests import support SETUP_PY = """\ from distutils.core import setup import foo setup(name='foo', version='0.1', py_modules=['foo'], url='xxx', author='xxx', author_email='xxx') """ try: import zlib ZLIB_SUPPORT = True except ImportError: ZLIB_SUPPORT = False class BuildDumbTestCase(support.TempdirManager, support.LoggingSilencer, support.EnvironGuard, unittest.TestCase): def setUp(self): super(BuildDumbTestCase, self).setUp() self.old_location = os.getcwd() self.old_sys_argv = sys.argv, sys.argv[:] def tearDown(self): os.chdir(self.old_location) sys.argv = self.old_sys_argv[0] sys.argv[:] = self.old_sys_argv[1] super(BuildDumbTestCase, self).tearDown() @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_simple_built(self): # let's create a simple package tmp_dir = self.mkdtemp() pkg_dir = os.path.join(tmp_dir, 'foo') os.mkdir(pkg_dir) self.write_file((pkg_dir, 'setup.py'), SETUP_PY) self.write_file((pkg_dir, 'foo.py'), '#') self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') self.write_file((pkg_dir, 'README'), '') dist = Distribution({'name': 'foo', 'version': '0.1', 'py_modules': ['foo'], 'url': 'xxx', 'author': 'xxx', 'author_email': 'xxx'}) dist.script_name = 'setup.py' os.chdir(pkg_dir) sys.argv = ['setup.py'] cmd = bdist_dumb(dist) # so the output is the same no matter # what is the platform cmd.format = 'zip' cmd.ensure_finalized() cmd.run() # see what we have dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) base = "%s.%s.zip" % (dist.get_fullname(), cmd.plat_name) self.assertEqual(dist_created, [base]) # now let's check what we have in the zip file fp = zipfile.ZipFile(os.path.join('dist', base)) try: contents = fp.namelist() finally: fp.close() contents = sorted(filter(None, map(os.path.basename, contents))) wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py'] if not sys.dont_write_bytecode: wanted.append('foo.%s.pyc' % sys.implementation.cache_tag) self.assertEqual(contents, sorted(wanted)) if __name__ == '__main__': unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_bdist_rpm.py000066400000000000000000000121301471045437300253140ustar00rootroot00000000000000"""Tests for distutils.command.bdist_rpm.""" import unittest import sys import os from test.support import requires_zlib from distutils.core import Distribution from distutils.command.bdist_rpm import bdist_rpm from distutils.tests import support from distutils.spawn import find_executable SETUP_PY = """\ from distutils.core import setup import foo setup(name='foo', version='0.1', py_modules=['foo'], url='xxx', author='xxx', author_email='xxx') """ class BuildRpmTestCase(support.TempdirManager, support.EnvironGuard, support.LoggingSilencer, unittest.TestCase): def setUp(self): try: sys.executable.encode("UTF-8") except UnicodeEncodeError: raise unittest.SkipTest("sys.executable is not encodable to UTF-8") super(BuildRpmTestCase, self).setUp() self.old_location = os.getcwd() self.old_sys_argv = sys.argv, sys.argv[:] def tearDown(self): os.chdir(self.old_location) sys.argv = self.old_sys_argv[0] sys.argv[:] = self.old_sys_argv[1] super(BuildRpmTestCase, self).tearDown() # XXX I am unable yet to make this test work without # spurious sdtout/stderr output under Mac OS X @unittest.skipUnless(sys.platform.startswith('linux'), 'spurious sdtout/stderr output under Mac OS X') @requires_zlib() @unittest.skipIf(find_executable('rpm') is None, 'the rpm command is not found') @unittest.skipIf(find_executable('rpmbuild') is None, 'the rpmbuild command is not found') # import foo fails with safe path @unittest.skipIf(sys.flags.safe_path, 'PYTHONSAFEPATH changes default sys.path') def test_quiet(self): # let's create a package tmp_dir = self.mkdtemp() os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation pkg_dir = os.path.join(tmp_dir, 'foo') os.mkdir(pkg_dir) self.write_file((pkg_dir, 'setup.py'), SETUP_PY) self.write_file((pkg_dir, 'foo.py'), '#') self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') self.write_file((pkg_dir, 'README'), '') dist = Distribution({'name': 'foo', 'version': '0.1', 'py_modules': ['foo'], 'url': 'xxx', 'author': 'xxx', 'author_email': 'xxx'}) dist.script_name = 'setup.py' os.chdir(pkg_dir) sys.argv = ['setup.py'] cmd = bdist_rpm(dist) cmd.fix_python = True # running in quiet mode cmd.quiet = 1 cmd.ensure_finalized() cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) self.assertIn('foo-0.1-1.noarch.rpm', dist_created) # bug #2945: upload ignores bdist_rpm files self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) # XXX I am unable yet to make this test work without # spurious sdtout/stderr output under Mac OS X @unittest.skipUnless(sys.platform.startswith('linux'), 'spurious sdtout/stderr output under Mac OS X') @requires_zlib() # http://bugs.python.org/issue1533164 @unittest.skipIf(find_executable('rpm') is None, 'the rpm command is not found') @unittest.skipIf(find_executable('rpmbuild') is None, 'the rpmbuild command is not found') # import foo fails with safe path @unittest.skipIf(sys.flags.safe_path, 'PYTHONSAFEPATH changes default sys.path') def test_no_optimize_flag(self): # let's create a package that breaks bdist_rpm tmp_dir = self.mkdtemp() os.environ['HOME'] = tmp_dir # to confine dir '.rpmdb' creation pkg_dir = os.path.join(tmp_dir, 'foo') os.mkdir(pkg_dir) self.write_file((pkg_dir, 'setup.py'), SETUP_PY) self.write_file((pkg_dir, 'foo.py'), '#') self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py') self.write_file((pkg_dir, 'README'), '') dist = Distribution({'name': 'foo', 'version': '0.1', 'py_modules': ['foo'], 'url': 'xxx', 'author': 'xxx', 'author_email': 'xxx'}) dist.script_name = 'setup.py' os.chdir(pkg_dir) sys.argv = ['setup.py'] cmd = bdist_rpm(dist) cmd.fix_python = True cmd.quiet = 1 cmd.ensure_finalized() cmd.run() dist_created = os.listdir(os.path.join(pkg_dir, 'dist')) self.assertIn('foo-0.1-1.noarch.rpm', dist_created) # bug #2945: upload ignores bdist_rpm files self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files) self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files) os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm')) if __name__ == '__main__': unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_build.py000066400000000000000000000036051471045437300244370ustar00rootroot00000000000000"""Tests for distutils.command.build.""" import unittest import os import sys from distutils.command.build import build from distutils.tests import support from sysconfig import get_platform class BuildTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @unittest.skipUnless(sys.executable, "test requires sys.executable") def test_finalize_options(self): pkg_dir, dist = self.create_dist() cmd = build(dist) cmd.finalize_options() # if not specified, plat_name gets the current platform self.assertEqual(cmd.plat_name, get_platform()) # build_purelib is build + lib wanted = os.path.join(cmd.build_base, 'lib') self.assertEqual(cmd.build_purelib, wanted) # build_platlib is 'build/lib.platform-x.x[-pydebug]' # examples: # build/lib.macosx-10.3-i386-2.7 plat_spec = '.%s-%d.%d' % (cmd.plat_name, *sys.version_info[:2]) if hasattr(sys, 'gettotalrefcount'): self.assertTrue(cmd.build_platlib.endswith('-pydebug')) plat_spec += '-pydebug' wanted = os.path.join(cmd.build_base, 'lib' + plat_spec) self.assertEqual(cmd.build_platlib, wanted) # by default, build_lib = build_purelib self.assertEqual(cmd.build_lib, cmd.build_purelib) # build_temp is build/temp. wanted = os.path.join(cmd.build_base, 'temp' + plat_spec) self.assertEqual(cmd.build_temp, wanted) # build_scripts is build/scripts-x.x wanted = os.path.join(cmd.build_base, 'scripts-%d.%d' % sys.version_info[:2]) self.assertEqual(cmd.build_scripts, wanted) # executable is os.path.normpath(sys.executable) self.assertEqual(cmd.executable, os.path.normpath(sys.executable)) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_build_clib.py000066400000000000000000000114141471045437300254250ustar00rootroot00000000000000"""Tests for distutils.command.build_clib.""" import unittest import os import sys import sysconfig from test.support import ( missing_compiler_executable, requires_subprocess ) from distutils.command.build_clib import build_clib from distutils.errors import DistutilsSetupError from distutils.tests import support class BuildCLibTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): def setUp(self): super().setUp() self._backup_CONFIG_VARS = dict(sysconfig._CONFIG_VARS) def tearDown(self): super().tearDown() sysconfig._CONFIG_VARS.clear() sysconfig._CONFIG_VARS.update(self._backup_CONFIG_VARS) def test_check_library_dist(self): pkg_dir, dist = self.create_dist() cmd = build_clib(dist) # 'libraries' option must be a list self.assertRaises(DistutilsSetupError, cmd.check_library_list, 'foo') # each element of 'libraries' must a 2-tuple self.assertRaises(DistutilsSetupError, cmd.check_library_list, ['foo1', 'foo2']) # first element of each tuple in 'libraries' # must be a string (the library name) self.assertRaises(DistutilsSetupError, cmd.check_library_list, [(1, 'foo1'), ('name', 'foo2')]) # library name may not contain directory separators self.assertRaises(DistutilsSetupError, cmd.check_library_list, [('name', 'foo1'), ('another/name', 'foo2')]) # second element of each tuple must be a dictionary (build info) self.assertRaises(DistutilsSetupError, cmd.check_library_list, [('name', {}), ('another', 'foo2')]) # those work libs = [('name', {}), ('name', {'ok': 'good'})] cmd.check_library_list(libs) def test_get_source_files(self): pkg_dir, dist = self.create_dist() cmd = build_clib(dist) # "in 'libraries' option 'sources' must be present and must be # a list of source filenames cmd.libraries = [('name', {})] self.assertRaises(DistutilsSetupError, cmd.get_source_files) cmd.libraries = [('name', {'sources': 1})] self.assertRaises(DistutilsSetupError, cmd.get_source_files) cmd.libraries = [('name', {'sources': ['a', 'b']})] self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')})] self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')}), ('name2', {'sources': ['c', 'd']})] self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd']) def test_build_libraries(self): pkg_dir, dist = self.create_dist() cmd = build_clib(dist) class FakeCompiler: def compile(*args, **kw): pass create_static_lib = compile cmd.compiler = FakeCompiler() # build_libraries is also doing a bit of typo checking lib = [('name', {'sources': 'notvalid'})] self.assertRaises(DistutilsSetupError, cmd.build_libraries, lib) lib = [('name', {'sources': list()})] cmd.build_libraries(lib) lib = [('name', {'sources': tuple()})] cmd.build_libraries(lib) def test_finalize_options(self): pkg_dir, dist = self.create_dist() cmd = build_clib(dist) cmd.include_dirs = 'one-dir' cmd.finalize_options() self.assertEqual(cmd.include_dirs, ['one-dir']) cmd.include_dirs = None cmd.finalize_options() self.assertEqual(cmd.include_dirs, []) cmd.distribution.libraries = 'WONTWORK' self.assertRaises(DistutilsSetupError, cmd.finalize_options) @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") @requires_subprocess() def test_run(self): pkg_dir, dist = self.create_dist() cmd = build_clib(dist) foo_c = os.path.join(pkg_dir, 'foo.c') self.write_file(foo_c, 'int main(void) { return 1;}\n') cmd.libraries = [('foo', {'sources': [foo_c]})] build_temp = os.path.join(pkg_dir, 'build') os.mkdir(build_temp) cmd.build_temp = build_temp cmd.build_clib = build_temp # Before we run the command, we want to make sure # all commands are present on the system. ccmd = missing_compiler_executable() if ccmd is not None: self.skipTest('The %r command is not found' % ccmd) # this should work cmd.run() # let's check the result self.assertIn('libfoo.a', os.listdir(build_temp)) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_build_ext.py000066400000000000000000000503341471045437300253200ustar00rootroot00000000000000import sys import os from io import StringIO import textwrap from distutils.core import Distribution from distutils.command.build_ext import build_ext from distutils import sysconfig from distutils.tests.support import (TempdirManager, LoggingSilencer, copy_xxmodule_c, fixup_build_ext) from distutils.extension import Extension from distutils.errors import ( CompileError, DistutilsPlatformError, DistutilsSetupError, UnknownFileError) import unittest from test import support from test.support import os_helper from test.support.script_helper import assert_python_ok from test.support import threading_helper # http://bugs.python.org/issue4373 # Don't load the xx module more than once. ALREADY_TESTED = False class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase): def setUp(self): # Create a simple test environment super(BuildExtTestCase, self).setUp() self.tmp_dir = self.mkdtemp() import site self.old_user_base = site.USER_BASE site.USER_BASE = self.mkdtemp() from distutils.command import build_ext build_ext.USER_BASE = site.USER_BASE self.old_config_vars = dict(sysconfig._config_vars) # bpo-30132: On Windows, a .pdb file may be created in the current # working directory. Create a temporary working directory to cleanup # everything at the end of the test. self.enterContext(os_helper.change_cwd(self.tmp_dir)) def tearDown(self): import site site.USER_BASE = self.old_user_base from distutils.command import build_ext build_ext.USER_BASE = self.old_user_base sysconfig._config_vars.clear() sysconfig._config_vars.update(self.old_config_vars) super(BuildExtTestCase, self).tearDown() def build_ext(self, *args, **kwargs): return build_ext(*args, **kwargs) @support.requires_subprocess() def test_build_ext(self): cmd = support.missing_compiler_executable() if cmd is not None: self.skipTest('The %r command is not found' % cmd) global ALREADY_TESTED copy_xxmodule_c(self.tmp_dir) xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') xx_ext = Extension('xx', [xx_c]) dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]}) dist.package_dir = self.tmp_dir cmd = self.build_ext(dist) fixup_build_ext(cmd) cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir old_stdout = sys.stdout if not support.verbose: # silence compiler output sys.stdout = StringIO() try: cmd.ensure_finalized() cmd.run() finally: sys.stdout = old_stdout if ALREADY_TESTED: self.skipTest('Already tested in %s' % ALREADY_TESTED) else: ALREADY_TESTED = type(self).__name__ code = textwrap.dedent(f""" tmp_dir = {self.tmp_dir!r} import sys import unittest from test import support sys.path.insert(0, tmp_dir) import xx class Tests(unittest.TestCase): def test_xx(self): for attr in ('error', 'foo', 'new', 'roj'): self.assertTrue(hasattr(xx, attr)) self.assertEqual(xx.foo(2, 5), 7) self.assertEqual(xx.foo(13,15), 28) self.assertEqual(xx.new().demo(), None) if support.HAVE_DOCSTRINGS: doc = 'This is a template module just for instruction.' self.assertEqual(xx.__doc__, doc) self.assertIsInstance(xx.Null(), xx.Null) self.assertIsInstance(xx.Str(), xx.Str) unittest.main() """) assert_python_ok('-c', code) def test_solaris_enable_shared(self): dist = Distribution({'name': 'xx'}) cmd = self.build_ext(dist) old = sys.platform sys.platform = 'sunos' # fooling finalize_options from distutils.sysconfig import _config_vars old_var = _config_vars.get('Py_ENABLE_SHARED') _config_vars['Py_ENABLE_SHARED'] = 1 try: cmd.ensure_finalized() finally: sys.platform = old if old_var is None: del _config_vars['Py_ENABLE_SHARED'] else: _config_vars['Py_ENABLE_SHARED'] = old_var # make sure we get some library dirs under solaris self.assertGreater(len(cmd.library_dirs), 0) def test_user_site(self): import site dist = Distribution({'name': 'xx'}) cmd = self.build_ext(dist) # making sure the user option is there options = [name for name, short, lable in cmd.user_options] self.assertIn('user', options) # setting a value cmd.user = 1 # setting user based lib and include lib = os.path.join(site.USER_BASE, 'lib') incl = os.path.join(site.USER_BASE, 'include') os.mkdir(lib) os.mkdir(incl) # let's run finalize cmd.ensure_finalized() # see if include_dirs and library_dirs # were set self.assertIn(lib, cmd.library_dirs) self.assertIn(lib, cmd.rpath) self.assertIn(incl, cmd.include_dirs) @threading_helper.requires_working_threading() def test_optional_extension(self): # this extension will fail, but let's ignore this failure # with the optional argument. modules = [Extension('foo', ['xxx'], optional=False)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = self.build_ext(dist) cmd.ensure_finalized() self.assertRaises((UnknownFileError, CompileError), cmd.run) # should raise an error modules = [Extension('foo', ['xxx'], optional=True)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = self.build_ext(dist) cmd.ensure_finalized() cmd.run() # should pass def test_finalize_options(self): # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. modules = [Extension('foo', ['xxx'], optional=False)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = self.build_ext(dist) cmd.finalize_options() py_include = sysconfig.get_python_inc() for p in py_include.split(os.path.pathsep): self.assertIn(p, cmd.include_dirs) plat_py_include = sysconfig.get_python_inc(plat_specific=1) for p in plat_py_include.split(os.path.pathsep): self.assertIn(p, cmd.include_dirs) # make sure cmd.libraries is turned into a list # if it's a string cmd = self.build_ext(dist) cmd.libraries = 'my_lib, other_lib lastlib' cmd.finalize_options() self.assertEqual(cmd.libraries, ['my_lib', 'other_lib', 'lastlib']) # make sure cmd.library_dirs is turned into a list # if it's a string cmd = self.build_ext(dist) cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep cmd.finalize_options() self.assertIn('my_lib_dir', cmd.library_dirs) self.assertIn('other_lib_dir', cmd.library_dirs) # make sure rpath is turned into a list # if it's a string cmd = self.build_ext(dist) cmd.rpath = 'one%stwo' % os.pathsep cmd.finalize_options() self.assertEqual(cmd.rpath, ['one', 'two']) # make sure cmd.link_objects is turned into a list # if it's a string cmd = build_ext(dist) cmd.link_objects = 'one two,three' cmd.finalize_options() self.assertEqual(cmd.link_objects, ['one', 'two', 'three']) # XXX more tests to perform for win32 # make sure define is turned into 2-tuples # strings if they are ','-separated strings cmd = self.build_ext(dist) cmd.define = 'one,two' cmd.finalize_options() self.assertEqual(cmd.define, [('one', '1'), ('two', '1')]) # make sure undef is turned into a list of # strings if they are ','-separated strings cmd = self.build_ext(dist) cmd.undef = 'one,two' cmd.finalize_options() self.assertEqual(cmd.undef, ['one', 'two']) # make sure swig_opts is turned into a list cmd = self.build_ext(dist) cmd.swig_opts = None cmd.finalize_options() self.assertEqual(cmd.swig_opts, []) cmd = self.build_ext(dist) cmd.swig_opts = '1 2' cmd.finalize_options() self.assertEqual(cmd.swig_opts, ['1', '2']) def test_check_extensions_list(self): dist = Distribution() cmd = self.build_ext(dist) cmd.finalize_options() #'extensions' option must be a list of Extension instances self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo') # each element of 'ext_modules' option must be an # Extension instance or 2-tuple exts = [('bar', 'foo', 'bar'), 'foo'] self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) # first element of each tuple in 'ext_modules' # must be the extension name (a string) and match # a python dotted-separated name exts = [('foo-bar', '')] self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) # second element of each tuple in 'ext_modules' # must be a dictionary (build info) exts = [('foo.bar', '')] self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) # ok this one should pass exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', 'some': 'bar'})] cmd.check_extensions_list(exts) ext = exts[0] self.assertIsInstance(ext, Extension) # check_extensions_list adds in ext the values passed # when they are in ('include_dirs', 'library_dirs', 'libraries' # 'extra_objects', 'extra_compile_args', 'extra_link_args') self.assertEqual(ext.libraries, 'foo') self.assertFalse(hasattr(ext, 'some')) # 'macros' element of build info dict must be 1- or 2-tuple exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})] self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) exts[0][1]['macros'] = [('1', '2'), ('3',)] cmd.check_extensions_list(exts) self.assertEqual(exts[0].undef_macros, ['3']) self.assertEqual(exts[0].define_macros, [('1', '2')]) def test_get_source_files(self): modules = [Extension('foo', ['xxx'], optional=False)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = self.build_ext(dist) cmd.ensure_finalized() self.assertEqual(cmd.get_source_files(), ['xxx']) def test_unicode_module_names(self): modules = [ Extension('foo', ['aaa'], optional=False), Extension('föö', ['uuu'], optional=False), ] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = self.build_ext(dist) cmd.ensure_finalized() self.assertRegex(cmd.get_ext_filename(modules[0].name), r'foo(_d)?\..*') self.assertRegex(cmd.get_ext_filename(modules[1].name), r'föö(_d)?\..*') self.assertEqual(cmd.get_export_symbols(modules[0]), ['PyInit_foo']) self.assertEqual(cmd.get_export_symbols(modules[1]), ['PyInitU_f_gkaa']) def test_compiler_option(self): # cmd.compiler is an option and # should not be overridden by a compiler instance # when the command is run dist = Distribution() cmd = self.build_ext(dist) cmd.compiler = 'unix' cmd.ensure_finalized() cmd.run() self.assertEqual(cmd.compiler, 'unix') @support.requires_subprocess() def test_get_outputs(self): cmd = support.missing_compiler_executable() if cmd is not None: self.skipTest('The %r command is not found' % cmd) tmp_dir = self.mkdtemp() c_file = os.path.join(tmp_dir, 'foo.c') self.write_file(c_file, 'void PyInit_foo(void) {}\n') ext = Extension('foo', [c_file], optional=False) dist = Distribution({'name': 'xx', 'ext_modules': [ext]}) cmd = self.build_ext(dist) fixup_build_ext(cmd) cmd.ensure_finalized() self.assertEqual(len(cmd.get_outputs()), 1) cmd.build_lib = os.path.join(self.tmp_dir, 'build') cmd.build_temp = os.path.join(self.tmp_dir, 'tempt') # issue #5977 : distutils build_ext.get_outputs # returns wrong result with --inplace other_tmp_dir = os.path.realpath(self.mkdtemp()) old_wd = os.getcwd() os.chdir(other_tmp_dir) try: cmd.inplace = 1 cmd.run() so_file = cmd.get_outputs()[0] finally: os.chdir(old_wd) self.assertTrue(os.path.exists(so_file)) ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') self.assertTrue(so_file.endswith(ext_suffix)) so_dir = os.path.dirname(so_file) self.assertEqual(so_dir, other_tmp_dir) cmd.inplace = 0 cmd.compiler = None cmd.run() so_file = cmd.get_outputs()[0] self.assertTrue(os.path.exists(so_file)) self.assertTrue(so_file.endswith(ext_suffix)) so_dir = os.path.dirname(so_file) self.assertEqual(so_dir, cmd.build_lib) # inplace = 0, cmd.package = 'bar' build_py = cmd.get_finalized_command('build_py') build_py.package_dir = {'': 'bar'} path = cmd.get_ext_fullpath('foo') # checking that the last directory is the build_dir path = os.path.split(path)[0] self.assertEqual(path, cmd.build_lib) # inplace = 1, cmd.package = 'bar' cmd.inplace = 1 other_tmp_dir = os.path.realpath(self.mkdtemp()) old_wd = os.getcwd() os.chdir(other_tmp_dir) try: path = cmd.get_ext_fullpath('foo') finally: os.chdir(old_wd) # checking that the last directory is bar path = os.path.split(path)[0] lastdir = os.path.split(path)[-1] self.assertEqual(lastdir, 'bar') def test_ext_fullpath(self): ext = sysconfig.get_config_var('EXT_SUFFIX') # building lxml.etree inplace #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c') #etree_ext = Extension('lxml.etree', [etree_c]) #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]}) dist = Distribution() cmd = self.build_ext(dist) cmd.inplace = 1 cmd.distribution.package_dir = {'': 'src'} cmd.distribution.packages = ['lxml', 'lxml.html'] curdir = os.getcwd() wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') self.assertEqual(wanted, path) # building lxml.etree not inplace cmd.inplace = 0 cmd.build_lib = os.path.join(curdir, 'tmpdir') wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') self.assertEqual(wanted, path) # building twisted.runner.portmap not inplace build_py = cmd.get_finalized_command('build_py') build_py.package_dir = {} cmd.distribution.packages = ['twisted', 'twisted.runner.portmap'] path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', 'portmap' + ext) self.assertEqual(wanted, path) # building twisted.runner.portmap inplace cmd.inplace = 1 path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) self.assertEqual(wanted, path) @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') def test_deployment_target_default(self): # Issue 9516: Test that, in the absence of the environment variable, # an extension module is compiled with the same deployment target as # the interpreter. self._try_compile_deployment_target('==', None) @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') def test_deployment_target_too_low(self): # Issue 9516: Test that an extension module is not allowed to be # compiled with a deployment target less than that of the interpreter. self.assertRaises(DistutilsPlatformError, self._try_compile_deployment_target, '>', '10.1') @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX') def test_deployment_target_higher_ok(self): # Issue 9516: Test that an extension module can be compiled with a # deployment target higher than that of the interpreter: the ext # module may depend on some newer OS feature. deptarget = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') if deptarget: # increment the minor version number (i.e. 10.6 -> 10.7) deptarget = [int(x) for x in deptarget.split('.')] deptarget[-1] += 1 deptarget = '.'.join(str(i) for i in deptarget) self._try_compile_deployment_target('<', deptarget) def _try_compile_deployment_target(self, operator, target): orig_environ = os.environ os.environ = orig_environ.copy() self.addCleanup(setattr, os, 'environ', orig_environ) if target is None: if os.environ.get('MACOSX_DEPLOYMENT_TARGET'): del os.environ['MACOSX_DEPLOYMENT_TARGET'] else: os.environ['MACOSX_DEPLOYMENT_TARGET'] = target deptarget_c = os.path.join(self.tmp_dir, 'deptargetmodule.c') with open(deptarget_c, 'w') as fp: fp.write(textwrap.dedent('''\ #include int dummy; #if TARGET %s MAC_OS_X_VERSION_MIN_REQUIRED #else #error "Unexpected target" #endif ''' % operator)) # get the deployment target that the interpreter was built with target = sysconfig.get_config_var('MACOSX_DEPLOYMENT_TARGET') target = tuple(map(int, target.split('.')[0:2])) # format the target value as defined in the Apple # Availability Macros. We can't use the macro names since # at least one value we test with will not exist yet. if target[:2] < (10, 10): # for 10.1 through 10.9.x -> "10n0" target = '%02d%01d0' % target else: # for 10.10 and beyond -> "10nn00" if len(target) >= 2: target = '%02d%02d00' % target else: # 11 and later can have no minor version (11 instead of 11.0) target = '%02d0000' % target deptarget_ext = Extension( 'deptarget', [deptarget_c], extra_compile_args=['-DTARGET=%s'%(target,)], ) dist = Distribution({ 'name': 'deptarget', 'ext_modules': [deptarget_ext] }) dist.package_dir = self.tmp_dir cmd = self.build_ext(dist) cmd.build_lib = self.tmp_dir cmd.build_temp = self.tmp_dir try: old_stdout = sys.stdout if not support.verbose: # silence compiler output sys.stdout = StringIO() try: cmd.ensure_finalized() cmd.run() finally: sys.stdout = old_stdout except CompileError: self.fail("Wrong deployment target during compilation") class ParallelBuildExtTestCase(BuildExtTestCase): def build_ext(self, *args, **kwargs): build_ext = super().build_ext(*args, **kwargs) build_ext.parallel = True return build_ext if __name__ == '__main__': unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_build_py.py000066400000000000000000000142571471045437300251540ustar00rootroot00000000000000"""Tests for distutils.command.build_py.""" import os import sys import unittest from distutils.command.build_py import build_py from distutils.core import Distribution from distutils.errors import DistutilsFileError from distutils.tests import support from test.support import requires_subprocess class BuildPyTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): def test_package_data(self): sources = self.mkdtemp() f = open(os.path.join(sources, "__init__.py"), "w") try: f.write("# Pretend this is a package.") finally: f.close() f = open(os.path.join(sources, "README.txt"), "w") try: f.write("Info about this package") finally: f.close() destination = self.mkdtemp() dist = Distribution({"packages": ["pkg"], "package_dir": {"pkg": sources}}) # script_name need not exist, it just need to be initialized dist.script_name = os.path.join(sources, "setup.py") dist.command_obj["build"] = support.DummyCommand( force=0, build_lib=destination) dist.packages = ["pkg"] dist.package_data = {"pkg": ["README.txt"]} dist.package_dir = {"pkg": sources} cmd = build_py(dist) cmd.compile = 1 cmd.ensure_finalized() self.assertEqual(cmd.package_data, dist.package_data) cmd.run() # This makes sure the list of outputs includes byte-compiled # files for Python modules but not for package data files # (there shouldn't *be* byte-code files for those!). self.assertEqual(len(cmd.get_outputs()), 3) pkgdest = os.path.join(destination, "pkg") files = os.listdir(pkgdest) pycache_dir = os.path.join(pkgdest, "__pycache__") self.assertIn("__init__.py", files) self.assertIn("README.txt", files) if sys.dont_write_bytecode: self.assertFalse(os.path.exists(pycache_dir)) else: pyc_files = os.listdir(pycache_dir) self.assertIn("__init__.%s.pyc" % sys.implementation.cache_tag, pyc_files) def test_empty_package_dir(self): # See bugs #1668596/#1720897 sources = self.mkdtemp() open(os.path.join(sources, "__init__.py"), "w").close() testdir = os.path.join(sources, "doc") os.mkdir(testdir) open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) dist = Distribution({"packages": ["pkg"], "package_dir": {"pkg": ""}, "package_data": {"pkg": ["doc/*"]}}) # script_name need not exist, it just need to be initialized dist.script_name = os.path.join(sources, "setup.py") dist.script_args = ["build"] dist.parse_command_line() try: dist.run_commands() except DistutilsFileError: self.fail("failed package_data test when package_dir is ''") @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') @requires_subprocess() def test_byte_compile(self): project_dir, dist = self.create_dist(py_modules=['boiledeggs']) os.chdir(project_dir) self.write_file('boiledeggs.py', 'import antigravity') cmd = build_py(dist) cmd.compile = 1 cmd.build_lib = 'here' cmd.finalize_options() cmd.run() found = os.listdir(cmd.build_lib) self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) self.assertEqual(found, ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]) @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') @requires_subprocess() def test_byte_compile_optimized(self): project_dir, dist = self.create_dist(py_modules=['boiledeggs']) os.chdir(project_dir) self.write_file('boiledeggs.py', 'import antigravity') cmd = build_py(dist) cmd.compile = 0 cmd.optimize = 1 cmd.build_lib = 'here' cmd.finalize_options() cmd.run() found = os.listdir(cmd.build_lib) self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py']) found = os.listdir(os.path.join(cmd.build_lib, '__pycache__')) expect = 'boiledeggs.{}.opt-1.pyc'.format(sys.implementation.cache_tag) self.assertEqual(sorted(found), [expect]) def test_dir_in_package_data(self): """ A directory in package_data should not be added to the filelist. """ # See bug 19286 sources = self.mkdtemp() pkg_dir = os.path.join(sources, "pkg") os.mkdir(pkg_dir) open(os.path.join(pkg_dir, "__init__.py"), "w").close() docdir = os.path.join(pkg_dir, "doc") os.mkdir(docdir) open(os.path.join(docdir, "testfile"), "w").close() # create the directory that could be incorrectly detected as a file os.mkdir(os.path.join(docdir, 'otherdir')) os.chdir(sources) dist = Distribution({"packages": ["pkg"], "package_data": {"pkg": ["doc/*"]}}) # script_name need not exist, it just need to be initialized dist.script_name = os.path.join(sources, "setup.py") dist.script_args = ["build"] dist.parse_command_line() try: dist.run_commands() except DistutilsFileError: self.fail("failed package_data when data dir includes a dir") def test_dont_write_bytecode(self): # makes sure byte_compile is not used dist = self.create_dist()[1] cmd = build_py(dist) cmd.compile = 1 cmd.optimize = 1 old_dont_write_bytecode = sys.dont_write_bytecode sys.dont_write_bytecode = True try: cmd.byte_compile([]) finally: sys.dont_write_bytecode = old_dont_write_bytecode self.assertIn('byte-compiling is disabled', self.logs[0][1] % self.logs[0][2]) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_build_scripts.py000066400000000000000000000066211471045437300262070ustar00rootroot00000000000000"""Tests for distutils.command.build_scripts.""" import os import unittest from distutils.command.build_scripts import build_scripts from distutils.core import Distribution from distutils import sysconfig from distutils.tests import support class BuildScriptsTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): def test_default_settings(self): cmd = self.get_build_scripts_cmd("/foo/bar", []) self.assertFalse(cmd.force) self.assertIsNone(cmd.build_dir) cmd.finalize_options() self.assertTrue(cmd.force) self.assertEqual(cmd.build_dir, "/foo/bar") def test_build(self): source = self.mkdtemp() target = self.mkdtemp() expected = self.write_sample_scripts(source) cmd = self.get_build_scripts_cmd(target, [os.path.join(source, fn) for fn in expected]) cmd.finalize_options() cmd.run() built = os.listdir(target) for name in expected: self.assertIn(name, built) def get_build_scripts_cmd(self, target, scripts): import sys dist = Distribution() dist.scripts = scripts dist.command_obj["build"] = support.DummyCommand( build_scripts=target, force=1, executable=sys.executable ) return build_scripts(dist) def write_sample_scripts(self, dir): expected = [] expected.append("script1.py") self.write_script(dir, "script1.py", ("#! /usr/bin/env python2.3\n" "# bogus script w/ Python sh-bang\n" "pass\n")) expected.append("script2.py") self.write_script(dir, "script2.py", ("#!/usr/bin/python\n" "# bogus script w/ Python sh-bang\n" "pass\n")) expected.append("shell.sh") self.write_script(dir, "shell.sh", ("#!/bin/sh\n" "# bogus shell script w/ sh-bang\n" "exit 0\n")) return expected def write_script(self, dir, name, text): f = open(os.path.join(dir, name), "w") try: f.write(text) finally: f.close() def test_version_int(self): source = self.mkdtemp() target = self.mkdtemp() expected = self.write_sample_scripts(source) cmd = self.get_build_scripts_cmd(target, [os.path.join(source, fn) for fn in expected]) cmd.finalize_options() # http://bugs.python.org/issue4524 # # On linux-g++-32 with command line `./configure --enable-ipv6 # --with-suffix=3`, python is compiled okay but the build scripts # failed when writing the name of the executable old = sysconfig.get_config_vars().get('VERSION') sysconfig._config_vars['VERSION'] = 4 try: cmd.run() finally: if old is not None: sysconfig._config_vars['VERSION'] = old built = os.listdir(target) for name in expected: self.assertIn(name, built) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_check.py000066400000000000000000000127361471045437300244220ustar00rootroot00000000000000"""Tests for distutils.command.check.""" import os import textwrap import unittest from distutils.command.check import check, HAS_DOCUTILS from distutils.tests import support from distutils.errors import DistutilsSetupError try: import pygments except ImportError: pygments = None HERE = os.path.dirname(__file__) class CheckTestCase(support.LoggingSilencer, support.TempdirManager, unittest.TestCase): def _run(self, metadata=None, cwd=None, **options): if metadata is None: metadata = {} if cwd is not None: old_dir = os.getcwd() os.chdir(cwd) pkg_info, dist = self.create_dist(**metadata) cmd = check(dist) cmd.initialize_options() for name, value in options.items(): setattr(cmd, name, value) cmd.ensure_finalized() cmd.run() if cwd is not None: os.chdir(old_dir) return cmd def test_check_metadata(self): # let's run the command with no metadata at all # by default, check is checking the metadata # should have some warnings cmd = self._run() self.assertEqual(cmd._warnings, 2) # now let's add the required fields # and run it again, to make sure we don't get # any warning anymore metadata = {'url': 'xxx', 'author': 'xxx', 'author_email': 'xxx', 'name': 'xxx', 'version': 'xxx'} cmd = self._run(metadata) self.assertEqual(cmd._warnings, 0) # now with the strict mode, we should # get an error if there are missing metadata self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1}) # and of course, no error when all metadata are present cmd = self._run(metadata, strict=1) self.assertEqual(cmd._warnings, 0) # now a test with non-ASCII characters metadata = {'url': 'xxx', 'author': '\u00c9ric', 'author_email': 'xxx', 'name': 'xxx', 'version': 'xxx', 'description': 'Something about esszet \u00df', 'long_description': 'More things about esszet \u00df'} cmd = self._run(metadata) self.assertEqual(cmd._warnings, 0) @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") def test_check_document(self): pkg_info, dist = self.create_dist() cmd = check(dist) # let's see if it detects broken rest broken_rest = 'title\n===\n\ntest' msgs = cmd._check_rst_data(broken_rest) self.assertEqual(len(msgs), 1) # and non-broken rest rest = 'title\n=====\n\ntest' msgs = cmd._check_rst_data(rest) self.assertEqual(len(msgs), 0) @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") def test_check_restructuredtext(self): # let's see if it detects broken rest in long_description broken_rest = 'title\n===\n\ntest' pkg_info, dist = self.create_dist(long_description=broken_rest) cmd = check(dist) cmd.check_restructuredtext() self.assertEqual(cmd._warnings, 1) # let's see if we have an error with strict=1 metadata = {'url': 'xxx', 'author': 'xxx', 'author_email': 'xxx', 'name': 'xxx', 'version': 'xxx', 'long_description': broken_rest} self.assertRaises(DistutilsSetupError, self._run, metadata, **{'strict': 1, 'restructuredtext': 1}) # and non-broken rest, including a non-ASCII character to test #12114 metadata['long_description'] = 'title\n=====\n\ntest \u00df' cmd = self._run(metadata, strict=1, restructuredtext=1) self.assertEqual(cmd._warnings, 0) # check that includes work to test #31292 metadata['long_description'] = 'title\n=====\n\n.. include:: includetest.rst' cmd = self._run(metadata, cwd=HERE, strict=1, restructuredtext=1) self.assertEqual(cmd._warnings, 0) @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils") def test_check_restructuredtext_with_syntax_highlight(self): # Don't fail if there is a `code` or `code-block` directive example_rst_docs = [] example_rst_docs.append(textwrap.dedent("""\ Here's some code: .. code:: python def foo(): pass """)) example_rst_docs.append(textwrap.dedent("""\ Here's some code: .. code-block:: python def foo(): pass """)) for rest_with_code in example_rst_docs: pkg_info, dist = self.create_dist(long_description=rest_with_code) cmd = check(dist) cmd.check_restructuredtext() msgs = cmd._check_rst_data(rest_with_code) if pygments is not None: self.assertEqual(len(msgs), 0) else: self.assertEqual(len(msgs), 1) self.assertEqual( str(msgs[0][1]), 'Cannot analyze code. Pygments package not found.' ) def test_check_all(self): metadata = {'url': 'xxx', 'author': 'xxx'} self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1, 'restructuredtext': 1}) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_clean.py000066400000000000000000000024601471045437300244200ustar00rootroot00000000000000"""Tests for distutils.command.clean.""" import os import unittest from distutils.command.clean import clean from distutils.tests import support class cleanTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): def test_simple_run(self): pkg_dir, dist = self.create_dist() cmd = clean(dist) # let's add some elements clean should remove dirs = [(d, os.path.join(pkg_dir, d)) for d in ('build_temp', 'build_lib', 'bdist_base', 'build_scripts', 'build_base')] for name, path in dirs: os.mkdir(path) setattr(cmd, name, path) if name == 'build_base': continue for f in ('one', 'two', 'three'): self.write_file(os.path.join(path, f)) # let's run the command cmd.all = 1 cmd.ensure_finalized() cmd.run() # make sure the files where removed for name, path in dirs: self.assertFalse(os.path.exists(path), '%s was not removed' % path) # let's run the command again (should spit warnings but succeed) cmd.all = 1 cmd.ensure_finalized() cmd.run() if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_cmd.py000066400000000000000000000072401471045437300241020ustar00rootroot00000000000000"""Tests for distutils.cmd.""" import unittest import os from test.support import captured_stdout from distutils.cmd import Command from distutils.dist import Distribution from distutils.errors import DistutilsOptionError from distutils import debug class MyCmd(Command): def initialize_options(self): pass class CommandTestCase(unittest.TestCase): def setUp(self): dist = Distribution() self.cmd = MyCmd(dist) def test_ensure_string_list(self): cmd = self.cmd cmd.not_string_list = ['one', 2, 'three'] cmd.yes_string_list = ['one', 'two', 'three'] cmd.not_string_list2 = object() cmd.yes_string_list2 = 'ok' cmd.ensure_string_list('yes_string_list') cmd.ensure_string_list('yes_string_list2') self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, 'not_string_list') self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, 'not_string_list2') cmd.option1 = 'ok,dok' cmd.ensure_string_list('option1') self.assertEqual(cmd.option1, ['ok', 'dok']) cmd.option2 = ['xxx', 'www'] cmd.ensure_string_list('option2') cmd.option3 = ['ok', 2] self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, 'option3') def test_make_file(self): cmd = self.cmd # making sure it raises when infiles is not a string or a list/tuple self.assertRaises(TypeError, cmd.make_file, infiles=1, outfile='', func='func', args=()) # making sure execute gets called properly def _execute(func, args, exec_msg, level): self.assertEqual(exec_msg, 'generating out from in') cmd.force = True cmd.execute = _execute cmd.make_file(infiles='in', outfile='out', func='func', args=()) def test_dump_options(self): msgs = [] def _announce(msg, level): msgs.append(msg) cmd = self.cmd cmd.announce = _announce cmd.option1 = 1 cmd.option2 = 1 cmd.user_options = [('option1', '', ''), ('option2', '', '')] cmd.dump_options() wanted = ["command options for 'MyCmd':", ' option1 = 1', ' option2 = 1'] self.assertEqual(msgs, wanted) def test_ensure_string(self): cmd = self.cmd cmd.option1 = 'ok' cmd.ensure_string('option1') cmd.option2 = None cmd.ensure_string('option2', 'xxx') self.assertTrue(hasattr(cmd, 'option2')) cmd.option3 = 1 self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3') def test_ensure_filename(self): cmd = self.cmd cmd.option1 = __file__ cmd.ensure_filename('option1') cmd.option2 = 'xxx' self.assertRaises(DistutilsOptionError, cmd.ensure_filename, 'option2') def test_ensure_dirname(self): cmd = self.cmd cmd.option1 = os.path.dirname(__file__) or os.curdir cmd.ensure_dirname('option1') cmd.option2 = 'xxx' self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') def test_debug_print(self): cmd = self.cmd with captured_stdout() as stdout: cmd.debug_print('xxx') stdout.seek(0) self.assertEqual(stdout.read(), '') debug.DEBUG = True try: with captured_stdout() as stdout: cmd.debug_print('xxx') stdout.seek(0) self.assertEqual(stdout.read(), 'xxx\n') finally: debug.DEBUG = False if __name__ == '__main__': unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_config.py000066400000000000000000000072731471045437300246120ustar00rootroot00000000000000"""Tests for distutils.pypirc.pypirc.""" import os import unittest from distutils.core import PyPIRCCommand from distutils.core import Distribution from distutils.log import set_threshold from distutils.log import WARN from distutils.tests import support PYPIRC = """\ [distutils] index-servers = server1 server2 server3 [server1] username:me password:secret [server2] username:meagain password: secret realm:acme repository:http://another.pypi/ [server3] username:cbiggles password:yh^%#rest-of-my-password """ PYPIRC_OLD = """\ [server-login] username:tarek password:secret """ WANTED = """\ [distutils] index-servers = pypi [pypi] username:tarek password:xxx """ class BasePyPIRCCommandTestCase(support.TempdirManager, support.LoggingSilencer, support.EnvironGuard, unittest.TestCase): def setUp(self): """Patches the environment.""" super(BasePyPIRCCommandTestCase, self).setUp() self.tmp_dir = self.mkdtemp() os.environ['HOME'] = self.tmp_dir os.environ['USERPROFILE'] = self.tmp_dir self.rc = os.path.join(self.tmp_dir, '.pypirc') self.dist = Distribution() class command(PyPIRCCommand): def __init__(self, dist): PyPIRCCommand.__init__(self, dist) def initialize_options(self): pass finalize_options = initialize_options self._cmd = command self.old_threshold = set_threshold(WARN) def tearDown(self): """Removes the patch.""" set_threshold(self.old_threshold) super(BasePyPIRCCommandTestCase, self).tearDown() class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase): def test_server_registration(self): # This test makes sure PyPIRCCommand knows how to: # 1. handle several sections in .pypirc # 2. handle the old format # new format self.write_file(self.rc, PYPIRC) cmd = self._cmd(self.dist) config = cmd._read_pypirc() config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), ('repository', 'https://upload.pypi.org/legacy/'), ('server', 'server1'), ('username', 'me')] self.assertEqual(config, waited) # old format self.write_file(self.rc, PYPIRC_OLD) config = cmd._read_pypirc() config = list(sorted(config.items())) waited = [('password', 'secret'), ('realm', 'pypi'), ('repository', 'https://upload.pypi.org/legacy/'), ('server', 'server-login'), ('username', 'tarek')] self.assertEqual(config, waited) def test_server_empty_registration(self): cmd = self._cmd(self.dist) rc = cmd._get_rc_file() self.assertFalse(os.path.exists(rc)) cmd._store_pypirc('tarek', 'xxx') self.assertTrue(os.path.exists(rc)) f = open(rc) try: content = f.read() self.assertEqual(content, WANTED) finally: f.close() def test_config_interpolation(self): # using the % character in .pypirc should not raise an error (#20120) self.write_file(self.rc, PYPIRC) cmd = self._cmd(self.dist) cmd.repository = 'server3' config = cmd._read_pypirc() config = list(sorted(config.items())) waited = [('password', 'yh^%#rest-of-my-password'), ('realm', 'pypi'), ('repository', 'https://upload.pypi.org/legacy/'), ('server', 'server3'), ('username', 'cbiggles')] self.assertEqual(config, waited) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_config_cmd.py000066400000000000000000000061351471045437300254310ustar00rootroot00000000000000"""Tests for distutils.command.config.""" import unittest import os import sys import sysconfig from test.support import ( missing_compiler_executable, requires_subprocess ) from distutils.command.config import dump_file, config from distutils.tests import support from distutils import log class ConfigTestCase(support.LoggingSilencer, support.TempdirManager, unittest.TestCase): def _info(self, msg, *args): for line in msg.splitlines(): self._logs.append(line) def setUp(self): super(ConfigTestCase, self).setUp() self._logs = [] self.old_log = log.info log.info = self._info self.old_config_vars = dict(sysconfig._CONFIG_VARS) def tearDown(self): log.info = self.old_log sysconfig._CONFIG_VARS.clear() sysconfig._CONFIG_VARS.update(self.old_config_vars) super(ConfigTestCase, self).tearDown() def test_dump_file(self): this_file = os.path.splitext(__file__)[0] + '.py' f = open(this_file) try: numlines = len(f.readlines()) finally: f.close() dump_file(this_file, 'I am the header') self.assertEqual(len(self._logs), numlines+1) @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") @requires_subprocess() def test_search_cpp(self): cmd = missing_compiler_executable(['preprocessor']) if cmd is not None: self.skipTest('The %r command is not found' % cmd) pkg_dir, dist = self.create_dist() cmd = config(dist) cmd._check_compiler() compiler = cmd.compiler if sys.platform[:3] == "aix" and "xlc" in compiler.preprocessor[0].lower(): self.skipTest('xlc: The -E option overrides the -P, -o, and -qsyntaxonly options') # simple pattern searches match = cmd.search_cpp(pattern='xxx', body='/* xxx */') self.assertEqual(match, 0) match = cmd.search_cpp(pattern='_configtest', body='/* xxx */') self.assertEqual(match, 1) def test_finalize_options(self): # finalize_options does a bit of transformation # on options pkg_dir, dist = self.create_dist() cmd = config(dist) cmd.include_dirs = 'one%stwo' % os.pathsep cmd.libraries = 'one' cmd.library_dirs = 'three%sfour' % os.pathsep cmd.ensure_finalized() self.assertEqual(cmd.include_dirs, ['one', 'two']) self.assertEqual(cmd.libraries, ['one']) self.assertEqual(cmd.library_dirs, ['three', 'four']) def test_clean(self): # _clean removes files tmp_dir = self.mkdtemp() f1 = os.path.join(tmp_dir, 'one') f2 = os.path.join(tmp_dir, 'two') self.write_file(f1, 'xxx') self.write_file(f2, 'xxx') for f in (f1, f2): self.assertTrue(os.path.exists(f)) pkg_dir, dist = self.create_dist() cmd = config(dist) cmd._clean(f1, f2) for f in (f1, f2): self.assertFalse(os.path.exists(f)) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_core.py000066400000000000000000000076301471045437300242720ustar00rootroot00000000000000"""Tests for distutils.core.""" import io import distutils.core import os import shutil import sys from test.support import captured_stdout from test.support import os_helper import unittest from distutils.tests import support from distutils import log # setup script that uses __file__ setup_using___file__ = """\ __file__ from distutils.core import setup setup() """ setup_prints_cwd = """\ import os print(os.getcwd()) from distutils.core import setup setup() """ setup_does_nothing = """\ from distutils.core import setup setup() """ setup_defines_subclass = """\ from distutils.core import setup from distutils.command.install import install as _install class install(_install): sub_commands = _install.sub_commands + ['cmd'] setup(cmdclass={'install': install}) """ class CoreTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): super(CoreTestCase, self).setUp() self.old_stdout = sys.stdout self.cleanup_testfn() self.old_argv = sys.argv, sys.argv[:] self.addCleanup(log.set_threshold, log._global_log.threshold) def tearDown(self): sys.stdout = self.old_stdout self.cleanup_testfn() sys.argv = self.old_argv[0] sys.argv[:] = self.old_argv[1] super(CoreTestCase, self).tearDown() def cleanup_testfn(self): path = os_helper.TESTFN if os.path.isfile(path): os.remove(path) elif os.path.isdir(path): shutil.rmtree(path) def write_setup(self, text, path=os_helper.TESTFN): f = open(path, "w") try: f.write(text) finally: f.close() return path def test_run_setup_provides_file(self): # Make sure the script can use __file__; if that's missing, the test # setup.py script will raise NameError. distutils.core.run_setup( self.write_setup(setup_using___file__)) def test_run_setup_preserves_sys_argv(self): # Make sure run_setup does not clobber sys.argv argv_copy = sys.argv.copy() distutils.core.run_setup( self.write_setup(setup_does_nothing)) self.assertEqual(sys.argv, argv_copy) def test_run_setup_defines_subclass(self): # Make sure the script can use __file__; if that's missing, the test # setup.py script will raise NameError. dist = distutils.core.run_setup( self.write_setup(setup_defines_subclass)) install = dist.get_command_obj('install') self.assertIn('cmd', install.sub_commands) def test_run_setup_uses_current_dir(self): # This tests that the setup script is run with the current directory # as its own current directory; this was temporarily broken by a # previous patch when TESTFN did not use the current directory. sys.stdout = io.StringIO() cwd = os.getcwd() # Create a directory and write the setup.py file there: os.mkdir(os_helper.TESTFN) setup_py = os.path.join(os_helper.TESTFN, "setup.py") distutils.core.run_setup( self.write_setup(setup_prints_cwd, path=setup_py)) output = sys.stdout.getvalue() if output.endswith("\n"): output = output[:-1] self.assertEqual(cwd, output) def test_debug_mode(self): # this covers the code called when DEBUG is set sys.argv = ['setup.py', '--name'] with captured_stdout() as stdout: distutils.core.setup(name='bar') stdout.seek(0) self.assertEqual(stdout.read(), 'bar\n') distutils.core.DEBUG = True try: with captured_stdout() as stdout: distutils.core.setup(name='bar') finally: distutils.core.DEBUG = False stdout.seek(0) wanted = "options (after parsing config files):\n" self.assertEqual(stdout.readlines()[0], wanted) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_cygwinccompiler.py000066400000000000000000000126111471045437300265330ustar00rootroot00000000000000"""Tests for distutils.cygwinccompiler.""" import unittest import sys import os from io import BytesIO from distutils import cygwinccompiler from distutils.cygwinccompiler import (check_config_h, CONFIG_H_OK, CONFIG_H_NOTOK, CONFIG_H_UNCERTAIN, get_versions, get_msvcr) from distutils.tests import support class FakePopen(object): test_class = None def __init__(self, cmd, shell, stdout): self.cmd = cmd.split()[0] exes = self.test_class._exes if self.cmd in exes: # issue #6438 in Python 3.x, Popen returns bytes self.stdout = BytesIO(exes[self.cmd]) else: self.stdout = os.popen(cmd, 'r') class CygwinCCompilerTestCase(support.TempdirManager, unittest.TestCase): def setUp(self): super(CygwinCCompilerTestCase, self).setUp() self.version = sys.version self.python_h = os.path.join(self.mkdtemp(), 'python.h') from distutils import sysconfig self.old_get_config_h_filename = sysconfig.get_config_h_filename sysconfig.get_config_h_filename = self._get_config_h_filename self.old_find_executable = cygwinccompiler.find_executable cygwinccompiler.find_executable = self._find_executable self._exes = {} self.old_popen = cygwinccompiler.Popen FakePopen.test_class = self cygwinccompiler.Popen = FakePopen def tearDown(self): sys.version = self.version from distutils import sysconfig sysconfig.get_config_h_filename = self.old_get_config_h_filename cygwinccompiler.find_executable = self.old_find_executable cygwinccompiler.Popen = self.old_popen super(CygwinCCompilerTestCase, self).tearDown() def _get_config_h_filename(self): return self.python_h def _find_executable(self, name): if name in self._exes: return name return None def test_check_config_h(self): # check_config_h looks for "GCC" in sys.version first # returns CONFIG_H_OK if found sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' '4.0.1 (Apple Computer, Inc. build 5370)]') self.assertEqual(check_config_h()[0], CONFIG_H_OK) # then it tries to see if it can find "__GNUC__" in pyconfig.h sys.version = 'something without the *CC word' # if the file doesn't exist it returns CONFIG_H_UNCERTAIN self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN) # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK self.write_file(self.python_h, 'xxx') self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK) # and CONFIG_H_OK if __GNUC__ is found self.write_file(self.python_h, 'xxx __GNUC__ xxx') self.assertEqual(check_config_h()[0], CONFIG_H_OK) def test_get_versions(self): # get_versions calls distutils.spawn.find_executable on # 'gcc', 'ld' and 'dllwrap' self.assertEqual(get_versions(), (None, None, None)) # Let's fake we have 'gcc' and it returns '3.4.5' self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF' res = get_versions() self.assertEqual(str(res[0]), '3.4.5') # and let's see what happens when the version # doesn't match the regular expression # (\d+\.\d+(\.\d+)*) self._exes['gcc'] = b'very strange output' res = get_versions() self.assertEqual(res[0], None) # same thing for ld self._exes['ld'] = b'GNU ld version 2.17.50 20060824' res = get_versions() self.assertEqual(str(res[1]), '2.17.50') self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77' res = get_versions() self.assertEqual(res[1], None) # and dllwrap self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF' res = get_versions() self.assertEqual(str(res[2]), '2.17.50') self._exes['dllwrap'] = b'Cheese Wrap' res = get_versions() self.assertEqual(res[2], None) def test_get_msvcr(self): # none sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') self.assertEqual(get_msvcr(), None) # MSVC 7.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1300 32 bits (Intel)]') self.assertEqual(get_msvcr(), ['msvcr70']) # MSVC 7.1 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1310 32 bits (Intel)]') self.assertEqual(get_msvcr(), ['msvcr71']) # VS2005 / MSVC 8.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1400 32 bits (Intel)]') self.assertEqual(get_msvcr(), ['msvcr80']) # VS2008 / MSVC 9.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1500 32 bits (Intel)]') self.assertEqual(get_msvcr(), ['msvcr90']) # unknown sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1999 32 bits (Intel)]') self.assertRaises(ValueError, get_msvcr) if __name__ == '__main__': unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_dep_util.py000066400000000000000000000052211471045437300251410ustar00rootroot00000000000000"""Tests for distutils.dep_util.""" import unittest import os from distutils.dep_util import newer, newer_pairwise, newer_group from distutils.errors import DistutilsFileError from distutils.tests import support class DepUtilTestCase(support.TempdirManager, unittest.TestCase): def test_newer(self): tmpdir = self.mkdtemp() new_file = os.path.join(tmpdir, 'new') old_file = os.path.abspath(__file__) # Raise DistutilsFileError if 'new_file' does not exist. self.assertRaises(DistutilsFileError, newer, new_file, old_file) # Return true if 'new_file' exists and is more recently modified than # 'old_file', or if 'new_file' exists and 'old_file' doesn't. self.write_file(new_file) self.assertTrue(newer(new_file, 'I_dont_exist')) self.assertTrue(newer(new_file, old_file)) # Return false if both exist and 'old_file' is the same age or younger # than 'new_file'. self.assertFalse(newer(old_file, new_file)) def test_newer_pairwise(self): tmpdir = self.mkdtemp() sources = os.path.join(tmpdir, 'sources') targets = os.path.join(tmpdir, 'targets') os.mkdir(sources) os.mkdir(targets) one = os.path.join(sources, 'one') two = os.path.join(sources, 'two') three = os.path.abspath(__file__) # I am the old file four = os.path.join(targets, 'four') self.write_file(one) self.write_file(two) self.write_file(four) self.assertEqual(newer_pairwise([one, two], [three, four]), ([one],[three])) def test_newer_group(self): tmpdir = self.mkdtemp() sources = os.path.join(tmpdir, 'sources') os.mkdir(sources) one = os.path.join(sources, 'one') two = os.path.join(sources, 'two') three = os.path.join(sources, 'three') old_file = os.path.abspath(__file__) # return true if 'old_file' is out-of-date with respect to any file # listed in 'sources'. self.write_file(one) self.write_file(two) self.write_file(three) self.assertTrue(newer_group([one, two, three], old_file)) self.assertFalse(newer_group([one, two, old_file], three)) # missing handling os.remove(one) self.assertRaises(OSError, newer_group, [one, two, old_file], three) self.assertFalse(newer_group([one, two, old_file], three, missing='ignore')) self.assertTrue(newer_group([one, two, old_file], three, missing='newer')) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_dir_util.py000066400000000000000000000111301471045437300251430ustar00rootroot00000000000000"""Tests for distutils.dir_util.""" import unittest import os import stat import sys from unittest.mock import patch from distutils import dir_util, errors from distutils.dir_util import (mkpath, remove_tree, create_tree, copy_tree, ensure_relative) from distutils import log from distutils.tests import support from test.support import is_emscripten, is_wasi class DirUtilTestCase(support.TempdirManager, unittest.TestCase): def _log(self, msg, *args): if len(args) > 0: self._logs.append(msg % args) else: self._logs.append(msg) def setUp(self): super(DirUtilTestCase, self).setUp() self._logs = [] tmp_dir = self.mkdtemp() self.root_target = os.path.join(tmp_dir, 'deep') self.target = os.path.join(self.root_target, 'here') self.target2 = os.path.join(tmp_dir, 'deep2') self.old_log = log.info log.info = self._log def tearDown(self): log.info = self.old_log super(DirUtilTestCase, self).tearDown() def test_mkpath_remove_tree_verbosity(self): mkpath(self.target, verbose=0) wanted = [] self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) mkpath(self.target, verbose=1) wanted = ['creating %s' % self.root_target, 'creating %s' % self.target] self.assertEqual(self._logs, wanted) self._logs = [] remove_tree(self.root_target, verbose=1) wanted = ["removing '%s' (and everything under it)" % self.root_target] self.assertEqual(self._logs, wanted) @unittest.skipIf(sys.platform.startswith('win'), "This test is only appropriate for POSIX-like systems.") @unittest.skipIf( is_emscripten or is_wasi, "Emscripten's/WASI's umask is a stub." ) def test_mkpath_with_custom_mode(self): # Get and set the current umask value for testing mode bits. umask = os.umask(0o002) os.umask(umask) mkpath(self.target, 0o700) self.assertEqual( stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask) mkpath(self.target2, 0o555) self.assertEqual( stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask) def test_create_tree_verbosity(self): create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) self.assertEqual(self._logs, []) remove_tree(self.root_target, verbose=0) wanted = ['creating %s' % self.root_target] create_tree(self.root_target, ['one', 'two', 'three'], verbose=1) self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) def test_copy_tree_verbosity(self): mkpath(self.target, verbose=0) copy_tree(self.target, self.target2, verbose=0) self.assertEqual(self._logs, []) remove_tree(self.root_target, verbose=0) mkpath(self.target, verbose=0) a_file = os.path.join(self.target, 'ok.txt') with open(a_file, 'w') as f: f.write('some content') wanted = ['copying %s -> %s' % (a_file, self.target2)] copy_tree(self.target, self.target2, verbose=1) self.assertEqual(self._logs, wanted) remove_tree(self.root_target, verbose=0) remove_tree(self.target2, verbose=0) def test_copy_tree_skips_nfs_temp_files(self): mkpath(self.target, verbose=0) a_file = os.path.join(self.target, 'ok.txt') nfs_file = os.path.join(self.target, '.nfs123abc') for f in a_file, nfs_file: with open(f, 'w') as fh: fh.write('some content') copy_tree(self.target, self.target2) self.assertEqual(os.listdir(self.target2), ['ok.txt']) remove_tree(self.root_target, verbose=0) remove_tree(self.target2, verbose=0) def test_ensure_relative(self): if os.sep == '/': self.assertEqual(ensure_relative('/home/foo'), 'home/foo') self.assertEqual(ensure_relative('some/path'), 'some/path') else: # \\ self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo') self.assertEqual(ensure_relative('home\\foo'), 'home\\foo') def test_copy_tree_exception_in_listdir(self): """ An exception in listdir should raise a DistutilsFileError """ with patch("os.listdir", side_effect=OSError()), \ self.assertRaises(errors.DistutilsFileError): src = self.tempdirs[-1] dir_util.copy_tree(src, None) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_dist.py000066400000000000000000000447301471045437300243070ustar00rootroot00000000000000"""Tests for distutils.dist.""" import os import io import sys import unittest import warnings import textwrap from unittest import mock from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command from test.support import ( captured_stdout, captured_stderr ) from test.support.os_helper import TESTFN from distutils.tests import support from distutils import log class test_dist(Command): """Sample distutils extension command.""" user_options = [ ("sample-option=", "S", "help text"), ] def initialize_options(self): self.sample_option = None class TestDistribution(Distribution): """Distribution subclasses that avoids the default search for configuration files. The ._config_files attribute must be set before .parse_config_files() is called. """ def find_config_files(self): return self._config_files class DistributionTestCase(support.LoggingSilencer, support.TempdirManager, support.EnvironGuard, unittest.TestCase): def setUp(self): super(DistributionTestCase, self).setUp() self.argv = sys.argv, sys.argv[:] del sys.argv[1:] def tearDown(self): sys.argv = self.argv[0] sys.argv[:] = self.argv[1] super(DistributionTestCase, self).tearDown() def create_distribution(self, configfiles=()): d = TestDistribution() d._config_files = configfiles d.parse_config_files() d.parse_command_line() return d def test_command_packages_unspecified(self): sys.argv.append("build") d = self.create_distribution() self.assertEqual(d.get_command_packages(), ["distutils.command"]) def test_command_packages_cmdline(self): from distutils.tests.test_dist import test_dist sys.argv.extend(["--command-packages", "foo.bar,distutils.tests", "test_dist", "-Ssometext", ]) d = self.create_distribution() # let's actually try to load our test command: self.assertEqual(d.get_command_packages(), ["distutils.command", "foo.bar", "distutils.tests"]) cmd = d.get_command_obj("test_dist") self.assertIsInstance(cmd, test_dist) self.assertEqual(cmd.sample_option, "sometext") def test_venv_install_options(self): sys.argv.append("install") self.addCleanup(os.unlink, TESTFN) fakepath = '/somedir' with open(TESTFN, "w") as f: print(("[install]\n" "install-base = {0}\n" "install-platbase = {0}\n" "install-lib = {0}\n" "install-platlib = {0}\n" "install-purelib = {0}\n" "install-headers = {0}\n" "install-scripts = {0}\n" "install-data = {0}\n" "prefix = {0}\n" "exec-prefix = {0}\n" "home = {0}\n" "user = {0}\n" "root = {0}").format(fakepath), file=f) # Base case: Not in a Virtual Environment with mock.patch.multiple(sys, prefix='/a', base_prefix='/a') as values: d = self.create_distribution([TESTFN]) option_tuple = (TESTFN, fakepath) result_dict = { 'install_base': option_tuple, 'install_platbase': option_tuple, 'install_lib': option_tuple, 'install_platlib': option_tuple, 'install_purelib': option_tuple, 'install_headers': option_tuple, 'install_scripts': option_tuple, 'install_data': option_tuple, 'prefix': option_tuple, 'exec_prefix': option_tuple, 'home': option_tuple, 'user': option_tuple, 'root': option_tuple, } self.assertEqual( sorted(d.command_options.get('install').keys()), sorted(result_dict.keys())) for (key, value) in d.command_options.get('install').items(): self.assertEqual(value, result_dict[key]) # Test case: In a Virtual Environment with mock.patch.multiple(sys, prefix='/a', base_prefix='/b') as values: d = self.create_distribution([TESTFN]) for key in result_dict.keys(): self.assertNotIn(key, d.command_options.get('install', {})) def test_command_packages_configfile(self): sys.argv.append("build") self.addCleanup(os.unlink, TESTFN) f = open(TESTFN, "w") try: print("[global]", file=f) print("command_packages = foo.bar, splat", file=f) finally: f.close() d = self.create_distribution([TESTFN]) self.assertEqual(d.get_command_packages(), ["distutils.command", "foo.bar", "splat"]) # ensure command line overrides config: sys.argv[1:] = ["--command-packages", "spork", "build"] d = self.create_distribution([TESTFN]) self.assertEqual(d.get_command_packages(), ["distutils.command", "spork"]) # Setting --command-packages to '' should cause the default to # be used even if a config file specified something else: sys.argv[1:] = ["--command-packages", "", "build"] d = self.create_distribution([TESTFN]) self.assertEqual(d.get_command_packages(), ["distutils.command"]) def test_empty_options(self): # an empty options dictionary should not stay in the # list of attributes # catching warnings warns = [] def _warn(msg): warns.append(msg) self.addCleanup(setattr, warnings, 'warn', warnings.warn) warnings.warn = _warn dist = Distribution(attrs={'author': 'xxx', 'name': 'xxx', 'version': 'xxx', 'url': 'xxxx', 'options': {}}) self.assertEqual(len(warns), 0) self.assertNotIn('options', dir(dist)) def test_finalize_options(self): attrs = {'keywords': 'one,two', 'platforms': 'one,two'} dist = Distribution(attrs=attrs) dist.finalize_options() # finalize_option splits platforms and keywords self.assertEqual(dist.metadata.platforms, ['one', 'two']) self.assertEqual(dist.metadata.keywords, ['one', 'two']) attrs = {'keywords': 'foo bar', 'platforms': 'foo bar'} dist = Distribution(attrs=attrs) dist.finalize_options() self.assertEqual(dist.metadata.platforms, ['foo bar']) self.assertEqual(dist.metadata.keywords, ['foo bar']) def test_get_command_packages(self): dist = Distribution() self.assertEqual(dist.command_packages, None) cmds = dist.get_command_packages() self.assertEqual(cmds, ['distutils.command']) self.assertEqual(dist.command_packages, ['distutils.command']) dist.command_packages = 'one,two' cmds = dist.get_command_packages() self.assertEqual(cmds, ['distutils.command', 'one', 'two']) def test_announce(self): # make sure the level is known dist = Distribution() args = ('ok',) kwargs = {'level': 'ok2'} self.assertRaises(ValueError, dist.announce, args, kwargs) def test_find_config_files_disable(self): # Ticket #1180: Allow user to disable their home config file. temp_home = self.mkdtemp() if os.name == 'posix': user_filename = os.path.join(temp_home, ".pydistutils.cfg") else: user_filename = os.path.join(temp_home, "pydistutils.cfg") with open(user_filename, 'w') as f: f.write('[distutils]\n') def _expander(path): return temp_home old_expander = os.path.expanduser os.path.expanduser = _expander try: d = Distribution() all_files = d.find_config_files() d = Distribution(attrs={'script_args': ['--no-user-cfg']}) files = d.find_config_files() finally: os.path.expanduser = old_expander # make sure --no-user-cfg disables the user cfg file self.assertEqual(len(all_files)-1, len(files)) class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): def setUp(self): super(MetadataTestCase, self).setUp() self.argv = sys.argv, sys.argv[:] def tearDown(self): sys.argv = self.argv[0] sys.argv[:] = self.argv[1] super(MetadataTestCase, self).tearDown() def format_metadata(self, dist): sio = io.StringIO() dist.metadata.write_pkg_file(sio) return sio.getvalue() def test_simple_metadata(self): attrs = {"name": "package", "version": "1.0"} dist = Distribution(attrs) meta = self.format_metadata(dist) self.assertIn("Metadata-Version: 1.0", meta) self.assertNotIn("provides:", meta.lower()) self.assertNotIn("requires:", meta.lower()) self.assertNotIn("obsoletes:", meta.lower()) def test_provides(self): attrs = {"name": "package", "version": "1.0", "provides": ["package", "package.sub"]} dist = Distribution(attrs) self.assertEqual(dist.metadata.get_provides(), ["package", "package.sub"]) self.assertEqual(dist.get_provides(), ["package", "package.sub"]) meta = self.format_metadata(dist) self.assertIn("Metadata-Version: 1.1", meta) self.assertNotIn("requires:", meta.lower()) self.assertNotIn("obsoletes:", meta.lower()) def test_provides_illegal(self): self.assertRaises(ValueError, Distribution, {"name": "package", "version": "1.0", "provides": ["my.pkg (splat)"]}) def test_requires(self): attrs = {"name": "package", "version": "1.0", "requires": ["other", "another (==1.0)"]} dist = Distribution(attrs) self.assertEqual(dist.metadata.get_requires(), ["other", "another (==1.0)"]) self.assertEqual(dist.get_requires(), ["other", "another (==1.0)"]) meta = self.format_metadata(dist) self.assertIn("Metadata-Version: 1.1", meta) self.assertNotIn("provides:", meta.lower()) self.assertIn("Requires: other", meta) self.assertIn("Requires: another (==1.0)", meta) self.assertNotIn("obsoletes:", meta.lower()) def test_requires_illegal(self): self.assertRaises(ValueError, Distribution, {"name": "package", "version": "1.0", "requires": ["my.pkg (splat)"]}) def test_requires_to_list(self): attrs = {"name": "package", "requires": iter(["other"])} dist = Distribution(attrs) self.assertIsInstance(dist.metadata.requires, list) def test_obsoletes(self): attrs = {"name": "package", "version": "1.0", "obsoletes": ["other", "another (<1.0)"]} dist = Distribution(attrs) self.assertEqual(dist.metadata.get_obsoletes(), ["other", "another (<1.0)"]) self.assertEqual(dist.get_obsoletes(), ["other", "another (<1.0)"]) meta = self.format_metadata(dist) self.assertIn("Metadata-Version: 1.1", meta) self.assertNotIn("provides:", meta.lower()) self.assertNotIn("requires:", meta.lower()) self.assertIn("Obsoletes: other", meta) self.assertIn("Obsoletes: another (<1.0)", meta) def test_obsoletes_illegal(self): self.assertRaises(ValueError, Distribution, {"name": "package", "version": "1.0", "obsoletes": ["my.pkg (splat)"]}) def test_obsoletes_to_list(self): attrs = {"name": "package", "obsoletes": iter(["other"])} dist = Distribution(attrs) self.assertIsInstance(dist.metadata.obsoletes, list) def test_classifier(self): attrs = {'name': 'Boa', 'version': '3.0', 'classifiers': ['Programming Language :: Python :: 3']} dist = Distribution(attrs) self.assertEqual(dist.get_classifiers(), ['Programming Language :: Python :: 3']) meta = self.format_metadata(dist) self.assertIn('Metadata-Version: 1.1', meta) def test_classifier_invalid_type(self): attrs = {'name': 'Boa', 'version': '3.0', 'classifiers': ('Programming Language :: Python :: 3',)} with captured_stderr() as error: d = Distribution(attrs) # should have warning about passing a non-list self.assertIn('should be a list', error.getvalue()) # should be converted to a list self.assertIsInstance(d.metadata.classifiers, list) self.assertEqual(d.metadata.classifiers, list(attrs['classifiers'])) def test_keywords(self): attrs = {'name': 'Monty', 'version': '1.0', 'keywords': ['spam', 'eggs', 'life of brian']} dist = Distribution(attrs) self.assertEqual(dist.get_keywords(), ['spam', 'eggs', 'life of brian']) def test_keywords_invalid_type(self): attrs = {'name': 'Monty', 'version': '1.0', 'keywords': ('spam', 'eggs', 'life of brian')} with captured_stderr() as error: d = Distribution(attrs) # should have warning about passing a non-list self.assertIn('should be a list', error.getvalue()) # should be converted to a list self.assertIsInstance(d.metadata.keywords, list) self.assertEqual(d.metadata.keywords, list(attrs['keywords'])) def test_platforms(self): attrs = {'name': 'Monty', 'version': '1.0', 'platforms': ['GNU/Linux', 'Some Evil Platform']} dist = Distribution(attrs) self.assertEqual(dist.get_platforms(), ['GNU/Linux', 'Some Evil Platform']) def test_platforms_invalid_types(self): attrs = {'name': 'Monty', 'version': '1.0', 'platforms': ('GNU/Linux', 'Some Evil Platform')} with captured_stderr() as error: d = Distribution(attrs) # should have warning about passing a non-list self.assertIn('should be a list', error.getvalue()) # should be converted to a list self.assertIsInstance(d.metadata.platforms, list) self.assertEqual(d.metadata.platforms, list(attrs['platforms'])) def test_download_url(self): attrs = {'name': 'Boa', 'version': '3.0', 'download_url': 'http://example.org/boa'} dist = Distribution(attrs) meta = self.format_metadata(dist) self.assertIn('Metadata-Version: 1.1', meta) def test_long_description(self): long_desc = textwrap.dedent("""\ example:: We start here and continue here and end here.""") attrs = {"name": "package", "version": "1.0", "long_description": long_desc} dist = Distribution(attrs) meta = self.format_metadata(dist) meta = meta.replace('\n' + 8 * ' ', '\n') self.assertIn(long_desc, meta) def test_custom_pydistutils(self): # fixes #2166 # make sure pydistutils.cfg is found if os.name == 'posix': user_filename = ".pydistutils.cfg" else: user_filename = "pydistutils.cfg" temp_dir = self.mkdtemp() user_filename = os.path.join(temp_dir, user_filename) f = open(user_filename, 'w') try: f.write('.') finally: f.close() try: dist = Distribution() # linux-style if sys.platform in ('linux', 'darwin'): os.environ['HOME'] = temp_dir files = dist.find_config_files() self.assertIn(user_filename, files) # win32-style if sys.platform == 'win32': # home drive should be found os.environ['USERPROFILE'] = temp_dir files = dist.find_config_files() self.assertIn(user_filename, files, '%r not found in %r' % (user_filename, files)) finally: os.remove(user_filename) def test_fix_help_options(self): help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] fancy_options = fix_help_options(help_tuples) self.assertEqual(fancy_options[0], ('a', 'b', 'c')) self.assertEqual(fancy_options[1], (1, 2, 3)) def test_show_help(self): # smoke test, just makes sure some help is displayed self.addCleanup(log.set_threshold, log._global_log.threshold) dist = Distribution() sys.argv = [] dist.help = 1 dist.script_name = 'setup.py' with captured_stdout() as s: dist.parse_command_line() output = [line for line in s.getvalue().split('\n') if line.strip() != ''] self.assertTrue(output) def test_read_metadata(self): attrs = {"name": "package", "version": "1.0", "long_description": "desc", "description": "xxx", "download_url": "http://example.com", "keywords": ['one', 'two'], "requires": ['foo']} dist = Distribution(attrs) metadata = dist.metadata # write it then reloads it PKG_INFO = io.StringIO() metadata.write_pkg_file(PKG_INFO) PKG_INFO.seek(0) metadata.read_pkg_file(PKG_INFO) self.assertEqual(metadata.name, "package") self.assertEqual(metadata.version, "1.0") self.assertEqual(metadata.description, "xxx") self.assertEqual(metadata.download_url, 'http://example.com') self.assertEqual(metadata.keywords, ['one', 'two']) self.assertEqual(metadata.platforms, ['UNKNOWN']) self.assertEqual(metadata.obsoletes, None) self.assertEqual(metadata.requires, ['foo']) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_extension.py000066400000000000000000000052031471045437300253500ustar00rootroot00000000000000"""Tests for distutils.extension.""" import unittest import os import warnings from test.support.warnings_helper import check_warnings from distutils.extension import read_setup_file, Extension class ExtensionTestCase(unittest.TestCase): def test_read_setup_file(self): # trying to read a Setup file # (sample extracted from the PyGame project) setup = os.path.join(os.path.dirname(__file__), 'Setup.sample') exts = read_setup_file(setup) names = [ext.name for ext in exts] names.sort() # here are the extensions read_setup_file should have created # out of the file wanted = ['_arraysurfarray', '_camera', '_numericsndarray', '_numericsurfarray', 'base', 'bufferproxy', 'cdrom', 'color', 'constants', 'display', 'draw', 'event', 'fastevent', 'font', 'gfxdraw', 'image', 'imageext', 'joystick', 'key', 'mask', 'mixer', 'mixer_music', 'mouse', 'movie', 'overlay', 'pixelarray', 'pypm', 'rect', 'rwobject', 'scrap', 'surface', 'surflock', 'time', 'transform'] self.assertEqual(names, wanted) def test_extension_init(self): # the first argument, which is the name, must be a string self.assertRaises(AssertionError, Extension, 1, []) ext = Extension('name', []) self.assertEqual(ext.name, 'name') # the second argument, which is the list of files, must # be a list of strings self.assertRaises(AssertionError, Extension, 'name', 'file') self.assertRaises(AssertionError, Extension, 'name', ['file', 1]) ext = Extension('name', ['file1', 'file2']) self.assertEqual(ext.sources, ['file1', 'file2']) # others arguments have defaults for attr in ('include_dirs', 'define_macros', 'undef_macros', 'library_dirs', 'libraries', 'runtime_library_dirs', 'extra_objects', 'extra_compile_args', 'extra_link_args', 'export_symbols', 'swig_opts', 'depends'): self.assertEqual(getattr(ext, attr), []) self.assertEqual(ext.language, None) self.assertEqual(ext.optional, None) # if there are unknown keyword options, warn about them with check_warnings() as w: warnings.simplefilter('always') ext = Extension('name', ['file1', 'file2'], chic=True) self.assertEqual(len(w.warnings), 1) self.assertEqual(str(w.warnings[0].message), "Unknown Extension options: 'chic'") if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_file_util.py000066400000000000000000000105601471045437300253120ustar00rootroot00000000000000"""Tests for distutils.file_util.""" import unittest import os import errno from unittest.mock import patch from distutils.file_util import move_file, copy_file from distutils import log from distutils.tests import support from distutils.errors import DistutilsFileError from test.support.os_helper import unlink class FileUtilTestCase(support.TempdirManager, unittest.TestCase): def _log(self, msg, *args): if len(args) > 0: self._logs.append(msg % args) else: self._logs.append(msg) def setUp(self): super(FileUtilTestCase, self).setUp() self._logs = [] self.old_log = log.info log.info = self._log tmp_dir = self.mkdtemp() self.source = os.path.join(tmp_dir, 'f1') self.target = os.path.join(tmp_dir, 'f2') self.target_dir = os.path.join(tmp_dir, 'd1') def tearDown(self): log.info = self.old_log super(FileUtilTestCase, self).tearDown() def test_move_file_verbosity(self): f = open(self.source, 'w') try: f.write('some content') finally: f.close() move_file(self.source, self.target, verbose=0) wanted = [] self.assertEqual(self._logs, wanted) # back to original state move_file(self.target, self.source, verbose=0) move_file(self.source, self.target, verbose=1) wanted = ['moving %s -> %s' % (self.source, self.target)] self.assertEqual(self._logs, wanted) # back to original state move_file(self.target, self.source, verbose=0) self._logs = [] # now the target is a dir os.mkdir(self.target_dir) move_file(self.source, self.target_dir, verbose=1) wanted = ['moving %s -> %s' % (self.source, self.target_dir)] self.assertEqual(self._logs, wanted) def test_move_file_exception_unpacking_rename(self): # see issue 22182 with patch("os.rename", side_effect=OSError("wrong", 1)), \ self.assertRaises(DistutilsFileError): with open(self.source, 'w') as fobj: fobj.write('spam eggs') move_file(self.source, self.target, verbose=0) def test_move_file_exception_unpacking_unlink(self): # see issue 22182 with patch("os.rename", side_effect=OSError(errno.EXDEV, "wrong")), \ patch("os.unlink", side_effect=OSError("wrong", 1)), \ self.assertRaises(DistutilsFileError): with open(self.source, 'w') as fobj: fobj.write('spam eggs') move_file(self.source, self.target, verbose=0) @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link') def test_copy_file_hard_link(self): with open(self.source, 'w') as f: f.write('some content') # Check first that copy_file() will not fall back on copying the file # instead of creating the hard link. try: os.link(self.source, self.target) except OSError as e: self.skipTest('os.link: %s' % e) else: unlink(self.target) st = os.stat(self.source) copy_file(self.source, self.target, link='hard') st2 = os.stat(self.source) st3 = os.stat(self.target) self.assertTrue(os.path.samestat(st, st2), (st, st2)) self.assertTrue(os.path.samestat(st2, st3), (st2, st3)) with open(self.source, 'r') as f: self.assertEqual(f.read(), 'some content') @unittest.skipUnless(hasattr(os, 'link'), 'requires os.link') def test_copy_file_hard_link_failure(self): # If hard linking fails, copy_file() falls back on copying file # (some special filesystems don't support hard linking even under # Unix, see issue #8876). with open(self.source, 'w') as f: f.write('some content') st = os.stat(self.source) with patch("os.link", side_effect=OSError(0, "linking unsupported")): copy_file(self.source, self.target, link='hard') st2 = os.stat(self.source) st3 = os.stat(self.target) self.assertTrue(os.path.samestat(st, st2), (st, st2)) self.assertFalse(os.path.samestat(st2, st3), (st2, st3)) for fn in (self.source, self.target): with open(fn, 'r') as f: self.assertEqual(f.read(), 'some content') if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_filelist.py000066400000000000000000000260361471045437300251560ustar00rootroot00000000000000"""Tests for distutils.filelist.""" import os import re import unittest from distutils import debug from distutils.log import WARN from distutils.errors import DistutilsTemplateError from distutils.filelist import glob_to_re, translate_pattern, FileList from distutils import filelist from test.support import os_helper from test.support import captured_stdout from distutils.tests import support MANIFEST_IN = """\ include ok include xo exclude xo include foo.tmp include buildout.cfg global-include *.x global-include *.txt global-exclude *.tmp recursive-include f *.oo recursive-exclude global *.x graft dir prune dir3 """ def make_local_path(s): """Converts '/' in a string to os.sep""" return s.replace('/', os.sep) class FileListTestCase(support.LoggingSilencer, unittest.TestCase): def assertNoWarnings(self): self.assertEqual(self.get_logs(WARN), []) self.clear_logs() def assertWarnings(self): self.assertGreater(len(self.get_logs(WARN)), 0) self.clear_logs() def test_glob_to_re(self): sep = os.sep if os.sep == '\\': sep = re.escape(os.sep) for glob, regex in ( # simple cases ('foo*', r'(?s:foo[^%(sep)s]*)\Z'), ('foo?', r'(?s:foo[^%(sep)s])\Z'), ('foo??', r'(?s:foo[^%(sep)s][^%(sep)s])\Z'), # special cases (r'foo\\*', r'(?s:foo\\\\[^%(sep)s]*)\Z'), (r'foo\\\*', r'(?s:foo\\\\\\[^%(sep)s]*)\Z'), ('foo????', r'(?s:foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s])\Z'), (r'foo\\??', r'(?s:foo\\\\[^%(sep)s][^%(sep)s])\Z')): regex = regex % {'sep': sep} self.assertEqual(glob_to_re(glob), regex) def test_process_template_line(self): # testing all MANIFEST.in template patterns file_list = FileList() l = make_local_path # simulated file list file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt', 'buildout.cfg', # filelist does not filter out VCS directories, # it's sdist that does l('.hg/last-message.txt'), l('global/one.txt'), l('global/two.txt'), l('global/files.x'), l('global/here.tmp'), l('f/o/f.oo'), l('dir/graft-one'), l('dir/dir2/graft2'), l('dir3/ok'), l('dir3/sub/ok.txt'), ] for line in MANIFEST_IN.split('\n'): if line.strip() == '': continue file_list.process_template_line(line) wanted = ['ok', 'buildout.cfg', 'four.txt', l('.hg/last-message.txt'), l('global/one.txt'), l('global/two.txt'), l('f/o/f.oo'), l('dir/graft-one'), l('dir/dir2/graft2'), ] self.assertEqual(file_list.files, wanted) def test_debug_print(self): file_list = FileList() with captured_stdout() as stdout: file_list.debug_print('xxx') self.assertEqual(stdout.getvalue(), '') debug.DEBUG = True try: with captured_stdout() as stdout: file_list.debug_print('xxx') self.assertEqual(stdout.getvalue(), 'xxx\n') finally: debug.DEBUG = False def test_set_allfiles(self): file_list = FileList() files = ['a', 'b', 'c'] file_list.set_allfiles(files) self.assertEqual(file_list.allfiles, files) def test_remove_duplicates(self): file_list = FileList() file_list.files = ['a', 'b', 'a', 'g', 'c', 'g'] # files must be sorted beforehand (sdist does it) file_list.sort() file_list.remove_duplicates() self.assertEqual(file_list.files, ['a', 'b', 'c', 'g']) def test_translate_pattern(self): # not regex self.assertTrue(hasattr( translate_pattern('a', anchor=True, is_regex=False), 'search')) # is a regex regex = re.compile('a') self.assertEqual( translate_pattern(regex, anchor=True, is_regex=True), regex) # plain string flagged as regex self.assertTrue(hasattr( translate_pattern('a', anchor=True, is_regex=True), 'search')) # glob support self.assertTrue(translate_pattern( '*.py', anchor=True, is_regex=False).search('filelist.py')) def test_exclude_pattern(self): # return False if no match file_list = FileList() self.assertFalse(file_list.exclude_pattern('*.py')) # return True if files match file_list = FileList() file_list.files = ['a.py', 'b.py'] self.assertTrue(file_list.exclude_pattern('*.py')) # test excludes file_list = FileList() file_list.files = ['a.py', 'a.txt'] file_list.exclude_pattern('*.py') self.assertEqual(file_list.files, ['a.txt']) def test_include_pattern(self): # return False if no match file_list = FileList() file_list.set_allfiles([]) self.assertFalse(file_list.include_pattern('*.py')) # return True if files match file_list = FileList() file_list.set_allfiles(['a.py', 'b.txt']) self.assertTrue(file_list.include_pattern('*.py')) # test * matches all files file_list = FileList() self.assertIsNone(file_list.allfiles) file_list.set_allfiles(['a.py', 'b.txt']) file_list.include_pattern('*') self.assertEqual(file_list.allfiles, ['a.py', 'b.txt']) def test_process_template(self): l = make_local_path # invalid lines file_list = FileList() for action in ('include', 'exclude', 'global-include', 'global-exclude', 'recursive-include', 'recursive-exclude', 'graft', 'prune', 'blarg'): self.assertRaises(DistutilsTemplateError, file_list.process_template_line, action) # include file_list = FileList() file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) file_list.process_template_line('include *.py') self.assertEqual(file_list.files, ['a.py']) self.assertNoWarnings() file_list.process_template_line('include *.rb') self.assertEqual(file_list.files, ['a.py']) self.assertWarnings() # exclude file_list = FileList() file_list.files = ['a.py', 'b.txt', l('d/c.py')] file_list.process_template_line('exclude *.py') self.assertEqual(file_list.files, ['b.txt', l('d/c.py')]) self.assertNoWarnings() file_list.process_template_line('exclude *.rb') self.assertEqual(file_list.files, ['b.txt', l('d/c.py')]) self.assertWarnings() # global-include file_list = FileList() file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')]) file_list.process_template_line('global-include *.py') self.assertEqual(file_list.files, ['a.py', l('d/c.py')]) self.assertNoWarnings() file_list.process_template_line('global-include *.rb') self.assertEqual(file_list.files, ['a.py', l('d/c.py')]) self.assertWarnings() # global-exclude file_list = FileList() file_list.files = ['a.py', 'b.txt', l('d/c.py')] file_list.process_template_line('global-exclude *.py') self.assertEqual(file_list.files, ['b.txt']) self.assertNoWarnings() file_list.process_template_line('global-exclude *.rb') self.assertEqual(file_list.files, ['b.txt']) self.assertWarnings() # recursive-include file_list = FileList() file_list.set_allfiles(['a.py', l('d/b.py'), l('d/c.txt'), l('d/d/e.py')]) file_list.process_template_line('recursive-include d *.py') self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) self.assertNoWarnings() file_list.process_template_line('recursive-include e *.py') self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) self.assertWarnings() # recursive-exclude file_list = FileList() file_list.files = ['a.py', l('d/b.py'), l('d/c.txt'), l('d/d/e.py')] file_list.process_template_line('recursive-exclude d *.py') self.assertEqual(file_list.files, ['a.py', l('d/c.txt')]) self.assertNoWarnings() file_list.process_template_line('recursive-exclude e *.py') self.assertEqual(file_list.files, ['a.py', l('d/c.txt')]) self.assertWarnings() # graft file_list = FileList() file_list.set_allfiles(['a.py', l('d/b.py'), l('d/d/e.py'), l('f/f.py')]) file_list.process_template_line('graft d') self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) self.assertNoWarnings() file_list.process_template_line('graft e') self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')]) self.assertWarnings() # prune file_list = FileList() file_list.files = ['a.py', l('d/b.py'), l('d/d/e.py'), l('f/f.py')] file_list.process_template_line('prune d') self.assertEqual(file_list.files, ['a.py', l('f/f.py')]) self.assertNoWarnings() file_list.process_template_line('prune e') self.assertEqual(file_list.files, ['a.py', l('f/f.py')]) self.assertWarnings() class FindAllTestCase(unittest.TestCase): @os_helper.skip_unless_symlink def test_missing_symlink(self): with os_helper.temp_cwd(): os.symlink('foo', 'bar') self.assertEqual(filelist.findall(), []) def test_basic_discovery(self): """ When findall is called with no parameters or with '.' as the parameter, the dot should be omitted from the results. """ with os_helper.temp_cwd(): os.mkdir('foo') file1 = os.path.join('foo', 'file1.txt') os_helper.create_empty_file(file1) os.mkdir('bar') file2 = os.path.join('bar', 'file2.txt') os_helper.create_empty_file(file2) expected = [file2, file1] self.assertEqual(sorted(filelist.findall()), expected) def test_non_local_discovery(self): """ When findall is called with another path, the full path name should be returned. """ with os_helper.temp_dir() as temp_dir: file1 = os.path.join(temp_dir, 'file1.txt') os_helper.create_empty_file(file1) expected = [file1] self.assertEqual(filelist.findall(temp_dir), expected) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_install.py000066400000000000000000000213551471045437300250100ustar00rootroot00000000000000"""Tests for distutils.command.install.""" import os import sys import unittest import site from test.support import captured_stdout, requires_subprocess from distutils import sysconfig from distutils.command.install import install, HAS_USER_SITE from distutils.command import install as install_module from distutils.command.build_ext import build_ext from distutils.command.install import INSTALL_SCHEMES from distutils.core import Distribution from distutils.errors import DistutilsOptionError from distutils.extension import Extension from distutils.tests import support from test import support as test_support def _make_ext_name(modname): return modname + sysconfig.get_config_var('EXT_SUFFIX') class InstallTestCase(support.TempdirManager, support.EnvironGuard, support.LoggingSilencer, unittest.TestCase): def setUp(self): super().setUp() self._backup_config_vars = dict(sysconfig._config_vars) def tearDown(self): super().tearDown() sysconfig._config_vars.clear() sysconfig._config_vars.update(self._backup_config_vars) def test_home_installation_scheme(self): # This ensure two things: # - that --home generates the desired set of directory names # - test --home is supported on all platforms builddir = self.mkdtemp() destination = os.path.join(builddir, "installation") dist = Distribution({"name": "foopkg"}) # script_name need not exist, it just need to be initialized dist.script_name = os.path.join(builddir, "setup.py") dist.command_obj["build"] = support.DummyCommand( build_base=builddir, build_lib=os.path.join(builddir, "lib"), ) cmd = install(dist) cmd.home = destination cmd.ensure_finalized() self.assertEqual(cmd.install_base, destination) self.assertEqual(cmd.install_platbase, destination) def check_path(got, expected): got = os.path.normpath(got) expected = os.path.normpath(expected) self.assertEqual(got, expected) libdir = os.path.join(destination, "lib", "python") check_path(cmd.install_lib, libdir) platlibdir = os.path.join(destination, sys.platlibdir, "python") check_path(cmd.install_platlib, platlibdir) check_path(cmd.install_purelib, libdir) check_path(cmd.install_headers, os.path.join(destination, "include", "python", "foopkg")) check_path(cmd.install_scripts, os.path.join(destination, "bin")) check_path(cmd.install_data, destination) @unittest.skipUnless(HAS_USER_SITE, 'need user site') def test_user_site(self): # test install with --user # preparing the environment for the test self.old_user_base = site.USER_BASE self.old_user_site = site.USER_SITE self.tmpdir = self.mkdtemp() self.user_base = os.path.join(self.tmpdir, 'B') self.user_site = os.path.join(self.tmpdir, 'S') site.USER_BASE = self.user_base site.USER_SITE = self.user_site install_module.USER_BASE = self.user_base install_module.USER_SITE = self.user_site def _expanduser(path): return self.tmpdir self.old_expand = os.path.expanduser os.path.expanduser = _expanduser def cleanup(): site.USER_BASE = self.old_user_base site.USER_SITE = self.old_user_site install_module.USER_BASE = self.old_user_base install_module.USER_SITE = self.old_user_site os.path.expanduser = self.old_expand self.addCleanup(cleanup) if HAS_USER_SITE: for key in ('nt_user', 'unix_user'): self.assertIn(key, INSTALL_SCHEMES) dist = Distribution({'name': 'xx'}) cmd = install(dist) # making sure the user option is there options = [name for name, short, lable in cmd.user_options] self.assertIn('user', options) # setting a value cmd.user = 1 # user base and site shouldn't be created yet self.assertFalse(os.path.exists(self.user_base)) self.assertFalse(os.path.exists(self.user_site)) # let's run finalize cmd.ensure_finalized() # now they should self.assertTrue(os.path.exists(self.user_base)) self.assertTrue(os.path.exists(self.user_site)) self.assertIn('userbase', cmd.config_vars) self.assertIn('usersite', cmd.config_vars) def test_handle_extra_path(self): dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'}) cmd = install(dist) # two elements cmd.handle_extra_path() self.assertEqual(cmd.extra_path, ['path', 'dirs']) self.assertEqual(cmd.extra_dirs, 'dirs') self.assertEqual(cmd.path_file, 'path') # one element cmd.extra_path = ['path'] cmd.handle_extra_path() self.assertEqual(cmd.extra_path, ['path']) self.assertEqual(cmd.extra_dirs, 'path') self.assertEqual(cmd.path_file, 'path') # none dist.extra_path = cmd.extra_path = None cmd.handle_extra_path() self.assertEqual(cmd.extra_path, None) self.assertEqual(cmd.extra_dirs, '') self.assertEqual(cmd.path_file, None) # three elements (no way !) cmd.extra_path = 'path,dirs,again' self.assertRaises(DistutilsOptionError, cmd.handle_extra_path) def test_finalize_options(self): dist = Distribution({'name': 'xx'}) cmd = install(dist) # must supply either prefix/exec-prefix/home or # install-base/install-platbase -- not both cmd.prefix = 'prefix' cmd.install_base = 'base' self.assertRaises(DistutilsOptionError, cmd.finalize_options) # must supply either home or prefix/exec-prefix -- not both cmd.install_base = None cmd.home = 'home' self.assertRaises(DistutilsOptionError, cmd.finalize_options) # can't combine user with prefix/exec_prefix/home or # install_(plat)base cmd.prefix = None cmd.user = 'user' self.assertRaises(DistutilsOptionError, cmd.finalize_options) def test_record(self): install_dir = self.mkdtemp() project_dir, dist = self.create_dist(py_modules=['hello'], scripts=['sayhi']) os.chdir(project_dir) self.write_file('hello.py', "def main(): print('o hai')") self.write_file('sayhi', 'from hello import main; main()') cmd = install(dist) dist.command_obj['install'] = cmd cmd.root = install_dir cmd.record = os.path.join(project_dir, 'filelist') cmd.ensure_finalized() cmd.run() f = open(cmd.record) try: content = f.read() finally: f.close() found = [os.path.basename(line) for line in content.splitlines()] expected = ['hello.py', 'hello.%s.pyc' % sys.implementation.cache_tag, 'sayhi', 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) @requires_subprocess() def test_record_extensions(self): cmd = test_support.missing_compiler_executable() if cmd is not None: self.skipTest('The %r command is not found' % cmd) install_dir = self.mkdtemp() project_dir, dist = self.create_dist(ext_modules=[ Extension('xx', ['xxmodule.c'])]) os.chdir(project_dir) support.copy_xxmodule_c(project_dir) buildextcmd = build_ext(dist) support.fixup_build_ext(buildextcmd) buildextcmd.ensure_finalized() cmd = install(dist) dist.command_obj['install'] = cmd dist.command_obj['build_ext'] = buildextcmd cmd.root = install_dir cmd.record = os.path.join(project_dir, 'filelist') cmd.ensure_finalized() cmd.run() f = open(cmd.record) try: content = f.read() finally: f.close() found = [os.path.basename(line) for line in content.splitlines()] expected = [_make_ext_name('xx'), 'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]] self.assertEqual(found, expected) def test_debug_mode(self): # this covers the code called when DEBUG is set old_logs_len = len(self.logs) install_module.DEBUG = True try: with captured_stdout(): self.test_record() finally: install_module.DEBUG = False self.assertGreater(len(self.logs), old_logs_len) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_install_data.py000066400000000000000000000046321471045437300260000ustar00rootroot00000000000000"""Tests for distutils.command.install_data.""" import os import unittest from distutils.command.install_data import install_data from distutils.tests import support class InstallDataTestCase(support.TempdirManager, support.LoggingSilencer, support.EnvironGuard, unittest.TestCase): def test_simple_run(self): pkg_dir, dist = self.create_dist() cmd = install_data(dist) cmd.install_dir = inst = os.path.join(pkg_dir, 'inst') # data_files can contain # - simple files # - a tuple with a path, and a list of file one = os.path.join(pkg_dir, 'one') self.write_file(one, 'xxx') inst2 = os.path.join(pkg_dir, 'inst2') two = os.path.join(pkg_dir, 'two') self.write_file(two, 'xxx') cmd.data_files = [one, (inst2, [two])] self.assertEqual(cmd.get_inputs(), [one, (inst2, [two])]) # let's run the command cmd.ensure_finalized() cmd.run() # let's check the result self.assertEqual(len(cmd.get_outputs()), 2) rtwo = os.path.split(two)[-1] self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) rone = os.path.split(one)[-1] self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] # let's try with warn_dir one cmd.warn_dir = 1 cmd.ensure_finalized() cmd.run() # let's check the result self.assertEqual(len(cmd.get_outputs()), 2) self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] # now using root and empty dir cmd.root = os.path.join(pkg_dir, 'root') inst3 = os.path.join(cmd.install_dir, 'inst3') inst4 = os.path.join(pkg_dir, 'inst4') three = os.path.join(cmd.install_dir, 'three') self.write_file(three, 'xx') cmd.data_files = [one, (inst2, [two]), ('inst3', [three]), (inst4, [])] cmd.ensure_finalized() cmd.run() # let's check the result self.assertEqual(len(cmd.get_outputs()), 4) self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) self.assertTrue(os.path.exists(os.path.join(inst, rone))) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_install_headers.py000066400000000000000000000021341471045437300264750ustar00rootroot00000000000000"""Tests for distutils.command.install_headers.""" import os import unittest from distutils.command.install_headers import install_headers from distutils.tests import support class InstallHeadersTestCase(support.TempdirManager, support.LoggingSilencer, support.EnvironGuard, unittest.TestCase): def test_simple_run(self): # we have two headers header_list = self.mkdtemp() header1 = os.path.join(header_list, 'header1') header2 = os.path.join(header_list, 'header2') self.write_file(header1) self.write_file(header2) headers = [header1, header2] pkg_dir, dist = self.create_dist(headers=headers) cmd = install_headers(dist) self.assertEqual(cmd.get_inputs(), headers) # let's run the command cmd.install_dir = os.path.join(pkg_dir, 'inst') cmd.ensure_finalized() cmd.run() # let's check the results self.assertEqual(len(cmd.get_outputs()), 2) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_install_lib.py000066400000000000000000000075631471045437300256430ustar00rootroot00000000000000"""Tests for distutils.command.install_data.""" import sys import os import importlib.util import unittest from distutils.command.install_lib import install_lib from distutils.extension import Extension from distutils.tests import support from distutils.errors import DistutilsOptionError from test.support import requires_subprocess class InstallLibTestCase(support.TempdirManager, support.LoggingSilencer, support.EnvironGuard, unittest.TestCase): def test_finalize_options(self): dist = self.create_dist()[1] cmd = install_lib(dist) cmd.finalize_options() self.assertEqual(cmd.compile, 1) self.assertEqual(cmd.optimize, 0) # optimize must be 0, 1, or 2 cmd.optimize = 'foo' self.assertRaises(DistutilsOptionError, cmd.finalize_options) cmd.optimize = '4' self.assertRaises(DistutilsOptionError, cmd.finalize_options) cmd.optimize = '2' cmd.finalize_options() self.assertEqual(cmd.optimize, 2) @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled') @requires_subprocess() def test_byte_compile(self): project_dir, dist = self.create_dist() os.chdir(project_dir) cmd = install_lib(dist) cmd.compile = cmd.optimize = 1 f = os.path.join(project_dir, 'foo.py') self.write_file(f, '# python file') cmd.byte_compile([f]) pyc_file = importlib.util.cache_from_source('foo.py', optimization='') pyc_opt_file = importlib.util.cache_from_source('foo.py', optimization=cmd.optimize) self.assertTrue(os.path.exists(pyc_file)) self.assertTrue(os.path.exists(pyc_opt_file)) def test_get_outputs(self): project_dir, dist = self.create_dist() os.chdir(project_dir) os.mkdir('spam') cmd = install_lib(dist) # setting up a dist environment cmd.compile = cmd.optimize = 1 cmd.install_dir = self.mkdtemp() f = os.path.join(project_dir, 'spam', '__init__.py') self.write_file(f, '# python package') cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] cmd.distribution.packages = ['spam'] cmd.distribution.script_name = 'setup.py' # get_outputs should return 4 elements: spam/__init__.py and .pyc, # foo.import-tag-abiflags.so / foo.pyd outputs = cmd.get_outputs() self.assertEqual(len(outputs), 4, outputs) def test_get_inputs(self): project_dir, dist = self.create_dist() os.chdir(project_dir) os.mkdir('spam') cmd = install_lib(dist) # setting up a dist environment cmd.compile = cmd.optimize = 1 cmd.install_dir = self.mkdtemp() f = os.path.join(project_dir, 'spam', '__init__.py') self.write_file(f, '# python package') cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] cmd.distribution.packages = ['spam'] cmd.distribution.script_name = 'setup.py' # get_inputs should return 2 elements: spam/__init__.py and # foo.import-tag-abiflags.so / foo.pyd inputs = cmd.get_inputs() self.assertEqual(len(inputs), 2, inputs) @requires_subprocess() def test_dont_write_bytecode(self): # makes sure byte_compile is not used dist = self.create_dist()[1] cmd = install_lib(dist) cmd.compile = 1 cmd.optimize = 1 old_dont_write_bytecode = sys.dont_write_bytecode sys.dont_write_bytecode = True try: cmd.byte_compile([]) finally: sys.dont_write_bytecode = old_dont_write_bytecode self.assertIn('byte-compiling is disabled', self.logs[0][1] % self.logs[0][2]) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_install_scripts.py000066400000000000000000000047071471045437300265610ustar00rootroot00000000000000"""Tests for distutils.command.install_scripts.""" import os import unittest from distutils.command.install_scripts import install_scripts from distutils.core import Distribution from distutils.tests import support class InstallScriptsTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): def test_default_settings(self): dist = Distribution() dist.command_obj["build"] = support.DummyCommand( build_scripts="/foo/bar") dist.command_obj["install"] = support.DummyCommand( install_scripts="/splat/funk", force=1, skip_build=1, ) cmd = install_scripts(dist) self.assertFalse(cmd.force) self.assertFalse(cmd.skip_build) self.assertIsNone(cmd.build_dir) self.assertIsNone(cmd.install_dir) cmd.finalize_options() self.assertTrue(cmd.force) self.assertTrue(cmd.skip_build) self.assertEqual(cmd.build_dir, "/foo/bar") self.assertEqual(cmd.install_dir, "/splat/funk") def test_installation(self): source = self.mkdtemp() expected = [] def write_script(name, text): expected.append(name) f = open(os.path.join(source, name), "w") try: f.write(text) finally: f.close() write_script("script1.py", ("#! /usr/bin/env python2.3\n" "# bogus script w/ Python sh-bang\n" "pass\n")) write_script("script2.py", ("#!/usr/bin/python\n" "# bogus script w/ Python sh-bang\n" "pass\n")) write_script("shell.sh", ("#!/bin/sh\n" "# bogus shell script w/ sh-bang\n" "exit 0\n")) target = self.mkdtemp() dist = Distribution() dist.command_obj["build"] = support.DummyCommand(build_scripts=source) dist.command_obj["install"] = support.DummyCommand( install_scripts=target, force=1, skip_build=1, ) cmd = install_scripts(dist) cmd.finalize_options() cmd.run() installed = os.listdir(target) for name in expected: self.assertIn(name, installed) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_log.py000066400000000000000000000033651471045437300241240ustar00rootroot00000000000000"""Tests for distutils.log""" import io import sys import unittest from test.support import swap_attr from distutils import log class TestLog(unittest.TestCase): def test_non_ascii(self): # Issues #8663, #34421: test that non-encodable text is escaped with # backslashreplace error handler and encodable non-ASCII text is # output as is. for errors in ('strict', 'backslashreplace', 'surrogateescape', 'replace', 'ignore'): with self.subTest(errors=errors): stdout = io.TextIOWrapper(io.BytesIO(), encoding='cp437', errors=errors) stderr = io.TextIOWrapper(io.BytesIO(), encoding='cp437', errors=errors) old_threshold = log.set_threshold(log.DEBUG) try: with swap_attr(sys, 'stdout', stdout), \ swap_attr(sys, 'stderr', stderr): log.debug('Dεbug\tMėssãge') log.fatal('Fαtal\tÈrrōr') finally: log.set_threshold(old_threshold) stdout.seek(0) self.assertEqual(stdout.read().rstrip(), 'Dεbug\tM?ss?ge' if errors == 'replace' else 'Dεbug\tMssge' if errors == 'ignore' else 'Dεbug\tM\\u0117ss\\xe3ge') stderr.seek(0) self.assertEqual(stderr.read().rstrip(), 'Fαtal\t?rr?r' if errors == 'replace' else 'Fαtal\trrr' if errors == 'ignore' else 'Fαtal\t\\xc8rr\\u014dr') if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_msvc9compiler.py000066400000000000000000000134351471045437300261360ustar00rootroot00000000000000"""Tests for distutils.msvc9compiler.""" import sys import unittest import os from distutils.errors import DistutilsPlatformError from distutils.tests import support # A manifest with the only assembly reference being the msvcrt assembly, so # should have the assembly completely stripped. Note that although the # assembly has a reference the assembly is removed - that is # currently a "feature", not a bug :) _MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\ """ # A manifest with references to assemblies other than msvcrt. When processed, # this assembly should be returned with just the msvcrt part removed. _MANIFEST_WITH_MULTIPLE_REFERENCES = """\ """ _CLEANED_MANIFEST = """\ """ if sys.platform=="win32": from distutils.msvccompiler import get_build_version if get_build_version()>=8.0: SKIP_MESSAGE = None else: SKIP_MESSAGE = "These tests are only for MSVC8.0 or above" else: SKIP_MESSAGE = "These tests are only for win32" @unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): def test_no_compiler(self): # makes sure query_vcvarsall raises # a DistutilsPlatformError if the compiler # is not found from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None from distutils import msvc9compiler old_find_vcvarsall = msvc9compiler.find_vcvarsall msvc9compiler.find_vcvarsall = _find_vcvarsall try: self.assertRaises(DistutilsPlatformError, query_vcvarsall, 'wont find this version') finally: msvc9compiler.find_vcvarsall = old_find_vcvarsall def test_reg_class(self): from distutils.msvc9compiler import Reg self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') # looking for values that should exist on all # windows registry versions. path = r'Control Panel\Desktop' v = Reg.get_value(path, 'dragfullwindows') self.assertIn(v, ('0', '1', '2')) import winreg HKCU = winreg.HKEY_CURRENT_USER keys = Reg.read_keys(HKCU, 'xxxx') self.assertEqual(keys, None) keys = Reg.read_keys(HKCU, r'Control Panel') self.assertIn('Desktop', keys) def test_remove_visual_c_ref(self): from distutils.msvc9compiler import MSVCCompiler tempdir = self.mkdtemp() manifest = os.path.join(tempdir, 'manifest') f = open(manifest, 'w') try: f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES) finally: f.close() compiler = MSVCCompiler() compiler._remove_visual_c_ref(manifest) # see what we got f = open(manifest) try: # removing trailing spaces content = '\n'.join([line.rstrip() for line in f.readlines()]) finally: f.close() # makes sure the manifest was properly cleaned self.assertEqual(content, _CLEANED_MANIFEST) def test_remove_entire_manifest(self): from distutils.msvc9compiler import MSVCCompiler tempdir = self.mkdtemp() manifest = os.path.join(tempdir, 'manifest') f = open(manifest, 'w') try: f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE) finally: f.close() compiler = MSVCCompiler() got = compiler._remove_visual_c_ref(manifest) self.assertIsNone(got) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_msvccompiler.py000066400000000000000000000052451471045437300260450ustar00rootroot00000000000000"""Tests for distutils._msvccompiler.""" import sys import unittest import os from distutils.errors import DistutilsPlatformError from distutils.tests import support SKIP_MESSAGE = (None if sys.platform == "win32" else "These tests are only for win32") @unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE) class msvccompilerTestCase(support.TempdirManager, unittest.TestCase): def test_no_compiler(self): import distutils._msvccompiler as _msvccompiler # makes sure query_vcvarsall raises # a DistutilsPlatformError if the compiler # is not found def _find_vcvarsall(plat_spec): return None, None old_find_vcvarsall = _msvccompiler._find_vcvarsall _msvccompiler._find_vcvarsall = _find_vcvarsall try: self.assertRaises(DistutilsPlatformError, _msvccompiler._get_vc_env, 'wont find this version') finally: _msvccompiler._find_vcvarsall = old_find_vcvarsall def test_get_vc_env_unicode(self): import distutils._msvccompiler as _msvccompiler test_var = 'ṰḖṤṪ┅ṼẨṜ' test_value = '₃⁴₅' # Ensure we don't early exit from _get_vc_env old_distutils_use_sdk = os.environ.pop('DISTUTILS_USE_SDK', None) os.environ[test_var] = test_value try: env = _msvccompiler._get_vc_env('x86') self.assertIn(test_var.lower(), env) self.assertEqual(test_value, env[test_var.lower()]) finally: os.environ.pop(test_var) if old_distutils_use_sdk: os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk def test_get_vc2017(self): import distutils._msvccompiler as _msvccompiler # This function cannot be mocked, so pass it if we find VS 2017 # and mark it skipped if we do not. version, path = _msvccompiler._find_vc2017() if version: self.assertGreaterEqual(version, 15) self.assertTrue(os.path.isdir(path)) else: raise unittest.SkipTest("VS 2017 is not installed") def test_get_vc2015(self): import distutils._msvccompiler as _msvccompiler # This function cannot be mocked, so pass it if we find VS 2015 # and mark it skipped if we do not. version, path = _msvccompiler._find_vc2015() if version: self.assertGreaterEqual(version, 14) self.assertTrue(os.path.isdir(path)) else: raise unittest.SkipTest("VS 2015 is not installed") if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_register.py000066400000000000000000000232041471045437300251610ustar00rootroot00000000000000"""Tests for distutils.command.register.""" import os import unittest import getpass import urllib import warnings from test.support.warnings_helper import check_warnings from distutils.command import register as register_module from distutils.command.register import register from distutils.errors import DistutilsSetupError from distutils.log import INFO from distutils.tests.test_config import BasePyPIRCCommandTestCase try: import docutils except ImportError: docutils = None PYPIRC_NOPASSWORD = """\ [distutils] index-servers = server1 [server1] username:me """ WANTED_PYPIRC = """\ [distutils] index-servers = pypi [pypi] username:tarek password:password """ class Inputs(object): """Fakes user inputs.""" def __init__(self, *answers): self.answers = answers self.index = 0 def __call__(self, prompt=''): try: return self.answers[self.index] finally: self.index += 1 class FakeOpener(object): """Fakes a PyPI server""" def __init__(self): self.reqs = [] def __call__(self, *args): return self def open(self, req, data=None, timeout=None): self.reqs.append(req) return self def read(self): return b'xxx' def getheader(self, name, default=None): return { 'content-type': 'text/plain; charset=utf-8', }.get(name.lower(), default) class RegisterTestCase(BasePyPIRCCommandTestCase): def setUp(self): super(RegisterTestCase, self).setUp() # patching the password prompt self._old_getpass = getpass.getpass def _getpass(prompt): return 'password' getpass.getpass = _getpass urllib.request._opener = None self.old_opener = urllib.request.build_opener self.conn = urllib.request.build_opener = FakeOpener() def tearDown(self): getpass.getpass = self._old_getpass urllib.request._opener = None urllib.request.build_opener = self.old_opener super(RegisterTestCase, self).tearDown() def _get_cmd(self, metadata=None): if metadata is None: metadata = {'url': 'xxx', 'author': 'xxx', 'author_email': 'xxx', 'name': 'xxx', 'version': 'xxx'} pkg_info, dist = self.create_dist(**metadata) return register(dist) def test_create_pypirc(self): # this test makes sure a .pypirc file # is created when requested. # let's create a register instance cmd = self._get_cmd() # we shouldn't have a .pypirc file yet self.assertFalse(os.path.exists(self.rc)) # patching input and getpass.getpass # so register gets happy # # Here's what we are faking : # use your existing login (choice 1.) # Username : 'tarek' # Password : 'password' # Save your login (y/N)? : 'y' inputs = Inputs('1', 'tarek', 'y') register_module.input = inputs.__call__ # let's run the command try: cmd.run() finally: del register_module.input # we should have a brand new .pypirc file self.assertTrue(os.path.exists(self.rc)) # with the content similar to WANTED_PYPIRC f = open(self.rc) try: content = f.read() self.assertEqual(content, WANTED_PYPIRC) finally: f.close() # now let's make sure the .pypirc file generated # really works : we shouldn't be asked anything # if we run the command again def _no_way(prompt=''): raise AssertionError(prompt) register_module.input = _no_way cmd.show_response = 1 cmd.run() # let's see what the server received : we should # have 2 similar requests self.assertEqual(len(self.conn.reqs), 2) req1 = dict(self.conn.reqs[0].headers) req2 = dict(self.conn.reqs[1].headers) self.assertEqual(req1['Content-length'], '1374') self.assertEqual(req2['Content-length'], '1374') self.assertIn(b'xxx', self.conn.reqs[1].data) def test_password_not_in_file(self): self.write_file(self.rc, PYPIRC_NOPASSWORD) cmd = self._get_cmd() cmd._set_config() cmd.finalize_options() cmd.send_metadata() # dist.password should be set # therefore used afterwards by other commands self.assertEqual(cmd.distribution.password, 'password') def test_registering(self): # this test runs choice 2 cmd = self._get_cmd() inputs = Inputs('2', 'tarek', 'tarek@ziade.org') register_module.input = inputs.__call__ try: # let's run the command cmd.run() finally: del register_module.input # we should have send a request self.assertEqual(len(self.conn.reqs), 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEqual(headers['Content-length'], '608') self.assertIn(b'tarek', req.data) def test_password_reset(self): # this test runs choice 3 cmd = self._get_cmd() inputs = Inputs('3', 'tarek@ziade.org') register_module.input = inputs.__call__ try: # let's run the command cmd.run() finally: del register_module.input # we should have send a request self.assertEqual(len(self.conn.reqs), 1) req = self.conn.reqs[0] headers = dict(req.headers) self.assertEqual(headers['Content-length'], '290') self.assertIn(b'tarek', req.data) @unittest.skipUnless(docutils is not None, 'needs docutils') def test_strict(self): # testing the script option # when on, the register command stops if # the metadata is incomplete or if # long_description is not reSt compliant # empty metadata cmd = self._get_cmd({}) cmd.ensure_finalized() cmd.strict = 1 self.assertRaises(DistutilsSetupError, cmd.run) # metadata are OK but long_description is broken metadata = {'url': 'xxx', 'author': 'xxx', 'author_email': 'éxéxé', 'name': 'xxx', 'version': 'xxx', 'long_description': 'title\n==\n\ntext'} cmd = self._get_cmd(metadata) cmd.ensure_finalized() cmd.strict = 1 self.assertRaises(DistutilsSetupError, cmd.run) # now something that works metadata['long_description'] = 'title\n=====\n\ntext' cmd = self._get_cmd(metadata) cmd.ensure_finalized() cmd.strict = 1 inputs = Inputs('1', 'tarek', 'y') register_module.input = inputs.__call__ # let's run the command try: cmd.run() finally: del register_module.input # strict is not by default cmd = self._get_cmd() cmd.ensure_finalized() inputs = Inputs('1', 'tarek', 'y') register_module.input = inputs.__call__ # let's run the command try: cmd.run() finally: del register_module.input # and finally a Unicode test (bug #12114) metadata = {'url': 'xxx', 'author': '\u00c9ric', 'author_email': 'xxx', 'name': 'xxx', 'version': 'xxx', 'description': 'Something about esszet \u00df', 'long_description': 'More things about esszet \u00df'} cmd = self._get_cmd(metadata) cmd.ensure_finalized() cmd.strict = 1 inputs = Inputs('1', 'tarek', 'y') register_module.input = inputs.__call__ # let's run the command try: cmd.run() finally: del register_module.input @unittest.skipUnless(docutils is not None, 'needs docutils') def test_register_invalid_long_description(self): description = ':funkie:`str`' # mimic Sphinx-specific markup metadata = {'url': 'xxx', 'author': 'xxx', 'author_email': 'xxx', 'name': 'xxx', 'version': 'xxx', 'long_description': description} cmd = self._get_cmd(metadata) cmd.ensure_finalized() cmd.strict = True inputs = Inputs('2', 'tarek', 'tarek@ziade.org') register_module.input = inputs self.addCleanup(delattr, register_module, 'input') self.assertRaises(DistutilsSetupError, cmd.run) def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated cmd = self._get_cmd() with check_warnings() as w: warnings.simplefilter("always") warnings.filterwarnings("ignore", ".*OptionParser class will be replaced.*") warnings.filterwarnings("ignore", ".*Option class will be removed.*") cmd.check_metadata() self.assertEqual(len(w.warnings), 1) def test_list_classifiers(self): cmd = self._get_cmd() cmd.list_classifiers = 1 cmd.run() results = self.get_logs(INFO) self.assertEqual(results, ['running check', 'xxx']) def test_show_response(self): # test that the --show-response option return a well formatted response cmd = self._get_cmd() inputs = Inputs('1', 'tarek', 'y') register_module.input = inputs.__call__ cmd.show_response = 1 try: cmd.run() finally: del register_module.input results = self.get_logs(INFO) self.assertEqual(results[3], 75 * '-' + '\nxxx\n' + 75 * '-') if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_sdist.py000066400000000000000000000411461471045437300244700ustar00rootroot00000000000000"""Tests for distutils.command.sdist.""" import os import tarfile import unittest import warnings import zipfile from os.path import join from textwrap import dedent from test.support import captured_stdout from test.support.warnings_helper import check_warnings try: import zlib ZLIB_SUPPORT = True except ImportError: ZLIB_SUPPORT = False try: import grp import pwd UID_GID_SUPPORT = True except ImportError: UID_GID_SUPPORT = False from distutils.command.sdist import sdist, show_formats from distutils.core import Distribution from distutils.tests.test_config import BasePyPIRCCommandTestCase from distutils.errors import DistutilsOptionError from distutils.spawn import find_executable from distutils.log import WARN from distutils.filelist import FileList from distutils.archive_util import ARCHIVE_FORMATS SETUP_PY = """ from distutils.core import setup import somecode setup(name='fake') """ MANIFEST = """\ # file GENERATED by distutils, do NOT edit README buildout.cfg inroot.txt setup.py data%(sep)sdata.dt scripts%(sep)sscript.py some%(sep)sfile.txt some%(sep)sother_file.txt somecode%(sep)s__init__.py somecode%(sep)sdoc.dat somecode%(sep)sdoc.txt """ class SDistTestCase(BasePyPIRCCommandTestCase): def setUp(self): # PyPIRCCommandTestCase creates a temp dir already # and put it in self.tmp_dir super(SDistTestCase, self).setUp() # setting up an environment self.old_path = os.getcwd() os.mkdir(join(self.tmp_dir, 'somecode')) os.mkdir(join(self.tmp_dir, 'dist')) # a package, and a README self.write_file((self.tmp_dir, 'README'), 'xxx') self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#') self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY) os.chdir(self.tmp_dir) def tearDown(self): # back to normal os.chdir(self.old_path) super(SDistTestCase, self).tearDown() def get_cmd(self, metadata=None): """Returns a cmd""" if metadata is None: metadata = {'name': 'fake', 'version': '1.0', 'url': 'xxx', 'author': 'xxx', 'author_email': 'xxx'} dist = Distribution(metadata) dist.script_name = 'setup.py' dist.packages = ['somecode'] dist.include_package_data = True cmd = sdist(dist) cmd.dist_dir = 'dist' return dist, cmd @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_prune_file_list(self): # this test creates a project with some VCS dirs and an NFS rename # file, then launches sdist to check they get pruned on all systems # creating VCS directories with some files in them os.mkdir(join(self.tmp_dir, 'somecode', '.svn')) self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx') os.mkdir(join(self.tmp_dir, 'somecode', '.hg')) self.write_file((self.tmp_dir, 'somecode', '.hg', 'ok'), 'xxx') os.mkdir(join(self.tmp_dir, 'somecode', '.git')) self.write_file((self.tmp_dir, 'somecode', '.git', 'ok'), 'xxx') self.write_file((self.tmp_dir, 'somecode', '.nfs0001'), 'xxx') # now building a sdist dist, cmd = self.get_cmd() # zip is available universally # (tar might not be installed under win32) cmd.formats = ['zip'] cmd.ensure_finalized() cmd.run() # now let's check what we have dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) self.assertEqual(files, ['fake-1.0.zip']) zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) try: content = zip_file.namelist() finally: zip_file.close() # making sure everything has been pruned correctly expected = ['', 'PKG-INFO', 'README', 'setup.py', 'somecode/', 'somecode/__init__.py'] self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') @unittest.skipIf(find_executable('tar') is None, "The tar command is not found") @unittest.skipIf(find_executable('gzip') is None, "The gzip command is not found") def test_make_distribution(self): # now building a sdist dist, cmd = self.get_cmd() # creating a gztar then a tar cmd.formats = ['gztar', 'tar'] cmd.ensure_finalized() cmd.run() # making sure we have two files dist_folder = join(self.tmp_dir, 'dist') result = os.listdir(dist_folder) result.sort() self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) os.remove(join(dist_folder, 'fake-1.0.tar')) os.remove(join(dist_folder, 'fake-1.0.tar.gz')) # now trying a tar then a gztar cmd.formats = ['tar', 'gztar'] cmd.ensure_finalized() cmd.run() result = os.listdir(dist_folder) result.sort() self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_add_defaults(self): # http://bugs.python.org/issue2279 # add_default should also include # data_files and package_data dist, cmd = self.get_cmd() # filling data_files by pointing files # in package_data dist.package_data = {'': ['*.cfg', '*.dat'], 'somecode': ['*.txt']} self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#') # adding some data in data_files data_dir = join(self.tmp_dir, 'data') os.mkdir(data_dir) self.write_file((data_dir, 'data.dt'), '#') some_dir = join(self.tmp_dir, 'some') os.mkdir(some_dir) # make sure VCS directories are pruned (#14004) hg_dir = join(self.tmp_dir, '.hg') os.mkdir(hg_dir) self.write_file((hg_dir, 'last-message.txt'), '#') # a buggy regex used to prevent this from working on windows (#6884) self.write_file((self.tmp_dir, 'buildout.cfg'), '#') self.write_file((self.tmp_dir, 'inroot.txt'), '#') self.write_file((some_dir, 'file.txt'), '#') self.write_file((some_dir, 'other_file.txt'), '#') dist.data_files = [('data', ['data/data.dt', 'buildout.cfg', 'inroot.txt', 'notexisting']), 'some/file.txt', 'some/other_file.txt'] # adding a script script_dir = join(self.tmp_dir, 'scripts') os.mkdir(script_dir) self.write_file((script_dir, 'script.py'), '#') dist.scripts = [join('scripts', 'script.py')] cmd.formats = ['zip'] cmd.use_defaults = True cmd.ensure_finalized() cmd.run() # now let's check what we have dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) self.assertEqual(files, ['fake-1.0.zip']) zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) try: content = zip_file.namelist() finally: zip_file.close() # making sure everything was added expected = ['', 'PKG-INFO', 'README', 'buildout.cfg', 'data/', 'data/data.dt', 'inroot.txt', 'scripts/', 'scripts/script.py', 'setup.py', 'some/', 'some/file.txt', 'some/other_file.txt', 'somecode/', 'somecode/__init__.py', 'somecode/doc.dat', 'somecode/doc.txt'] self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected]) # checking the MANIFEST f = open(join(self.tmp_dir, 'MANIFEST')) try: manifest = f.read() finally: f.close() self.assertEqual(manifest, MANIFEST % {'sep': os.sep}) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_metadata_check_option(self): # testing the `medata-check` option dist, cmd = self.get_cmd(metadata={}) # this should raise some warnings ! # with the `check` subcommand cmd.ensure_finalized() cmd.run() warnings = [msg for msg in self.get_logs(WARN) if msg.startswith('warning: check:')] self.assertEqual(len(warnings), 2) # trying with a complete set of metadata self.clear_logs() dist, cmd = self.get_cmd() cmd.ensure_finalized() cmd.metadata_check = 0 cmd.run() warnings = [msg for msg in self.get_logs(WARN) if msg.startswith('warning: check:')] self.assertEqual(len(warnings), 0) def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated dist, cmd = self.get_cmd() with check_warnings() as w: warnings.simplefilter("always") cmd.check_metadata() self.assertEqual(len(w.warnings), 1) def test_show_formats(self): with captured_stdout() as stdout: show_formats() # the output should be a header line + one line per format num_formats = len(ARCHIVE_FORMATS.keys()) output = [line for line in stdout.getvalue().split('\n') if line.strip().startswith('--formats=')] self.assertEqual(len(output), num_formats) def test_finalize_options(self): dist, cmd = self.get_cmd() cmd.finalize_options() # default options set by finalize self.assertEqual(cmd.manifest, 'MANIFEST') self.assertEqual(cmd.template, 'MANIFEST.in') self.assertEqual(cmd.dist_dir, 'dist') # formats has to be a string splitable on (' ', ',') or # a stringlist cmd.formats = 1 self.assertRaises(DistutilsOptionError, cmd.finalize_options) cmd.formats = ['zip'] cmd.finalize_options() # formats has to be known cmd.formats = 'supazipa' self.assertRaises(DistutilsOptionError, cmd.finalize_options) # the following tests make sure there is a nice error message instead # of a traceback when parsing an invalid manifest template def _check_template(self, content): dist, cmd = self.get_cmd() os.chdir(self.tmp_dir) self.write_file('MANIFEST.in', content) cmd.ensure_finalized() cmd.filelist = FileList() cmd.read_template() warnings = self.get_logs(WARN) self.assertEqual(len(warnings), 1) def test_invalid_template_unknown_command(self): self._check_template('taunt knights *') def test_invalid_template_wrong_arguments(self): # this manifest command takes one argument self._check_template('prune') @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only') def test_invalid_template_wrong_path(self): # on Windows, trailing slashes are not allowed # this used to crash instead of raising a warning: #8286 self._check_template('include examples/') @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_get_file_list(self): # make sure MANIFEST is recalculated dist, cmd = self.get_cmd() # filling data_files by pointing files in package_data dist.package_data = {'somecode': ['*.txt']} self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') cmd.formats = ['gztar'] cmd.ensure_finalized() cmd.run() f = open(cmd.manifest) try: manifest = [line.strip() for line in f.read().split('\n') if line.strip() != ''] finally: f.close() self.assertEqual(len(manifest), 5) # adding a file self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') # make sure build_py is reinitialized, like a fresh run build_py = dist.get_command_obj('build_py') build_py.finalized = False build_py.ensure_finalized() cmd.run() f = open(cmd.manifest) try: manifest2 = [line.strip() for line in f.read().split('\n') if line.strip() != ''] finally: f.close() # do we have the new file in MANIFEST ? self.assertEqual(len(manifest2), 6) self.assertIn('doc2.txt', manifest2[-1]) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_manifest_marker(self): # check that autogenerated MANIFESTs have a marker dist, cmd = self.get_cmd() cmd.ensure_finalized() cmd.run() f = open(cmd.manifest) try: manifest = [line.strip() for line in f.read().split('\n') if line.strip() != ''] finally: f.close() self.assertEqual(manifest[0], '# file GENERATED by distutils, do NOT edit') @unittest.skipUnless(ZLIB_SUPPORT, "Need zlib support to run") def test_manifest_comments(self): # make sure comments don't cause exceptions or wrong includes contents = dedent("""\ # bad.py #bad.py good.py """) dist, cmd = self.get_cmd() cmd.ensure_finalized() self.write_file((self.tmp_dir, cmd.manifest), contents) self.write_file((self.tmp_dir, 'good.py'), '# pick me!') self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!") self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!") cmd.run() self.assertEqual(cmd.filelist.files, ['good.py']) @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_manual_manifest(self): # check that a MANIFEST without a marker is left alone dist, cmd = self.get_cmd() cmd.formats = ['gztar'] cmd.ensure_finalized() self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') self.write_file((self.tmp_dir, 'README.manual'), 'This project maintains its MANIFEST file itself.') cmd.run() self.assertEqual(cmd.filelist.files, ['README.manual']) f = open(cmd.manifest) try: manifest = [line.strip() for line in f.read().split('\n') if line.strip() != ''] finally: f.close() self.assertEqual(manifest, ['README.manual']) archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') archive = tarfile.open(archive_name) try: filenames = [tarinfo.name for tarinfo in archive] finally: archive.close() self.assertEqual(sorted(filenames), ['fake-1.0', 'fake-1.0/PKG-INFO', 'fake-1.0/README.manual']) @unittest.skipUnless(ZLIB_SUPPORT, "requires zlib") @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") @unittest.skipIf(find_executable('tar') is None, "The tar command is not found") @unittest.skipIf(find_executable('gzip') is None, "The gzip command is not found") def test_make_distribution_owner_group(self): # now building a sdist dist, cmd = self.get_cmd() # creating a gztar and specifying the owner+group cmd.formats = ['gztar'] cmd.owner = pwd.getpwuid(0)[0] cmd.group = grp.getgrgid(0)[0] cmd.ensure_finalized() cmd.run() # making sure we have the good rights archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') archive = tarfile.open(archive_name) try: for member in archive.getmembers(): self.assertEqual(member.uid, 0) self.assertEqual(member.gid, 0) finally: archive.close() # building a sdist again dist, cmd = self.get_cmd() # creating a gztar cmd.formats = ['gztar'] cmd.ensure_finalized() cmd.run() # making sure we have the good rights archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz') archive = tarfile.open(archive_name) # note that we are not testing the group ownership here # because, depending on the platforms and the container # rights (see #7408) try: for member in archive.getmembers(): self.assertEqual(member.uid, os.getuid()) finally: archive.close() if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_spawn.py000066400000000000000000000124121471045437300244640ustar00rootroot00000000000000"""Tests for distutils.spawn.""" import os import stat import sys import unittest.mock from test.support import unix_shell, requires_subprocess from test.support import os_helper from distutils.spawn import find_executable from distutils.spawn import spawn from distutils.errors import DistutilsExecError from distutils.tests import support @requires_subprocess() class SpawnTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @unittest.skipUnless(os.name in ('nt', 'posix'), 'Runs only under posix or nt') def test_spawn(self): tmpdir = self.mkdtemp() # creating something executable # through the shell that returns 1 if sys.platform != 'win32': exe = os.path.join(tmpdir, 'foo.sh') self.write_file(exe, '#!%s\nexit 1' % unix_shell) else: exe = os.path.join(tmpdir, 'foo.bat') self.write_file(exe, 'exit 1') os.chmod(exe, 0o777) self.assertRaises(DistutilsExecError, spawn, [exe]) # now something that works if sys.platform != 'win32': exe = os.path.join(tmpdir, 'foo.sh') self.write_file(exe, '#!%s\nexit 0' % unix_shell) else: exe = os.path.join(tmpdir, 'foo.bat') self.write_file(exe, 'exit 0') os.chmod(exe, 0o777) spawn([exe]) # should work without any error def test_find_executable(self): with os_helper.temp_dir() as tmp_dir: # use TESTFN to get a pseudo-unique filename program_noeext = os_helper.TESTFN # Give the temporary program an ".exe" suffix for all. # It's needed on Windows and not harmful on other platforms. program = program_noeext + ".exe" filename = os.path.join(tmp_dir, program) with open(filename, "wb"): pass os.chmod(filename, stat.S_IXUSR) # test path parameter rv = find_executable(program, path=tmp_dir) self.assertEqual(rv, filename) if sys.platform == 'win32': # test without ".exe" extension rv = find_executable(program_noeext, path=tmp_dir) self.assertEqual(rv, filename) # test find in the current directory with os_helper.change_cwd(tmp_dir): rv = find_executable(program) self.assertEqual(rv, program) # test non-existent program dont_exist_program = "dontexist_" + program rv = find_executable(dont_exist_program , path=tmp_dir) self.assertIsNone(rv) # PATH='': no match, except in the current directory with os_helper.EnvironmentVarGuard() as env: env['PATH'] = '' with unittest.mock.patch('distutils.spawn.os.confstr', return_value=tmp_dir, create=True), \ unittest.mock.patch('distutils.spawn.os.defpath', tmp_dir): rv = find_executable(program) self.assertIsNone(rv) # look in current directory with os_helper.change_cwd(tmp_dir): rv = find_executable(program) self.assertEqual(rv, program) # PATH=':': explicitly looks in the current directory with os_helper.EnvironmentVarGuard() as env: env['PATH'] = os.pathsep with unittest.mock.patch('distutils.spawn.os.confstr', return_value='', create=True), \ unittest.mock.patch('distutils.spawn.os.defpath', ''): rv = find_executable(program) self.assertIsNone(rv) # look in current directory with os_helper.change_cwd(tmp_dir): rv = find_executable(program) self.assertEqual(rv, program) # missing PATH: test os.confstr("CS_PATH") and os.defpath with os_helper.EnvironmentVarGuard() as env: env.pop('PATH', None) # without confstr with unittest.mock.patch('distutils.spawn.os.confstr', side_effect=ValueError, create=True), \ unittest.mock.patch('distutils.spawn.os.defpath', tmp_dir): rv = find_executable(program) self.assertEqual(rv, filename) # with confstr with unittest.mock.patch('distutils.spawn.os.confstr', return_value=tmp_dir, create=True), \ unittest.mock.patch('distutils.spawn.os.defpath', ''): rv = find_executable(program) self.assertEqual(rv, filename) def test_spawn_missing_exe(self): with self.assertRaises(DistutilsExecError) as ctx: spawn(['does-not-exist']) self.assertIn("command 'does-not-exist' failed", str(ctx.exception)) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_sysconfig.py000066400000000000000000000244731471045437300253520ustar00rootroot00000000000000"""Tests for distutils.sysconfig.""" import contextlib import os import shutil import subprocess import sys import textwrap import unittest from distutils import sysconfig from distutils.ccompiler import get_default_compiler from distutils.tests import support from test.support import swap_item, requires_subprocess, is_wasi from test.support.os_helper import TESTFN from test.support.warnings_helper import check_warnings class SysconfigTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): super(SysconfigTestCase, self).setUp() self.makefile = None def tearDown(self): if self.makefile is not None: os.unlink(self.makefile) self.cleanup_testfn() super(SysconfigTestCase, self).tearDown() def cleanup_testfn(self): if os.path.isfile(TESTFN): os.remove(TESTFN) elif os.path.isdir(TESTFN): shutil.rmtree(TESTFN) @unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds") def test_get_config_h_filename(self): config_h = sysconfig.get_config_h_filename() self.assertTrue(os.path.isfile(config_h), config_h) def test_get_python_lib(self): # XXX doesn't work on Linux when Python was never installed before #self.assertTrue(os.path.isdir(lib_dir), lib_dir) # test for pythonxx.lib? self.assertNotEqual(sysconfig.get_python_lib(), sysconfig.get_python_lib(prefix=TESTFN)) def test_get_config_vars(self): cvars = sysconfig.get_config_vars() self.assertIsInstance(cvars, dict) self.assertTrue(cvars) @unittest.skipIf(is_wasi, "Incompatible with WASI mapdir and OOT builds") def test_srcdir(self): # See Issues #15322, #15364. srcdir = sysconfig.get_config_var('srcdir') self.assertTrue(os.path.isabs(srcdir), srcdir) self.assertTrue(os.path.isdir(srcdir), srcdir) if sysconfig.python_build: # The python executable has not been installed so srcdir # should be a full source checkout. Python_h = os.path.join(srcdir, 'Include', 'Python.h') self.assertTrue(os.path.exists(Python_h), Python_h) # /PC/pyconfig.h always exists even if unused on POSIX. pyconfig_h = os.path.join(srcdir, 'PC', 'pyconfig.h') self.assertTrue(os.path.exists(pyconfig_h), pyconfig_h) pyconfig_h_in = os.path.join(srcdir, 'pyconfig.h.in') self.assertTrue(os.path.exists(pyconfig_h_in), pyconfig_h_in) elif os.name == 'posix': self.assertEqual( os.path.dirname(sysconfig.get_makefile_filename()), srcdir) def test_srcdir_independent_of_cwd(self): # srcdir should be independent of the current working directory # See Issues #15322, #15364. srcdir = sysconfig.get_config_var('srcdir') cwd = os.getcwd() try: os.chdir('..') srcdir2 = sysconfig.get_config_var('srcdir') finally: os.chdir(cwd) self.assertEqual(srcdir, srcdir2) def customize_compiler(self): # make sure AR gets caught class compiler: compiler_type = 'unix' def set_executables(self, **kw): self.exes = kw sysconfig_vars = { 'AR': 'sc_ar', 'CC': 'sc_cc', 'CXX': 'sc_cxx', 'ARFLAGS': '--sc-arflags', 'CFLAGS': '--sc-cflags', 'CCSHARED': '--sc-ccshared', 'LDSHARED': 'sc_ldshared', 'SHLIB_SUFFIX': 'sc_shutil_suffix', # On macOS, disable _osx_support.customize_compiler() 'CUSTOMIZED_OSX_COMPILER': 'True', } comp = compiler() with contextlib.ExitStack() as cm: for key, value in sysconfig_vars.items(): cm.enter_context(swap_item(sysconfig._config_vars, key, value)) sysconfig.customize_compiler(comp) return comp @unittest.skipUnless(get_default_compiler() == 'unix', 'not testing if default compiler is not unix') def test_customize_compiler(self): # Make sure that sysconfig._config_vars is initialized sysconfig.get_config_vars() os.environ['AR'] = 'env_ar' os.environ['CC'] = 'env_cc' os.environ['CPP'] = 'env_cpp' os.environ['CXX'] = 'env_cxx --env-cxx-flags' os.environ['LDSHARED'] = 'env_ldshared' os.environ['LDFLAGS'] = '--env-ldflags' os.environ['ARFLAGS'] = '--env-arflags' os.environ['CFLAGS'] = '--env-cflags' os.environ['CPPFLAGS'] = '--env-cppflags' comp = self.customize_compiler() self.assertEqual(comp.exes['archiver'], 'env_ar --env-arflags') self.assertEqual(comp.exes['preprocessor'], 'env_cpp --env-cppflags') self.assertEqual(comp.exes['compiler'], 'env_cc --sc-cflags --env-cflags --env-cppflags') self.assertEqual(comp.exes['compiler_so'], ('env_cc --sc-cflags ' '--env-cflags ''--env-cppflags --sc-ccshared')) self.assertEqual(comp.exes['compiler_cxx'], 'env_cxx --env-cxx-flags') self.assertEqual(comp.exes['linker_exe'], 'env_cc') self.assertEqual(comp.exes['linker_so'], ('env_ldshared --env-ldflags --env-cflags' ' --env-cppflags')) self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') del os.environ['AR'] del os.environ['CC'] del os.environ['CPP'] del os.environ['CXX'] del os.environ['LDSHARED'] del os.environ['LDFLAGS'] del os.environ['ARFLAGS'] del os.environ['CFLAGS'] del os.environ['CPPFLAGS'] comp = self.customize_compiler() self.assertEqual(comp.exes['archiver'], 'sc_ar --sc-arflags') self.assertEqual(comp.exes['preprocessor'], 'sc_cc -E') self.assertEqual(comp.exes['compiler'], 'sc_cc --sc-cflags') self.assertEqual(comp.exes['compiler_so'], 'sc_cc --sc-cflags --sc-ccshared') self.assertEqual(comp.exes['compiler_cxx'], 'sc_cxx') self.assertEqual(comp.exes['linker_exe'], 'sc_cc') self.assertEqual(comp.exes['linker_so'], 'sc_ldshared') self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix') def test_parse_makefile_base(self): self.makefile = TESTFN fd = open(self.makefile, 'w') try: fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=LIB'" '\n') fd.write('VAR=$OTHER\nOTHER=foo') finally: fd.close() d = sysconfig.parse_makefile(self.makefile) self.assertEqual(d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", 'OTHER': 'foo'}) def test_parse_makefile_literal_dollar(self): self.makefile = TESTFN fd = open(self.makefile, 'w') try: fd.write(r"CONFIG_ARGS= '--arg1=optarg1' 'ENV=\$$LIB'" '\n') fd.write('VAR=$OTHER\nOTHER=foo') finally: fd.close() d = sysconfig.parse_makefile(self.makefile) self.assertEqual(d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", 'OTHER': 'foo'}) def test_sysconfig_module(self): import sysconfig as global_sysconfig self.assertEqual(global_sysconfig.get_config_var('CFLAGS'), sysconfig.get_config_var('CFLAGS')) self.assertEqual(global_sysconfig.get_config_var('LDFLAGS'), sysconfig.get_config_var('LDFLAGS')) @unittest.skipIf(sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'), 'compiler flags customized') def test_sysconfig_compiler_vars(self): # On OS X, binary installers support extension module building on # various levels of the operating system with differing Xcode # configurations. This requires customization of some of the # compiler configuration directives to suit the environment on # the installed machine. Some of these customizations may require # running external programs and, so, are deferred until needed by # the first extension module build. With Python 3.3, only # the Distutils version of sysconfig is used for extension module # builds, which happens earlier in the Distutils tests. This may # cause the following tests to fail since no tests have caused # the global version of sysconfig to call the customization yet. # The solution for now is to simply skip this test in this case. # The longer-term solution is to only have one version of sysconfig. import sysconfig as global_sysconfig if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'): self.skipTest('compiler flags customized') self.assertEqual(global_sysconfig.get_config_var('LDSHARED'), sysconfig.get_config_var('LDSHARED')) self.assertEqual(global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC')) @requires_subprocess() def test_customize_compiler_before_get_config_vars(self): # Issue #21923: test that a Distribution compiler # instance can be called without an explicit call to # get_config_vars(). with open(TESTFN, 'w') as f: f.writelines(textwrap.dedent('''\ from distutils.core import Distribution config = Distribution().get_command_obj('config') # try_compile may pass or it may fail if no compiler # is found but it should not raise an exception. rc = config.try_compile('int x;') ''')) p = subprocess.Popen([str(sys.executable), TESTFN], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True) outs, errs = p.communicate() self.assertEqual(0, p.returncode, "Subprocess failed: " + outs) if __name__ == '__main__': unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_text_file.py000066400000000000000000000063701471045437300253250ustar00rootroot00000000000000"""Tests for distutils.text_file.""" import os import unittest from distutils.text_file import TextFile from distutils.tests import support TEST_DATA = """# test file line 3 \\ # intervening comment continues on next line """ class TextFileTestCase(support.TempdirManager, unittest.TestCase): def test_class(self): # old tests moved from text_file.__main__ # so they are really called by the buildbots # result 1: no fancy options result1 = ['# test file\n', '\n', 'line 3 \\\n', '# intervening comment\n', ' continues on next line\n'] # result 2: just strip comments result2 = ["\n", "line 3 \\\n", " continues on next line\n"] # result 3: just strip blank lines result3 = ["# test file\n", "line 3 \\\n", "# intervening comment\n", " continues on next line\n"] # result 4: default, strip comments, blank lines, # and trailing whitespace result4 = ["line 3 \\", " continues on next line"] # result 5: strip comments and blanks, plus join lines (but don't # "collapse" joined lines result5 = ["line 3 continues on next line"] # result 6: strip comments and blanks, plus join lines (and # "collapse" joined lines result6 = ["line 3 continues on next line"] def test_input(count, description, file, expected_result): result = file.readlines() self.assertEqual(result, expected_result) tmpdir = self.mkdtemp() filename = os.path.join(tmpdir, "test.txt") out_file = open(filename, "w") try: out_file.write(TEST_DATA) finally: out_file.close() in_file = TextFile(filename, strip_comments=0, skip_blanks=0, lstrip_ws=0, rstrip_ws=0) try: test_input(1, "no processing", in_file, result1) finally: in_file.close() in_file = TextFile(filename, strip_comments=1, skip_blanks=0, lstrip_ws=0, rstrip_ws=0) try: test_input(2, "strip comments", in_file, result2) finally: in_file.close() in_file = TextFile(filename, strip_comments=0, skip_blanks=1, lstrip_ws=0, rstrip_ws=0) try: test_input(3, "strip blanks", in_file, result3) finally: in_file.close() in_file = TextFile(filename) try: test_input(4, "default processing", in_file, result4) finally: in_file.close() in_file = TextFile(filename, strip_comments=1, skip_blanks=1, join_lines=1, rstrip_ws=1) try: test_input(5, "join lines without collapsing", in_file, result5) finally: in_file.close() in_file = TextFile(filename, strip_comments=1, skip_blanks=1, join_lines=1, rstrip_ws=1, collapse_join=1) try: test_input(6, "join lines with collapsing", in_file, result6) finally: in_file.close() if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_unixccompiler.py000066400000000000000000000111441471045437300262160ustar00rootroot00000000000000"""Tests for distutils.unixccompiler.""" import sys import unittest from test.support.os_helper import EnvironmentVarGuard from distutils import sysconfig from distutils.unixccompiler import UnixCCompiler class UnixCCompilerTestCase(unittest.TestCase): def setUp(self): self._backup_platform = sys.platform self._backup_get_config_var = sysconfig.get_config_var self._backup_config_vars = dict(sysconfig._config_vars) class CompilerWrapper(UnixCCompiler): def rpath_foo(self): return self.runtime_library_dir_option('/foo') self.cc = CompilerWrapper() def tearDown(self): sys.platform = self._backup_platform sysconfig.get_config_var = self._backup_get_config_var sysconfig._config_vars.clear() sysconfig._config_vars.update(self._backup_config_vars) @unittest.skipIf(sys.platform == 'win32', "can't test on Windows") def test_runtime_libdir_option(self): # Issue#5900 # # Ensure RUNPATH is added to extension modules with RPATH if # GNU ld is used # darwin sys.platform = 'darwin' self.assertEqual(self.cc.rpath_foo(), '-L/foo') # hp-ux sys.platform = 'hp-ux' old_gcv = sysconfig.get_config_var def gcv(v): return 'xxx' sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo']) def gcv(v): return 'gcc' sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) def gcv(v): return 'g++' sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo']) sysconfig.get_config_var = old_gcv # GCC GNULD sys.platform = 'bar' def gcv(v): if v == 'CC': return 'gcc' elif v == 'GNULD': return 'yes' sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') # GCC non-GNULD sys.platform = 'bar' def gcv(v): if v == 'CC': return 'gcc' elif v == 'GNULD': return 'no' sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo') # GCC GNULD with fully qualified configuration prefix # see #7617 sys.platform = 'bar' def gcv(v): if v == 'CC': return 'x86_64-pc-linux-gnu-gcc-4.4.2' elif v == 'GNULD': return 'yes' sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo') # non-GCC GNULD sys.platform = 'bar' def gcv(v): if v == 'CC': return 'cc' elif v == 'GNULD': return 'yes' sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-R/foo') # non-GCC non-GNULD sys.platform = 'bar' def gcv(v): if v == 'CC': return 'cc' elif v == 'GNULD': return 'no' sysconfig.get_config_var = gcv self.assertEqual(self.cc.rpath_foo(), '-R/foo') @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') def test_osx_cc_overrides_ldshared(self): # Issue #18080: # ensure that setting CC env variable also changes default linker def gcv(v): if v == 'LDSHARED': return 'gcc-4.2 -bundle -undefined dynamic_lookup ' return 'gcc-4.2' sysconfig.get_config_var = gcv with EnvironmentVarGuard() as env: env['CC'] = 'my_cc' del env['LDSHARED'] sysconfig.customize_compiler(self.cc) self.assertEqual(self.cc.linker_so[0], 'my_cc') @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for OS X') def test_osx_explicit_ldshared(self): # Issue #18080: # ensure that setting CC env variable does not change # explicit LDSHARED setting for linker def gcv(v): if v == 'LDSHARED': return 'gcc-4.2 -bundle -undefined dynamic_lookup ' return 'gcc-4.2' sysconfig.get_config_var = gcv with EnvironmentVarGuard() as env: env['CC'] = 'my_cc' env['LDSHARED'] = 'my_ld -bundle -dynamic' sysconfig.customize_compiler(self.cc) self.assertEqual(self.cc.linker_so[0], 'my_ld') if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_upload.py000066400000000000000000000155571471045437300246350ustar00rootroot00000000000000"""Tests for distutils.command.upload.""" import os import unittest import unittest.mock as mock from urllib.error import HTTPError from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution from distutils.errors import DistutilsError from distutils.log import ERROR, INFO from distutils.tests.test_config import PYPIRC, BasePyPIRCCommandTestCase PYPIRC_LONG_PASSWORD = """\ [distutils] index-servers = server1 server2 [server1] username:me password:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa [server2] username:meagain password: secret realm:acme repository:http://another.pypi/ """ PYPIRC_NOPASSWORD = """\ [distutils] index-servers = server1 [server1] username:me """ class FakeOpen(object): def __init__(self, url, msg=None, code=None): self.url = url if not isinstance(url, str): self.req = url else: self.req = None self.msg = msg or 'OK' self.code = code or 200 def getheader(self, name, default=None): return { 'content-type': 'text/plain; charset=utf-8', }.get(name.lower(), default) def read(self): return b'xyzzy' def getcode(self): return self.code class uploadTestCase(BasePyPIRCCommandTestCase): def setUp(self): super(uploadTestCase, self).setUp() self.old_open = upload_mod.urlopen upload_mod.urlopen = self._urlopen self.last_open = None self.next_msg = None self.next_code = None def tearDown(self): upload_mod.urlopen = self.old_open super(uploadTestCase, self).tearDown() def _urlopen(self, url): self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code) return self.last_open def test_finalize_options(self): # new format self.write_file(self.rc, PYPIRC) dist = Distribution() cmd = upload(dist) cmd.finalize_options() for attr, waited in (('username', 'me'), ('password', 'secret'), ('realm', 'pypi'), ('repository', 'https://upload.pypi.org/legacy/')): self.assertEqual(getattr(cmd, attr), waited) def test_saved_password(self): # file with no password self.write_file(self.rc, PYPIRC_NOPASSWORD) # make sure it passes dist = Distribution() cmd = upload(dist) cmd.finalize_options() self.assertEqual(cmd.password, None) # make sure we get it as well, if another command # initialized it at the dist level dist.password = 'xxx' cmd = upload(dist) cmd.finalize_options() self.assertEqual(cmd.password, 'xxx') def test_upload(self): tmp = self.mkdtemp() path = os.path.join(tmp, 'xxx') self.write_file(path) command, pyversion, filename = 'xxx', '2.6', path dist_files = [(command, pyversion, filename)] self.write_file(self.rc, PYPIRC_LONG_PASSWORD) # lets run it pkg_dir, dist = self.create_dist(dist_files=dist_files) cmd = upload(dist) cmd.show_response = 1 cmd.ensure_finalized() cmd.run() # what did we send ? headers = dict(self.last_open.req.headers) self.assertGreaterEqual(int(headers['Content-length']), 2162) content_type = headers['Content-type'] self.assertTrue(content_type.startswith('multipart/form-data')) self.assertEqual(self.last_open.req.get_method(), 'POST') expected_url = 'https://upload.pypi.org/legacy/' self.assertEqual(self.last_open.req.get_full_url(), expected_url) data = self.last_open.req.data self.assertIn(b'xxx',data) self.assertIn(b'protocol_version', data) self.assertIn(b'sha256_digest', data) self.assertIn( b'cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf' b'6860', data ) if b'md5_digest' in data: self.assertIn(b'f561aaf6ef0bf14d4208bb46a4ccb3ad', data) if b'blake2_256_digest' in data: self.assertIn( b'b6f289a27d4fe90da63c503bfe0a9b761a8f76bb86148565065f040be' b'6d1c3044cf7ded78ef800509bccb4b648e507d88dc6383d67642aadcc' b'ce443f1534330a', data ) # The PyPI response body was echoed results = self.get_logs(INFO) self.assertEqual(results[-1], 75 * '-' + '\nxyzzy\n' + 75 * '-') # bpo-32304: archives whose last byte was b'\r' were corrupted due to # normalization intended for Mac OS 9. def test_upload_correct_cr(self): # content that ends with \r should not be modified. tmp = self.mkdtemp() path = os.path.join(tmp, 'xxx') self.write_file(path, content='yy\r') command, pyversion, filename = 'xxx', '2.6', path dist_files = [(command, pyversion, filename)] self.write_file(self.rc, PYPIRC_LONG_PASSWORD) # other fields that ended with \r used to be modified, now are # preserved. pkg_dir, dist = self.create_dist( dist_files=dist_files, description='long description\r' ) cmd = upload(dist) cmd.show_response = 1 cmd.ensure_finalized() cmd.run() headers = dict(self.last_open.req.headers) self.assertGreaterEqual(int(headers['Content-length']), 2172) self.assertIn(b'long description\r', self.last_open.req.data) def test_upload_fails(self): self.next_msg = "Not Found" self.next_code = 404 self.assertRaises(DistutilsError, self.test_upload) def test_wrong_exception_order(self): tmp = self.mkdtemp() path = os.path.join(tmp, 'xxx') self.write_file(path) dist_files = [('xxx', '2.6', path)] # command, pyversion, filename self.write_file(self.rc, PYPIRC_LONG_PASSWORD) pkg_dir, dist = self.create_dist(dist_files=dist_files) tests = [ (OSError('oserror'), 'oserror', OSError), (HTTPError('url', 400, 'httperror', {}, None), 'Upload failed (400): httperror', DistutilsError), ] for exception, expected, raised_exception in tests: with self.subTest(exception=type(exception).__name__): with mock.patch('distutils.command.upload.urlopen', new=mock.Mock(side_effect=exception)): with self.assertRaises(raised_exception): cmd = upload(dist) cmd.ensure_finalized() cmd.run() results = self.get_logs(ERROR) self.assertIn(expected, results[-1]) self.clear_logs() if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_util.py000066400000000000000000000265311471045437300243200ustar00rootroot00000000000000"""Tests for distutils.util.""" import os import sys import unittest from copy import copy from unittest import mock from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError from distutils.util import (get_platform, convert_path, change_root, check_environ, split_quoted, strtobool, rfc822_escape, byte_compile, grok_environment_error) from distutils import util # used to patch _environ_checked from distutils.sysconfig import get_config_vars from distutils import sysconfig from distutils.tests import support import _osx_support class UtilTestCase(support.EnvironGuard, unittest.TestCase): def setUp(self): super(UtilTestCase, self).setUp() # saving the environment self.name = os.name self.platform = sys.platform self.version = sys.version self.sep = os.sep self.join = os.path.join self.isabs = os.path.isabs self.splitdrive = os.path.splitdrive self._config_vars = copy(sysconfig._config_vars) # patching os.uname if hasattr(os, 'uname'): self.uname = os.uname self._uname = os.uname() else: self.uname = None self._uname = None os.uname = self._get_uname def tearDown(self): # getting back the environment os.name = self.name sys.platform = self.platform sys.version = self.version os.sep = self.sep os.path.join = self.join os.path.isabs = self.isabs os.path.splitdrive = self.splitdrive if self.uname is not None: os.uname = self.uname else: del os.uname sysconfig._config_vars.clear() sysconfig._config_vars.update(self._config_vars) super(UtilTestCase, self).tearDown() def _set_uname(self, uname): self._uname = uname def _get_uname(self): return self._uname def test_get_platform(self): # windows XP, 32bits os.name = 'nt' sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Intel)]') sys.platform = 'win32' self.assertEqual(get_platform(), 'win32') # windows XP, amd64 os.name = 'nt' sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Amd64)]') sys.platform = 'win32' self.assertEqual(get_platform(), 'win-amd64') # macbook os.name = 'posix' sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) ' '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]') sys.platform = 'darwin' self._set_uname(('Darwin', 'macziade', '8.11.1', ('Darwin Kernel Version 8.11.1: ' 'Wed Oct 10 18:23:28 PDT 2007; ' 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386')) _osx_support._remove_original_values(get_config_vars()) get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3' get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g ' '-fwrapv -O3 -Wall -Wstrict-prototypes') cursize = sys.maxsize sys.maxsize = (2 ** 31)-1 try: self.assertEqual(get_platform(), 'macosx-10.3-i386') finally: sys.maxsize = cursize # macbook with fat binaries (fat, universal or fat64) _osx_support._remove_original_values(get_config_vars()) get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4' get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEqual(get_platform(), 'macosx-10.4-fat') _osx_support._remove_original_values(get_config_vars()) os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.1' self.assertEqual(get_platform(), 'macosx-10.4-fat') _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEqual(get_platform(), 'macosx-10.4-intel') _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEqual(get_platform(), 'macosx-10.4-fat3') _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEqual(get_platform(), 'macosx-10.4-universal') _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') self.assertEqual(get_platform(), 'macosx-10.4-fat64') for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): _osx_support._remove_original_values(get_config_vars()) get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3'%(arch,)) self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,)) # linux debian sarge os.name = 'posix' sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) ' '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]') sys.platform = 'linux2' self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) self.assertEqual(get_platform(), 'linux-i686') # XXX more platforms to tests here def test_convert_path(self): # linux/mac os.sep = '/' def _join(path): return '/'.join(path) os.path.join = _join self.assertEqual(convert_path('/home/to/my/stuff'), '/home/to/my/stuff') # win os.sep = '\\' def _join(*path): return '\\'.join(path) os.path.join = _join self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') self.assertEqual(convert_path('home/to/my/stuff'), 'home\\to\\my\\stuff') self.assertEqual(convert_path('.'), os.curdir) def test_change_root(self): # linux/mac os.name = 'posix' def _isabs(path): return path[0] == '/' os.path.isabs = _isabs def _join(*path): return '/'.join(path) os.path.join = _join self.assertEqual(change_root('/root', '/old/its/here'), '/root/old/its/here') self.assertEqual(change_root('/root', 'its/here'), '/root/its/here') # windows os.name = 'nt' def _isabs(path): return path.startswith('c:\\') os.path.isabs = _isabs def _splitdrive(path): if path.startswith('c:'): return ('', path.replace('c:', '')) return ('', path) os.path.splitdrive = _splitdrive def _join(*path): return '\\'.join(path) os.path.join = _join self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'), 'c:\\root\\old\\its\\here') self.assertEqual(change_root('c:\\root', 'its\\here'), 'c:\\root\\its\\here') # BugsBunny os (it's a great os) os.name = 'BugsBunny' self.assertRaises(DistutilsPlatformError, change_root, 'c:\\root', 'its\\here') # XXX platforms to be covered: mac def test_check_environ(self): util._environ_checked = 0 os.environ.pop('HOME', None) check_environ() self.assertEqual(os.environ['PLAT'], get_platform()) self.assertEqual(util._environ_checked, 1) @unittest.skipUnless(os.name == 'posix', 'specific to posix') def test_check_environ_getpwuid(self): util._environ_checked = 0 os.environ.pop('HOME', None) try: import pwd except ImportError: raise unittest.SkipTest("Test requires pwd module.") # only set pw_dir field, other fields are not used result = pwd.struct_passwd((None, None, None, None, None, '/home/distutils', None)) with mock.patch.object(pwd, 'getpwuid', return_value=result): check_environ() self.assertEqual(os.environ['HOME'], '/home/distutils') util._environ_checked = 0 os.environ.pop('HOME', None) # bpo-10496: Catch pwd.getpwuid() error with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError): check_environ() self.assertNotIn('HOME', os.environ) def test_split_quoted(self): self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), ['one', 'two', 'three', 'four']) def test_strtobool(self): yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1') no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N') for y in yes: self.assertTrue(strtobool(y)) for n in no: self.assertFalse(strtobool(n)) def test_rfc822_escape(self): header = 'I am a\npoor\nlonesome\nheader\n' res = rfc822_escape(header) wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' 'header%(8s)s') % {'8s': '\n'+8*' '} self.assertEqual(res, wanted) def test_dont_write_bytecode(self): # makes sure byte_compile raise a DistutilsError # if sys.dont_write_bytecode is True old_dont_write_bytecode = sys.dont_write_bytecode sys.dont_write_bytecode = True try: self.assertRaises(DistutilsByteCompileError, byte_compile, []) finally: sys.dont_write_bytecode = old_dont_write_bytecode def test_grok_environment_error(self): # test obsolete function to ensure backward compat (#4931) exc = IOError("Unable to find batch file") msg = grok_environment_error(exc) self.assertEqual(msg, "error: Unable to find batch file") if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_version.py000066400000000000000000000064071471045437300250300ustar00rootroot00000000000000"""Tests for distutils.version.""" import unittest from distutils.version import LooseVersion from distutils.version import StrictVersion class VersionTestCase(unittest.TestCase): def test_prerelease(self): version = StrictVersion('1.2.3a1') self.assertEqual(version.version, (1, 2, 3)) self.assertEqual(version.prerelease, ('a', 1)) self.assertEqual(str(version), '1.2.3a1') version = StrictVersion('1.2.0') self.assertEqual(str(version), '1.2') def test_cmp_strict(self): versions = (('1.5.1', '1.5.2b2', -1), ('161', '3.10a', ValueError), ('8.02', '8.02', 0), ('3.4j', '1996.07.12', ValueError), ('3.2.pl0', '3.1.1.6', ValueError), ('2g6', '11g', ValueError), ('0.9', '2.2', -1), ('1.2.1', '1.2', 1), ('1.1', '1.2.2', -1), ('1.2', '1.1', 1), ('1.2.1', '1.2.2', -1), ('1.2.2', '1.2', 1), ('1.2', '1.2.2', -1), ('0.4.0', '0.4', 0), ('1.13++', '5.5.kw', ValueError)) for v1, v2, wanted in versions: try: res = StrictVersion(v1)._cmp(StrictVersion(v2)) except ValueError: if wanted is ValueError: continue else: raise AssertionError(("cmp(%s, %s) " "shouldn't raise ValueError") % (v1, v2)) self.assertEqual(res, wanted, 'cmp(%s, %s) should be %s, got %s' % (v1, v2, wanted, res)) res = StrictVersion(v1)._cmp(v2) self.assertEqual(res, wanted, 'cmp(%s, %s) should be %s, got %s' % (v1, v2, wanted, res)) res = StrictVersion(v1)._cmp(object()) self.assertIs(res, NotImplemented, 'cmp(%s, %s) should be NotImplemented, got %s' % (v1, v2, res)) def test_cmp(self): versions = (('1.5.1', '1.5.2b2', -1), ('161', '3.10a', 1), ('8.02', '8.02', 0), ('3.4j', '1996.07.12', -1), ('3.2.pl0', '3.1.1.6', 1), ('2g6', '11g', -1), ('0.960923', '2.2beta29', -1), ('1.13++', '5.5.kw', -1)) for v1, v2, wanted in versions: res = LooseVersion(v1)._cmp(LooseVersion(v2)) self.assertEqual(res, wanted, 'cmp(%s, %s) should be %s, got %s' % (v1, v2, wanted, res)) res = LooseVersion(v1)._cmp(v2) self.assertEqual(res, wanted, 'cmp(%s, %s) should be %s, got %s' % (v1, v2, wanted, res)) res = LooseVersion(v1)._cmp(object()) self.assertIs(res, NotImplemented, 'cmp(%s, %s) should be NotImplemented, got %s' % (v1, v2, res)) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/distutils/distutils/tests/test_versionpredicate.py000066400000000000000000000004261471045437300267040ustar00rootroot00000000000000"""Tests harness for distutils.versionpredicate. """ import distutils.versionpredicate import doctest def load_tests(loader, tests, pattern): tests.addTest(doctest.DocTestSuite(distutils.versionpredicate)) return tests if __name__ == '__main__': unittest.main() python-deadlib-3.13.0/distutils/distutils/text_file.py000066400000000000000000000303031471045437300231150ustar00rootroot00000000000000"""text_file provides the TextFile class, which gives an interface to text files that (optionally) takes care of stripping comments, ignoring blank lines, and joining lines with backslashes.""" import sys, io class TextFile: """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. Provides a '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 'unreadline()' for implementing line-at-a-time lookahead. Constructor is called as: TextFile (filename=None, file=None, **options) It bombs (RuntimeError) if both 'filename' and 'file' are None; 'filename' should be a string, and 'file' a file object (or something that provides 'readline()' and 'close()' methods). It is recommended that you supply at least 'filename', so that TextFile can include it in warning messages. If 'file' is not supplied, TextFile creates its own using 'io.open()'. The options are all boolean, and affect the value returned by 'readline()': strip_comments [default: true] strip from "#" to end-of-line, as well as any whitespace leading up to the "#" -- unless it is escaped by a backslash lstrip_ws [default: false] strip leading whitespace from each line before returning it rstrip_ws [default: true] strip trailing whitespace (including line terminator!) from each line before returning it skip_blanks [default: true} skip lines that are empty *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 [default: false] if a backslash is the last 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 [default: false] strip leading whitespace from lines that are joined to their predecessor; only matters if (join_lines and not lstrip_ws) errors [default: 'strict'] error handler used to decode the file content Note that since 'rstrip_ws' can strip the trailing newline, the semantics of 'readline()' must differ from those of the builtin file object's 'readline()' method! In particular, '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.""" default_options = { 'strip_comments': 1, 'skip_blanks': 1, 'lstrip_ws': 0, 'rstrip_ws': 1, 'join_lines': 0, 'collapse_join': 0, 'errors': 'strict', } def __init__(self, filename=None, file=None, **options): """Construct a new TextFile object. At least one of 'filename' (a string) and 'file' (a file-like object) must be supplied. They keyword argument options are described above and affect the values returned by 'readline()'.""" if filename is None and file is None: raise RuntimeError("you must supply either or both of 'filename' and 'file'") # set values for all options -- either from client option hash # or fallback to default_options for opt in self.default_options.keys(): if opt in options: setattr(self, opt, options[opt]) else: setattr(self, opt, self.default_options[opt]) # sanity check client option hash for opt in options.keys(): if opt not in self.default_options: raise KeyError("invalid TextFile option '%s'" % opt) if file is None: self.open(filename) else: self.filename = filename self.file = file self.current_line = 0 # assuming that file is at BOF! # 'linebuf' is a stack of lines that will be emptied before we # actually read from the file; it's only populated by an # 'unreadline()' operation self.linebuf = [] def open(self, filename): """Open a new file named 'filename'. This overrides both the 'filename' and 'file' arguments to the constructor.""" self.filename = filename self.file = io.open(self.filename, 'r', errors=self.errors) self.current_line = 0 def close(self): """Close the current file and forget everything we know about it (filename, current line number).""" file = self.file self.file = None self.filename = None self.current_line = None file.close() def gen_error(self, msg, line=None): outmsg = [] if line is None: line = self.current_line outmsg.append(self.filename + ", ") if isinstance(line, (list, tuple)): outmsg.append("lines %d-%d: " % tuple(line)) else: outmsg.append("line %d: " % line) outmsg.append(str(msg)) return "".join(outmsg) def error(self, msg, line=None): raise ValueError("error: " + self.gen_error(msg, line)) def warn(self, 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, eg. "lines 3-5". If 'line' 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.""" sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n") def readline(self): """Read and return a single logical line from the current file (or from an internal buffer if lines have previously been "unread" with '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 'warn()' after '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.""" # If any "unread" lines waiting in 'linebuf', return the top # one. (We don't actually buffer read-ahead data -- lines only # get put in 'linebuf' if the client explicitly does an # 'unreadline()'. if self.linebuf: line = self.linebuf[-1] del self.linebuf[-1] return line buildup_line = '' while True: # read the line, make it None if EOF line = self.file.readline() if line == '': line = None if self.strip_comments and line: # Look for the first "#" in the line. If none, never # mind. If we find one and it's the first character, or # is not preceded by "\", then it starts a comment -- # strip the comment, strip whitespace before it, and # carry on. Otherwise, it's just an escaped "#", so # unescape it (and any other escaped "#"'s that might be # lurking in there) and otherwise leave the line alone. pos = line.find("#") if pos == -1: # no "#" -- no comments pass # It's definitely a comment -- either "#" is the first # character, or it's elsewhere and unescaped. elif pos == 0 or line[pos-1] != "\\": # Have to preserve the trailing newline, because it's # the job of a later step (rstrip_ws) to remove it -- # and if rstrip_ws is false, we'd better preserve it! # (NB. this means that if the final line is all comment # and has no trailing newline, we will think that it's # EOF; I think that's OK.) eol = (line[-1] == '\n') and '\n' or '' line = line[0:pos] + eol # If all that's left is whitespace, then skip line # *now*, before we try to join it to 'buildup_line' -- # that way constructs like # hello \\ # # comment that should be ignored # there # result in "hello there". if line.strip() == "": continue else: # it's an escaped "#" line = line.replace("\\#", "#") # did previous line end with a backslash? then accumulate if self.join_lines and buildup_line: # oops: end of file if line is None: self.warn("continuation line immediately precedes " "end-of-file") return buildup_line if self.collapse_join: line = line.lstrip() line = buildup_line + line # careful: pay attention to line number when incrementing it if isinstance(self.current_line, list): self.current_line[1] = self.current_line[1] + 1 else: self.current_line = [self.current_line, self.current_line + 1] # just an ordinary line, read it as usual else: if line is None: # eof return None # still have to be careful about incrementing the line number! if isinstance(self.current_line, list): self.current_line = self.current_line[1] + 1 else: self.current_line = self.current_line + 1 # strip whitespace however the client wants (leading and # trailing, or one or the other, or neither) if self.lstrip_ws and self.rstrip_ws: line = line.strip() elif self.lstrip_ws: line = line.lstrip() elif self.rstrip_ws: line = line.rstrip() # blank line (whether we rstrip'ed or not)? skip to next line # if appropriate if (line == '' or line == '\n') and self.skip_blanks: continue if self.join_lines: if line[-1] == '\\': buildup_line = line[:-1] continue if line[-2:] == '\\\n': buildup_line = line[0:-2] + '\n' continue # well, I guess there's some actual content there: return it return line def readlines(self): """Read and return the list of all logical lines remaining in the current file.""" lines = [] while True: line = self.readline() if line is None: return lines lines.append(line) def unreadline(self, line): """Push 'line' (a string) onto an internal buffer that will be checked by future 'readline()' calls. Handy for implementing a parser with line-at-a-time lookahead.""" self.linebuf.append(line) python-deadlib-3.13.0/distutils/distutils/unixccompiler.py000066400000000000000000000347421471045437300240260ustar00rootroot00000000000000"""distutils.unixccompiler Contains the UnixCCompiler class, a subclass of CCompiler that handles the "typical" Unix-style command-line C compiler: * macros defined with -Dname[=value] * macros undefined with -Uname * include search directories specified with -Idir * libraries specified with -lllib * library search directories specified with -Ldir * compile handled by 'cc' (or similar) executable with -c option: compiles .c to .o * link static library handled by 'ar' command (possibly with 'ranlib') * link shared library handled by 'cc -shared' """ import os, sys, re from distutils import sysconfig from distutils.dep_util import newer from distutils.ccompiler import \ CCompiler, gen_preprocess_options, gen_lib_options from distutils.errors import \ DistutilsExecError, CompileError, LibError, LinkError from distutils import log if sys.platform == 'darwin': import _osx_support # XXX Things not currently handled: # * optimization/debug/warning flags; we just use whatever's in Python's # Makefile and live with it. Is this adequate? If not, we might # have to have a bunch of subclasses GNUCCompiler, SGICCompiler, # SunCCompiler, and I suspect down that road lies madness. # * even if we don't know a warning flag from an optimization flag, # we need some way for outsiders to feed preprocessor/compiler/linker # flags in to us -- eg. a sysadmin might want to mandate certain flags # via a site config file, or a user might want to set something for # compiling this module distribution only via the setup.py command # line, whatever. As long as these options come from something on the # current system, they can be as system-dependent as they like, and we # should just happily stuff them into the preprocessor/compiler/linker # options and carry on. class UnixCCompiler(CCompiler): compiler_type = 'unix' # These are used by CCompiler in two places: the constructor sets # instance attributes 'preprocessor', 'compiler', etc. from them, and # 'set_executable()' allows any of these to be set. The defaults here # are pretty generic; they will probably have to be set by an outsider # (eg. using information discovered by the sysconfig about building # Python extensions). executables = {'preprocessor' : None, 'compiler' : ["cc"], 'compiler_so' : ["cc"], 'compiler_cxx' : ["cc"], 'linker_so' : ["cc", "-shared"], 'linker_exe' : ["cc"], 'archiver' : ["ar", "-cr"], 'ranlib' : None, } if sys.platform[:6] == "darwin": executables['ranlib'] = ["ranlib"] # Needed for the filename generation methods provided by the base # class, CCompiler. NB. whoever instantiates/uses a particular # UnixCCompiler instance should set 'shared_lib_ext' -- we set a # reasonable common default here, but it's not necessarily used on all # Unices! src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"] obj_extension = ".o" static_lib_extension = ".a" shared_lib_extension = ".so" dylib_lib_extension = ".dylib" xcode_stub_lib_extension = ".tbd" static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" xcode_stub_lib_format = dylib_lib_format if sys.platform == "cygwin": exe_extension = ".exe" def preprocess(self, source, output_file=None, macros=None, include_dirs=None, extra_preargs=None, extra_postargs=None): fixed_args = self._fix_compile_args(None, macros, include_dirs) ignore, macros, include_dirs = fixed_args pp_opts = gen_preprocess_options(macros, include_dirs) pp_args = self.preprocessor + pp_opts if output_file: pp_args.extend(['-o', output_file]) if extra_preargs: pp_args[:0] = extra_preargs if extra_postargs: pp_args.extend(extra_postargs) pp_args.append(source) # We need to preprocess: either we're being forced to, or we're # generating output to stdout, or there's a target output file and # the source file is newer than the target (or the target doesn't # exist). if self.force or output_file is None or newer(source, output_file): if output_file: self.mkpath(os.path.dirname(output_file)) try: self.spawn(pp_args) except DistutilsExecError as msg: raise CompileError(msg) def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): compiler_so = self.compiler_so if sys.platform == 'darwin': compiler_so = _osx_support.compiler_fixup(compiler_so, cc_args + extra_postargs) try: self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs) except DistutilsExecError as msg: raise CompileError(msg) def create_static_lib(self, objects, output_libname, output_dir=None, debug=0, target_lang=None): objects, output_dir = self._fix_object_args(objects, output_dir) output_filename = \ self.library_filename(output_libname, output_dir=output_dir) if self._need_link(objects, output_filename): self.mkpath(os.path.dirname(output_filename)) self.spawn(self.archiver + [output_filename] + objects + self.objects) # Not many Unices required ranlib anymore -- SunOS 4.x is, I # think the only major Unix that does. Maybe we need some # platform intelligence here to skip ranlib if it's not # needed -- or maybe Python's configure script took care of # it for us, hence the check for leading colon. if self.ranlib: try: self.spawn(self.ranlib + [output_filename]) except DistutilsExecError as msg: raise LibError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) def link(self, 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): objects, output_dir = self._fix_object_args(objects, output_dir) fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs) libraries, library_dirs, runtime_library_dirs = fixed_args lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries) if not isinstance(output_dir, (str, type(None))): raise TypeError("'output_dir' must be a string or None") if output_dir is not None: output_filename = os.path.join(output_dir, output_filename) if self._need_link(objects, output_filename): ld_args = (objects + self.objects + lib_opts + ['-o', output_filename]) if debug: ld_args[:0] = ['-g'] if extra_preargs: ld_args[:0] = extra_preargs if extra_postargs: ld_args.extend(extra_postargs) self.mkpath(os.path.dirname(output_filename)) try: if target_desc == CCompiler.EXECUTABLE: linker = self.linker_exe[:] else: linker = self.linker_so[:] if target_lang == "c++" and self.compiler_cxx: # skip over environment variable settings if /usr/bin/env # is used to set up the linker's environment. # This is needed on OSX. Note: this assumes that the # normal and C++ compiler have the same environment # settings. i = 0 if os.path.basename(linker[0]) == "env": i = 1 while '=' in linker[i]: i += 1 if os.path.basename(linker[i]) == 'ld_so_aix': # AIX platforms prefix the compiler with the ld_so_aix # script, so we need to adjust our linker index offset = 1 else: offset = 0 linker[i+offset] = self.compiler_cxx[i] if sys.platform == 'darwin': linker = _osx_support.compiler_fixup(linker, ld_args) self.spawn(linker + ld_args) except DistutilsExecError as msg: raise LinkError(msg) else: log.debug("skipping %s (up-to-date)", output_filename) # -- Miscellaneous methods ----------------------------------------- # These are all used by the 'gen_lib_options() function, in # ccompiler.py. def library_dir_option(self, dir): return "-L" + dir def _is_gcc(self, compiler_name): # clang uses same syntax for rpath as gcc return any(name in compiler_name for name in ("gcc", "g++", "clang")) def runtime_library_dir_option(self, dir): # XXX Hackish, at the very least. See Python bug #445902: # http://sourceforge.net/tracker/index.php # ?func=detail&aid=445902&group_id=5470&atid=105470 # Linkers on different platforms need different options to # specify that directories need to be added to the list of # directories searched for dependencies when a dynamic library # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to # be told to pass the -R option through to the linker, whereas # other compilers and gcc on other systems just know this. # Other compilers may need something slightly different. At # this time, there's no way to determine this information from # the configuration data stored in the Python installation, so # we use this hack. compiler = os.path.basename(sysconfig.get_config_var("CC")) if sys.platform[:6] == "darwin": # MacOSX's linker doesn't understand the -R flag at all return "-L" + dir elif sys.platform[:7] == "freebsd": return "-Wl,-rpath=" + dir elif sys.platform[:5] == "hp-ux": if self._is_gcc(compiler): return ["-Wl,+s", "-L" + dir] return ["+s", "-L" + dir] else: if self._is_gcc(compiler): # gcc on non-GNU systems does not need -Wl, but can # use it anyway. Since distutils has always passed in # -Wl whenever gcc was used in the past it is probably # safest to keep doing so. if sysconfig.get_config_var("GNULD") == "yes": # GNU ld needs an extra option to get a RUNPATH # instead of just an RPATH. return "-Wl,--enable-new-dtags,-R" + dir else: return "-Wl,-R" + dir else: # No idea how --enable-new-dtags would be passed on to # ld if this system was using GNU ld. Don't know if a # system like this even exists. return "-R" + dir def library_option(self, lib): return "-l" + lib def find_library_file(self, dirs, lib, debug=0): shared_f = self.library_filename(lib, lib_type='shared') dylib_f = self.library_filename(lib, lib_type='dylib') xcode_stub_f = self.library_filename(lib, lib_type='xcode_stub') static_f = self.library_filename(lib, lib_type='static') if sys.platform == 'darwin': # On OSX users can specify an alternate SDK using # '-isysroot', calculate the SDK root if it is specified # (and use it further on) # # Note that, as of Xcode 7, Apple SDKs may contain textual stub # libraries with .tbd extensions rather than the normal .dylib # shared libraries installed in /. The Apple compiler tool # chain handles this transparently but it can cause problems # for programs that are being built with an SDK and searching # for specific libraries. Callers of find_library_file need to # keep in mind that the base filename of the returned SDK library # file might have a different extension from that of the library # file installed on the running system, for example: # /Applications/Xcode.app/Contents/Developer/Platforms/ # MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/ # usr/lib/libedit.tbd # vs # /usr/lib/libedit.dylib cflags = sysconfig.get_config_var('CFLAGS') m = re.search(r'-isysroot\s*(\S+)', cflags) if m is None: sysroot = _osx_support._default_sysroot(sysconfig.get_config_var('CC')) else: sysroot = m.group(1) for dir in dirs: shared = os.path.join(dir, shared_f) dylib = os.path.join(dir, dylib_f) static = os.path.join(dir, static_f) xcode_stub = os.path.join(dir, xcode_stub_f) if sys.platform == 'darwin' and ( dir.startswith('/System/') or ( dir.startswith('/usr/') and not dir.startswith('/usr/local/'))): shared = os.path.join(sysroot, dir[1:], shared_f) dylib = os.path.join(sysroot, dir[1:], dylib_f) static = os.path.join(sysroot, dir[1:], static_f) xcode_stub = os.path.join(sysroot, dir[1:], xcode_stub_f) # We're second-guessing the linker here, with not much hard # data to go on: GCC seems to prefer the shared library, so I'm # assuming that *all* Unix C compilers do. And of course I'm # ignoring even GCC's "-static" option. So sue me. if os.path.exists(dylib): return dylib elif os.path.exists(xcode_stub): return xcode_stub elif os.path.exists(shared): return shared elif os.path.exists(static): return static # Oops, didn't find it in *any* of 'dirs' return None python-deadlib-3.13.0/distutils/distutils/util.py000066400000000000000000000510501471045437300221110ustar00rootroot00000000000000"""distutils.util Miscellaneous utility functions -- anything that doesn't fit into one of the other *util.py modules. """ import os import re import importlib.util import string import sys import distutils from distutils.errors import DistutilsPlatformError from distutils.dep_util import newer from distutils.spawn import spawn from distutils import log from distutils.errors import DistutilsByteCompileError def get_host_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; eg. on Linux, the kernel version isn't particularly important. Examples of returned values: linux-i586 linux-alpha (?) solaris-2.6-sun4u Windows will return one of: win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) win32 (all others - specifically, sys.platform is returned) For other non-POSIX platforms, currently just returns 'sys.platform'. """ if os.name == 'nt': if 'amd64' in sys.version.lower(): return 'win-amd64' if '(arm)' in sys.version.lower(): return 'win-arm32' if '(arm64)' in sys.version.lower(): return 'win-arm64' return sys.platform # Set for cross builds explicitly if "_PYTHON_HOST_PLATFORM" in os.environ: return os.environ["_PYTHON_HOST_PLATFORM"] if os.name != "posix" or not hasattr(os, 'uname'): # XXX what about the architecture? NT is Intel or Alpha, # Mac OS is M68k or PPC, etc. return sys.platform # Try to distinguish various flavours of Unix (osname, host, release, version, machine) = os.uname() # Convert the OS name to lowercase, remove '/' characters, and translate # spaces (for "Power Macintosh") osname = osname.lower().replace('/', '') machine = machine.replace(' ', '_') machine = machine.replace('/', '-') if osname[:5] == "linux": # At least on Linux/Intel, 'machine' is the processor -- # i386, etc. # XXX what about Alpha, SPARC, etc? return "%s-%s" % (osname, machine) elif osname[:5] == "sunos": if release[0] >= "5": # SunOS 5 == Solaris 2 osname = "solaris" release = "%d.%s" % (int(release[0]) - 3, release[2:]) # We can't use "platform.architecture()[0]" because a # bootstrap problem. We use a dict to get an error # if some suspicious happens. bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} machine += ".%s" % bitness[sys.maxsize] # fall through to standard osname-release-machine representation elif osname[:3] == "aix": from _aix_support import aix_platform return aix_platform() elif osname[:6] == "cygwin": osname = "cygwin" rel_re = re.compile (r'[\d.]+', re.ASCII) m = rel_re.match(release) if m: release = m.group() elif osname[:6] == "darwin": import _osx_support, distutils.sysconfig osname, release, machine = _osx_support.get_platform_osx( distutils.sysconfig.get_config_vars(), osname, release, machine) return "%s-%s-%s" % (osname, release, machine) def get_platform(): if os.name == 'nt': TARGET_TO_PLAT = { 'x86' : 'win32', 'x64' : 'win-amd64', 'arm' : 'win-arm32', } return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform() else: return get_host_platform() def 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 ValueError on non-Unix-ish systems if 'pathname' either starts or ends with a slash. """ if os.sep == '/': return pathname if not pathname: return pathname if pathname[0] == '/': raise ValueError("path '%s' cannot be absolute" % pathname) if pathname[-1] == '/': raise ValueError("path '%s' cannot end with '/'" % pathname) paths = pathname.split('/') while '.' in paths: paths.remove('.') if not paths: return os.curdir return os.path.join(*paths) # convert_path () def 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 and Mac OS. """ if os.name == 'posix': if not os.path.isabs(pathname): return os.path.join(new_root, pathname) else: return os.path.join(new_root, pathname[1:]) elif os.name == 'nt': (drive, path) = os.path.splitdrive(pathname) if path[0] == '\\': path = path[1:] return os.path.join(new_root, path) else: raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) _environ_checked = 0 def 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: HOME - user's home directory (Unix only) PLAT - description of the current platform, including hardware and OS (see 'get_platform()') """ global _environ_checked if _environ_checked: return if os.name == 'posix' and 'HOME' not in os.environ: try: import pwd os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] except (ImportError, KeyError): # bpo-10496: if the current user identifier doesn't exist in the # password database, do nothing pass if 'PLAT' not in os.environ: os.environ['PLAT'] = get_platform() _environ_checked = 1 def subst_vars (s, local_vars): """Perform shell/Perl-style variable substitution on 'string'. 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 'check_environ()'. Raise ValueError for any variables not found in either 'local_vars' or 'os.environ'. """ check_environ() def _subst (match, local_vars=local_vars): var_name = match.group(1) if var_name in local_vars: return str(local_vars[var_name]) else: return os.environ[var_name] try: return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) except KeyError as var: raise ValueError("invalid variable '$%s'" % var) # subst_vars () def grok_environment_error (exc, prefix="error: "): # Function kept for backward compatibility. # Used to try clever things with EnvironmentErrors, # but nowadays str(exception) produces good messages. return prefix + str(exc) # Needed by 'split_quoted()' _wordchars_re = _squote_re = _dquote_re = None def _init_regex(): global _wordchars_re, _squote_re, _dquote_re _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') def 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. """ # This is a nice algorithm for splitting up a single string, since it # doesn't require character-by-character examination. It was a little # bit of a brain-bender to get it working right, though... if _wordchars_re is None: _init_regex() s = s.strip() words = [] pos = 0 while s: m = _wordchars_re.match(s, pos) end = m.end() if end == len(s): words.append(s[:end]) break if s[end] in string.whitespace: # unescaped, unquoted whitespace: now words.append(s[:end]) # we definitely have a word delimiter s = s[end:].lstrip() pos = 0 elif s[end] == '\\': # preserve whatever is being escaped; # will become part of the current word s = s[:end] + s[end+1:] pos = end+1 else: if s[end] == "'": # slurp singly-quoted string m = _squote_re.match(s, end) elif s[end] == '"': # slurp doubly-quoted string m = _dquote_re.match(s, end) else: raise RuntimeError("this can't happen (bad char '%c')" % s[end]) if m is None: raise ValueError("bad string (mismatched %s quotes?)" % s[end]) (beg, end) = m.span() s = s[:beg] + s[beg+1:end-1] + s[end:] pos = m.end() - 2 if pos >= len(s): words.append(s) break return words # split_quoted () def execute (func, args, msg=None, verbose=0, dry_run=0): """Perform some action that affects the outside world (eg. by 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. """ if msg is None: msg = "%s%r" % (func.__name__, args) if msg[-2:] == ',)': # correct for singleton tuple msg = msg[0:-2] + ')' log.info(msg) if not dry_run: func(*args) def 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 ValueError if 'val' is anything else. """ val = val.lower() if val in ('y', 'yes', 't', 'true', 'on', '1'): return 1 elif val in ('n', 'no', 'f', 'false', 'off', '0'): return 0 else: raise ValueError("invalid truth value %r" % (val,)) def 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 .pyc files in a __pycache__ subdirectory. 'py_files' is a list of files to compile; any files that don't end in ".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 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 py_compile module, or indirectly by writing a temporary script and executing it. Normally, you should let '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. """ # Late import to fix a bootstrap issue: _posixsubprocess is built by # setup.py, but setup.py uses distutils. import subprocess # nothing is done if sys.dont_write_bytecode is True if sys.dont_write_bytecode: raise DistutilsByteCompileError('byte-compiling is disabled.') # First, if the caller didn't force us into direct or indirect mode, # figure out which mode we should be in. We take a conservative # approach: choose direct mode *only* if the current interpreter is # in debug mode and optimize is 0. If we're not in debug mode (-O # or -OO), we don't know which level of optimization this # interpreter is running with, so we can't do direct # byte-compilation and be certain that it's the right thing. Thus, # always compile indirectly if the current interpreter is in either # optimize mode, or if either optimization level was requested by # the caller. if direct is None: direct = (__debug__ and optimize == 0) # "Indirect" byte-compilation: write a temporary script and then # run it with the appropriate flags. if not direct: try: from tempfile import mkstemp (script_fd, script_name) = mkstemp(".py") except ImportError: from tempfile import mktemp (script_fd, script_name) = None, mktemp(".py") log.info("writing byte-compilation script '%s'", script_name) if not dry_run: if script_fd is not None: script = os.fdopen(script_fd, "w") else: script = open(script_name, "w") with script: script.write("""\ from distutils.util import byte_compile files = [ """) # XXX would be nice to write absolute filenames, just for # safety's sake (script should be more robust in the face of # chdir'ing before running it). But this requires abspath'ing # 'prefix' as well, and that breaks the hack in build_lib's # 'byte_compile()' method that carefully tacks on a trailing # slash (os.sep really) to make sure the prefix here is "just # right". This whole prefix business is rather delicate -- the # problem is that it's really a directory, but I'm treating it # as a dumb string, so trailing slashes and so forth matter. #py_files = map(os.path.abspath, py_files) #if prefix: # prefix = os.path.abspath(prefix) script.write(",\n".join(map(repr, py_files)) + "]\n") script.write(""" byte_compile(files, optimize=%r, force=%r, prefix=%r, base_dir=%r, verbose=%r, dry_run=0, direct=1) """ % (optimize, force, prefix, base_dir, verbose)) msg = distutils._DEPRECATION_MESSAGE cmd = [sys.executable] cmd.extend(subprocess._optim_args_from_interpreter_flags()) cmd.append(f'-Wignore:{msg}:DeprecationWarning') cmd.append(script_name) spawn(cmd, dry_run=dry_run) execute(os.remove, (script_name,), "removing %s" % script_name, dry_run=dry_run) # "Direct" byte-compilation: use the py_compile module to compile # right here, right now. Note that the script generated in indirect # mode simply calls 'byte_compile()' in direct mode, a weird sort of # cross-process recursion. Hey, it works! else: from py_compile import compile for file in py_files: if file[-3:] != ".py": # This lets us be lazy and not filter filenames in # the "install_lib" command. continue # Terminology from the py_compile module: # cfile - byte-compiled file # dfile - purported source filename (same as 'file' by default) if optimize >= 0: opt = '' if optimize == 0 else optimize cfile = importlib.util.cache_from_source( file, optimization=opt) else: cfile = importlib.util.cache_from_source(file) dfile = file if prefix: if file[:len(prefix)] != prefix: raise ValueError("invalid prefix: filename %r doesn't start with %r" % (file, prefix)) dfile = dfile[len(prefix):] if base_dir: dfile = os.path.join(base_dir, dfile) cfile_base = os.path.basename(cfile) if direct: if force or newer(file, cfile): log.info("byte-compiling %s to %s", file, cfile_base) if not dry_run: compile(file, cfile, dfile) else: log.debug("skipping byte-compilation of %s to %s", file, cfile_base) # byte_compile () def rfc822_escape (header): """Return a version of the string escaped for inclusion in an RFC-822 header, by ensuring there are 8 spaces space after each newline. """ lines = header.split('\n') sep = '\n' + 8 * ' ' return sep.join(lines) # 2to3 support def run_2to3(files, fixer_names=None, options=None, explicit=None): """Invoke 2to3 on a list of Python files. The files should all come from the build area, as the modification is done in-place. To reduce the build time, only files modified since the last invocation of this function should be passed in the files argument.""" if not files: return # Make this class local, to delay import of 2to3 from lib2to3.refactor import RefactoringTool, get_fixers_from_package class DistutilsRefactoringTool(RefactoringTool): def log_error(self, msg, *args, **kw): log.error(msg, *args) def log_message(self, msg, *args): log.info(msg, *args) def log_debug(self, msg, *args): log.debug(msg, *args) if fixer_names is None: fixer_names = get_fixers_from_package('lib2to3.fixes') r = DistutilsRefactoringTool(fixer_names, options=options) r.refactor(files, write=True) def copydir_run_2to3(src, dest, template=None, fixer_names=None, options=None, explicit=None): """Recursively copy a directory, only copying new and changed files, running run_2to3 over all newly copied Python modules afterward. If you give a template string, it's parsed like a MANIFEST.in. """ from distutils.dir_util import mkpath from distutils.file_util import copy_file from distutils.filelist import FileList filelist = FileList() curdir = os.getcwd() os.chdir(src) try: filelist.findall() finally: os.chdir(curdir) filelist.files[:] = filelist.allfiles if template: for line in template.splitlines(): line = line.strip() if not line: continue filelist.process_template_line(line) copied = [] for filename in filelist.files: outname = os.path.join(dest, filename) mkpath(os.path.dirname(outname)) res = copy_file(os.path.join(src, filename), outname, update=1) if res[1]: copied.append(outname) run_2to3([fn for fn in copied if fn.lower().endswith('.py')], fixer_names=fixer_names, options=options, explicit=explicit) return copied class Mixin2to3: '''Mixin class for commands that run 2to3. To configure 2to3, setup scripts may either change the class variables, or inherit from individual commands to override how 2to3 is invoked.''' # provide list of fixers to run; # defaults to all from lib2to3.fixers fixer_names = None # options dictionary options = None # list of fixers to invoke even though they are marked as explicit explicit = None def run_2to3(self, files): return run_2to3(files, self.fixer_names, self.options, self.explicit) python-deadlib-3.13.0/distutils/distutils/version.py000066400000000000000000000303421471045437300226220ustar00rootroot00000000000000# # distutils/version.py # # Implements multiple version numbering conventions for the # Python Module Distribution Utilities. # # $Id$ # """Provides classes to represent module version numbers (one class for each style of version numbering). There are currently two such classes implemented: StrictVersion and LooseVersion. Every version number class implements the following interface: * the 'parse' method takes a string and parses it to some internal representation; if the string is an invalid version number, 'parse' raises a ValueError exception * the class constructor takes an optional string argument which, if supplied, is passed to 'parse' * __str__ reconstructs the string that was passed to 'parse' (or an equivalent string -- ie. one that will generate an equivalent version number instance) * __repr__ generates Python code to recreate the version number instance * _cmp compares the current instance with either another instance of the same class or a string (which will be parsed to an instance of the same class, thus must follow the same rules) """ import re class Version: """Abstract base class for version numbering classes. Just provides constructor (__init__) and reproducer (__repr__), because those seem to be the same for all version numbering classes; and route rich comparisons to _cmp. """ def __init__ (self, vstring=None): if vstring: self.parse(vstring) def __repr__ (self): return "%s ('%s')" % (self.__class__.__name__, str(self)) def __eq__(self, other): c = self._cmp(other) if c is NotImplemented: return c return c == 0 def __lt__(self, other): c = self._cmp(other) if c is NotImplemented: return c return c < 0 def __le__(self, other): c = self._cmp(other) if c is NotImplemented: return c return c <= 0 def __gt__(self, other): c = self._cmp(other) if c is NotImplemented: return c return c > 0 def __ge__(self, other): c = self._cmp(other) if c is NotImplemented: return c return c >= 0 # Interface for version-number classes -- must be implemented # by the following classes (the concrete ones -- Version should # be treated as an abstract class). # __init__ (string) - create and take same action as 'parse' # (string parameter is optional) # parse (string) - convert a string representation to whatever # internal representation is appropriate for # this style of version numbering # __str__ (self) - convert back to a string; should be very similar # (if not identical to) the string supplied to parse # __repr__ (self) - generate Python code to recreate # the instance # _cmp (self, other) - compare two version numbers ('other' may # be an unparsed version string, or another # instance of your version class) class StrictVersion (Version): """Version numbering for anal retentives and software idealists. Implements the standard interface for version number classes as described above. A version number consists of two or three dot-separated numeric components, with an optional "pre-release" tag on the end. The pre-release tag consists of the letter 'a' or 'b' followed by a number. If the numeric components of two version numbers are equal, then one with a pre-release tag will always be deemed earlier (lesser) than one without. The following are valid version numbers (shown in the order that would be obtained by sorting according to the supplied cmp function): 0.4 0.4.0 (these two are equivalent) 0.4.1 0.5a1 0.5b3 0.5 0.9.6 1.0 1.0.4a3 1.0.4b1 1.0.4 The following are examples of invalid version numbers: 1 2.7.2.2 1.3.a4 1.3pl1 1.3c4 The rationale for this version numbering system will be explained in the distutils documentation. """ version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$', re.VERBOSE | re.ASCII) def parse (self, vstring): match = self.version_re.match(vstring) if not match: raise ValueError("invalid version number '%s'" % vstring) (major, minor, patch, prerelease, prerelease_num) = \ match.group(1, 2, 4, 5, 6) if patch: self.version = tuple(map(int, [major, minor, patch])) else: self.version = tuple(map(int, [major, minor])) + (0,) if prerelease: self.prerelease = (prerelease[0], int(prerelease_num)) else: self.prerelease = None def __str__ (self): if self.version[2] == 0: vstring = '.'.join(map(str, self.version[0:2])) else: vstring = '.'.join(map(str, self.version)) if self.prerelease: vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) return vstring def _cmp (self, other): if isinstance(other, str): other = StrictVersion(other) elif not isinstance(other, StrictVersion): return NotImplemented if self.version != other.version: # numeric versions don't match # prerelease stuff doesn't matter if self.version < other.version: return -1 else: return 1 # have to compare prerelease # case 1: neither has prerelease; they're equal # case 2: self has prerelease, other doesn't; other is greater # case 3: self doesn't have prerelease, other does: self is greater # case 4: both have prerelease: must compare them! if (not self.prerelease and not other.prerelease): return 0 elif (self.prerelease and not other.prerelease): return -1 elif (not self.prerelease and other.prerelease): return 1 elif (self.prerelease and other.prerelease): if self.prerelease == other.prerelease: return 0 elif self.prerelease < other.prerelease: return -1 else: return 1 else: assert False, "never get here" # end class StrictVersion # The rules according to Greg Stein: # 1) a version number has 1 or more numbers separated by a period or by # sequences of letters. If only periods, then these are compared # left-to-right to determine an ordering. # 2) sequences of letters are part of the tuple for comparison and are # compared lexicographically # 3) recognize the numeric components may have leading zeroes # # The LooseVersion class below implements these rules: a version number # string is split up into a tuple of integer and string components, and # comparison is a simple tuple comparison. This means that version # numbers behave in a predictable and obvious way, but a way that might # not necessarily be how people *want* version numbers to behave. There # wouldn't be a problem if people could stick to purely numeric version # numbers: just split on period and compare the numbers as tuples. # However, people insist on putting letters into their version numbers; # the most common purpose seems to be: # - indicating a "pre-release" version # ('alpha', 'beta', 'a', 'b', 'pre', 'p') # - indicating a post-release patch ('p', 'pl', 'patch') # but of course this can't cover all version number schemes, and there's # no way to know what a programmer means without asking him. # # The problem is what to do with letters (and other non-numeric # characters) in a version number. The current implementation does the # obvious and predictable thing: keep them as strings and compare # lexically within a tuple comparison. This has the desired effect if # an appended letter sequence implies something "post-release": # eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002". # # However, if letters in a version number imply a pre-release version, # the "obvious" thing isn't correct. Eg. you would expect that # "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison # implemented here, this just isn't so. # # Two possible solutions come to mind. The first is to tie the # comparison algorithm to a particular set of semantic rules, as has # been done in the StrictVersion class above. This works great as long # as everyone can go along with bondage and discipline. Hopefully a # (large) subset of Python module programmers will agree that the # particular flavour of bondage and discipline provided by StrictVersion # provides enough benefit to be worth using, and will submit their # version numbering scheme to its domination. The free-thinking # anarchists in the lot will never give in, though, and something needs # to be done to accommodate them. # # Perhaps a "moderately strict" version class could be implemented that # lets almost anything slide (syntactically), and makes some heuristic # assumptions about non-digits in version number strings. This could # sink into special-case-hell, though; if I was as talented and # idiosyncratic as Larry Wall, I'd go ahead and implement a class that # somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is # just as happy dealing with things like "2g6" and "1.13++". I don't # think I'm smart enough to do it right though. # # In any case, I've coded the test suite for this module (see # ../test/test_version.py) specifically to fail on things like comparing # "1.2a2" and "1.2". That's not because the *code* is doing anything # wrong, it's because the simple, obvious design doesn't match my # complicated, hairy expectations for real-world version numbers. It # would be a snap to fix the test suite to say, "Yep, LooseVersion does # the Right Thing" (ie. the code matches the conception). But I'd rather # have a conception that matches common notions about version numbers. class LooseVersion (Version): """Version numbering for anarchists and software realists. Implements the standard interface for version number classes as described above. A version number consists of a series of numbers, separated by either periods or strings of letters. When comparing version numbers, the numeric components will be compared numerically, and the alphabetic components lexically. The following are all valid version numbers, in no particular order: 1.5.1 1.5.2b2 161 3.10a 8.02 3.4j 1996.07.12 3.2.pl0 3.1.1.6 2g6 11g 0.960923 2.2beta29 1.13++ 5.5.kw 2.0b1pl0 In fact, there is no such thing as an invalid version number under this scheme; the rules for comparison are simple and predictable, but may not always give the results you want (for some definition of "want"). """ component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE) def __init__ (self, vstring=None): if vstring: self.parse(vstring) def parse (self, vstring): # I've given up on thinking I can reconstruct the version string # from the parsed tuple -- so I just store the string here for # use by __str__ self.vstring = vstring components = [x for x in self.component_re.split(vstring) if x and x != '.'] for i, obj in enumerate(components): try: components[i] = int(obj) except ValueError: pass self.version = components def __str__ (self): return self.vstring def __repr__ (self): return "LooseVersion ('%s')" % str(self) def _cmp (self, other): if isinstance(other, str): other = LooseVersion(other) elif not isinstance(other, LooseVersion): return NotImplemented if self.version == other.version: return 0 if self.version < other.version: return -1 if self.version > other.version: return 1 # end class LooseVersion python-deadlib-3.13.0/distutils/distutils/versionpredicate.py000066400000000000000000000120151471045437300245000ustar00rootroot00000000000000"""Module for parsing and testing package version predicate strings. """ import re import distutils.version import operator re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)", re.ASCII) # (package) (rest) re_paren = re.compile(r"^\s*\((.*)\)\s*$") # (list) inside of parentheses re_splitComparison = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") # (comp) (version) def splitUp(pred): """Parse a single version comparison. Return (comparison string, StrictVersion) """ res = re_splitComparison.match(pred) if not res: raise ValueError("bad package restriction syntax: %r" % pred) comp, verStr = res.groups() return (comp, distutils.version.StrictVersion(verStr)) compmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq, ">": operator.gt, ">=": operator.ge, "!=": operator.ne} class VersionPredicate: """Parse and test package version predicates. >>> v = VersionPredicate('pyepat.abc (>1.0, <3333.3a1, !=1555.1b3)') The `name` attribute provides the full dotted name that is given:: >>> v.name 'pyepat.abc' The str() of a `VersionPredicate` provides a normalized human-readable version of the expression:: >>> print(v) pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3) The `satisfied_by()` method can be used to determine with a given version number is included in the set described by the version restrictions:: >>> v.satisfied_by('1.1') True >>> v.satisfied_by('1.4') True >>> v.satisfied_by('1.0') False >>> v.satisfied_by('4444.4') False >>> v.satisfied_by('1555.1b3') False `VersionPredicate` is flexible in accepting extra whitespace:: >>> v = VersionPredicate(' pat( == 0.1 ) ') >>> v.name 'pat' >>> v.satisfied_by('0.1') True >>> v.satisfied_by('0.2') False If any version numbers passed in do not conform to the restrictions of `StrictVersion`, a `ValueError` is raised:: >>> v = VersionPredicate('p1.p2.p3.p4(>=1.0, <=1.3a1, !=1.2zb3)') Traceback (most recent call last): ... ValueError: invalid version number '1.2zb3' It the module or package name given does not conform to what's allowed as a legal module or package name, `ValueError` is raised:: >>> v = VersionPredicate('foo-bar') Traceback (most recent call last): ... ValueError: expected parenthesized list: '-bar' >>> v = VersionPredicate('foo bar (12.21)') Traceback (most recent call last): ... ValueError: expected parenthesized list: 'bar (12.21)' """ def __init__(self, versionPredicateStr): """Parse a version predicate string. """ # Fields: # name: package name # pred: list of (comparison string, StrictVersion) versionPredicateStr = versionPredicateStr.strip() if not versionPredicateStr: raise ValueError("empty package restriction") match = re_validPackage.match(versionPredicateStr) if not match: raise ValueError("bad package name in %r" % versionPredicateStr) self.name, paren = match.groups() paren = paren.strip() if paren: match = re_paren.match(paren) if not match: raise ValueError("expected parenthesized list: %r" % paren) str = match.groups()[0] self.pred = [splitUp(aPred) for aPred in str.split(",")] if not self.pred: raise ValueError("empty parenthesized list in %r" % versionPredicateStr) else: self.pred = [] def __str__(self): if self.pred: seq = [cond + " " + str(ver) for cond, ver in self.pred] return self.name + " (" + ", ".join(seq) + ")" else: return self.name def satisfied_by(self, version): """True if version is compatible with all the predicates in self. The parameter version must be acceptable to the StrictVersion constructor. It may be either a string or StrictVersion. """ for cond, ver in self.pred: if not compmap[cond](version, ver): return False return True _provision_rx = None def split_provision(value): """Return the name and optional version number of a provision. The version number, if given, will be returned as a `StrictVersion` instance, otherwise it will be `None`. >>> split_provision('mypkg') ('mypkg', None) >>> split_provision(' mypkg( 1.2 ) ') ('mypkg', StrictVersion ('1.2')) """ global _provision_rx if _provision_rx is None: _provision_rx = re.compile( r"([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", re.ASCII) value = value.strip() m = _provision_rx.match(value) if not m: raise ValueError("illegal provides specification: %r" % value) ver = m.group(2) or None if ver: ver = distutils.version.StrictVersion(ver) return m.group(1), ver python-deadlib-3.13.0/distutils/pyproject.toml000066400000000000000000000013011471045437300214440ustar00rootroot00000000000000[project] name = "standard-distutils" version = "3.11.9" description = "Standard library distutils redistribution. \"dead battery\"." keywords = ["stdlib"] authors = [ { name = "Python Developers", email = "python-deadlib@youknowone.org" } ] readme = "README.rst" license = { text = "PSF-2.0" } classifiers = [ "License :: OSI Approved :: Python Software Foundation License", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", ] [tool.setuptools.packages] find = {include = ["distutils*"]} [project.urls] "Homepage" = "https://github.com/youknowone/python-deadlib" [build-system] requires = ["setuptools>=75.0"] build-backend = "setuptools.build_meta" python-deadlib-3.13.0/distutils/tests/000077500000000000000000000000001471045437300176775ustar00rootroot00000000000000python-deadlib-3.13.0/distutils/tests/__init__.py000066400000000000000000000000001471045437300217760ustar00rootroot00000000000000python-deadlib-3.13.0/distutils/tests/test_distutils.py000066400000000000000000000010451471045437300233340ustar00rootroot00000000000000"""Tests for distutils. The tests for distutils are defined in the distutils.tests package. """ import unittest from test import support from test.support import warnings_helper with warnings_helper.check_warnings( ("The distutils package is deprecated", DeprecationWarning), quiet=True): from distutils.tests import load_tests def tearDownModule(): support.reap_children() if support.check_sanitizer(address=True): raise unittest.SkipTest("Exposes ASAN flakiness in GitHub CI") if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/imghdr/000077500000000000000000000000001471045437300157635ustar00rootroot00000000000000python-deadlib-3.13.0/imghdr/Doc/000077500000000000000000000000001471045437300164705ustar00rootroot00000000000000python-deadlib-3.13.0/imghdr/Doc/imghdr.rst000066400000000000000000000060101471045437300204710ustar00rootroot00000000000000:mod:`imghdr` --- Determine the type of an image ================================================ .. module:: imghdr :synopsis: Determine the type of image contained in a file or byte stream. :deprecated: **Source code:** :source:`Lib/imghdr.py` .. deprecated-removed:: 3.11 3.13 The :mod:`imghdr` module is deprecated (see :pep:`PEP 594 <594#imghdr>` for details and alternatives). -------------- The :mod:`imghdr` module determines the type of image contained in a file or byte stream. The :mod:`imghdr` module defines the following function: .. function:: what(file, h=None) Test the image data contained in the file named *file* and return a string describing the image type. If *h* is provided, the *file* argument is ignored and *h* is assumed to contain the byte stream to test. .. versionchanged:: 3.6 Accepts a :term:`path-like object`. The following image types are recognized, as listed below with the return value from :func:`what`: +------------+-----------------------------------+ | Value | Image format | +============+===================================+ | ``'rgb'`` | SGI ImgLib Files | +------------+-----------------------------------+ | ``'gif'`` | GIF 87a and 89a Files | +------------+-----------------------------------+ | ``'pbm'`` | Portable Bitmap Files | +------------+-----------------------------------+ | ``'pgm'`` | Portable Graymap Files | +------------+-----------------------------------+ | ``'ppm'`` | Portable Pixmap Files | +------------+-----------------------------------+ | ``'tiff'`` | TIFF Files | +------------+-----------------------------------+ | ``'rast'`` | Sun Raster Files | +------------+-----------------------------------+ | ``'xbm'`` | X Bitmap Files | +------------+-----------------------------------+ | ``'jpeg'`` | JPEG data in JFIF or Exif formats | +------------+-----------------------------------+ | ``'bmp'`` | BMP files | +------------+-----------------------------------+ | ``'png'`` | Portable Network Graphics | +------------+-----------------------------------+ | ``'webp'`` | WebP files | +------------+-----------------------------------+ | ``'exr'`` | OpenEXR Files | +------------+-----------------------------------+ .. versionadded:: 3.5 The *exr* and *webp* formats were added. You can extend the list of file types :mod:`imghdr` can recognize by appending to this variable: .. data:: tests A list of functions performing the individual tests. Each function takes two arguments: the byte-stream and an open file-like object. When :func:`what` is called with a byte-stream, the file-like object will be ``None``. The test function should return a string describing the image type if the test succeeded, or ``None`` if it failed. Example:: >>> import imghdr >>> imghdr.what('bass.gif') 'gif' python-deadlib-3.13.0/imghdr/LICENSE000066400000000000000000000050111471045437300167650ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/imghdr/README.rst000066400000000000000000000005161471045437300174540ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/imghdr/imghdr/000077500000000000000000000000001471045437300172355ustar00rootroot00000000000000python-deadlib-3.13.0/imghdr/imghdr/__init__.py000066400000000000000000000110731471045437300213500ustar00rootroot00000000000000"""Recognize image file formats based on their first few bytes.""" from os import PathLike import warnings __all__ = ["what"] # python-deadlib: Replace deprecation warning not to raise exception warnings.warn( f"{__name__} was removed in Python 3.13. " f"Please be aware that you are currently NOT using standard '{__name__}', " f"but instead a separately installed 'standard-{__name__}'.", DeprecationWarning, stacklevel=2 ) #-------------------------# # Recognize image headers # #-------------------------# def what(file, h=None): """Return the type of image contained in a file or byte stream.""" f = None try: if h is None: if isinstance(file, (str, PathLike)): f = open(file, 'rb') h = f.read(32) else: location = file.tell() h = file.read(32) file.seek(location) for tf in tests: res = tf(h, f) if res: return res finally: if f: f.close() return None #---------------------------------# # Subroutines per image file type # #---------------------------------# tests = [] def test_jpeg(h, f): """Test for JPEG data with JFIF or Exif markers; and raw JPEG.""" if h[6:10] in (b'JFIF', b'Exif'): return 'jpeg' elif h[:4] == b'\xff\xd8\xff\xdb': return 'jpeg' tests.append(test_jpeg) def test_png(h, f): """Verify if the image is a PNG.""" if h.startswith(b'\211PNG\r\n\032\n'): return 'png' tests.append(test_png) def test_gif(h, f): """Verify if the image is a GIF ('87 or '89 variants).""" if h[:6] in (b'GIF87a', b'GIF89a'): return 'gif' tests.append(test_gif) def test_tiff(h, f): """Verify if the image is a TIFF (can be in Motorola or Intel byte order).""" if h[:2] in (b'MM', b'II'): return 'tiff' tests.append(test_tiff) def test_rgb(h, f): """test for the SGI image library.""" if h.startswith(b'\001\332'): return 'rgb' tests.append(test_rgb) def test_pbm(h, f): """Verify if the image is a PBM (portable bitmap).""" if len(h) >= 3 and \ h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r': return 'pbm' tests.append(test_pbm) def test_pgm(h, f): """Verify if the image is a PGM (portable graymap).""" if len(h) >= 3 and \ h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r': return 'pgm' tests.append(test_pgm) def test_ppm(h, f): """Verify if the image is a PPM (portable pixmap).""" if len(h) >= 3 and \ h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r': return 'ppm' tests.append(test_ppm) def test_rast(h, f): """test for the Sun raster file.""" if h.startswith(b'\x59\xA6\x6A\x95'): return 'rast' tests.append(test_rast) def test_xbm(h, f): """Verify if the image is a X bitmap (X10 or X11).""" if h.startswith(b'#define '): return 'xbm' tests.append(test_xbm) def test_bmp(h, f): """Verify if the image is a BMP file.""" if h.startswith(b'BM'): return 'bmp' tests.append(test_bmp) def test_webp(h, f): """Verify if the image is a WebP.""" if h.startswith(b'RIFF') and h[8:12] == b'WEBP': return 'webp' tests.append(test_webp) def test_exr(h, f): """verify is the image ia a OpenEXR fileOpenEXR.""" if h.startswith(b'\x76\x2f\x31\x01'): return 'exr' tests.append(test_exr) #--------------------# # Small test program # #--------------------# def test(): import sys recursive = 0 if sys.argv[1:] and sys.argv[1] == '-r': del sys.argv[1:2] recursive = 1 try: if sys.argv[1:]: testall(sys.argv[1:], recursive, 1) else: testall(['.'], recursive, 1) except KeyboardInterrupt: sys.stderr.write('\n[Interrupted]\n') sys.exit(1) def testall(list, recursive, toplevel): import sys import os for filename in list: if os.path.isdir(filename): print(filename + '/:', end=' ') if recursive or toplevel: print('recursing down:') import glob names = glob.glob(os.path.join(glob.escape(filename), '*')) testall(names, recursive, 0) else: print('*** directory (use -r) ***') else: print(filename + ':', end=' ') sys.stdout.flush() try: print(what(filename)) except OSError: print('*** not found ***') if __name__ == '__main__': test() python-deadlib-3.13.0/imghdr/pyproject.toml000066400000000000000000000012701471045437300206770ustar00rootroot00000000000000[project] name = "standard-imghdr" version = "3.13.0" description = "Standard library imghdr redistribution. \"dead battery\"." keywords = ["stdlib"] authors = [ { name = "Python Developers", email = "python-deadlib@youknowone.org" } ] readme = "README.rst" license = { text = "PSF-2.0" } classifiers = [ "License :: OSI Approved :: Python Software Foundation License", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", ] [tool.setuptools.packages] find = {include = ["imghdr*"]} [project.urls] "Homepage" = "https://github.com/youknowone/python-deadlib" [build-system] requires = ["setuptools>=75.0"] build-backend = "setuptools.build_meta" python-deadlib-3.13.0/imghdr/tests/000077500000000000000000000000001471045437300171255ustar00rootroot00000000000000python-deadlib-3.13.0/imghdr/tests/__init__.py000066400000000000000000000000001471045437300212240ustar00rootroot00000000000000python-deadlib-3.13.0/imghdr/tests/imghdrdata/000077500000000000000000000000001471045437300212315ustar00rootroot00000000000000python-deadlib-3.13.0/imghdr/tests/imghdrdata/python-raw.jpg000066400000000000000000000010151471045437300240400ustar00rootroot00000000000000C       C "$" 12#!1a ?jsԕc%eZiE@ Tʸ =l5wmWqK9"SiqJ8K*qi-!;tZuHe[{!)gkIeOȅ)K*z<~U b@=\CzeXQ!dh)<p;j7i6h5`2OEB7-=ÏPLHEyAt=o:i6i6i6`2CKA6$ƑROKH}Dx@s=m9i6i6i6HUJ> ďN~LH}Ct=l9f6l8i6i6HIF{Bu>p;j7i6ALEyAt>o:i6( NJH~Dx@q:.::9998<<:8;;;<<;:(G/2$4t4T43/(python-deadlib-3.13.0/imghdr/tests/imghdrdata/python.gif000066400000000000000000000006251471045437300232440ustar00rootroot00000000000000GIF89a?!*#$%-4=00699>:EADKNUXbnojl2`6f6g6i7h7j9l:o:p;pt?v@xB{C|C}EFGHHJLLLOPR! ImageMagickgamma=0!?,b-i| Uj2mQV4t2WTH~<M ?_64R*g" ?0.]M ?G."? ?1R? B+)?V ^ ^? >?CA;python-deadlib-3.13.0/imghdr/tests/imghdrdata/python.jpg000066400000000000000000000010371471045437300232550ustar00rootroot00000000000000JFIFC       C "$" 12#!1a ?jsԕc%eZiE@ Tʸ =l5wmWqK9"SiqJ8K*qi-!;tZuHe[{!)gkIeOȅ)K*z<~U b@=\CzeXQ!dh)<2`65hE6i1EEAF6gnjC}2Ko@u=sš=4*!ƛN?9/HD~@xLEAy>t:o6iIFB{>u;p7j8lOKD}@x=s9mUPLH=tKAMF;pB7-JGC|?v8#aWMB.$laC9:pbXN/D:0%:0&IVtRNS (A~H C$O=\LGkFN[oKz4!<5y  ,@L{;BE82qbKGDZIDATc`FfV6(gg $$&%spy\<|iB pzFXfVvN

t:o6iIFB{>u;p7j6iNLHC}=t9l6f8l6i6iROKHD}@x=s9m6i6i6iUJ>PLHEAy=t:o6i6i6i2`KA6MIFB{>u;p7j6i5h2`EB7-JGC|?v6i1EEA8-#FD}@x6gnjaWMB8.$C}Ay=t2KolaWMC9.$@u=s:plbXNC9/$šbXN=4*!ƛXND:0%ND:0&?9/&python-deadlib-3.13.0/imghdr/tests/imghdrdata/python.ras000066400000000000000000000020401471045437300232550ustar00rootroot00000000000000Yj NJHD~@xt:o6i( IFB{>u;p7j6iA~NĠLHC}=t9l6f8l6i6iHROKHD}@x=s9m6i6i6iHUJ> PLHEAy=t:o6i6i6i2`CKA6$MIFB{>u;p7j6i5h2`OEB7-=JGC|?v6i\1ELHGAkEA8-#FFD}@x6gNnjaWMB8.$GC}Ay=t[2KolaWMC9.$Ao@u=s:pKlbXNC9/$zš4!<5bXN=4*!yƛA  XND:0%A5, ND:0&@ L?9/&{;5BGE8python-deadlib-3.13.0/imghdr/tests/imghdrdata/python.sgi000066400000000000000000000036571471045437300232710ustar00rootroot00000000000000N3uHHy%Y@\$U2fT(p1b ?s'h<>o          5BGE8Ǘ?9/&L{;ND:0&@ XND:0% A5, ¾bXN=4*!!<5yA @=: usplbXNC9/$oK z4CA= }yt2KolaWMC9.$[ AFD@6}xgnjaWMB8.$NGJGC?6|vi1EEA8-#\LHGAkFMIFB>;7652{upjih`EB7-O=PLHEA=:62ytoi`üKA6 C$ROKHD@=96}xsmiUJ> H NLHC=96866}tlfliiĿ~HIFB>;76{upjiALEA>:6ytoi( NJHD@<7~xqh python-deadlib-3.13.0/imghdr/tests/imghdrdata/python.tiff000066400000000000000000000024561471045437300234330ustar00rootroot00000000000000II*NJHD~@xt:o6i( IFB{>u;p7j6iAN~LHC}=t9l6f8l6i6iHRƽOKHD}@x=s9m6i6i6iHUJ> PLHEAy=t:o6i6i6i2`CKA6$MIFB{>u;p7j6i5h2`OEB7-=JGC|?v6i1E\LHGAEkA8-#FFD}@x6gNnjaWMB8.$GC}Ay=t2K[olaWMC9.$A@uo=s:pKlbXNC9/$šz4!<5bXN=4*!ƛyA  XND:0%A5, ND:0&@ ?L9/&{;5BGE8   ")>?R ףzTLff&(\ PX9Tpython.tiffpython-deadlib-3.13.0/imghdr/tests/imghdrdata/python.webp000066400000000000000000000006601471045437300234330ustar00rootroot00000000000000RIFFWEBPVP8X ALPH'$z_*tq&0w2\+0xxQ5 OjUֶmF/Nv` for details). The :mod:`mimetypes` module provides an alternative. -------------- Mailcap files are used to configure how MIME-aware applications such as mail readers and web browsers react to files with different MIME types. (The name "mailcap" is derived from the phrase "mail capability".) For example, a mailcap file might contain a line like ``video/mpeg; xmpeg %s``. Then, if the user encounters an email message or web document with the MIME type :mimetype:`video/mpeg`, ``%s`` will be replaced by a filename (usually one belonging to a temporary file) and the :program:`xmpeg` program can be automatically started to view the file. The mailcap format is documented in :rfc:`1524`, "A User Agent Configuration Mechanism For Multimedia Mail Format Information", but is not an internet standard. However, mailcap files are supported on most Unix systems. .. function:: findmatch(caps, MIMEtype, key='view', filename='/dev/null', plist=[]) Return a 2-tuple; the first element is a string containing the command line to be executed (which can be passed to :func:`os.system`), and the second element is the mailcap entry for a given MIME type. If no matching MIME type can be found, ``(None, None)`` is returned. *key* is the name of the field desired, which represents the type of activity to be performed; the default value is 'view', since in the most common case you simply want to view the body of the MIME-typed data. Other possible values might be 'compose' and 'edit', if you wanted to create a new body of the given MIME type or alter the existing body data. See :rfc:`1524` for a complete list of these fields. *filename* is the filename to be substituted for ``%s`` in the command line; the default value is ``'/dev/null'`` which is almost certainly not what you want, so usually you'll override it by specifying a filename. *plist* can be a list containing named parameters; the default value is simply an empty list. Each entry in the list must be a string containing the parameter name, an equals sign (``'='``), and the parameter's value. Mailcap entries can contain named parameters like ``%{foo}``, which will be replaced by the value of the parameter named 'foo'. For example, if the command line ``showpartial %{id} %{number} %{total}`` was in a mailcap file, and *plist* was set to ``['id=1', 'number=2', 'total=3']``, the resulting command line would be ``'showpartial 1 2 3'``. In a mailcap file, the "test" field can optionally be specified to test some external condition (such as the machine architecture, or the window system in use) to determine whether or not the mailcap line applies. :func:`findmatch` will automatically check such conditions and skip the entry if the check fails. .. versionchanged:: 3.11 To prevent security issues with shell metacharacters (symbols that have special effects in a shell command line), ``findmatch`` will refuse to inject ASCII characters other than alphanumerics and ``@+=:,./-_`` into the returned command line. If a disallowed character appears in *filename*, ``findmatch`` will always return ``(None, None)`` as if no entry was found. If such a character appears elsewhere (a value in *plist* or in *MIMEtype*), ``findmatch`` will ignore all mailcap entries which use that value. A :mod:`warning ` will be raised in either case. .. function:: getcaps() Returns a dictionary mapping MIME types to a list of mailcap file entries. This dictionary must be passed to the :func:`findmatch` function. An entry is stored as a list of dictionaries, but it shouldn't be necessary to know the details of this representation. The information is derived from all of the mailcap files found on the system. Settings in the user's mailcap file :file:`$HOME/.mailcap` will override settings in the system mailcap files :file:`/etc/mailcap`, :file:`/usr/etc/mailcap`, and :file:`/usr/local/etc/mailcap`. An example usage:: >>> import mailcap >>> d = mailcap.getcaps() >>> mailcap.findmatch(d, 'video/mpeg', filename='tmp1223') ('xmpeg tmp1223', {'view': 'xmpeg %s'}) python-deadlib-3.13.0/mailcap/LICENSE000066400000000000000000000050111471045437300171210ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/mailcap/README.rst000066400000000000000000000005161471045437300176100ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/mailcap/mailcap/000077500000000000000000000000001471045437300175255ustar00rootroot00000000000000python-deadlib-3.13.0/mailcap/mailcap/__init__.py000066400000000000000000000223431471045437300216420ustar00rootroot00000000000000"""Mailcap file handling. See RFC 1524.""" import os import warnings import re __all__ = ["getcaps","findmatch"] # python-deadlib: Replace deprecation warning not to raise exception warnings.warn( f"{__name__} was removed in Python 3.13. See the mimetypes module for an alternative. " f"Please be aware that you are currently NOT using standard '{__name__}', " f"but instead a separately installed 'standard-{__name__}'.", DeprecationWarning, stacklevel=2 ) def lineno_sort_key(entry): # Sort in ascending order, with unspecified entries at the end if 'lineno' in entry: return 0, entry['lineno'] else: return 1, 0 _find_unsafe = re.compile(r'[^\xa1-\U0010FFFF\w@+=:,./-]').search class UnsafeMailcapInput(Warning): """Warning raised when refusing unsafe input""" # Part 1: top-level interface. def getcaps(): """Return a dictionary containing the mailcap database. The dictionary maps a MIME type (in all lowercase, e.g. 'text/plain') to a list of dictionaries corresponding to mailcap entries. The list collects all the entries for that MIME type from all available mailcap files. Each dictionary contains key-value pairs for that MIME type, where the viewing command is stored with the key "view". """ caps = {} lineno = 0 for mailcap in listmailcapfiles(): try: fp = open(mailcap, 'r') except OSError: continue with fp: morecaps, lineno = _readmailcapfile(fp, lineno) for key, value in morecaps.items(): if not key in caps: caps[key] = value else: caps[key] = caps[key] + value return caps def listmailcapfiles(): """Return a list of all mailcap files found on the system.""" # This is mostly a Unix thing, but we use the OS path separator anyway if 'MAILCAPS' in os.environ: pathstr = os.environ['MAILCAPS'] mailcaps = pathstr.split(os.pathsep) else: if 'HOME' in os.environ: home = os.environ['HOME'] else: # Don't bother with getpwuid() home = '.' # Last resort mailcaps = [home + '/.mailcap', '/etc/mailcap', '/usr/etc/mailcap', '/usr/local/etc/mailcap'] return mailcaps # Part 2: the parser. def readmailcapfile(fp): """Read a mailcap file and return a dictionary keyed by MIME type.""" warnings.warn('readmailcapfile is deprecated, use getcaps instead', DeprecationWarning, 2) caps, _ = _readmailcapfile(fp, None) return caps def _readmailcapfile(fp, lineno): """Read a mailcap file and return a dictionary keyed by MIME type. Each MIME type is mapped to an entry consisting of a list of dictionaries; the list will contain more than one such dictionary if a given MIME type appears more than once in the mailcap file. Each dictionary contains key-value pairs for that MIME type, where the viewing command is stored with the key "view". """ caps = {} while line := fp.readline(): # Ignore comments and blank lines if line[0] == '#' or line.strip() == '': continue nextline = line # Join continuation lines while nextline[-2:] == '\\\n': nextline = fp.readline() if not nextline: nextline = '\n' line = line[:-2] + nextline # Parse the line key, fields = parseline(line) if not (key and fields): continue if lineno is not None: fields['lineno'] = lineno lineno += 1 # Normalize the key types = key.split('/') for j in range(len(types)): types[j] = types[j].strip() key = '/'.join(types).lower() # Update the database if key in caps: caps[key].append(fields) else: caps[key] = [fields] return caps, lineno def parseline(line): """Parse one entry in a mailcap file and return a dictionary. The viewing command is stored as the value with the key "view", and the rest of the fields produce key-value pairs in the dict. """ fields = [] i, n = 0, len(line) while i < n: field, i = parsefield(line, i, n) fields.append(field) i = i+1 # Skip semicolon if len(fields) < 2: return None, None key, view, rest = fields[0], fields[1], fields[2:] fields = {'view': view} for field in rest: i = field.find('=') if i < 0: fkey = field fvalue = "" else: fkey = field[:i].strip() fvalue = field[i+1:].strip() if fkey in fields: # Ignore it pass else: fields[fkey] = fvalue return key, fields def parsefield(line, i, n): """Separate one key-value pair in a mailcap entry.""" start = i while i < n: c = line[i] if c == ';': break elif c == '\\': i = i+2 else: i = i+1 return line[start:i].strip(), i # Part 3: using the database. def findmatch(caps, MIMEtype, key='view', filename="/dev/null", plist=[]): """Find a match for a mailcap entry. Return a tuple containing the command line, and the mailcap entry used; (None, None) if no match is found. This may invoke the 'test' command of several matching entries before deciding which entry to use. """ if _find_unsafe(filename): msg = "Refusing to use mailcap with filename %r. Use a safe temporary filename." % (filename,) warnings.warn(msg, UnsafeMailcapInput) return None, None entries = lookup(caps, MIMEtype, key) # XXX This code should somehow check for the needsterminal flag. for e in entries: if 'test' in e: test = subst(e['test'], filename, plist) if test is None: continue if test and os.system(test) != 0: continue command = subst(e[key], MIMEtype, filename, plist) if command is not None: return command, e return None, None def lookup(caps, MIMEtype, key=None): entries = [] if MIMEtype in caps: entries = entries + caps[MIMEtype] MIMEtypes = MIMEtype.split('/') MIMEtype = MIMEtypes[0] + '/*' if MIMEtype in caps: entries = entries + caps[MIMEtype] if key is not None: entries = [e for e in entries if key in e] entries = sorted(entries, key=lineno_sort_key) return entries def subst(field, MIMEtype, filename, plist=[]): # XXX Actually, this is Unix-specific res = '' i, n = 0, len(field) while i < n: c = field[i]; i = i+1 if c != '%': if c == '\\': c = field[i:i+1]; i = i+1 res = res + c else: c = field[i]; i = i+1 if c == '%': res = res + c elif c == 's': res = res + filename elif c == 't': if _find_unsafe(MIMEtype): msg = "Refusing to substitute MIME type %r into a shell command." % (MIMEtype,) warnings.warn(msg, UnsafeMailcapInput) return None res = res + MIMEtype elif c == '{': start = i while i < n and field[i] != '}': i = i+1 name = field[start:i] i = i+1 param = findparam(name, plist) if _find_unsafe(param): msg = "Refusing to substitute parameter %r (%s) into a shell command" % (param, name) warnings.warn(msg, UnsafeMailcapInput) return None res = res + param # XXX To do: # %n == number of parts if type is multipart/* # %F == list of alternating type and filename for parts else: res = res + '%' + c return res def findparam(name, plist): name = name.lower() + '=' n = len(name) for p in plist: if p[:n].lower() == name: return p[n:] return '' # Part 4: test program. def test(): import sys caps = getcaps() if not sys.argv[1:]: show(caps) return for i in range(1, len(sys.argv), 2): args = sys.argv[i:i+2] if len(args) < 2: print("usage: mailcap [MIMEtype file] ...") return MIMEtype = args[0] file = args[1] command, e = findmatch(caps, MIMEtype, 'view', file) if not command: print("No viewer found for", type) else: print("Executing:", command) sts = os.system(command) sts = os.waitstatus_to_exitcode(sts) if sts: print("Exit status:", sts) def show(caps): print("Mailcap files:") for fn in listmailcapfiles(): print("\t" + fn) print() if not caps: caps = getcaps() print("Mailcap entries:") print() ckeys = sorted(caps) for type in ckeys: print(type) entries = caps[type] for e in entries: keys = sorted(e) for k in keys: print(" %-15s" % k, e[k]) print() if __name__ == '__main__': test() python-deadlib-3.13.0/mailcap/pyproject.toml000066400000000000000000000012731471045437300210360ustar00rootroot00000000000000[project] name = "standard-mailcap" version = "3.13.0" description = "Standard library mailcap redistribution. \"dead battery\"." keywords = ["stdlib"] authors = [ { name = "Python Developers", email = "python-deadlib@youknowone.org" } ] readme = "README.rst" license = { text = "PSF-2.0" } classifiers = [ "License :: OSI Approved :: Python Software Foundation License", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", ] [tool.setuptools.packages] find = {include = ["mailcap*"]} [project.urls] "Homepage" = "https://github.com/youknowone/python-deadlib" [build-system] requires = ["setuptools>=75.0"] build-backend = "setuptools.build_meta" python-deadlib-3.13.0/mailcap/tests/000077500000000000000000000000001471045437300172615ustar00rootroot00000000000000python-deadlib-3.13.0/mailcap/tests/__init__.py000066400000000000000000000000001471045437300213600ustar00rootroot00000000000000python-deadlib-3.13.0/mailcap/tests/mailcap.txt000066400000000000000000000023661471045437300214370ustar00rootroot00000000000000# Mailcap file for test_mailcap; based on RFC 1524 # Referred to by test_mailcap.py # # This is a comment. # application/frame; showframe %s; print="cat %s | lp" application/postscript; ps-to-terminal %s;\ needsterminal application/postscript; ps-to-terminal %s; \ compose=idraw %s application/x-dvi; xdvi %s application/x-movie; movieplayer %s; compose=moviemaker %s; \ description="Movie"; \ x11-bitmap="/usr/lib/Zmail/bitmaps/movie.xbm" application/*; echo "This is \"%t\" but \ is 50 \% Greek to me" \; cat %s; copiousoutput audio/basic; showaudio %s; compose=audiocompose %s; edit=audiocompose %s;\ description="An audio fragment" audio/* ; /usr/local/bin/showaudio %t image/rgb; display %s #image/gif; display %s image/x-xwindowdump; display %s # The continuation char shouldn't \ # make a difference in a comment. message/external-body; showexternal %s %{access-type} %{name} %{site} \ %{directory} %{mode} %{server}; needsterminal; composetyped = extcompose %s; \ description="A reference to data stored in an external location" text/richtext; shownonascii iso-8859-8 -e richtext -p %s; test=test "`echo \ %{charset} | tr '[A-Z]' '[a-z]'`" = iso-8859-8; copiousoutput video/*; animate %s video/mpeg; mpeg_play %spython-deadlib-3.13.0/mailcap/tests/test_mailcap.py000066400000000000000000000267551471045437300223170ustar00rootroot00000000000000import copy import os import sys import test.support import unittest from test.support import os_helper from test.support import warnings_helper mailcap = warnings_helper.import_deprecated('mailcap') # Location of mailcap file MAILCAPFILE = test.support.findfile("tests/mailcap.txt") # Dict to act as mock mailcap entry for this test # The keys and values should match the contents of MAILCAPFILE MAILCAPDICT = { 'application/x-movie': [{'compose': 'moviemaker %s', 'x11-bitmap': '"/usr/lib/Zmail/bitmaps/movie.xbm"', 'description': '"Movie"', 'view': 'movieplayer %s', 'lineno': 4}], 'application/*': [{'copiousoutput': '', 'view': 'echo "This is \\"%t\\" but is 50 \\% Greek to me" \\; cat %s', 'lineno': 5}], 'audio/basic': [{'edit': 'audiocompose %s', 'compose': 'audiocompose %s', 'description': '"An audio fragment"', 'view': 'showaudio %s', 'lineno': 6}], 'video/mpeg': [{'view': 'mpeg_play %s', 'lineno': 13}], 'application/postscript': [{'needsterminal': '', 'view': 'ps-to-terminal %s', 'lineno': 1}, {'compose': 'idraw %s', 'view': 'ps-to-terminal %s', 'lineno': 2}], 'application/x-dvi': [{'view': 'xdvi %s', 'lineno': 3}], 'message/external-body': [{'composetyped': 'extcompose %s', 'description': '"A reference to data stored in an external location"', 'needsterminal': '', 'view': 'showexternal %s %{access-type} %{name} %{site} %{directory} %{mode} %{server}', 'lineno': 10}], 'text/richtext': [{'test': 'test "`echo %{charset} | tr \'[A-Z]\' \'[a-z]\'`" = iso-8859-8', 'copiousoutput': '', 'view': 'shownonascii iso-8859-8 -e richtext -p %s', 'lineno': 11}], 'image/x-xwindowdump': [{'view': 'display %s', 'lineno': 9}], 'audio/*': [{'view': '/usr/local/bin/showaudio %t', 'lineno': 7}], 'video/*': [{'view': 'animate %s', 'lineno': 12}], 'application/frame': [{'print': '"cat %s | lp"', 'view': 'showframe %s', 'lineno': 0}], 'image/rgb': [{'view': 'display %s', 'lineno': 8}] } # For backwards compatibility, readmailcapfile() and lookup() still support # the old version of mailcapdict without line numbers. MAILCAPDICT_DEPRECATED = copy.deepcopy(MAILCAPDICT) for entry_list in MAILCAPDICT_DEPRECATED.values(): for entry in entry_list: entry.pop('lineno') class HelperFunctionTest(unittest.TestCase): def test_listmailcapfiles(self): # The return value for listmailcapfiles() will vary by system. # So verify that listmailcapfiles() returns a list of strings that is of # non-zero length. mcfiles = mailcap.listmailcapfiles() self.assertIsInstance(mcfiles, list) for m in mcfiles: self.assertIsInstance(m, str) with os_helper.EnvironmentVarGuard() as env: # According to RFC 1524, if MAILCAPS env variable exists, use that # and only that. if "MAILCAPS" in env: env_mailcaps = env["MAILCAPS"].split(os.pathsep) else: env_mailcaps = ["/testdir1/.mailcap", "/testdir2/mailcap"] env["MAILCAPS"] = os.pathsep.join(env_mailcaps) mcfiles = mailcap.listmailcapfiles() self.assertEqual(env_mailcaps, mcfiles) def test_readmailcapfile(self): # Test readmailcapfile() using test file. It should match MAILCAPDICT. with open(MAILCAPFILE, 'r') as mcf: with self.assertWarns(DeprecationWarning): d = mailcap.readmailcapfile(mcf) self.assertDictEqual(d, MAILCAPDICT_DEPRECATED) def test_lookup(self): # Test without key expected = [{'view': 'animate %s', 'lineno': 12}, {'view': 'mpeg_play %s', 'lineno': 13}] actual = mailcap.lookup(MAILCAPDICT, 'video/mpeg') self.assertListEqual(expected, actual) # Test with key key = 'compose' expected = [{'edit': 'audiocompose %s', 'compose': 'audiocompose %s', 'description': '"An audio fragment"', 'view': 'showaudio %s', 'lineno': 6}] actual = mailcap.lookup(MAILCAPDICT, 'audio/basic', key) self.assertListEqual(expected, actual) # Test on user-defined dicts without line numbers expected = [{'view': 'mpeg_play %s'}, {'view': 'animate %s'}] actual = mailcap.lookup(MAILCAPDICT_DEPRECATED, 'video/mpeg') self.assertListEqual(expected, actual) def test_subst(self): plist = ['id=1', 'number=2', 'total=3'] # test case: ([field, MIMEtype, filename, plist=[]], ) test_cases = [ (["", "audio/*", "foo.txt"], ""), (["echo foo", "audio/*", "foo.txt"], "echo foo"), (["echo %s", "audio/*", "foo.txt"], "echo foo.txt"), (["echo %t", "audio/wav", "foo.txt"], "echo audio/wav"), (["echo \\%t", "audio/*", "foo.txt"], "echo %t"), (["echo foo", "audio/*", "foo.txt", plist], "echo foo"), (["echo %{total}", "audio/*", "foo.txt", plist], "echo 3") ] for tc in test_cases: self.assertEqual(mailcap.subst(*tc[0]), tc[1]) class GetcapsTest(unittest.TestCase): def test_mock_getcaps(self): # Test mailcap.getcaps() using mock mailcap file in this dir. # Temporarily override any existing system mailcap file by pointing the # MAILCAPS environment variable to our mock file. with os_helper.EnvironmentVarGuard() as env: env["MAILCAPS"] = MAILCAPFILE caps = mailcap.getcaps() self.assertDictEqual(caps, MAILCAPDICT) def test_system_mailcap(self): # Test mailcap.getcaps() with mailcap file(s) on system, if any. caps = mailcap.getcaps() self.assertIsInstance(caps, dict) mailcapfiles = mailcap.listmailcapfiles() existingmcfiles = [mcf for mcf in mailcapfiles if os.path.exists(mcf)] if existingmcfiles: # At least 1 mailcap file exists, so test that. for (k, v) in caps.items(): self.assertIsInstance(k, str) self.assertIsInstance(v, list) for e in v: self.assertIsInstance(e, dict) else: # No mailcap files on system. getcaps() should return empty dict. self.assertEqual({}, caps) class FindmatchTest(unittest.TestCase): def test_findmatch(self): # default findmatch arguments c = MAILCAPDICT fname = "foo.txt" plist = ["access-type=default", "name=john", "site=python.org", "directory=/tmp", "mode=foo", "server=bar"] audio_basic_entry = { 'edit': 'audiocompose %s', 'compose': 'audiocompose %s', 'description': '"An audio fragment"', 'view': 'showaudio %s', 'lineno': 6 } audio_entry = {"view": "/usr/local/bin/showaudio %t", 'lineno': 7} video_entry = {'view': 'animate %s', 'lineno': 12} message_entry = { 'composetyped': 'extcompose %s', 'description': '"A reference to data stored in an external location"', 'needsterminal': '', 'view': 'showexternal %s %{access-type} %{name} %{site} %{directory} %{mode} %{server}', 'lineno': 10, } # test case: (findmatch args, findmatch keyword args, expected output) # positional args: caps, MIMEtype # keyword args: key="view", filename="/dev/null", plist=[] # output: (command line, mailcap entry) cases = [ ([{}, "video/mpeg"], {}, (None, None)), ([c, "foo/bar"], {}, (None, None)), ([c, "video/mpeg"], {}, ('animate /dev/null', video_entry)), ([c, "audio/basic", "edit"], {}, ("audiocompose /dev/null", audio_basic_entry)), ([c, "audio/basic", "compose"], {}, ("audiocompose /dev/null", audio_basic_entry)), ([c, "audio/basic", "description"], {}, ('"An audio fragment"', audio_basic_entry)), ([c, "audio/basic", "foobar"], {}, (None, None)), ([c, "video/*"], {"filename": fname}, ("animate %s" % fname, video_entry)), ([c, "audio/basic", "compose"], {"filename": fname}, ("audiocompose %s" % fname, audio_basic_entry)), ([c, "audio/basic"], {"key": "description", "filename": fname}, ('"An audio fragment"', audio_basic_entry)), ([c, "audio/wav"], {"filename": fname}, ("/usr/local/bin/showaudio audio/wav", audio_entry)), ([c, "message/external-body"], {"plist": plist}, ("showexternal /dev/null default john python.org /tmp foo bar", message_entry)) ] self._run_cases(cases) @unittest.skipUnless(os.name == "posix", "Requires 'test' command on system") @unittest.skipIf(sys.platform == "vxworks", "'test' command is not supported on VxWorks") @unittest.skipUnless( test.support.has_subprocess_support, "'test' command needs process support." ) def test_test(self): # findmatch() will automatically check any "test" conditions and skip # the entry if the check fails. caps = {"test/pass": [{"test": "test 1 -eq 1"}], "test/fail": [{"test": "test 1 -eq 0"}]} # test case: (findmatch args, findmatch keyword args, expected output) # positional args: caps, MIMEtype, key ("test") # keyword args: N/A # output: (command line, mailcap entry) cases = [ # findmatch will return the mailcap entry for test/pass because it evaluates to true ([caps, "test/pass", "test"], {}, ("test 1 -eq 1", {"test": "test 1 -eq 1"})), # findmatch will return None because test/fail evaluates to false ([caps, "test/fail", "test"], {}, (None, None)) ] self._run_cases(cases) def test_unsafe_mailcap_input(self): with self.assertWarnsRegex(mailcap.UnsafeMailcapInput, 'Refusing to substitute parameter.*' 'into a shell command'): unsafe_param = mailcap.subst("echo %{total}", "audio/wav", "foo.txt", ["total=*"]) self.assertEqual(unsafe_param, None) with self.assertWarnsRegex(mailcap.UnsafeMailcapInput, 'Refusing to substitute MIME type' '.*into a shell'): unsafe_mimetype = mailcap.subst("echo %t", "audio/*", "foo.txt") self.assertEqual(unsafe_mimetype, None) with self.assertWarnsRegex(mailcap.UnsafeMailcapInput, 'Refusing to use mailcap with filename.*' 'Use a safe temporary filename.'): unsafe_filename = mailcap.findmatch(MAILCAPDICT, "audio/wav", filename="foo*.txt") self.assertEqual(unsafe_filename, (None, None)) def _run_cases(self, cases): for c in cases: self.assertEqual(mailcap.findmatch(*c[0], **c[1]), c[2]) if __name__ == '__main__': unittest.main() python-deadlib-3.13.0/nntplib/000077500000000000000000000000001471045437300161575ustar00rootroot00000000000000python-deadlib-3.13.0/nntplib/Doc/000077500000000000000000000000001471045437300166645ustar00rootroot00000000000000python-deadlib-3.13.0/nntplib/Doc/nntplib.rst000066400000000000000000000532001471045437300210640ustar00rootroot00000000000000:mod:`nntplib` --- NNTP protocol client ======================================= .. module:: nntplib :synopsis: NNTP protocol client (requires sockets). :deprecated: **Source code:** :source:`Lib/nntplib.py` .. index:: pair: NNTP; protocol single: Network News Transfer Protocol .. deprecated:: 3.11 The :mod:`nntplib` module is deprecated (see :pep:`594` for details). .. testsetup:: import warnings with warnings.catch_warnings(): warnings.simplefilter('ignore', category=DeprecationWarning) import nntplib .. testcleanup:: try: s.quit() except NameError: pass import sys # Force a warning if any other file imports nntplib sys.modules.pop('nntplib') -------------- This module defines the class :class:`NNTP` which implements the client side of the Network News Transfer Protocol. It can be used to implement a news reader or poster, or automated news processors. It is compatible with :rfc:`3977` as well as the older :rfc:`977` and :rfc:`2980`. .. include:: ../includes/wasm-notavail.rst Here are two small examples of how it can be used. To list some statistics about a newsgroup and print the subjects of the last 10 articles:: >>> s = nntplib.NNTP('news.gmane.io') >>> resp, count, first, last, name = s.group('gmane.comp.python.committers') >>> print('Group', name, 'has', count, 'articles, range', first, 'to', last) Group gmane.comp.python.committers has 1096 articles, range 1 to 1096 >>> resp, overviews = s.over((last - 9, last)) >>> for id, over in overviews: ... print(id, nntplib.decode_header(over['subject'])) ... 1087 Re: Commit privileges for Łukasz Langa 1088 Re: 3.2 alpha 2 freeze 1089 Re: 3.2 alpha 2 freeze 1090 Re: Commit privileges for Łukasz Langa 1091 Re: Commit privileges for Łukasz Langa 1092 Updated ssh key 1093 Re: Updated ssh key 1094 Re: Updated ssh key 1095 Hello fellow committers! 1096 Re: Hello fellow committers! >>> s.quit() '205 Bye!' To post an article from a binary file (this assumes that the article has valid headers, and that you have right to post on the particular newsgroup):: >>> s = nntplib.NNTP('news.gmane.io') >>> f = open('article.txt', 'rb') >>> s.post(f) '240 Article posted successfully.' >>> s.quit() '205 Bye!' The module itself defines the following classes: .. class:: NNTP(host, port=119, user=None, password=None, readermode=None, usenetrc=False, [timeout]) Return a new :class:`NNTP` object, representing a connection to the NNTP server running on host *host*, listening at port *port*. An optional *timeout* can be specified for the socket connection. If the optional *user* and *password* are provided, or if suitable credentials are present in :file:`/.netrc` and the optional flag *usenetrc* is true, the ``AUTHINFO USER`` and ``AUTHINFO PASS`` commands are used to identify and authenticate the user to the server. If the optional flag *readermode* is true, then a ``mode reader`` command is sent before authentication is performed. Reader mode is sometimes necessary if you are connecting to an NNTP server on the local machine and intend to call reader-specific commands, such as ``group``. If you get unexpected :exc:`NNTPPermanentError`\ s, you might need to set *readermode*. The :class:`NNTP` class supports the :keyword:`with` statement to unconditionally consume :exc:`OSError` exceptions and to close the NNTP connection when done, e.g.: >>> from nntplib import NNTP >>> with NNTP('news.gmane.io') as n: ... n.group('gmane.comp.python.committers') ... # doctest: +SKIP ('211 1755 1 1755 gmane.comp.python.committers', 1755, 1, 1755, 'gmane.comp.python.committers') >>> .. audit-event:: nntplib.connect self,host,port nntplib.NNTP .. audit-event:: nntplib.putline self,line nntplib.NNTP All commands will raise an :ref:`auditing event ` ``nntplib.putline`` with arguments ``self`` and ``line``, where ``line`` is the bytes about to be sent to the remote host. .. versionchanged:: 3.2 *usenetrc* is now ``False`` by default. .. versionchanged:: 3.3 Support for the :keyword:`with` statement was added. .. versionchanged:: 3.9 If the *timeout* parameter is set to be zero, it will raise a :class:`ValueError` to prevent the creation of a non-blocking socket. .. class:: NNTP_SSL(host, port=563, user=None, password=None, ssl_context=None, readermode=None, usenetrc=False, [timeout]) Return a new :class:`NNTP_SSL` object, representing an encrypted connection to the NNTP server running on host *host*, listening at port *port*. :class:`NNTP_SSL` objects have the same methods as :class:`NNTP` objects. If *port* is omitted, port 563 (NNTPS) is used. *ssl_context* is also optional, and is a :class:`~ssl.SSLContext` object. Please read :ref:`ssl-security` for best practices. All other parameters behave the same as for :class:`NNTP`. Note that SSL-on-563 is discouraged per :rfc:`4642`, in favor of STARTTLS as described below. However, some servers only support the former. .. audit-event:: nntplib.connect self,host,port nntplib.NNTP_SSL .. audit-event:: nntplib.putline self,line nntplib.NNTP_SSL All commands will raise an :ref:`auditing event ` ``nntplib.putline`` with arguments ``self`` and ``line``, where ``line`` is the bytes about to be sent to the remote host. .. versionadded:: 3.2 .. versionchanged:: 3.4 The class now supports hostname check with :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see :data:`ssl.HAS_SNI`). .. versionchanged:: 3.9 If the *timeout* parameter is set to be zero, it will raise a :class:`ValueError` to prevent the creation of a non-blocking socket. .. exception:: NNTPError Derived from the standard exception :exc:`Exception`, this is the base class for all exceptions raised by the :mod:`nntplib` module. Instances of this class have the following attribute: .. attribute:: response The response of the server if available, as a :class:`str` object. .. exception:: NNTPReplyError Exception raised when an unexpected reply is received from the server. .. exception:: NNTPTemporaryError Exception raised when a response code in the range 400--499 is received. .. exception:: NNTPPermanentError Exception raised when a response code in the range 500--599 is received. .. exception:: NNTPProtocolError Exception raised when a reply is received from the server that does not begin with a digit in the range 1--5. .. exception:: NNTPDataError Exception raised when there is some error in the response data. .. _nntp-objects: NNTP Objects ------------ When connected, :class:`NNTP` and :class:`NNTP_SSL` objects support the following methods and attributes. Attributes ^^^^^^^^^^ .. attribute:: NNTP.nntp_version An integer representing the version of the NNTP protocol supported by the server. In practice, this should be ``2`` for servers advertising :rfc:`3977` compliance and ``1`` for others. .. versionadded:: 3.2 .. attribute:: NNTP.nntp_implementation A string describing the software name and version of the NNTP server, or :const:`None` if not advertised by the server. .. versionadded:: 3.2 Methods ^^^^^^^ The *response* that is returned as the first item in the return tuple of almost all methods is the server's response: a string beginning with a three-digit code. If the server's response indicates an error, the method raises one of the above exceptions. Many of the following methods take an optional keyword-only argument *file*. When the *file* argument is supplied, it must be either a :term:`file object` opened for binary writing, or the name of an on-disk file to be written to. The method will then write any data returned by the server (except for the response line and the terminating dot) to the file; any list of lines, tuples or objects that the method normally returns will be empty. .. versionchanged:: 3.2 Many of the following methods have been reworked and fixed, which makes them incompatible with their 3.1 counterparts. .. method:: NNTP.quit() Send a ``QUIT`` command and close the connection. Once this method has been called, no other methods of the NNTP object should be called. .. method:: NNTP.getwelcome() Return the welcome message sent by the server in reply to the initial connection. (This message sometimes contains disclaimers or help information that may be relevant to the user.) .. method:: NNTP.getcapabilities() Return the :rfc:`3977` capabilities advertised by the server, as a :class:`dict` instance mapping capability names to (possibly empty) lists of values. On legacy servers which don't understand the ``CAPABILITIES`` command, an empty dictionary is returned instead. >>> s = NNTP('news.gmane.io') >>> 'POST' in s.getcapabilities() True .. versionadded:: 3.2 .. method:: NNTP.login(user=None, password=None, usenetrc=True) Send ``AUTHINFO`` commands with the user name and password. If *user* and *password* are ``None`` and *usenetrc* is true, credentials from ``~/.netrc`` will be used if possible. Unless intentionally delayed, login is normally performed during the :class:`NNTP` object initialization and separately calling this function is unnecessary. To force authentication to be delayed, you must not set *user* or *password* when creating the object, and must set *usenetrc* to False. .. versionadded:: 3.2 .. method:: NNTP.starttls(context=None) Send a ``STARTTLS`` command. This will enable encryption on the NNTP connection. The *context* argument is optional and should be a :class:`ssl.SSLContext` object. Please read :ref:`ssl-security` for best practices. Note that this may not be done after authentication information has been transmitted, and authentication occurs by default if possible during a :class:`NNTP` object initialization. See :meth:`NNTP.login` for information on suppressing this behavior. .. versionadded:: 3.2 .. versionchanged:: 3.4 The method now supports hostname check with :attr:`ssl.SSLContext.check_hostname` and *Server Name Indication* (see :data:`ssl.HAS_SNI`). .. method:: NNTP.newgroups(date, *, file=None) Send a ``NEWGROUPS`` command. The *date* argument should be a :class:`datetime.date` or :class:`datetime.datetime` object. Return a pair ``(response, groups)`` where *groups* is a list representing the groups that are new since the given *date*. If *file* is supplied, though, then *groups* will be empty. >>> from datetime import date, timedelta >>> resp, groups = s.newgroups(date.today() - timedelta(days=3)) >>> len(groups) # doctest: +SKIP 85 >>> groups[0] # doctest: +SKIP GroupInfo(group='gmane.network.tor.devel', last='4', first='1', flag='m') .. method:: NNTP.newnews(group, date, *, file=None) Send a ``NEWNEWS`` command. Here, *group* is a group name or ``'*'``, and *date* has the same meaning as for :meth:`newgroups`. Return a pair ``(response, articles)`` where *articles* is a list of message ids. This command is frequently disabled by NNTP server administrators. .. method:: NNTP.list(group_pattern=None, *, file=None) Send a ``LIST`` or ``LIST ACTIVE`` command. Return a pair ``(response, list)`` where *list* is a list of tuples representing all the groups available from this NNTP server, optionally matching the pattern string *group_pattern*. Each tuple has the form ``(group, last, first, flag)``, where *group* is a group name, *last* and *first* are the last and first article numbers, and *flag* usually takes one of these values: * ``y``: Local postings and articles from peers are allowed. * ``m``: The group is moderated and all postings must be approved. * ``n``: No local postings are allowed, only articles from peers. * ``j``: Articles from peers are filed in the junk group instead. * ``x``: No local postings, and articles from peers are ignored. * ``=foo.bar``: Articles are filed in the ``foo.bar`` group instead. If *flag* has another value, then the status of the newsgroup should be considered unknown. This command can return very large results, especially if *group_pattern* is not specified. It is best to cache the results offline unless you really need to refresh them. .. versionchanged:: 3.2 *group_pattern* was added. .. method:: NNTP.descriptions(grouppattern) Send a ``LIST NEWSGROUPS`` command, where *grouppattern* is a wildmat string as specified in :rfc:`3977` (it's essentially the same as DOS or UNIX shell wildcard strings). Return a pair ``(response, descriptions)``, where *descriptions* is a dictionary mapping group names to textual descriptions. >>> resp, descs = s.descriptions('gmane.comp.python.*') >>> len(descs) # doctest: +SKIP 295 >>> descs.popitem() # doctest: +SKIP ('gmane.comp.python.bio.general', 'BioPython discussion list (Moderated)') .. method:: NNTP.description(group) Get a description for a single group *group*. If more than one group matches (if 'group' is a real wildmat string), return the first match. If no group matches, return an empty string. This elides the response code from the server. If the response code is needed, use :meth:`descriptions`. .. method:: NNTP.group(name) Send a ``GROUP`` command, where *name* is the group name. The group is selected as the current group, if it exists. Return a tuple ``(response, count, first, last, name)`` where *count* is the (estimated) number of articles in the group, *first* is the first article number in the group, *last* is the last article number in the group, and *name* is the group name. .. method:: NNTP.over(message_spec, *, file=None) Send an ``OVER`` command, or an ``XOVER`` command on legacy servers. *message_spec* can be either a string representing a message id, or a ``(first, last)`` tuple of numbers indicating a range of articles in the current group, or a ``(first, None)`` tuple indicating a range of articles starting from *first* to the last article in the current group, or :const:`None` to select the current article in the current group. Return a pair ``(response, overviews)``. *overviews* is a list of ``(article_number, overview)`` tuples, one for each article selected by *message_spec*. Each *overview* is a dictionary with the same number of items, but this number depends on the server. These items are either message headers (the key is then the lower-cased header name) or metadata items (the key is then the metadata name prepended with ``":"``). The following items are guaranteed to be present by the NNTP specification: * the ``subject``, ``from``, ``date``, ``message-id`` and ``references`` headers * the ``:bytes`` metadata: the number of bytes in the entire raw article (including headers and body) * the ``:lines`` metadata: the number of lines in the article body The value of each item is either a string, or :const:`None` if not present. It is advisable to use the :func:`decode_header` function on header values when they may contain non-ASCII characters:: >>> _, _, first, last, _ = s.group('gmane.comp.python.devel') >>> resp, overviews = s.over((last, last)) >>> art_num, over = overviews[0] >>> art_num 117216 >>> list(over.keys()) ['xref', 'from', ':lines', ':bytes', 'references', 'date', 'message-id', 'subject'] >>> over['from'] '=?UTF-8?B?Ik1hcnRpbiB2LiBMw7Z3aXMi?= ' >>> nntplib.decode_header(over['from']) '"Martin v. Löwis" ' .. versionadded:: 3.2 .. method:: NNTP.help(*, file=None) Send a ``HELP`` command. Return a pair ``(response, list)`` where *list* is a list of help strings. .. method:: NNTP.stat(message_spec=None) Send a ``STAT`` command, where *message_spec* is either a message id (enclosed in ``'<'`` and ``'>'``) or an article number in the current group. If *message_spec* is omitted or :const:`None`, the current article in the current group is considered. Return a triple ``(response, number, id)`` where *number* is the article number and *id* is the message id. >>> _, _, first, last, _ = s.group('gmane.comp.python.devel') >>> resp, number, message_id = s.stat(first) >>> number, message_id (9099, '<20030112190404.GE29873@epoch.metaslash.com>') .. method:: NNTP.next() Send a ``NEXT`` command. Return as for :meth:`.stat`. .. method:: NNTP.last() Send a ``LAST`` command. Return as for :meth:`.stat`. .. method:: NNTP.article(message_spec=None, *, file=None) Send an ``ARTICLE`` command, where *message_spec* has the same meaning as for :meth:`.stat`. Return a tuple ``(response, info)`` where *info* is a :class:`~collections.namedtuple` with three attributes *number*, *message_id* and *lines* (in that order). *number* is the article number in the group (or 0 if the information is not available), *message_id* the message id as a string, and *lines* a list of lines (without terminating newlines) comprising the raw message including headers and body. >>> resp, info = s.article('<20030112190404.GE29873@epoch.metaslash.com>') >>> info.number 0 >>> info.message_id '<20030112190404.GE29873@epoch.metaslash.com>' >>> len(info.lines) 65 >>> info.lines[0] b'Path: main.gmane.org!not-for-mail' >>> info.lines[1] b'From: Neal Norwitz ' >>> info.lines[-3:] [b'There is a patch for 2.3 as well as 2.2.', b'', b'Neal'] .. method:: NNTP.head(message_spec=None, *, file=None) Same as :meth:`article()`, but sends a ``HEAD`` command. The *lines* returned (or written to *file*) will only contain the message headers, not the body. .. method:: NNTP.body(message_spec=None, *, file=None) Same as :meth:`article()`, but sends a ``BODY`` command. The *lines* returned (or written to *file*) will only contain the message body, not the headers. .. method:: NNTP.post(data) Post an article using the ``POST`` command. The *data* argument is either a :term:`file object` opened for binary reading, or any iterable of bytes objects (representing raw lines of the article to be posted). It should represent a well-formed news article, including the required headers. The :meth:`post` method automatically escapes lines beginning with ``.`` and appends the termination line. If the method succeeds, the server's response is returned. If the server refuses posting, a :class:`NNTPReplyError` is raised. .. method:: NNTP.ihave(message_id, data) Send an ``IHAVE`` command. *message_id* is the id of the message to send to the server (enclosed in ``'<'`` and ``'>'``). The *data* parameter and the return value are the same as for :meth:`post()`. .. method:: NNTP.date() Return a pair ``(response, date)``. *date* is a :class:`~datetime.datetime` object containing the current date and time of the server. .. method:: NNTP.slave() Send a ``SLAVE`` command. Return the server's *response*. .. method:: NNTP.set_debuglevel(level) Set the instance's debugging level. This controls the amount of debugging output printed. The default, ``0``, produces no debugging output. A value of ``1`` produces a moderate amount of debugging output, generally a single line per request or response. A value of ``2`` or higher produces the maximum amount of debugging output, logging each line sent and received on the connection (including message text). The following are optional NNTP extensions defined in :rfc:`2980`. Some of them have been superseded by newer commands in :rfc:`3977`. .. method:: NNTP.xhdr(hdr, str, *, file=None) Send an ``XHDR`` command. The *hdr* argument is a header keyword, e.g. ``'subject'``. The *str* argument should have the form ``'first-last'`` where *first* and *last* are the first and last article numbers to search. Return a pair ``(response, list)``, where *list* is a list of pairs ``(id, text)``, where *id* is an article number (as a string) and *text* is the text of the requested header for that article. If the *file* parameter is supplied, then the output of the ``XHDR`` command is stored in a file. If *file* is a string, then the method will open a file with that name, write to it then close it. If *file* is a :term:`file object`, then it will start calling :meth:`write` on it to store the lines of the command output. If *file* is supplied, then the returned *list* is an empty list. .. method:: NNTP.xover(start, end, *, file=None) Send an ``XOVER`` command. *start* and *end* are article numbers delimiting the range of articles to select. The return value is the same of for :meth:`over()`. It is recommended to use :meth:`over()` instead, since it will automatically use the newer ``OVER`` command if available. Utility functions ----------------- The module also defines the following utility function: .. function:: decode_header(header_str) Decode a header value, un-escaping any escaped non-ASCII characters. *header_str* must be a :class:`str` object. The unescaped value is returned. Using this function is recommended to display some headers in a human readable form:: >>> decode_header("Some subject") 'Some subject' >>> decode_header("=?ISO-8859-15?Q?D=E9buter_en_Python?=") 'Débuter en Python' >>> decode_header("Re: =?UTF-8?B?cHJvYmzDqG1lIGRlIG1hdHJpY2U=?=") 'Re: problème de matrice' python-deadlib-3.13.0/nntplib/LICENSE000066400000000000000000000050111471045437300171610ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/nntplib/README.rst000066400000000000000000000005161471045437300176500ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/nntplib/nntplib/000077500000000000000000000000001471045437300176255ustar00rootroot00000000000000python-deadlib-3.13.0/nntplib/nntplib/__init__.py000066400000000000000000001206141471045437300217420ustar00rootroot00000000000000"""An NNTP client class based on: - RFC 977: Network News Transfer Protocol - RFC 2980: Common NNTP Extensions - RFC 3977: Network News Transfer Protocol (version 2) Example: >>> from nntplib import NNTP >>> s = NNTP('news') >>> resp, count, first, last, name = s.group('comp.lang.python') >>> print('Group', name, 'has', count, 'articles, range', first, 'to', last) Group comp.lang.python has 51 articles, range 5770 to 5821 >>> resp, subs = s.xhdr('subject', '{0}-{1}'.format(first, last)) >>> resp = s.quit() >>> Here 'resp' is the server response line. Error responses are turned into exceptions. To post an article from a file: >>> f = open(filename, 'rb') # file containing article, including header >>> resp = s.post(f) >>> For descriptions of all methods, read the comments in the code below. Note that all arguments and return values representing article numbers are strings, not numbers, since they are rarely used for calculations. """ # RFC 977 by Brian Kantor and Phil Lapsley. # xover, xgtitle, xpath, date methods by Kevan Heydon # Incompatible changes from the 2.x nntplib: # - all commands are encoded as UTF-8 data (using the "surrogateescape" # error handler), except for raw message data (POST, IHAVE) # - all responses are decoded as UTF-8 data (using the "surrogateescape" # error handler), except for raw message data (ARTICLE, HEAD, BODY) # - the `file` argument to various methods is keyword-only # # - NNTP.date() returns a datetime object # - NNTP.newgroups() and NNTP.newnews() take a datetime (or date) object, # rather than a pair of (date, time) strings. # - NNTP.newgroups() and NNTP.list() return a list of GroupInfo named tuples # - NNTP.descriptions() returns a dict mapping group names to descriptions # - NNTP.xover() returns a list of dicts mapping field names (header or metadata) # to field values; each dict representing a message overview. # - NNTP.article(), NNTP.head() and NNTP.body() return a (response, ArticleInfo) # tuple. # - the "internal" methods have been marked private (they now start with # an underscore) # Other changes from the 2.x/3.1 nntplib: # - automatic querying of capabilities at connect # - New method NNTP.getcapabilities() # - New method NNTP.over() # - New helper function decode_header() # - NNTP.post() and NNTP.ihave() accept file objects, bytes-like objects and # arbitrary iterables yielding lines. # - An extensive test suite :-) # TODO: # - return structured data (GroupInfo etc.) everywhere # - support HDR # Imports import re import socket import collections import datetime import sys import warnings try: import ssl except ImportError: _have_ssl = False else: _have_ssl = True from email.header import decode_header as _email_decode_header from socket import _GLOBAL_DEFAULT_TIMEOUT __all__ = ["NNTP", "NNTPError", "NNTPReplyError", "NNTPTemporaryError", "NNTPPermanentError", "NNTPProtocolError", "NNTPDataError", "decode_header", ] # python-deadlib: Replace deprecation warning not to raise exception warnings.warn( f"{__name__} was removed in Python 3.13. " f"Please be aware that you are currently NOT using standard '{__name__}', " f"but instead a separately installed 'standard-{__name__}'.", DeprecationWarning, stacklevel=2 ) # maximal line length when calling readline(). This is to prevent # reading arbitrary length lines. RFC 3977 limits NNTP line length to # 512 characters, including CRLF. We have selected 2048 just to be on # the safe side. _MAXLINE = 2048 # Exceptions raised when an error or invalid response is received class NNTPError(Exception): """Base class for all nntplib exceptions""" def __init__(self, *args): Exception.__init__(self, *args) try: self.response = args[0] except IndexError: self.response = 'No response given' class NNTPReplyError(NNTPError): """Unexpected [123]xx reply""" pass class NNTPTemporaryError(NNTPError): """4xx errors""" pass class NNTPPermanentError(NNTPError): """5xx errors""" pass class NNTPProtocolError(NNTPError): """Response does not begin with [1-5]""" pass class NNTPDataError(NNTPError): """Error in response data""" pass # Standard port used by NNTP servers NNTP_PORT = 119 NNTP_SSL_PORT = 563 # Response numbers that are followed by additional text (e.g. article) _LONGRESP = { '100', # HELP '101', # CAPABILITIES '211', # LISTGROUP (also not multi-line with GROUP) '215', # LIST '220', # ARTICLE '221', # HEAD, XHDR '222', # BODY '224', # OVER, XOVER '225', # HDR '230', # NEWNEWS '231', # NEWGROUPS '282', # XGTITLE } # Default decoded value for LIST OVERVIEW.FMT if not supported _DEFAULT_OVERVIEW_FMT = [ "subject", "from", "date", "message-id", "references", ":bytes", ":lines"] # Alternative names allowed in LIST OVERVIEW.FMT response _OVERVIEW_FMT_ALTERNATIVES = { 'bytes': ':bytes', 'lines': ':lines', } # Line terminators (we always output CRLF, but accept any of CRLF, CR, LF) _CRLF = b'\r\n' GroupInfo = collections.namedtuple('GroupInfo', ['group', 'last', 'first', 'flag']) ArticleInfo = collections.namedtuple('ArticleInfo', ['number', 'message_id', 'lines']) # Helper function(s) def decode_header(header_str): """Takes a unicode string representing a munged header value and decodes it as a (possibly non-ASCII) readable value.""" parts = [] for v, enc in _email_decode_header(header_str): if isinstance(v, bytes): parts.append(v.decode(enc or 'ascii')) else: parts.append(v) return ''.join(parts) def _parse_overview_fmt(lines): """Parse a list of string representing the response to LIST OVERVIEW.FMT and return a list of header/metadata names. Raises NNTPDataError if the response is not compliant (cf. RFC 3977, section 8.4).""" fmt = [] for line in lines: if line[0] == ':': # Metadata name (e.g. ":bytes") name, _, suffix = line[1:].partition(':') name = ':' + name else: # Header name (e.g. "Subject:" or "Xref:full") name, _, suffix = line.partition(':') name = name.lower() name = _OVERVIEW_FMT_ALTERNATIVES.get(name, name) # Should we do something with the suffix? fmt.append(name) defaults = _DEFAULT_OVERVIEW_FMT if len(fmt) < len(defaults): raise NNTPDataError("LIST OVERVIEW.FMT response too short") if fmt[:len(defaults)] != defaults: raise NNTPDataError("LIST OVERVIEW.FMT redefines default fields") return fmt def _parse_overview(lines, fmt, data_process_func=None): """Parse the response to an OVER or XOVER command according to the overview format `fmt`.""" n_defaults = len(_DEFAULT_OVERVIEW_FMT) overview = [] for line in lines: fields = {} article_number, *tokens = line.split('\t') article_number = int(article_number) for i, token in enumerate(tokens): if i >= len(fmt): # XXX should we raise an error? Some servers might not # support LIST OVERVIEW.FMT and still return additional # headers. continue field_name = fmt[i] is_metadata = field_name.startswith(':') if i >= n_defaults and not is_metadata: # Non-default header names are included in full in the response # (unless the field is totally empty) h = field_name + ": " if token and token[:len(h)].lower() != h: raise NNTPDataError("OVER/XOVER response doesn't include " "names of additional headers") token = token[len(h):] if token else None fields[fmt[i]] = token overview.append((article_number, fields)) return overview def _parse_datetime(date_str, time_str=None): """Parse a pair of (date, time) strings, and return a datetime object. If only the date is given, it is assumed to be date and time concatenated together (e.g. response to the DATE command). """ if time_str is None: time_str = date_str[-6:] date_str = date_str[:-6] hours = int(time_str[:2]) minutes = int(time_str[2:4]) seconds = int(time_str[4:]) year = int(date_str[:-4]) month = int(date_str[-4:-2]) day = int(date_str[-2:]) # RFC 3977 doesn't say how to interpret 2-char years. Assume that # there are no dates before 1970 on Usenet. if year < 70: year += 2000 elif year < 100: year += 1900 return datetime.datetime(year, month, day, hours, minutes, seconds) def _unparse_datetime(dt, legacy=False): """Format a date or datetime object as a pair of (date, time) strings in the format required by the NEWNEWS and NEWGROUPS commands. If a date object is passed, the time is assumed to be midnight (00h00). The returned representation depends on the legacy flag: * if legacy is False (the default): date has the YYYYMMDD format and time the HHMMSS format * if legacy is True: date has the YYMMDD format and time the HHMMSS format. RFC 3977 compliant servers should understand both formats; therefore, legacy is only needed when talking to old servers. """ if not isinstance(dt, datetime.datetime): time_str = "000000" else: time_str = "{0.hour:02d}{0.minute:02d}{0.second:02d}".format(dt) y = dt.year if legacy: y = y % 100 date_str = "{0:02d}{1.month:02d}{1.day:02d}".format(y, dt) else: date_str = "{0:04d}{1.month:02d}{1.day:02d}".format(y, dt) return date_str, time_str if _have_ssl: def _encrypt_on(sock, context, hostname): """Wrap a socket in SSL/TLS. Arguments: - sock: Socket to wrap - context: SSL context to use for the encrypted connection Returns: - sock: New, encrypted socket. """ # Generate a default SSL context if none was passed. if context is None: context = ssl._create_stdlib_context() return context.wrap_socket(sock, server_hostname=hostname) # The classes themselves class NNTP: # UTF-8 is the character set for all NNTP commands and responses: they # are automatically encoded (when sending) and decoded (and receiving) # by this class. # However, some multi-line data blocks can contain arbitrary bytes (for # example, latin-1 or utf-16 data in the body of a message). Commands # taking (POST, IHAVE) or returning (HEAD, BODY, ARTICLE) raw message # data will therefore only accept and produce bytes objects. # Furthermore, since there could be non-compliant servers out there, # we use 'surrogateescape' as the error handler for fault tolerance # and easy round-tripping. This could be useful for some applications # (e.g. NNTP gateways). encoding = 'utf-8' errors = 'surrogateescape' def __init__(self, host, port=NNTP_PORT, user=None, password=None, readermode=None, usenetrc=False, timeout=_GLOBAL_DEFAULT_TIMEOUT): """Initialize an instance. Arguments: - host: hostname to connect to - port: port to connect to (default the standard NNTP port) - user: username to authenticate with - password: password to use with username - readermode: if true, send 'mode reader' command after connecting. - usenetrc: allow loading username and password from ~/.netrc file if not specified explicitly - timeout: timeout (in seconds) used for socket connections readermode is sometimes necessary if you are connecting to an NNTP server on the local machine and intend to call reader-specific commands, such as `group'. If you get unexpected NNTPPermanentErrors, you might need to set readermode. """ self.host = host self.port = port self.sock = self._create_socket(timeout) self.file = None try: self.file = self.sock.makefile("rwb") self._base_init(readermode) if user or usenetrc: self.login(user, password, usenetrc) except: if self.file: self.file.close() self.sock.close() raise def _base_init(self, readermode): """Partial initialization for the NNTP protocol. This instance method is extracted for supporting the test code. """ self.debugging = 0 self.welcome = self._getresp() # Inquire about capabilities (RFC 3977). self._caps = None self.getcapabilities() # 'MODE READER' is sometimes necessary to enable 'reader' mode. # However, the order in which 'MODE READER' and 'AUTHINFO' need to # arrive differs between some NNTP servers. If _setreadermode() fails # with an authorization failed error, it will set this to True; # the login() routine will interpret that as a request to try again # after performing its normal function. # Enable only if we're not already in READER mode anyway. self.readermode_afterauth = False if readermode and 'READER' not in self._caps: self._setreadermode() if not self.readermode_afterauth: # Capabilities might have changed after MODE READER self._caps = None self.getcapabilities() # RFC 4642 2.2.2: Both the client and the server MUST know if there is # a TLS session active. A client MUST NOT attempt to start a TLS # session if a TLS session is already active. self.tls_on = False # Log in and encryption setup order is left to subclasses. self.authenticated = False def __enter__(self): return self def __exit__(self, *args): is_connected = lambda: hasattr(self, "file") if is_connected(): try: self.quit() except (OSError, EOFError): pass finally: if is_connected(): self._close() def _create_socket(self, timeout): if timeout is not None and not timeout: raise ValueError('Non-blocking socket (timeout=0) is not supported') sys.audit("nntplib.connect", self, self.host, self.port) return socket.create_connection((self.host, self.port), timeout) def getwelcome(self): """Get the welcome message from the server (this is read and squirreled away by __init__()). If the response code is 200, posting is allowed; if it 201, posting is not allowed.""" if self.debugging: print('*welcome*', repr(self.welcome)) return self.welcome def getcapabilities(self): """Get the server capabilities, as read by __init__(). If the CAPABILITIES command is not supported, an empty dict is returned.""" if self._caps is None: self.nntp_version = 1 self.nntp_implementation = None try: resp, caps = self.capabilities() except (NNTPPermanentError, NNTPTemporaryError): # Server doesn't support capabilities self._caps = {} else: self._caps = caps if 'VERSION' in caps: # The server can advertise several supported versions, # choose the highest. self.nntp_version = max(map(int, caps['VERSION'])) if 'IMPLEMENTATION' in caps: self.nntp_implementation = ' '.join(caps['IMPLEMENTATION']) return self._caps def set_debuglevel(self, level): """Set the debugging level. Argument 'level' means: 0: no debugging output (default) 1: print commands and responses but not body text etc. 2: also print raw lines read and sent before stripping CR/LF""" self.debugging = level debug = set_debuglevel def _putline(self, line): """Internal: send one line to the server, appending CRLF. The `line` must be a bytes-like object.""" sys.audit("nntplib.putline", self, line) line = line + _CRLF if self.debugging > 1: print('*put*', repr(line)) self.file.write(line) self.file.flush() def _putcmd(self, line): """Internal: send one command to the server (through _putline()). The `line` must be a unicode string.""" if self.debugging: print('*cmd*', repr(line)) line = line.encode(self.encoding, self.errors) self._putline(line) def _getline(self, strip_crlf=True): """Internal: return one line from the server, stripping _CRLF. Raise EOFError if the connection is closed. Returns a bytes object.""" line = self.file.readline(_MAXLINE +1) if len(line) > _MAXLINE: raise NNTPDataError('line too long') if self.debugging > 1: print('*get*', repr(line)) if not line: raise EOFError if strip_crlf: if line[-2:] == _CRLF: line = line[:-2] elif line[-1:] in _CRLF: line = line[:-1] return line def _getresp(self): """Internal: get a response from the server. Raise various errors if the response indicates an error. Returns a unicode string.""" resp = self._getline() if self.debugging: print('*resp*', repr(resp)) resp = resp.decode(self.encoding, self.errors) c = resp[:1] if c == '4': raise NNTPTemporaryError(resp) if c == '5': raise NNTPPermanentError(resp) if c not in '123': raise NNTPProtocolError(resp) return resp def _getlongresp(self, file=None): """Internal: get a response plus following text from the server. Raise various errors if the response indicates an error. Returns a (response, lines) tuple where `response` is a unicode string and `lines` is a list of bytes objects. If `file` is a file-like object, it must be open in binary mode. """ openedFile = None try: # If a string was passed then open a file with that name if isinstance(file, (str, bytes)): openedFile = file = open(file, "wb") resp = self._getresp() if resp[:3] not in _LONGRESP: raise NNTPReplyError(resp) lines = [] if file is not None: # XXX lines = None instead? terminators = (b'.' + _CRLF, b'.\n') while 1: line = self._getline(False) if line in terminators: break if line.startswith(b'..'): line = line[1:] file.write(line) else: terminator = b'.' while 1: line = self._getline() if line == terminator: break if line.startswith(b'..'): line = line[1:] lines.append(line) finally: # If this method created the file, then it must close it if openedFile: openedFile.close() return resp, lines def _shortcmd(self, line): """Internal: send a command and get the response. Same return value as _getresp().""" self._putcmd(line) return self._getresp() def _longcmd(self, line, file=None): """Internal: send a command and get the response plus following text. Same return value as _getlongresp().""" self._putcmd(line) return self._getlongresp(file) def _longcmdstring(self, line, file=None): """Internal: send a command and get the response plus following text. Same as _longcmd() and _getlongresp(), except that the returned `lines` are unicode strings rather than bytes objects. """ self._putcmd(line) resp, list = self._getlongresp(file) return resp, [line.decode(self.encoding, self.errors) for line in list] def _getoverviewfmt(self): """Internal: get the overview format. Queries the server if not already done, else returns the cached value.""" try: return self._cachedoverviewfmt except AttributeError: pass try: resp, lines = self._longcmdstring("LIST OVERVIEW.FMT") except NNTPPermanentError: # Not supported by server? fmt = _DEFAULT_OVERVIEW_FMT[:] else: fmt = _parse_overview_fmt(lines) self._cachedoverviewfmt = fmt return fmt def _grouplist(self, lines): # Parse lines into "group last first flag" return [GroupInfo(*line.split()) for line in lines] def capabilities(self): """Process a CAPABILITIES command. Not supported by all servers. Return: - resp: server response if successful - caps: a dictionary mapping capability names to lists of tokens (for example {'VERSION': ['2'], 'OVER': [], LIST: ['ACTIVE', 'HEADERS'] }) """ caps = {} resp, lines = self._longcmdstring("CAPABILITIES") for line in lines: name, *tokens = line.split() caps[name] = tokens return resp, caps def newgroups(self, date, *, file=None): """Process a NEWGROUPS command. Arguments: - date: a date or datetime object Return: - resp: server response if successful - list: list of newsgroup names """ if not isinstance(date, (datetime.date, datetime.date)): raise TypeError( "the date parameter must be a date or datetime object, " "not '{:40}'".format(date.__class__.__name__)) date_str, time_str = _unparse_datetime(date, self.nntp_version < 2) cmd = 'NEWGROUPS {0} {1}'.format(date_str, time_str) resp, lines = self._longcmdstring(cmd, file) return resp, self._grouplist(lines) def newnews(self, group, date, *, file=None): """Process a NEWNEWS command. Arguments: - group: group name or '*' - date: a date or datetime object Return: - resp: server response if successful - list: list of message ids """ if not isinstance(date, (datetime.date, datetime.date)): raise TypeError( "the date parameter must be a date or datetime object, " "not '{:40}'".format(date.__class__.__name__)) date_str, time_str = _unparse_datetime(date, self.nntp_version < 2) cmd = 'NEWNEWS {0} {1} {2}'.format(group, date_str, time_str) return self._longcmdstring(cmd, file) def list(self, group_pattern=None, *, file=None): """Process a LIST or LIST ACTIVE command. Arguments: - group_pattern: a pattern indicating which groups to query - file: Filename string or file object to store the result in Returns: - resp: server response if successful - list: list of (group, last, first, flag) (strings) """ if group_pattern is not None: command = 'LIST ACTIVE ' + group_pattern else: command = 'LIST' resp, lines = self._longcmdstring(command, file) return resp, self._grouplist(lines) def _getdescriptions(self, group_pattern, return_all): line_pat = re.compile('^(?P[^ \t]+)[ \t]+(.*)$') # Try the more std (acc. to RFC2980) LIST NEWSGROUPS first resp, lines = self._longcmdstring('LIST NEWSGROUPS ' + group_pattern) if not resp.startswith('215'): # Now the deprecated XGTITLE. This either raises an error # or succeeds with the same output structure as LIST # NEWSGROUPS. resp, lines = self._longcmdstring('XGTITLE ' + group_pattern) groups = {} for raw_line in lines: match = line_pat.search(raw_line.strip()) if match: name, desc = match.group(1, 2) if not return_all: return desc groups[name] = desc if return_all: return resp, groups else: # Nothing found return '' def description(self, group): """Get a description for a single group. If more than one group matches ('group' is a pattern), return the first. If no group matches, return an empty string. This elides the response code from the server, since it can only be '215' or '285' (for xgtitle) anyway. If the response code is needed, use the 'descriptions' method. NOTE: This neither checks for a wildcard in 'group' nor does it check whether the group actually exists.""" return self._getdescriptions(group, False) def descriptions(self, group_pattern): """Get descriptions for a range of groups.""" return self._getdescriptions(group_pattern, True) def group(self, name): """Process a GROUP command. Argument: - group: the group name Returns: - resp: server response if successful - count: number of articles - first: first article number - last: last article number - name: the group name """ resp = self._shortcmd('GROUP ' + name) if not resp.startswith('211'): raise NNTPReplyError(resp) words = resp.split() count = first = last = 0 n = len(words) if n > 1: count = words[1] if n > 2: first = words[2] if n > 3: last = words[3] if n > 4: name = words[4].lower() return resp, int(count), int(first), int(last), name def help(self, *, file=None): """Process a HELP command. Argument: - file: Filename string or file object to store the result in Returns: - resp: server response if successful - list: list of strings returned by the server in response to the HELP command """ return self._longcmdstring('HELP', file) def _statparse(self, resp): """Internal: parse the response line of a STAT, NEXT, LAST, ARTICLE, HEAD or BODY command.""" if not resp.startswith('22'): raise NNTPReplyError(resp) words = resp.split() art_num = int(words[1]) message_id = words[2] return resp, art_num, message_id def _statcmd(self, line): """Internal: process a STAT, NEXT or LAST command.""" resp = self._shortcmd(line) return self._statparse(resp) def stat(self, message_spec=None): """Process a STAT command. Argument: - message_spec: article number or message id (if not specified, the current article is selected) Returns: - resp: server response if successful - art_num: the article number - message_id: the message id """ if message_spec: return self._statcmd('STAT {0}'.format(message_spec)) else: return self._statcmd('STAT') def next(self): """Process a NEXT command. No arguments. Return as for STAT.""" return self._statcmd('NEXT') def last(self): """Process a LAST command. No arguments. Return as for STAT.""" return self._statcmd('LAST') def _artcmd(self, line, file=None): """Internal: process a HEAD, BODY or ARTICLE command.""" resp, lines = self._longcmd(line, file) resp, art_num, message_id = self._statparse(resp) return resp, ArticleInfo(art_num, message_id, lines) def head(self, message_spec=None, *, file=None): """Process a HEAD command. Argument: - message_spec: article number or message id - file: filename string or file object to store the headers in Returns: - resp: server response if successful - ArticleInfo: (article number, message id, list of header lines) """ if message_spec is not None: cmd = 'HEAD {0}'.format(message_spec) else: cmd = 'HEAD' return self._artcmd(cmd, file) def body(self, message_spec=None, *, file=None): """Process a BODY command. Argument: - message_spec: article number or message id - file: filename string or file object to store the body in Returns: - resp: server response if successful - ArticleInfo: (article number, message id, list of body lines) """ if message_spec is not None: cmd = 'BODY {0}'.format(message_spec) else: cmd = 'BODY' return self._artcmd(cmd, file) def article(self, message_spec=None, *, file=None): """Process an ARTICLE command. Argument: - message_spec: article number or message id - file: filename string or file object to store the article in Returns: - resp: server response if successful - ArticleInfo: (article number, message id, list of article lines) """ if message_spec is not None: cmd = 'ARTICLE {0}'.format(message_spec) else: cmd = 'ARTICLE' return self._artcmd(cmd, file) def slave(self): """Process a SLAVE command. Returns: - resp: server response if successful """ return self._shortcmd('SLAVE') def xhdr(self, hdr, str, *, file=None): """Process an XHDR command (optional server extension). Arguments: - hdr: the header type (e.g. 'subject') - str: an article nr, a message id, or a range nr1-nr2 - file: Filename string or file object to store the result in Returns: - resp: server response if successful - list: list of (nr, value) strings """ pat = re.compile('^([0-9]+) ?(.*)\n?') resp, lines = self._longcmdstring('XHDR {0} {1}'.format(hdr, str), file) def remove_number(line): m = pat.match(line) return m.group(1, 2) if m else line return resp, [remove_number(line) for line in lines] def xover(self, start, end, *, file=None): """Process an XOVER command (optional server extension) Arguments: - start: start of range - end: end of range - file: Filename string or file object to store the result in Returns: - resp: server response if successful - list: list of dicts containing the response fields """ resp, lines = self._longcmdstring('XOVER {0}-{1}'.format(start, end), file) fmt = self._getoverviewfmt() return resp, _parse_overview(lines, fmt) def over(self, message_spec, *, file=None): """Process an OVER command. If the command isn't supported, fall back to XOVER. Arguments: - message_spec: - either a message id, indicating the article to fetch information about - or a (start, end) tuple, indicating a range of article numbers; if end is None, information up to the newest message will be retrieved - or None, indicating the current article number must be used - file: Filename string or file object to store the result in Returns: - resp: server response if successful - list: list of dicts containing the response fields NOTE: the "message id" form isn't supported by XOVER """ cmd = 'OVER' if 'OVER' in self._caps else 'XOVER' if isinstance(message_spec, (tuple, list)): start, end = message_spec cmd += ' {0}-{1}'.format(start, end or '') elif message_spec is not None: cmd = cmd + ' ' + message_spec resp, lines = self._longcmdstring(cmd, file) fmt = self._getoverviewfmt() return resp, _parse_overview(lines, fmt) def date(self): """Process the DATE command. Returns: - resp: server response if successful - date: datetime object """ resp = self._shortcmd("DATE") if not resp.startswith('111'): raise NNTPReplyError(resp) elem = resp.split() if len(elem) != 2: raise NNTPDataError(resp) date = elem[1] if len(date) != 14: raise NNTPDataError(resp) return resp, _parse_datetime(date, None) def _post(self, command, f): resp = self._shortcmd(command) # Raises a specific exception if posting is not allowed if not resp.startswith('3'): raise NNTPReplyError(resp) if isinstance(f, (bytes, bytearray)): f = f.splitlines() # We don't use _putline() because: # - we don't want additional CRLF if the file or iterable is already # in the right format # - we don't want a spurious flush() after each line is written for line in f: if not line.endswith(_CRLF): line = line.rstrip(b"\r\n") + _CRLF if line.startswith(b'.'): line = b'.' + line self.file.write(line) self.file.write(b".\r\n") self.file.flush() return self._getresp() def post(self, data): """Process a POST command. Arguments: - data: bytes object, iterable or file containing the article Returns: - resp: server response if successful""" return self._post('POST', data) def ihave(self, message_id, data): """Process an IHAVE command. Arguments: - message_id: message-id of the article - data: file containing the article Returns: - resp: server response if successful Note that if the server refuses the article an exception is raised.""" return self._post('IHAVE {0}'.format(message_id), data) def _close(self): try: if self.file: self.file.close() del self.file finally: self.sock.close() def quit(self): """Process a QUIT command and close the socket. Returns: - resp: server response if successful""" try: resp = self._shortcmd('QUIT') finally: self._close() return resp def login(self, user=None, password=None, usenetrc=True): if self.authenticated: raise ValueError("Already logged in.") if not user and not usenetrc: raise ValueError( "At least one of `user` and `usenetrc` must be specified") # If no login/password was specified but netrc was requested, # try to get them from ~/.netrc # Presume that if .netrc has an entry, NNRP authentication is required. try: if usenetrc and not user: import netrc credentials = netrc.netrc() auth = credentials.authenticators(self.host) if auth: user = auth[0] password = auth[2] except OSError: pass # Perform NNTP authentication if needed. if not user: return resp = self._shortcmd('authinfo user ' + user) if resp.startswith('381'): if not password: raise NNTPReplyError(resp) else: resp = self._shortcmd('authinfo pass ' + password) if not resp.startswith('281'): raise NNTPPermanentError(resp) # Capabilities might have changed after login self._caps = None self.getcapabilities() # Attempt to send mode reader if it was requested after login. # Only do so if we're not in reader mode already. if self.readermode_afterauth and 'READER' not in self._caps: self._setreadermode() # Capabilities might have changed after MODE READER self._caps = None self.getcapabilities() def _setreadermode(self): try: self.welcome = self._shortcmd('mode reader') except NNTPPermanentError: # Error 5xx, probably 'not implemented' pass except NNTPTemporaryError as e: if e.response.startswith('480'): # Need authorization before 'mode reader' self.readermode_afterauth = True else: raise if _have_ssl: def starttls(self, context=None): """Process a STARTTLS command. Arguments: - context: SSL context to use for the encrypted connection """ # Per RFC 4642, STARTTLS MUST NOT be sent after authentication or if # a TLS session already exists. if self.tls_on: raise ValueError("TLS is already enabled.") if self.authenticated: raise ValueError("TLS cannot be started after authentication.") resp = self._shortcmd('STARTTLS') if resp.startswith('382'): self.file.close() self.sock = _encrypt_on(self.sock, context, self.host) self.file = self.sock.makefile("rwb") self.tls_on = True # Capabilities may change after TLS starts up, so ask for them # again. self._caps = None self.getcapabilities() else: raise NNTPError("TLS failed to start.") if _have_ssl: class NNTP_SSL(NNTP): def __init__(self, host, port=NNTP_SSL_PORT, user=None, password=None, ssl_context=None, readermode=None, usenetrc=False, timeout=_GLOBAL_DEFAULT_TIMEOUT): """This works identically to NNTP.__init__, except for the change in default port and the `ssl_context` argument for SSL connections. """ self.ssl_context = ssl_context super().__init__(host, port, user, password, readermode, usenetrc, timeout) def _create_socket(self, timeout): sock = super()._create_socket(timeout) try: sock = _encrypt_on(sock, self.ssl_context, self.host) except: sock.close() raise else: return sock __all__.append("NNTP_SSL") # Test retrieval when run as a script. if __name__ == '__main__': import argparse parser = argparse.ArgumentParser(description="""\ nntplib built-in demo - display the latest articles in a newsgroup""") parser.add_argument('-g', '--group', default='gmane.comp.python.general', help='group to fetch messages from (default: %(default)s)') parser.add_argument('-s', '--server', default='news.gmane.io', help='NNTP server hostname (default: %(default)s)') parser.add_argument('-p', '--port', default=-1, type=int, help='NNTP port number (default: %s / %s)' % (NNTP_PORT, NNTP_SSL_PORT)) parser.add_argument('-n', '--nb-articles', default=10, type=int, help='number of articles to fetch (default: %(default)s)') parser.add_argument('-S', '--ssl', action='store_true', default=False, help='use NNTP over SSL') args = parser.parse_args() port = args.port if not args.ssl: if port == -1: port = NNTP_PORT s = NNTP(host=args.server, port=port) else: if port == -1: port = NNTP_SSL_PORT s = NNTP_SSL(host=args.server, port=port) caps = s.getcapabilities() if 'STARTTLS' in caps: s.starttls() resp, count, first, last, name = s.group(args.group) print('Group', name, 'has', count, 'articles, range', first, 'to', last) def cut(s, lim): if len(s) > lim: s = s[:lim - 4] + "..." return s first = str(int(last) - args.nb_articles + 1) resp, overviews = s.xover(first, last) for artnum, over in overviews: author = decode_header(over['from']).split('<', 1)[0] subject = decode_header(over['subject']) lines = int(over[':lines']) print("{:7} {:20} {:42} ({})".format( artnum, cut(author, 20), cut(subject, 42), lines) ) s.quit() python-deadlib-3.13.0/nntplib/pyproject.toml000066400000000000000000000012731471045437300210760ustar00rootroot00000000000000[project] name = "standard-nntplib" version = "3.13.0" description = "Standard library nntplib redistribution. \"dead battery\"." keywords = ["stdlib"] authors = [ { name = "Python Developers", email = "python-deadlib@youknowone.org" } ] readme = "README.rst" license = { text = "PSF-2.0" } classifiers = [ "License :: OSI Approved :: Python Software Foundation License", "Topic :: Software Development :: Libraries", "Programming Language :: Python :: 3", ] [tool.setuptools.packages] find = {include = ["nntplib*"]} [project.urls] "Homepage" = "https://github.com/youknowone/python-deadlib" [build-system] requires = ["setuptools>=75.0"] build-backend = "setuptools.build_meta" python-deadlib-3.13.0/nntplib/tests/000077500000000000000000000000001471045437300173215ustar00rootroot00000000000000python-deadlib-3.13.0/nntplib/tests/__init__.py000066400000000000000000000000001471045437300214200ustar00rootroot00000000000000python-deadlib-3.13.0/nntplib/tests/certdata/000077500000000000000000000000001471045437300211105ustar00rootroot00000000000000python-deadlib-3.13.0/nntplib/tests/certdata/keycert3.pem000066400000000000000000000223501471045437300233460ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQDFtLOteQlQojN7 ztkux7m0hmGKkP1hh0hbKqTcD87jkLAqAwZWenjZMjCbbZ3vP+AObCIkYIKzPXY7 Yi+H5M3O2mXIDxoHGjL/GWtoEyDNXvm9UC+MRuSOq2MaLHHQG0Rx2TxcYrMVUM7b 93rpN1LGRrCv1gISXM4EvEJooAR7Aadj0pG/o0fqDAdFjH6QZbhn1iZle+eGbjcf dgH/H0F8dn1PPGoViHXicbsQ4kB6002Pf+aXP4b2QKAbflyNHEKHPHEOOTXrFjMd c+bqKW24epEsMZI59qx9hU/4Rvp3/v+vEwTL7Nm7ilptzZn2cvGCW39LC0nNYLOz kO3H8xwA75h6uykdB+WO/v2CKIK9M/ZO+9QNrmaokfKDamCk39b8hlCwNL6LsVpv d3XTS5Wn4YWn92EqiltUJJoPo7pc7VTdWCg4zVFn4Q8Zh4NFNn/qTB8lEMgrsNTV 5cyZ7zhoBiUMSO45bmo2NsnE7ce/JUhlqe5uh0PT1MIBgTV+oDMCAwEAAQKCAYEA udsy4gwblqK0tVnxz0lQqYV+os3EdO/BNHr1Oi7eNg2pngTz603812mYSjUVOHma vtQmkH3twGQyBoc52Y1dcGzdK+IOfMjDUg7qao840ffL3I1J9ZwbdodlhZBsec94 W3J1jP/4DDzICf8vm5g3h0+i/9m2Xt7BibAU2dg7/grC+lNUUoxDqaEfIOF/hW0q muq1c8e0EisAROIh5FzUqhWVnWxU6eM7tuFlkuyu4whLLHB3LI466Lo+CTqT9M+v jJYlvS5+AZW3qMBp6WOI8C+VIiBL178mo+Igkyyy5AYXcWeNkjp6ygRWvtWXIhCv CI29mf+BP/54jAY0rQRXJ2UcSHXmM6PTDkE/L2OKeiY1Ou8gLOwun3yBVdbkXJMb PWmUW4N8qSIJQ+vE2TDqmkqAT6m+ilzOXl1O+LLTvGyMnOiiSLXK9mC4ND3tqaQu hvKivnI1doErcWUaIf1DHiJmLrGxrTCUKjCEoefqVq2/dDdtCfx7CqUvjl3DYKMB AoHBAP+Vdi6D07gZFepEGCaJ+YH6cxEyO73CNnea/F1whVAzOv91kHS32jC9PAI3 /wYlX+DLcN9mVF/q62V4SLZYfOxTPW4vWO0A45URe9s9Z795fdAcQ5jt3QFOVSnk 3XSaCkIOwckuwabGJi4+foiUEOnLLzQi1/g7x12dwejxVNhqhz5KFkOQPv8fQRed sb5LVLYDeprsB2Vsx0fHwg4z9FvTIxLBeI7+sJD30lNpYZrCl/T9x4e1SV2Rwn2W bghxgQKBwQDGBx07biZK9RB5g4qPl+G6vz0M+/KBfpwQbMYxSyct7u6gfGD9mWBO qocIIr39Unac3kUL237Cn3HbgiGCRe7Mwd7XqnSSGWM5oWSlVQxEKTXYUlTbd9O9 DKuyQGOl/AMEwD4ZbEOfQNmnd1U4nh1AV052FQY8Ry/atGFT9fApA/5X/bbenOwQ YGDsokLzPf2BIDncpE+VNevUMoMI7EnySgjjfpL+cRld0qpLqBMo2h5VddeJ/5YM 1YcNfMQiw7MCgcEAwXqXuKa7A8aZvHpH/gS9CRRbP01TxFbdfLWrDeE8SnY9111c Ob9kQTk/0D4rpK9uYXIgxD1m6iWghXQFN2TNTOnGuz7EhsYBgrt1k4Zsn5qND5oV 4hNPFsoB1nEW5EooMdGSCYaHuoSOKrvMdgAAvbu+xC0MaTJ3vfrK7Fik7h/WueTD 7emohuFWGVabU38bZZ5EljrPboxmX4Rs9uuFtG2lQ3GKnlVXvKaeZd6EsO9WsXPc NHOcUmUhYokaSvIBAoHAGCxGJTsM8Zl4qVylTWH87A7sJOmccLJD2r1sdBf4cGL6 PhzwugQ+/VtToGqdRo8Ka5u2Ufw5PQi5nVIFRSHERLpluW3VTQBMXHyXDJeVJ7zg Fcf3E9NMxYcGbnvtrhVVSP8ulWvh1U7VQtwOSxsB9xixOzjVygXmkYvzVYxwBJG4 OoV+DS6aomUhb8Fe6tJmX5zPc1+bV1t9ril8VVqCrFDdROfuiaDEt+8/Wnzp2dLG YShBZ1cLugVWtw7D4nqBAoHAF29k64iAxY5Y4OOibVkqjUCPyqG2oxiXqgO7CxZp FGUat5UtV2mIBlSENs1o5AZ1nPlgWtPtg0xVCaG2t/Rq7ugvUfAnAhUK6zX8FS+T gCXE+7iKuuIJiCo13/iAwF/CLfuXvj4CZ71ta0wX9w99f1FcPEk0x+ytiyuWJK8K tyubL34JwNrnkh/8e3LcV3L88Sk9ZmxeTz31f3cA3Fy2ZJOAUMD9dKXeKtY7azzt MkhXedRsdLSKqMh0VGeGHoLS -----END PRIVATE KEY----- Certificate: Data: Version: 3 (0x2) Serial Number: cb:2d:80:99:5a:69:52:5c Signature Algorithm: sha256WithRSAEncryption Issuer: C=XY, O=Python Software Foundation CA, CN=our-ca-server Validity Not Before: Aug 29 14:23:16 2018 GMT Not After : Oct 28 14:23:16 2037 GMT Subject: C=XY, L=Castle Anthrax, O=Python Software Foundation, CN=localhost Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (3072 bit) Modulus: 00:c5:b4:b3:ad:79:09:50:a2:33:7b:ce:d9:2e:c7: b9:b4:86:61:8a:90:fd:61:87:48:5b:2a:a4:dc:0f: ce:e3:90:b0:2a:03:06:56:7a:78:d9:32:30:9b:6d: 9d:ef:3f:e0:0e:6c:22:24:60:82:b3:3d:76:3b:62: 2f:87:e4:cd:ce:da:65:c8:0f:1a:07:1a:32:ff:19: 6b:68:13:20:cd:5e:f9:bd:50:2f:8c:46:e4:8e:ab: 63:1a:2c:71:d0:1b:44:71:d9:3c:5c:62:b3:15:50: ce:db:f7:7a:e9:37:52:c6:46:b0:af:d6:02:12:5c: ce:04:bc:42:68:a0:04:7b:01:a7:63:d2:91:bf:a3: 47:ea:0c:07:45:8c:7e:90:65:b8:67:d6:26:65:7b: e7:86:6e:37:1f:76:01:ff:1f:41:7c:76:7d:4f:3c: 6a:15:88:75:e2:71:bb:10:e2:40:7a:d3:4d:8f:7f: e6:97:3f:86:f6:40:a0:1b:7e:5c:8d:1c:42:87:3c: 71:0e:39:35:eb:16:33:1d:73:e6:ea:29:6d:b8:7a: 91:2c:31:92:39:f6:ac:7d:85:4f:f8:46:fa:77:fe: ff:af:13:04:cb:ec:d9:bb:8a:5a:6d:cd:99:f6:72: f1:82:5b:7f:4b:0b:49:cd:60:b3:b3:90:ed:c7:f3: 1c:00:ef:98:7a:bb:29:1d:07:e5:8e:fe:fd:82:28: 82:bd:33:f6:4e:fb:d4:0d:ae:66:a8:91:f2:83:6a: 60:a4:df:d6:fc:86:50:b0:34:be:8b:b1:5a:6f:77: 75:d3:4b:95:a7:e1:85:a7:f7:61:2a:8a:5b:54:24: 9a:0f:a3:ba:5c:ed:54:dd:58:28:38:cd:51:67:e1: 0f:19:87:83:45:36:7f:ea:4c:1f:25:10:c8:2b:b0: d4:d5:e5:cc:99:ef:38:68:06:25:0c:48:ee:39:6e: 6a:36:36:c9:c4:ed:c7:bf:25:48:65:a9:ee:6e:87: 43:d3:d4:c2:01:81:35:7e:a0:33 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Alternative Name: DNS:localhost X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Key Identifier: 85:75:10:25:D0:2C:80:50:24:1A:5B:57:70:DE:B5:CB:71:A9:3B:7B X509v3 Authority Key Identifier: keyid:B3:8A:A0:A2:BA:71:F1:A8:24:79:D4:A4:5B:25:36:15:1E:49:C8:CD DirName:/C=XY/O=Python Software Foundation CA/CN=our-ca-server serial:CB:2D:80:99:5A:69:52:5B Authority Information Access: CA Issuers - URI:http://testca.pythontest.net/testca/pycacert.cer OCSP - URI:http://testca.pythontest.net/testca/ocsp/ X509v3 CRL Distribution Points: Full Name: URI:http://testca.pythontest.net/testca/revocation.crl Signature Algorithm: sha256WithRSAEncryption 95:f3:56:bb:d5:8c:70:bd:d1:de:da:63:b0:29:d7:db:60:27: d6:59:fd:61:1b:30:c6:d0:5d:73:7d:34:e1:68:e3:28:a6:89: e6:60:bd:89:d3:0e:f4:72:ad:72:76:f8:86:21:fd:75:3c:f8: 6d:be:9c:04:e1:82:03:69:6c:ae:d0:55:ba:5e:f2:ca:f5:0f: 8e:d6:d9:8d:c8:56:46:f4:f8:ac:74:2a:19:7b:8e:47:70:1f: fb:fb:bd:69:02:a1:a5:4a:6e:21:1c:04:14:15:55:bf:bf:24: 43:c8:17:03:be:3e:2c:ea:db:c8:af:1d:fd:52:df:d6:15:49: 9e:c2:44:69:ef:f1:45:43:83:b2:1e:cf:14:1c:13:3f:fe:9c: 71:cb:e7:1b:18:56:36:a7:af:44:f1:0b:a1:79:44:46:f9:43: 46:29:d8:b0:ca:49:4d:65:60:d3:f6:8e:74:bc:62:9e:1e:8d: 4b:29:9a:b4:0d:f0:a2:77:5b:34:e4:11:2f:a7:25:c5:e5:07: 76:12:ae:be:75:73:15:e4:0a:7d:53:38:56:3f:79:6d:6e:ca: ed:80:ab:56:ed:7e:8b:1c:e7:e3:d4:62:30:22:70:e7:29:b2: 03:3c:fe:fa:3d:f0:36:c0:4d:11:a2:99:d3:29:31:27:b8:c5: b8:15:a3:3c:4f:9b:73:5e:2b:b2:fb:cb:fd:75:47:b8:17:bd: 21:d8:e6:c1:b9:ff:73:81:d8:25:08:6d:08:5e:1c:a5:83:50: de:67:e6:da:d0:8e:5a:d3:f2:2a:b1:3f:b8:80:21:07:6a:71: 15:6d:05:eb:51:b3:59:8d:d4:15:46:7e:02:a8:13:01:16:99: bd:03:cc:70:71:2a:23:16:78:af:d1:d5:01:9d:04:b4:63:93: 9a:04:3a:92:2e:e6:7e:73:93:a5:fe:50:9b:bd:0e:ea:54:86: 6f:7c:e5:14:77:fe:c2:28:5a:4a:0e:d7:2d:8c:e9:ed:61:29: b2:53:ff:6c:04:bc -----BEGIN CERTIFICATE----- MIIF8TCCBFmgAwIBAgIJAMstgJlaaVJcMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNV BAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUgRm91bmRhdGlvbiBDQTEW MBQGA1UEAwwNb3VyLWNhLXNlcnZlcjAeFw0xODA4MjkxNDIzMTZaFw0zNzEwMjgx NDIzMTZaMF8xCzAJBgNVBAYTAlhZMRcwFQYDVQQHDA5DYXN0bGUgQW50aHJheDEj MCEGA1UECgwaUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24xEjAQBgNVBAMMCWxv Y2FsaG9zdDCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAMW0s615CVCi M3vO2S7HubSGYYqQ/WGHSFsqpNwPzuOQsCoDBlZ6eNkyMJttne8/4A5sIiRggrM9 djtiL4fkzc7aZcgPGgcaMv8Za2gTIM1e+b1QL4xG5I6rYxoscdAbRHHZPFxisxVQ ztv3euk3UsZGsK/WAhJczgS8QmigBHsBp2PSkb+jR+oMB0WMfpBluGfWJmV754Zu Nx92Af8fQXx2fU88ahWIdeJxuxDiQHrTTY9/5pc/hvZAoBt+XI0cQoc8cQ45NesW Mx1z5uopbbh6kSwxkjn2rH2FT/hG+nf+/68TBMvs2buKWm3NmfZy8YJbf0sLSc1g s7OQ7cfzHADvmHq7KR0H5Y7+/YIogr0z9k771A2uZqiR8oNqYKTf1vyGULA0voux Wm93ddNLlafhhaf3YSqKW1Qkmg+julztVN1YKDjNUWfhDxmHg0U2f+pMHyUQyCuw 1NXlzJnvOGgGJQxI7jluajY2ycTtx78lSGWp7m6HQ9PUwgGBNX6gMwIDAQABo4IB wDCCAbwwFAYDVR0RBA0wC4IJbG9jYWxob3N0MA4GA1UdDwEB/wQEAwIFoDAdBgNV HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDAYDVR0TAQH/BAIwADAdBgNVHQ4E FgQUhXUQJdAsgFAkGltXcN61y3GpO3swfQYDVR0jBHYwdIAUs4qgorpx8agkedSk WyU2FR5JyM2hUaRPME0xCzAJBgNVBAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29m dHdhcmUgRm91bmRhdGlvbiBDQTEWMBQGA1UEAwwNb3VyLWNhLXNlcnZlcoIJAMst gJlaaVJbMIGDBggrBgEFBQcBAQR3MHUwPAYIKwYBBQUHMAKGMGh0dHA6Ly90ZXN0 Y2EucHl0aG9udGVzdC5uZXQvdGVzdGNhL3B5Y2FjZXJ0LmNlcjA1BggrBgEFBQcw AYYpaHR0cDovL3Rlc3RjYS5weXRob250ZXN0Lm5ldC90ZXN0Y2Evb2NzcC8wQwYD VR0fBDwwOjA4oDagNIYyaHR0cDovL3Rlc3RjYS5weXRob250ZXN0Lm5ldC90ZXN0 Y2EvcmV2b2NhdGlvbi5jcmwwDQYJKoZIhvcNAQELBQADggGBAJXzVrvVjHC90d7a Y7Ap19tgJ9ZZ/WEbMMbQXXN9NOFo4yimieZgvYnTDvRyrXJ2+IYh/XU8+G2+nATh ggNpbK7QVbpe8sr1D47W2Y3IVkb0+Kx0Khl7jkdwH/v7vWkCoaVKbiEcBBQVVb+/ JEPIFwO+Pizq28ivHf1S39YVSZ7CRGnv8UVDg7IezxQcEz/+nHHL5xsYVjanr0Tx C6F5REb5Q0Yp2LDKSU1lYNP2jnS8Yp4ejUspmrQN8KJ3WzTkES+nJcXlB3YSrr51 cxXkCn1TOFY/eW1uyu2Aq1btfosc5+PUYjAicOcpsgM8/vo98DbATRGimdMpMSe4 xbgVozxPm3NeK7L7y/11R7gXvSHY5sG5/3OB2CUIbQheHKWDUN5n5trQjlrT8iqx P7iAIQdqcRVtBetRs1mN1BVGfgKoEwEWmb0DzHBxKiMWeK/R1QGdBLRjk5oEOpIu 5n5zk6X+UJu9DupUhm985RR3/sIoWkoO1y2M6e1hKbJT/2wEvA== -----END CERTIFICATE----- python-deadlib-3.13.0/nntplib/tests/test_nntplib.py000066400000000000000000001753121471045437300224110ustar00rootroot00000000000000import io import socket import datetime import textwrap import unittest import functools import contextlib import os.path import re import threading from test import support from test.support import socket_helper, warnings_helper nntplib = warnings_helper.import_deprecated("nntplib") from nntplib import NNTP, GroupInfo from unittest.mock import patch try: import ssl except ImportError: ssl = None certfile = os.path.join(os.path.dirname(__file__), 'certdata', 'keycert3.pem') if ssl is not None: SSLError = ssl.SSLError else: class SSLError(Exception): """Non-existent exception class when we lack SSL support.""" reason = "This will never be raised." # TODO: # - test the `file` arg to more commands # - test error conditions # - test auth and `usenetrc` class NetworkedNNTPTestsMixin: ssl_context = None def test_welcome(self): welcome = self.server.getwelcome() self.assertEqual(str, type(welcome)) def test_help(self): resp, lines = self.server.help() self.assertTrue(resp.startswith("100 "), resp) for line in lines: self.assertEqual(str, type(line)) def test_list(self): resp, groups = self.server.list() if len(groups) > 0: self.assertEqual(GroupInfo, type(groups[0])) self.assertEqual(str, type(groups[0].group)) def test_list_active(self): resp, groups = self.server.list(self.GROUP_PAT) if len(groups) > 0: self.assertEqual(GroupInfo, type(groups[0])) self.assertEqual(str, type(groups[0].group)) def test_unknown_command(self): with self.assertRaises(nntplib.NNTPPermanentError) as cm: self.server._shortcmd("XYZZY") resp = cm.exception.response self.assertTrue(resp.startswith("500 "), resp) def test_newgroups(self): # gmane gets a constant influx of new groups. In order not to stress # the server too much, we choose a recent date in the past. dt = datetime.date.today() - datetime.timedelta(days=7) resp, groups = self.server.newgroups(dt) if len(groups) > 0: self.assertIsInstance(groups[0], GroupInfo) self.assertIsInstance(groups[0].group, str) def test_description(self): def _check_desc(desc): # Sanity checks self.assertIsInstance(desc, str) self.assertNotIn(self.GROUP_NAME, desc) desc = self.server.description(self.GROUP_NAME) _check_desc(desc) # Another sanity check self.assertIn(self.DESC, desc) # With a pattern desc = self.server.description(self.GROUP_PAT) _check_desc(desc) # Shouldn't exist desc = self.server.description("zk.brrtt.baz") self.assertEqual(desc, '') def test_descriptions(self): resp, descs = self.server.descriptions(self.GROUP_PAT) # 215 for LIST NEWSGROUPS, 282 for XGTITLE self.assertTrue( resp.startswith("215 ") or resp.startswith("282 "), resp) self.assertIsInstance(descs, dict) desc = descs[self.GROUP_NAME] self.assertEqual(desc, self.server.description(self.GROUP_NAME)) def test_group(self): result = self.server.group(self.GROUP_NAME) self.assertEqual(5, len(result)) resp, count, first, last, group = result self.assertEqual(group, self.GROUP_NAME) self.assertIsInstance(count, int) self.assertIsInstance(first, int) self.assertIsInstance(last, int) self.assertLessEqual(first, last) self.assertTrue(resp.startswith("211 "), resp) def test_date(self): resp, date = self.server.date() self.assertIsInstance(date, datetime.datetime) # Sanity check self.assertGreaterEqual(date.year, 1995) self.assertLessEqual(date.year, 2030) def _check_art_dict(self, art_dict): # Some sanity checks for a field dictionary returned by OVER / XOVER self.assertIsInstance(art_dict, dict) # NNTP has 7 mandatory fields self.assertGreaterEqual(art_dict.keys(), {"subject", "from", "date", "message-id", "references", ":bytes", ":lines"} ) for v in art_dict.values(): self.assertIsInstance(v, (str, type(None))) def test_xover(self): resp, count, first, last, name = self.server.group(self.GROUP_NAME) resp, lines = self.server.xover(last - 5, last) if len(lines) == 0: self.skipTest("no articles retrieved") # The 'last' article is not necessarily part of the output (cancelled?) art_num, art_dict = lines[0] self.assertGreaterEqual(art_num, last - 5) self.assertLessEqual(art_num, last) self._check_art_dict(art_dict) @unittest.skipIf(True, 'temporarily skipped until a permanent solution' ' is found for issue #28971') def test_over(self): resp, count, first, last, name = self.server.group(self.GROUP_NAME) start = last - 10 # The "start-" article range form resp, lines = self.server.over((start, None)) art_num, art_dict = lines[0] self._check_art_dict(art_dict) # The "start-end" article range form resp, lines = self.server.over((start, last)) art_num, art_dict = lines[-1] # The 'last' article is not necessarily part of the output (cancelled?) self.assertGreaterEqual(art_num, start) self.assertLessEqual(art_num, last) self._check_art_dict(art_dict) # XXX The "message_id" form is unsupported by gmane # 503 Overview by message-ID unsupported def test_xhdr(self): resp, count, first, last, name = self.server.group(self.GROUP_NAME) resp, lines = self.server.xhdr('subject', last) for line in lines: self.assertEqual(str, type(line[1])) def check_article_resp(self, resp, article, art_num=None): self.assertIsInstance(article, nntplib.ArticleInfo) if art_num is not None: self.assertEqual(article.number, art_num) for line in article.lines: self.assertIsInstance(line, bytes) # XXX this could exceptionally happen... self.assertNotIn(article.lines[-1], (b".", b".\n", b".\r\n")) @unittest.skipIf(True, "FIXME: see bpo-32128") def test_article_head_body(self): resp, count, first, last, name = self.server.group(self.GROUP_NAME) # Try to find an available article for art_num in (last, first, last - 1): try: resp, head = self.server.head(art_num) except nntplib.NNTPTemporaryError as e: if not e.response.startswith("423 "): raise # "423 No such article" => choose another one continue break else: self.skipTest("could not find a suitable article number") self.assertTrue(resp.startswith("221 "), resp) self.check_article_resp(resp, head, art_num) resp, body = self.server.body(art_num) self.assertTrue(resp.startswith("222 "), resp) self.check_article_resp(resp, body, art_num) resp, article = self.server.article(art_num) self.assertTrue(resp.startswith("220 "), resp) self.check_article_resp(resp, article, art_num) # Tolerate running the tests from behind a NNTP virus checker denylist = lambda line: line.startswith(b'X-Antivirus') filtered_head_lines = [line for line in head.lines if not denylist(line)] filtered_lines = [line for line in article.lines if not denylist(line)] self.assertEqual(filtered_lines, filtered_head_lines + [b''] + body.lines) def test_capabilities(self): # The server under test implements NNTP version 2 and has a # couple of well-known capabilities. Just sanity check that we # got them. def _check_caps(caps): caps_list = caps['LIST'] self.assertIsInstance(caps_list, (list, tuple)) self.assertIn('OVERVIEW.FMT', caps_list) self.assertGreaterEqual(self.server.nntp_version, 2) _check_caps(self.server.getcapabilities()) # This re-emits the command resp, caps = self.server.capabilities() _check_caps(caps) def test_zlogin(self): # This test must be the penultimate because further commands will be # refused. baduser = "notarealuser" badpw = "notarealpassword" # Check that bogus credentials cause failure self.assertRaises(nntplib.NNTPError, self.server.login, user=baduser, password=badpw, usenetrc=False) # FIXME: We should check that correct credentials succeed, but that # would require valid details for some server somewhere to be in the # test suite, I think. Gmane is anonymous, at least as used for the # other tests. def test_zzquit(self): # This test must be called last, hence the name cls = type(self) try: self.server.quit() finally: cls.server = None @classmethod def wrap_methods(cls): # Wrap all methods in a transient_internet() exception catcher # XXX put a generic version in test.support? def wrap_meth(meth): @functools.wraps(meth) def wrapped(self): with socket_helper.transient_internet(self.NNTP_HOST): meth(self) return wrapped for name in dir(cls): if not name.startswith('test_'): continue meth = getattr(cls, name) if not callable(meth): continue # Need to use a closure so that meth remains bound to its current # value setattr(cls, name, wrap_meth(meth)) def test_timeout(self): with self.assertRaises(ValueError): self.NNTP_CLASS(self.NNTP_HOST, timeout=0, usenetrc=False) def test_with_statement(self): def is_connected(): if not hasattr(server, 'file'): return False try: server.help() except (OSError, EOFError): return False return True kwargs = dict( timeout=support.INTERNET_TIMEOUT, usenetrc=False ) if self.ssl_context is not None: kwargs["ssl_context"] = self.ssl_context try: server = self.NNTP_CLASS(self.NNTP_HOST, **kwargs) with server: self.assertTrue(is_connected()) self.assertTrue(server.help()) self.assertFalse(is_connected()) server = self.NNTP_CLASS(self.NNTP_HOST, **kwargs) with server: server.quit() self.assertFalse(is_connected()) except SSLError as ssl_err: # matches "[SSL: DH_KEY_TOO_SMALL] dh key too small" if re.search(r'(?i)KEY.TOO.SMALL', ssl_err.reason): raise unittest.SkipTest(f"Got {ssl_err} connecting " f"to {self.NNTP_HOST!r}") raise NetworkedNNTPTestsMixin.wrap_methods() EOF_ERRORS = (EOFError,) if ssl is not None: EOF_ERRORS += (ssl.SSLEOFError,) class NetworkedNNTPTests(NetworkedNNTPTestsMixin, unittest.TestCase): # This server supports STARTTLS (gmane doesn't) NNTP_HOST = 'news.trigofacile.com' GROUP_NAME = 'fr.comp.lang.python' GROUP_PAT = 'fr.comp.lang.*' DESC = 'Python' NNTP_CLASS = NNTP @classmethod def setUpClass(cls): support.requires("network") kwargs = dict( timeout=support.INTERNET_TIMEOUT, usenetrc=False ) if cls.ssl_context is not None: kwargs["ssl_context"] = cls.ssl_context with socket_helper.transient_internet(cls.NNTP_HOST): try: cls.server = cls.NNTP_CLASS(cls.NNTP_HOST, **kwargs) except SSLError as ssl_err: # matches "[SSL: DH_KEY_TOO_SMALL] dh key too small" if re.search(r'(?i)KEY.TOO.SMALL', ssl_err.reason): raise unittest.SkipTest(f"{cls} got {ssl_err} connecting " f"to {cls.NNTP_HOST!r}") print(cls.NNTP_HOST) raise except EOF_ERRORS: raise unittest.SkipTest(f"{cls} got EOF error on connecting " f"to {cls.NNTP_HOST!r}") @classmethod def tearDownClass(cls): if cls.server is not None: cls.server.quit() @unittest.skipUnless(ssl, 'requires SSL support') class NetworkedNNTP_SSLTests(NetworkedNNTPTests): # Technical limits for this public NNTP server (see http://www.aioe.org): # "Only two concurrent connections per IP address are allowed and # 400 connections per day are accepted from each IP address." NNTP_HOST = 'nntp.aioe.org' # bpo-42794: aioe.test is one of the official groups on this server # used for testing: https://news.aioe.org/manual/aioe-hierarchy/ GROUP_NAME = 'aioe.test' GROUP_PAT = 'aioe.*' DESC = 'test' NNTP_CLASS = getattr(nntplib, 'NNTP_SSL', None) # Disabled as it produces too much data test_list = None # Disabled as the connection will already be encrypted. test_starttls = None if ssl is not None: ssl_context = ssl._create_unverified_context() ssl_context.set_ciphers("DEFAULT") ssl_context.maximum_version = ssl.TLSVersion.TLSv1_2 # # Non-networked tests using a local server (or something mocking it). # class _NNTPServerIO(io.RawIOBase): """A raw IO object allowing NNTP commands to be received and processed by a handler. The handler can push responses which can then be read from the IO object.""" def __init__(self, handler): io.RawIOBase.__init__(self) # The channel from the client self.c2s = io.BytesIO() # The channel to the client self.s2c = io.BytesIO() self.handler = handler self.handler.start(self.c2s.readline, self.push_data) def readable(self): return True def writable(self): return True def push_data(self, data): """Push (buffer) some data to send to the client.""" pos = self.s2c.tell() self.s2c.seek(0, 2) self.s2c.write(data) self.s2c.seek(pos) def write(self, b): """The client sends us some data""" pos = self.c2s.tell() self.c2s.write(b) self.c2s.seek(pos) self.handler.process_pending() return len(b) def readinto(self, buf): """The client wants to read a response""" self.handler.process_pending() b = self.s2c.read(len(buf)) n = len(b) buf[:n] = b return n def make_mock_file(handler): sio = _NNTPServerIO(handler) # Using BufferedRWPair instead of BufferedRandom ensures the file # isn't seekable. file = io.BufferedRWPair(sio, sio) return (sio, file) class NNTPServer(nntplib.NNTP): def __init__(self, f, host, readermode=None): self.file = f self.host = host self._base_init(readermode) def _close(self): self.file.close() del self.file class MockedNNTPTestsMixin: # Override in derived classes handler_class = None def setUp(self): super().setUp() self.make_server() def tearDown(self): super().tearDown() del self.server def make_server(self, *args, **kwargs): self.handler = self.handler_class() self.sio, file = make_mock_file(self.handler) self.server = NNTPServer(file, 'test.server', *args, **kwargs) return self.server class MockedNNTPWithReaderModeMixin(MockedNNTPTestsMixin): def setUp(self): super().setUp() self.make_server(readermode=True) class NNTPv1Handler: """A handler for RFC 977""" welcome = "200 NNTP mock server" def start(self, readline, push_data): self.in_body = False self.allow_posting = True self._readline = readline self._push_data = push_data self._logged_in = False self._user_sent = False # Our welcome self.handle_welcome() def _decode(self, data): return str(data, "utf-8", "surrogateescape") def process_pending(self): if self.in_body: while True: line = self._readline() if not line: return self.body.append(line) if line == b".\r\n": break try: meth, tokens = self.body_callback meth(*tokens, body=self.body) finally: self.body_callback = None self.body = None self.in_body = False while True: line = self._decode(self._readline()) if not line: return if not line.endswith("\r\n"): raise ValueError("line doesn't end with \\r\\n: {!r}".format(line)) line = line[:-2] cmd, *tokens = line.split() #meth = getattr(self.handler, "handle_" + cmd.upper(), None) meth = getattr(self, "handle_" + cmd.upper(), None) if meth is None: self.handle_unknown() else: try: meth(*tokens) except Exception as e: raise ValueError("command failed: {!r}".format(line)) from e else: if self.in_body: self.body_callback = meth, tokens self.body = [] def expect_body(self): """Flag that the client is expected to post a request body""" self.in_body = True def push_data(self, data): """Push some binary data""" self._push_data(data) def push_lit(self, lit): """Push a string literal""" lit = textwrap.dedent(lit) lit = "\r\n".join(lit.splitlines()) + "\r\n" lit = lit.encode('utf-8') self.push_data(lit) def handle_unknown(self): self.push_lit("500 What?") def handle_welcome(self): self.push_lit(self.welcome) def handle_QUIT(self): self.push_lit("205 Bye!") def handle_DATE(self): self.push_lit("111 20100914001155") def handle_GROUP(self, group): if group == "fr.comp.lang.python": self.push_lit("211 486 761 1265 fr.comp.lang.python") else: self.push_lit("411 No such group {}".format(group)) def handle_HELP(self): self.push_lit("""\ 100 Legal commands authinfo user Name|pass Password|generic date help Report problems to .""") def handle_STAT(self, message_spec=None): if message_spec is None: self.push_lit("412 No newsgroup selected") elif message_spec == "3000234": self.push_lit("223 3000234 <45223423@example.com>") elif message_spec == "<45223423@example.com>": self.push_lit("223 0 <45223423@example.com>") else: self.push_lit("430 No Such Article Found") def handle_NEXT(self): self.push_lit("223 3000237 <668929@example.org> retrieved") def handle_LAST(self): self.push_lit("223 3000234 <45223423@example.com> retrieved") def handle_LIST(self, action=None, param=None): if action is None: self.push_lit("""\ 215 Newsgroups in form "group high low flags". comp.lang.python 0000052340 0000002828 y comp.lang.python.announce 0000001153 0000000993 m free.it.comp.lang.python 0000000002 0000000002 y fr.comp.lang.python 0000001254 0000000760 y free.it.comp.lang.python.learner 0000000000 0000000001 y tw.bbs.comp.lang.python 0000000304 0000000304 y .""") elif action == "ACTIVE": if param == "*distutils*": self.push_lit("""\ 215 Newsgroups in form "group high low flags" gmane.comp.python.distutils.devel 0000014104 0000000001 m gmane.comp.python.distutils.cvs 0000000000 0000000001 m .""") else: self.push_lit("""\ 215 Newsgroups in form "group high low flags" .""") elif action == "OVERVIEW.FMT": self.push_lit("""\ 215 Order of fields in overview database. Subject: From: Date: Message-ID: References: Bytes: Lines: Xref:full .""") elif action == "NEWSGROUPS": assert param is not None if param == "comp.lang.python": self.push_lit("""\ 215 Descriptions in form "group description". comp.lang.python\tThe Python computer language. .""") elif param == "comp.lang.python*": self.push_lit("""\ 215 Descriptions in form "group description". comp.lang.python.announce\tAnnouncements about the Python language. (Moderated) comp.lang.python\tThe Python computer language. .""") else: self.push_lit("""\ 215 Descriptions in form "group description". .""") else: self.push_lit('501 Unknown LIST keyword') def handle_NEWNEWS(self, group, date_str, time_str): # We hard code different return messages depending on passed # argument and date syntax. if (group == "comp.lang.python" and date_str == "20100913" and time_str == "082004"): # Date was passed in RFC 3977 format (NNTP "v2") self.push_lit("""\ 230 list of newsarticles (NNTP v2) created after Mon Sep 13 08:20:04 2010 follows .""") elif (group == "comp.lang.python" and date_str == "100913" and time_str == "082004"): # Date was passed in RFC 977 format (NNTP "v1") self.push_lit("""\ 230 list of newsarticles (NNTP v1) created after Mon Sep 13 08:20:04 2010 follows .""") elif (group == 'comp.lang.python' and date_str in ('20100101', '100101') and time_str == '090000'): self.push_lit('too long line' * 3000 + '\n.') else: self.push_lit("""\ 230 An empty list of newsarticles follows .""") # (Note for experiments: many servers disable NEWNEWS. # As of this writing, sicinfo3.epfl.ch doesn't.) def handle_XOVER(self, message_spec): if message_spec == "57-59": self.push_lit( "224 Overview information for 57-58 follows\n" "57\tRe: ANN: New Plone book with strong Python (and Zope) themes throughout" "\tDoug Hellmann " "\tSat, 19 Jun 2010 18:04:08 -0400" "\t<4FD05F05-F98B-44DC-8111-C6009C925F0C@gmail.com>" "\t\t7103\t16" "\tXref: news.gmane.io gmane.comp.python.authors:57" "\n" "58\tLooking for a few good bloggers" "\tDoug Hellmann " "\tThu, 22 Jul 2010 09:14:14 -0400" "\t" "\t\t6683\t16" "\t" "\n" # A UTF-8 overview line from fr.comp.lang.python "59\tRe: Message d'erreur incompréhensible (par moi)" "\tEric Brunel " "\tWed, 15 Sep 2010 18:09:15 +0200" "\t" "\t<4c90ec87$0$32425$ba4acef3@reader.news.orange.fr>\t1641\t27" "\tXref: saria.nerim.net fr.comp.lang.python:1265" "\n" ".\n") else: self.push_lit("""\ 224 No articles .""") def handle_POST(self, *, body=None): if body is None: if self.allow_posting: self.push_lit("340 Input article; end with .") self.expect_body() else: self.push_lit("440 Posting not permitted") else: assert self.allow_posting self.push_lit("240 Article received OK") self.posted_body = body def handle_IHAVE(self, message_id, *, body=None): if body is None: if (self.allow_posting and message_id == ""): self.push_lit("335 Send it; end with .") self.expect_body() else: self.push_lit("435 Article not wanted") else: assert self.allow_posting self.push_lit("235 Article transferred OK") self.posted_body = body sample_head = """\ From: "Demo User" Subject: I am just a test article Content-Type: text/plain; charset=UTF-8; format=flowed Message-ID: """ sample_body = """\ This is just a test article. ..Here is a dot-starting line. -- Signed by Andr\xe9.""" sample_article = sample_head + "\n\n" + sample_body def handle_ARTICLE(self, message_spec=None): if message_spec is None: self.push_lit("220 3000237 <45223423@example.com>") elif message_spec == "<45223423@example.com>": self.push_lit("220 0 <45223423@example.com>") elif message_spec == "3000234": self.push_lit("220 3000234 <45223423@example.com>") else: self.push_lit("430 No Such Article Found") return self.push_lit(self.sample_article) self.push_lit(".") def handle_HEAD(self, message_spec=None): if message_spec is None: self.push_lit("221 3000237 <45223423@example.com>") elif message_spec == "<45223423@example.com>": self.push_lit("221 0 <45223423@example.com>") elif message_spec == "3000234": self.push_lit("221 3000234 <45223423@example.com>") else: self.push_lit("430 No Such Article Found") return self.push_lit(self.sample_head) self.push_lit(".") def handle_BODY(self, message_spec=None): if message_spec is None: self.push_lit("222 3000237 <45223423@example.com>") elif message_spec == "<45223423@example.com>": self.push_lit("222 0 <45223423@example.com>") elif message_spec == "3000234": self.push_lit("222 3000234 <45223423@example.com>") else: self.push_lit("430 No Such Article Found") return self.push_lit(self.sample_body) self.push_lit(".") def handle_AUTHINFO(self, cred_type, data): if self._logged_in: self.push_lit('502 Already Logged In') elif cred_type == 'user': if self._user_sent: self.push_lit('482 User Credential Already Sent') else: self.push_lit('381 Password Required') self._user_sent = True elif cred_type == 'pass': self.push_lit('281 Login Successful') self._logged_in = True else: raise Exception('Unknown cred type {}'.format(cred_type)) class NNTPv2Handler(NNTPv1Handler): """A handler for RFC 3977 (NNTP "v2")""" def handle_CAPABILITIES(self): fmt = """\ 101 Capability list: VERSION 2 3 IMPLEMENTATION INN 2.5.1{} HDR LIST ACTIVE ACTIVE.TIMES DISTRIB.PATS HEADERS NEWSGROUPS OVERVIEW.FMT OVER POST READER .""" if not self._logged_in: self.push_lit(fmt.format('\n AUTHINFO USER')) else: self.push_lit(fmt.format('')) def handle_MODE(self, _): raise Exception('MODE READER sent despite READER has been advertised') def handle_OVER(self, message_spec=None): return self.handle_XOVER(message_spec) class CapsAfterLoginNNTPv2Handler(NNTPv2Handler): """A handler that allows CAPABILITIES only after login""" def handle_CAPABILITIES(self): if not self._logged_in: self.push_lit('480 You must log in.') else: super().handle_CAPABILITIES() class ModeSwitchingNNTPv2Handler(NNTPv2Handler): """A server that starts in transit mode""" def __init__(self): self._switched = False def handle_CAPABILITIES(self): fmt = """\ 101 Capability list: VERSION 2 3 IMPLEMENTATION INN 2.5.1 HDR LIST ACTIVE ACTIVE.TIMES DISTRIB.PATS HEADERS NEWSGROUPS OVERVIEW.FMT OVER POST {}READER .""" if self._switched: self.push_lit(fmt.format('')) else: self.push_lit(fmt.format('MODE-')) def handle_MODE(self, what): assert not self._switched and what == 'reader' self._switched = True self.push_lit('200 Posting allowed') class NNTPv1v2TestsMixin: def setUp(self): super().setUp() def test_welcome(self): self.assertEqual(self.server.welcome, self.handler.welcome) def test_authinfo(self): if self.nntp_version == 2: self.assertIn('AUTHINFO', self.server._caps) self.server.login('testuser', 'testpw') # if AUTHINFO is gone from _caps we also know that getcapabilities() # has been called after login as it should self.assertNotIn('AUTHINFO', self.server._caps) def test_date(self): resp, date = self.server.date() self.assertEqual(resp, "111 20100914001155") self.assertEqual(date, datetime.datetime(2010, 9, 14, 0, 11, 55)) def test_quit(self): self.assertFalse(self.sio.closed) resp = self.server.quit() self.assertEqual(resp, "205 Bye!") self.assertTrue(self.sio.closed) def test_help(self): resp, help = self.server.help() self.assertEqual(resp, "100 Legal commands") self.assertEqual(help, [ ' authinfo user Name|pass Password|generic ', ' date', ' help', 'Report problems to ', ]) def test_list(self): resp, groups = self.server.list() self.assertEqual(len(groups), 6) g = groups[1] self.assertEqual(g, GroupInfo("comp.lang.python.announce", "0000001153", "0000000993", "m")) resp, groups = self.server.list("*distutils*") self.assertEqual(len(groups), 2) g = groups[0] self.assertEqual(g, GroupInfo("gmane.comp.python.distutils.devel", "0000014104", "0000000001", "m")) def test_stat(self): resp, art_num, message_id = self.server.stat(3000234) self.assertEqual(resp, "223 3000234 <45223423@example.com>") self.assertEqual(art_num, 3000234) self.assertEqual(message_id, "<45223423@example.com>") resp, art_num, message_id = self.server.stat("<45223423@example.com>") self.assertEqual(resp, "223 0 <45223423@example.com>") self.assertEqual(art_num, 0) self.assertEqual(message_id, "<45223423@example.com>") with self.assertRaises(nntplib.NNTPTemporaryError) as cm: self.server.stat("") self.assertEqual(cm.exception.response, "430 No Such Article Found") with self.assertRaises(nntplib.NNTPTemporaryError) as cm: self.server.stat() self.assertEqual(cm.exception.response, "412 No newsgroup selected") def test_next(self): resp, art_num, message_id = self.server.next() self.assertEqual(resp, "223 3000237 <668929@example.org> retrieved") self.assertEqual(art_num, 3000237) self.assertEqual(message_id, "<668929@example.org>") def test_last(self): resp, art_num, message_id = self.server.last() self.assertEqual(resp, "223 3000234 <45223423@example.com> retrieved") self.assertEqual(art_num, 3000234) self.assertEqual(message_id, "<45223423@example.com>") def test_description(self): desc = self.server.description("comp.lang.python") self.assertEqual(desc, "The Python computer language.") desc = self.server.description("comp.lang.pythonx") self.assertEqual(desc, "") def test_descriptions(self): resp, groups = self.server.descriptions("comp.lang.python") self.assertEqual(resp, '215 Descriptions in form "group description".') self.assertEqual(groups, { "comp.lang.python": "The Python computer language.", }) resp, groups = self.server.descriptions("comp.lang.python*") self.assertEqual(groups, { "comp.lang.python": "The Python computer language.", "comp.lang.python.announce": "Announcements about the Python language. (Moderated)", }) resp, groups = self.server.descriptions("comp.lang.pythonx") self.assertEqual(groups, {}) def test_group(self): resp, count, first, last, group = self.server.group("fr.comp.lang.python") self.assertTrue(resp.startswith("211 "), resp) self.assertEqual(first, 761) self.assertEqual(last, 1265) self.assertEqual(count, 486) self.assertEqual(group, "fr.comp.lang.python") with self.assertRaises(nntplib.NNTPTemporaryError) as cm: self.server.group("comp.lang.python.devel") exc = cm.exception self.assertTrue(exc.response.startswith("411 No such group"), exc.response) def test_newnews(self): # NEWNEWS comp.lang.python [20]100913 082004 dt = datetime.datetime(2010, 9, 13, 8, 20, 4) resp, ids = self.server.newnews("comp.lang.python", dt) expected = ( "230 list of newsarticles (NNTP v{0}) " "created after Mon Sep 13 08:20:04 2010 follows" ).format(self.nntp_version) self.assertEqual(resp, expected) self.assertEqual(ids, [ "", "", ]) # NEWNEWS fr.comp.lang.python [20]100913 082004 dt = datetime.datetime(2010, 9, 13, 8, 20, 4) resp, ids = self.server.newnews("fr.comp.lang.python", dt) self.assertEqual(resp, "230 An empty list of newsarticles follows") self.assertEqual(ids, []) def _check_article_body(self, lines): self.assertEqual(len(lines), 4) self.assertEqual(lines[-1].decode('utf-8'), "-- Signed by André.") self.assertEqual(lines[-2], b"") self.assertEqual(lines[-3], b".Here is a dot-starting line.") self.assertEqual(lines[-4], b"This is just a test article.") def _check_article_head(self, lines): self.assertEqual(len(lines), 4) self.assertEqual(lines[0], b'From: "Demo User" ') self.assertEqual(lines[3], b"Message-ID: ") def _check_article_data(self, lines): self.assertEqual(len(lines), 9) self._check_article_head(lines[:4]) self._check_article_body(lines[-4:]) self.assertEqual(lines[4], b"") def test_article(self): # ARTICLE resp, info = self.server.article() self.assertEqual(resp, "220 3000237 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 3000237) self.assertEqual(message_id, "<45223423@example.com>") self._check_article_data(lines) # ARTICLE num resp, info = self.server.article(3000234) self.assertEqual(resp, "220 3000234 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 3000234) self.assertEqual(message_id, "<45223423@example.com>") self._check_article_data(lines) # ARTICLE id resp, info = self.server.article("<45223423@example.com>") self.assertEqual(resp, "220 0 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 0) self.assertEqual(message_id, "<45223423@example.com>") self._check_article_data(lines) # Non-existent id with self.assertRaises(nntplib.NNTPTemporaryError) as cm: self.server.article("") self.assertEqual(cm.exception.response, "430 No Such Article Found") def test_article_file(self): # With a "file" argument f = io.BytesIO() resp, info = self.server.article(file=f) self.assertEqual(resp, "220 3000237 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 3000237) self.assertEqual(message_id, "<45223423@example.com>") self.assertEqual(lines, []) data = f.getvalue() self.assertTrue(data.startswith( b'From: "Demo User" \r\n' b'Subject: I am just a test article\r\n' ), ascii(data)) self.assertTrue(data.endswith( b'This is just a test article.\r\n' b'.Here is a dot-starting line.\r\n' b'\r\n' b'-- Signed by Andr\xc3\xa9.\r\n' ), ascii(data)) def test_head(self): # HEAD resp, info = self.server.head() self.assertEqual(resp, "221 3000237 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 3000237) self.assertEqual(message_id, "<45223423@example.com>") self._check_article_head(lines) # HEAD num resp, info = self.server.head(3000234) self.assertEqual(resp, "221 3000234 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 3000234) self.assertEqual(message_id, "<45223423@example.com>") self._check_article_head(lines) # HEAD id resp, info = self.server.head("<45223423@example.com>") self.assertEqual(resp, "221 0 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 0) self.assertEqual(message_id, "<45223423@example.com>") self._check_article_head(lines) # Non-existent id with self.assertRaises(nntplib.NNTPTemporaryError) as cm: self.server.head("") self.assertEqual(cm.exception.response, "430 No Such Article Found") def test_head_file(self): f = io.BytesIO() resp, info = self.server.head(file=f) self.assertEqual(resp, "221 3000237 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 3000237) self.assertEqual(message_id, "<45223423@example.com>") self.assertEqual(lines, []) data = f.getvalue() self.assertTrue(data.startswith( b'From: "Demo User" \r\n' b'Subject: I am just a test article\r\n' ), ascii(data)) self.assertFalse(data.endswith( b'This is just a test article.\r\n' b'.Here is a dot-starting line.\r\n' b'\r\n' b'-- Signed by Andr\xc3\xa9.\r\n' ), ascii(data)) def test_body(self): # BODY resp, info = self.server.body() self.assertEqual(resp, "222 3000237 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 3000237) self.assertEqual(message_id, "<45223423@example.com>") self._check_article_body(lines) # BODY num resp, info = self.server.body(3000234) self.assertEqual(resp, "222 3000234 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 3000234) self.assertEqual(message_id, "<45223423@example.com>") self._check_article_body(lines) # BODY id resp, info = self.server.body("<45223423@example.com>") self.assertEqual(resp, "222 0 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 0) self.assertEqual(message_id, "<45223423@example.com>") self._check_article_body(lines) # Non-existent id with self.assertRaises(nntplib.NNTPTemporaryError) as cm: self.server.body("") self.assertEqual(cm.exception.response, "430 No Such Article Found") def test_body_file(self): f = io.BytesIO() resp, info = self.server.body(file=f) self.assertEqual(resp, "222 3000237 <45223423@example.com>") art_num, message_id, lines = info self.assertEqual(art_num, 3000237) self.assertEqual(message_id, "<45223423@example.com>") self.assertEqual(lines, []) data = f.getvalue() self.assertFalse(data.startswith( b'From: "Demo User" \r\n' b'Subject: I am just a test article\r\n' ), ascii(data)) self.assertTrue(data.endswith( b'This is just a test article.\r\n' b'.Here is a dot-starting line.\r\n' b'\r\n' b'-- Signed by Andr\xc3\xa9.\r\n' ), ascii(data)) def check_over_xover_resp(self, resp, overviews): self.assertTrue(resp.startswith("224 "), resp) self.assertEqual(len(overviews), 3) art_num, over = overviews[0] self.assertEqual(art_num, 57) self.assertEqual(over, { "from": "Doug Hellmann ", "subject": "Re: ANN: New Plone book with strong Python (and Zope) themes throughout", "date": "Sat, 19 Jun 2010 18:04:08 -0400", "message-id": "<4FD05F05-F98B-44DC-8111-C6009C925F0C@gmail.com>", "references": "", ":bytes": "7103", ":lines": "16", "xref": "news.gmane.io gmane.comp.python.authors:57" }) art_num, over = overviews[1] self.assertEqual(over["xref"], None) art_num, over = overviews[2] self.assertEqual(over["subject"], "Re: Message d'erreur incompréhensible (par moi)") def test_xover(self): resp, overviews = self.server.xover(57, 59) self.check_over_xover_resp(resp, overviews) def test_over(self): # In NNTP "v1", this will fallback on XOVER resp, overviews = self.server.over((57, 59)) self.check_over_xover_resp(resp, overviews) sample_post = ( b'From: "Demo User" \r\n' b'Subject: I am just a test article\r\n' b'Content-Type: text/plain; charset=UTF-8; format=flowed\r\n' b'Message-ID: \r\n' b'\r\n' b'This is just a test article.\r\n' b'.Here is a dot-starting line.\r\n' b'\r\n' b'-- Signed by Andr\xc3\xa9.\r\n' ) def _check_posted_body(self): # Check the raw body as received by the server lines = self.handler.posted_body # One additional line for the "." terminator self.assertEqual(len(lines), 10) self.assertEqual(lines[-1], b'.\r\n') self.assertEqual(lines[-2], b'-- Signed by Andr\xc3\xa9.\r\n') self.assertEqual(lines[-3], b'\r\n') self.assertEqual(lines[-4], b'..Here is a dot-starting line.\r\n') self.assertEqual(lines[0], b'From: "Demo User" \r\n') def _check_post_ihave_sub(self, func, *args, file_factory): # First the prepared post with CRLF endings post = self.sample_post func_args = args + (file_factory(post),) self.handler.posted_body = None resp = func(*func_args) self._check_posted_body() # Then the same post with "normal" line endings - they should be # converted by NNTP.post and NNTP.ihave. post = self.sample_post.replace(b"\r\n", b"\n") func_args = args + (file_factory(post),) self.handler.posted_body = None resp = func(*func_args) self._check_posted_body() return resp def check_post_ihave(self, func, success_resp, *args): # With a bytes object resp = self._check_post_ihave_sub(func, *args, file_factory=bytes) self.assertEqual(resp, success_resp) # With a bytearray object resp = self._check_post_ihave_sub(func, *args, file_factory=bytearray) self.assertEqual(resp, success_resp) # With a file object resp = self._check_post_ihave_sub(func, *args, file_factory=io.BytesIO) self.assertEqual(resp, success_resp) # With an iterable of terminated lines def iterlines(b): return iter(b.splitlines(keepends=True)) resp = self._check_post_ihave_sub(func, *args, file_factory=iterlines) self.assertEqual(resp, success_resp) # With an iterable of non-terminated lines def iterlines(b): return iter(b.splitlines(keepends=False)) resp = self._check_post_ihave_sub(func, *args, file_factory=iterlines) self.assertEqual(resp, success_resp) def test_post(self): self.check_post_ihave(self.server.post, "240 Article received OK") self.handler.allow_posting = False with self.assertRaises(nntplib.NNTPTemporaryError) as cm: self.server.post(self.sample_post) self.assertEqual(cm.exception.response, "440 Posting not permitted") def test_ihave(self): self.check_post_ihave(self.server.ihave, "235 Article transferred OK", "") with self.assertRaises(nntplib.NNTPTemporaryError) as cm: self.server.ihave("", self.sample_post) self.assertEqual(cm.exception.response, "435 Article not wanted") def test_too_long_lines(self): dt = datetime.datetime(2010, 1, 1, 9, 0, 0) self.assertRaises(nntplib.NNTPDataError, self.server.newnews, "comp.lang.python", dt) class NNTPv1Tests(NNTPv1v2TestsMixin, MockedNNTPTestsMixin, unittest.TestCase): """Tests an NNTP v1 server (no capabilities).""" nntp_version = 1 handler_class = NNTPv1Handler def test_caps(self): caps = self.server.getcapabilities() self.assertEqual(caps, {}) self.assertEqual(self.server.nntp_version, 1) self.assertEqual(self.server.nntp_implementation, None) class NNTPv2Tests(NNTPv1v2TestsMixin, MockedNNTPTestsMixin, unittest.TestCase): """Tests an NNTP v2 server (with capabilities).""" nntp_version = 2 handler_class = NNTPv2Handler def test_caps(self): caps = self.server.getcapabilities() self.assertEqual(caps, { 'VERSION': ['2', '3'], 'IMPLEMENTATION': ['INN', '2.5.1'], 'AUTHINFO': ['USER'], 'HDR': [], 'LIST': ['ACTIVE', 'ACTIVE.TIMES', 'DISTRIB.PATS', 'HEADERS', 'NEWSGROUPS', 'OVERVIEW.FMT'], 'OVER': [], 'POST': [], 'READER': [], }) self.assertEqual(self.server.nntp_version, 3) self.assertEqual(self.server.nntp_implementation, 'INN 2.5.1') class CapsAfterLoginNNTPv2Tests(MockedNNTPTestsMixin, unittest.TestCase): """Tests a probably NNTP v2 server with capabilities only after login.""" nntp_version = 2 handler_class = CapsAfterLoginNNTPv2Handler def test_caps_only_after_login(self): self.assertEqual(self.server._caps, {}) self.server.login('testuser', 'testpw') self.assertIn('VERSION', self.server._caps) class SendReaderNNTPv2Tests(MockedNNTPWithReaderModeMixin, unittest.TestCase): """Same tests as for v2 but we tell NTTP to send MODE READER to a server that isn't in READER mode by default.""" nntp_version = 2 handler_class = ModeSwitchingNNTPv2Handler def test_we_are_in_reader_mode_after_connect(self): self.assertIn('READER', self.server._caps) class MiscTests(unittest.TestCase): def test_decode_header(self): def gives(a, b): self.assertEqual(nntplib.decode_header(a), b) gives("" , "") gives("a plain header", "a plain header") gives(" with extra spaces ", " with extra spaces ") gives("=?ISO-8859-15?Q?D=E9buter_en_Python?=", "Débuter en Python") gives("=?utf-8?q?Re=3A_=5Bsqlite=5D_probl=C3=A8me_avec_ORDER_BY_sur_des_cha?=" " =?utf-8?q?=C3=AEnes_de_caract=C3=A8res_accentu=C3=A9es?=", "Re: [sqlite] problème avec ORDER BY sur des chaînes de caractères accentuées") gives("Re: =?UTF-8?B?cHJvYmzDqG1lIGRlIG1hdHJpY2U=?=", "Re: problème de matrice") # A natively utf-8 header (found in the real world!) gives("Re: Message d'erreur incompréhensible (par moi)", "Re: Message d'erreur incompréhensible (par moi)") def test_parse_overview_fmt(self): # The minimal (default) response lines = ["Subject:", "From:", "Date:", "Message-ID:", "References:", ":bytes", ":lines"] self.assertEqual(nntplib._parse_overview_fmt(lines), ["subject", "from", "date", "message-id", "references", ":bytes", ":lines"]) # The minimal response using alternative names lines = ["Subject:", "From:", "Date:", "Message-ID:", "References:", "Bytes:", "Lines:"] self.assertEqual(nntplib._parse_overview_fmt(lines), ["subject", "from", "date", "message-id", "references", ":bytes", ":lines"]) # Variations in casing lines = ["subject:", "FROM:", "DaTe:", "message-ID:", "References:", "BYTES:", "Lines:"] self.assertEqual(nntplib._parse_overview_fmt(lines), ["subject", "from", "date", "message-id", "references", ":bytes", ":lines"]) # First example from RFC 3977 lines = ["Subject:", "From:", "Date:", "Message-ID:", "References:", ":bytes", ":lines", "Xref:full", "Distribution:full"] self.assertEqual(nntplib._parse_overview_fmt(lines), ["subject", "from", "date", "message-id", "references", ":bytes", ":lines", "xref", "distribution"]) # Second example from RFC 3977 lines = ["Subject:", "From:", "Date:", "Message-ID:", "References:", "Bytes:", "Lines:", "Xref:FULL", "Distribution:FULL"] self.assertEqual(nntplib._parse_overview_fmt(lines), ["subject", "from", "date", "message-id", "references", ":bytes", ":lines", "xref", "distribution"]) # A classic response from INN lines = ["Subject:", "From:", "Date:", "Message-ID:", "References:", "Bytes:", "Lines:", "Xref:full"] self.assertEqual(nntplib._parse_overview_fmt(lines), ["subject", "from", "date", "message-id", "references", ":bytes", ":lines", "xref"]) def test_parse_overview(self): fmt = nntplib._DEFAULT_OVERVIEW_FMT + ["xref"] # First example from RFC 3977 lines = [ '3000234\tI am just a test article\t"Demo User" ' '\t6 Oct 1998 04:38:40 -0500\t' '<45223423@example.com>\t<45454@example.net>\t1234\t' '17\tXref: news.example.com misc.test:3000363', ] overview = nntplib._parse_overview(lines, fmt) (art_num, fields), = overview self.assertEqual(art_num, 3000234) self.assertEqual(fields, { 'subject': 'I am just a test article', 'from': '"Demo User" ', 'date': '6 Oct 1998 04:38:40 -0500', 'message-id': '<45223423@example.com>', 'references': '<45454@example.net>', ':bytes': '1234', ':lines': '17', 'xref': 'news.example.com misc.test:3000363', }) # Second example; here the "Xref" field is totally absent (including # the header name) and comes out as None lines = [ '3000234\tI am just a test article\t"Demo User" ' '\t6 Oct 1998 04:38:40 -0500\t' '<45223423@example.com>\t<45454@example.net>\t1234\t' '17\t\t', ] overview = nntplib._parse_overview(lines, fmt) (art_num, fields), = overview self.assertEqual(fields['xref'], None) # Third example; the "Xref" is an empty string, while "references" # is a single space. lines = [ '3000234\tI am just a test article\t"Demo User" ' '\t6 Oct 1998 04:38:40 -0500\t' '<45223423@example.com>\t \t1234\t' '17\tXref: \t', ] overview = nntplib._parse_overview(lines, fmt) (art_num, fields), = overview self.assertEqual(fields['references'], ' ') self.assertEqual(fields['xref'], '') def test_parse_datetime(self): def gives(a, b, *c): self.assertEqual(nntplib._parse_datetime(a, b), datetime.datetime(*c)) # Output of DATE command gives("19990623135624", None, 1999, 6, 23, 13, 56, 24) # Variations gives("19990623", "135624", 1999, 6, 23, 13, 56, 24) gives("990623", "135624", 1999, 6, 23, 13, 56, 24) gives("090623", "135624", 2009, 6, 23, 13, 56, 24) def test_unparse_datetime(self): # Test non-legacy mode # 1) with a datetime def gives(y, M, d, h, m, s, date_str, time_str): dt = datetime.datetime(y, M, d, h, m, s) self.assertEqual(nntplib._unparse_datetime(dt), (date_str, time_str)) self.assertEqual(nntplib._unparse_datetime(dt, False), (date_str, time_str)) gives(1999, 6, 23, 13, 56, 24, "19990623", "135624") gives(2000, 6, 23, 13, 56, 24, "20000623", "135624") gives(2010, 6, 5, 1, 2, 3, "20100605", "010203") # 2) with a date def gives(y, M, d, date_str, time_str): dt = datetime.date(y, M, d) self.assertEqual(nntplib._unparse_datetime(dt), (date_str, time_str)) self.assertEqual(nntplib._unparse_datetime(dt, False), (date_str, time_str)) gives(1999, 6, 23, "19990623", "000000") gives(2000, 6, 23, "20000623", "000000") gives(2010, 6, 5, "20100605", "000000") def test_unparse_datetime_legacy(self): # Test legacy mode (RFC 977) # 1) with a datetime def gives(y, M, d, h, m, s, date_str, time_str): dt = datetime.datetime(y, M, d, h, m, s) self.assertEqual(nntplib._unparse_datetime(dt, True), (date_str, time_str)) gives(1999, 6, 23, 13, 56, 24, "990623", "135624") gives(2000, 6, 23, 13, 56, 24, "000623", "135624") gives(2010, 6, 5, 1, 2, 3, "100605", "010203") # 2) with a date def gives(y, M, d, date_str, time_str): dt = datetime.date(y, M, d) self.assertEqual(nntplib._unparse_datetime(dt, True), (date_str, time_str)) gives(1999, 6, 23, "990623", "000000") gives(2000, 6, 23, "000623", "000000") gives(2010, 6, 5, "100605", "000000") @unittest.skipUnless(ssl, 'requires SSL support') def test_ssl_support(self): self.assertTrue(hasattr(nntplib, 'NNTP_SSL')) class PublicAPITests(unittest.TestCase): """Ensures that the correct values are exposed in the public API.""" def test_module_all_attribute(self): self.assertTrue(hasattr(nntplib, '__all__')) target_api = ['NNTP', 'NNTPError', 'NNTPReplyError', 'NNTPTemporaryError', 'NNTPPermanentError', 'NNTPProtocolError', 'NNTPDataError', 'decode_header'] if ssl is not None: target_api.append('NNTP_SSL') self.assertEqual(set(nntplib.__all__), set(target_api)) class MockSocketTests(unittest.TestCase): """Tests involving a mock socket object Used where the _NNTPServerIO file object is not enough.""" nntp_class = nntplib.NNTP def check_constructor_error_conditions( self, handler_class, expected_error_type, expected_error_msg, login=None, password=None): class mock_socket_module: def create_connection(address, timeout): return MockSocket() class MockSocket: def close(self): nonlocal socket_closed socket_closed = True def makefile(socket, mode): handler = handler_class() _, file = make_mock_file(handler) files.append(file) return file socket_closed = False files = [] with patch('nntplib.socket', mock_socket_module), \ self.assertRaisesRegex(expected_error_type, expected_error_msg): self.nntp_class('dummy', user=login, password=password) self.assertTrue(socket_closed) for f in files: self.assertTrue(f.closed) def test_bad_welcome(self): #Test a bad welcome message class Handler(NNTPv1Handler): welcome = 'Bad Welcome' self.check_constructor_error_conditions( Handler, nntplib.NNTPProtocolError, Handler.welcome) def test_service_temporarily_unavailable(self): #Test service temporarily unavailable class Handler(NNTPv1Handler): welcome = '400 Service temporarily unavailable' self.check_constructor_error_conditions( Handler, nntplib.NNTPTemporaryError, Handler.welcome) def test_service_permanently_unavailable(self): #Test service permanently unavailable class Handler(NNTPv1Handler): welcome = '502 Service permanently unavailable' self.check_constructor_error_conditions( Handler, nntplib.NNTPPermanentError, Handler.welcome) def test_bad_capabilities(self): #Test a bad capabilities response class Handler(NNTPv1Handler): def handle_CAPABILITIES(self): self.push_lit(capabilities_response) capabilities_response = '201 bad capability' self.check_constructor_error_conditions( Handler, nntplib.NNTPReplyError, capabilities_response) def test_login_aborted(self): #Test a bad authinfo response login = 't@e.com' password = 'python' class Handler(NNTPv1Handler): def handle_AUTHINFO(self, *args): self.push_lit(authinfo_response) authinfo_response = '503 Mechanism not recognized' self.check_constructor_error_conditions( Handler, nntplib.NNTPPermanentError, authinfo_response, login, password) class bypass_context: """Bypass encryption and actual SSL module""" def wrap_socket(sock, **args): return sock @unittest.skipUnless(ssl, 'requires SSL support') class MockSslTests(MockSocketTests): @staticmethod def nntp_class(*pos, **kw): return nntplib.NNTP_SSL(*pos, ssl_context=bypass_context, **kw) class LocalServerTests(unittest.TestCase): def setUp(self): sock = socket.socket() port = socket_helper.bind_port(sock) sock.listen() self.background = threading.Thread( target=self.run_server, args=(sock,)) self.background.start() self.addCleanup(self.background.join) self.nntp = self.enterContext(NNTP(socket_helper.HOST, port, usenetrc=False)) def run_server(self, sock): # Could be generalized to handle more commands in separate methods with sock: [client, _] = sock.accept() with contextlib.ExitStack() as cleanup: cleanup.enter_context(client) reader = cleanup.enter_context(client.makefile('rb')) client.sendall(b'200 Server ready\r\n') while True: cmd = reader.readline() if cmd == b'CAPABILITIES\r\n': client.sendall( b'101 Capability list:\r\n' b'VERSION 2\r\n' b'STARTTLS\r\n' b'.\r\n' ) elif cmd == b'STARTTLS\r\n': reader.close() client.sendall(b'382 Begin TLS negotiation now\r\n') context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) context.load_cert_chain(certfile) client = context.wrap_socket( client, server_side=True) cleanup.enter_context(client) reader = cleanup.enter_context(client.makefile('rb')) elif cmd == b'QUIT\r\n': client.sendall(b'205 Bye!\r\n') break else: raise ValueError('Unexpected command {!r}'.format(cmd)) @unittest.skipUnless(ssl, 'requires SSL support') def test_starttls(self): file = self.nntp.file sock = self.nntp.sock self.nntp.starttls() # Check that the socket and internal pseudo-file really were # changed. self.assertNotEqual(file, self.nntp.file) self.assertNotEqual(sock, self.nntp.sock) # Check that the new socket really is an SSL one self.assertIsInstance(self.nntp.sock, ssl.SSLSocket) # Check that trying starttls when it's already active fails. self.assertRaises(ValueError, self.nntp.starttls) if __name__ == "__main__": unittest.main() python-deadlib-3.13.0/pipes/000077500000000000000000000000001471045437300156315ustar00rootroot00000000000000python-deadlib-3.13.0/pipes/Doc/000077500000000000000000000000001471045437300163365ustar00rootroot00000000000000python-deadlib-3.13.0/pipes/Doc/pipes.rst000066400000000000000000000053441471045437300202160ustar00rootroot00000000000000:mod:`pipes` --- Interface to shell pipelines ============================================= .. module:: pipes :platform: Unix :synopsis: A Python interface to Unix shell pipelines. :deprecated: .. sectionauthor:: Moshe Zadka **Source code:** :source:`Lib/pipes.py` .. deprecated-removed:: 3.11 3.13 The :mod:`pipes` module is deprecated (see :pep:`PEP 594 <594#pipes>` for details). Please use the :mod:`subprocess` module instead. -------------- The :mod:`pipes` module defines a class to abstract the concept of a *pipeline* --- a sequence of converters from one file to another. Because the module uses :program:`/bin/sh` command lines, a POSIX or compatible shell for :func:`os.system` and :func:`os.popen` is required. .. availability:: Unix, not VxWorks. The :mod:`pipes` module defines the following class: .. class:: Template() An abstraction of a pipeline. Example:: >>> import pipes >>> t = pipes.Template() >>> t.append('tr a-z A-Z', '--') >>> f = t.open('pipefile', 'w') >>> f.write('hello world') >>> f.close() >>> open('pipefile').read() 'HELLO WORLD' .. _template-objects: Template Objects ---------------- Template objects following methods: .. method:: Template.reset() Restore a pipeline template to its initial state. .. method:: Template.clone() Return a new, equivalent, pipeline template. .. method:: Template.debug(flag) If *flag* is true, turn debugging on. Otherwise, turn debugging off. When debugging is on, commands to be executed are printed, and the shell is given ``set -x`` command to be more verbose. .. method:: Template.append(cmd, kind) Append a new action at the end. The *cmd* variable must be a valid bourne shell command. The *kind* variable consists of two letters. The first letter can be either of ``'-'`` (which means the command reads its standard input), ``'f'`` (which means the commands reads a given file on the command line) or ``'.'`` (which means the commands reads no input, and hence must be first.) Similarly, the second letter can be either of ``'-'`` (which means the command writes to standard output), ``'f'`` (which means the command writes a file on the command line) or ``'.'`` (which means the command does not write anything, and hence must be last.) .. method:: Template.prepend(cmd, kind) Add a new action at the beginning. See :meth:`append` for explanations of the arguments. .. method:: Template.open(file, mode) Return a file-like object, open to *file*, but read from or written to by the pipeline. Note that only one of ``'r'``, ``'w'`` may be given. .. method:: Template.copy(infile, outfile) Copy *infile* to *outfile* through the pipe. python-deadlib-3.13.0/pipes/LICENSE000066400000000000000000000050111471045437300166330ustar00rootroot00000000000000Copyright © 2001-2023 Python Software Foundation; All Rights Reserved This code originally taken from the Python 3.11.3 distribution and it is therefore now released under the following Python-style license: 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using nntplib software 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 nntplib alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright © 2001-2023 Python Software Foundation; All Rights Reserved" are retained in nntplib alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates nntplib 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 nntplib. 4. PSF is making nntplib 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 NNTPLIB WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF NNTPLIB FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING NNTPLIB, 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 nntplib, Licensee agrees to be bound by the terms and conditions of this License Agreement. python-deadlib-3.13.0/pipes/README.rst000066400000000000000000000005161471045437300173220ustar00rootroot00000000000000Dead battery redistribution =========================== Python is moving forward! Python finally started to remove dead batteries. For more information, see `PEP 594 `_. If your project depends on a module that has been removed from the standard, here is the redistribution of the dead batteries. python-deadlib-3.13.0/pipes/pipes/000077500000000000000000000000001471045437300167515ustar00rootroot00000000000000python-deadlib-3.13.0/pipes/pipes/__init__.py000066400000000000000000000220371471045437300210660ustar00rootroot00000000000000"""Conversion pipeline templates. The problem: ------------ Suppose you have some data that you want to convert to another format, such as from GIF image format to PPM image format. Maybe the conversion involves several steps (e.g. piping it through compress or uuencode). Some of the conversion steps may require that their input is a disk file, others may be able to read standard input; similar for their output. The input to the entire conversion may also be read from a disk file or from an open file, and similar for its output. The module lets you construct a pipeline template by sticking one or more conversion steps together. It will take care of creating and removing temporary files if they are necessary to hold intermediate data. You can then use the template to do conversions from many different sources to many different destinations. The temporary file names used are different each time the template is used. The templates are objects so you can create templates for many different conversion steps and store them in a dictionary, for instance. Directions: ----------- To create a template: t = Template() To add a conversion step to a template: t.append(command, kind) where kind is a string of two characters: the first is '-' if the command reads its standard input or 'f' if it requires a file; the second likewise for the output. The command must be valid /bin/sh syntax. If input or output files are required, they are passed as $IN and $OUT; otherwise, it must be possible to use the command in a pipeline. To add a conversion step at the beginning: t.prepend(command, kind) To convert a file to another file using a template: sts = t.copy(infile, outfile) If infile or outfile are the empty string, standard input is read or standard output is written, respectively. The return value is the exit status of the conversion pipeline. To open a file for reading or writing through a conversion pipeline: fp = t.open(file, mode) where mode is 'r' to read the file, or 'w' to write it -- just like for the built-in function open() or for os.popen(). To create a new template object initialized to a given one: t2 = t.clone() """ # ' import re import os import tempfile import warnings # we import the quote function rather than the module for backward compat # (quote used to be an undocumented but used function in pipes) from shlex import quote # python-deadlib: Replace deprecation warning not to raise exception warnings.warn( f"{__name__} was removed in Python 3.13. " f"Please be aware that you are currently NOT using standard '{__name__}', " f"but instead a separately installed 'standard-{__name__}'.", DeprecationWarning, stacklevel=2 ) __all__ = ["Template"] # Conversion step kinds FILEIN_FILEOUT = 'ff' # Must read & write real files STDIN_FILEOUT = '-f' # Must write a real file FILEIN_STDOUT = 'f-' # Must read a real file STDIN_STDOUT = '--' # Normal pipeline element SOURCE = '.-' # Must be first, writes stdout SINK = '-.' # Must be last, reads stdin stepkinds = [FILEIN_FILEOUT, STDIN_FILEOUT, FILEIN_STDOUT, STDIN_STDOUT, \ SOURCE, SINK] class Template: """Class representing a pipeline template.""" def __init__(self): """Template() returns a fresh pipeline template.""" self.debugging = 0 self.reset() def __repr__(self): """t.__repr__() implements repr(t).""" return '