├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── dockerfiles │ ├── Dockerfile_debian │ ├── Dockerfile_fedora │ ├── Dockerfile_redhat │ └── Dockerfile_ubuntu │ ├── dockertests.yml │ ├── release-deploy.yml │ └── testsuite.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── CHANGES.md ├── CONTRIBUTING.md ├── COPYING ├── Dockerfile ├── MANIFEST.in ├── README.md ├── docs ├── Makefile ├── conf.py ├── convenience.rst ├── customize.rst ├── index.rst ├── intro.rst ├── make.bat ├── modules.rst ├── requirements.txt ├── troubleshooting.rst └── usage.rst ├── extra_requirements.txt ├── legacy_requirements.txt ├── mypy.ini ├── pyfakefs ├── __init__.py ├── _version.py ├── fake_file.py ├── fake_filesystem.py ├── fake_filesystem_shutil.py ├── fake_filesystem_unittest.py ├── fake_io.py ├── fake_legacy_modules.py ├── fake_open.py ├── fake_os.py ├── fake_path.py ├── fake_pathlib.py ├── fake_scandir.py ├── helpers.py ├── legacy_packages.py ├── mox3_stubout.py ├── patched_packages.py ├── py.typed ├── pytest_plugin.py ├── pytest_session_tests │ └── __init__.py ├── pytest_tests │ ├── __init__.py │ ├── conftest.py │ ├── data │ │ └── test.parquet │ ├── example.py │ ├── fake_fcntl_test.py │ ├── hook_test │ │ ├── conftest.py │ │ └── pytest_hook_test.py │ ├── io.py │ ├── lib_using_pathlib.py │ ├── local_import.py │ ├── ns_package │ │ └── test │ │ │ ├── ns_package │ │ │ └── test │ │ │ │ └── test_file.py │ │ │ └── setup.py │ ├── pytest_check_failed_plugin_test.py │ ├── pytest_doctest_test.py │ ├── pytest_fixture_param_test.py │ ├── pytest_fixture_test.py │ ├── pytest_module_fixture_test.py │ ├── pytest_plugin_failing_helper.py │ ├── pytest_plugin_test.py │ ├── pytest_reload_pandas_test.py │ ├── test_patch_on_setup.py │ ├── test_reload_local_import.py │ └── unhashable.py └── tests │ ├── __init__.py │ ├── all_tests.py │ ├── all_tests_without_extra_packages.py │ ├── dynamic_patch_test.py │ ├── example.py │ ├── example_test.py │ ├── fake_filesystem_glob_test.py │ ├── fake_filesystem_shutil_test.py │ ├── fake_filesystem_test.py │ ├── fake_filesystem_unittest_test.py │ ├── fake_filesystem_vs_real_test.py │ ├── fake_legacy_modules_test.py │ ├── fake_open_test.py │ ├── fake_os_test.py │ ├── fake_pathlib_test.py │ ├── fake_stat_time_test.py │ ├── fake_tempfile_test.py │ ├── fixtures │ ├── __init__.py │ ├── config_module.py │ ├── deprecated_property.py │ ├── excel_test.xlsx │ └── module_with_attributes.py │ ├── import_as_example.py │ ├── logsio.py │ ├── mox3_stubout_example.py │ ├── mox3_stubout_test.py │ ├── patched_packages_test.py │ ├── performance_test.py │ ├── skipped_pathlib.py │ └── test_utils.py ├── pyproject.toml ├── requirements.txt ├── requirements_dev.txt ├── setup.cfg ├── setup.py └── tox.ini /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | Describe the observed behavior, and what you expect to happen instead. 12 | In case of a crash, please provide the complete stack trace. 13 | 14 | **How To Reproduce** 15 | Please provide a unit test or a minimal reproducible example that shows 16 | the problem. 17 | 18 | **Your environment** 19 | Please run the following in the environment where the problem happened and 20 | paste the output. 21 | ```bash 22 | python -c "import platform; print(platform.platform())" 23 | python -c "import sys; print('Python', sys.version)" 24 | python -c "from pyfakefs import __version__; print('pyfakefs', __version__)" 25 | python -c "import pytest; print('pytest', pytest.__version__)" 26 | ``` 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A description of any alternative solutions or features you've considered. 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | #### Describe the changes 6 | The related issue or a description of the bug or feature that this PR addresses. 7 | 8 | #### Tasks 9 | - [ ] Unit tests added that reproduce the issue or prove feature is working 10 | - [ ] Fix or feature added 11 | - [ ] Entry to release notes added 12 | - [ ] Pre-commit CI shows no errors 13 | - [ ] Unit tests passing 14 | - [ ] For documentation changes: The Read the Docs preview builds and looks as expected 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: weekly 7 | -------------------------------------------------------------------------------- /.github/workflows/dockerfiles/Dockerfile_debian: -------------------------------------------------------------------------------- 1 | # Copyright 2018 John McGehee. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM debian 16 | MAINTAINER jmcgeheeiv@users.noreply.github.com 17 | 18 | RUN apt-get update && apt-get install -y locales 19 | RUN locale-gen en_US.UTF-8 20 | ENV LANG en_US.UTF-8 21 | ENV LANGUAGE en_US:en 22 | ENV LC_ALL en_US.UTF-8 23 | ARG github_repo=pytest-dev/pyfakefs 24 | ARG github_branch=main 25 | 26 | RUN apt-get update && apt-get install -y \ 27 | python3-pip \ 28 | unzip \ 29 | wget \ 30 | python3-venv 31 | 32 | RUN apt-get clean 33 | 34 | RUN useradd pyfakefs 35 | 36 | RUN mkdir -p work \ 37 | && wget https://github.com/$github_repo/archive/$github_branch.zip -O archive.zip \ 38 | && unzip archive.zip -d work 39 | RUN WORK_DIR=`ls -d work/*`; mv $WORK_DIR work/pyfakefs 40 | RUN chown -R pyfakefs:pyfakefs work/pyfakefs 41 | WORKDIR work/pyfakefs 42 | RUN python3 -m venv ../venv 43 | RUN ../venv/bin/pip install -r requirements.txt 44 | RUN ../venv/bin/pip install -r extra_requirements.txt 45 | RUN ../venv/bin/pip install parquet pyarrow 46 | RUN ../venv/bin/pip install -e . 47 | 48 | USER pyfakefs 49 | ENV PYTHONPATH work/pyfakefs 50 | ENV TEST_REAL_FS=1 51 | CMD ["../venv/bin/pytest", "pyfakefs/pytest_tests", "pyfakefs/tests"] 52 | -------------------------------------------------------------------------------- /.github/workflows/dockerfiles/Dockerfile_fedora: -------------------------------------------------------------------------------- 1 | # Copyright 2018 John McGehee. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM fedora:latest 16 | MAINTAINER jmcgeheeiv@users.noreply.github.com 17 | 18 | ENV LANG en_US.UTF-8 19 | ENV LANGUAGE en_US:en 20 | ENV LC_ALL en_US.UTF-8 21 | ARG github_repo=pytest-dev/pyfakefs 22 | ARG github_branch=main 23 | 24 | RUN dnf install -y python3-pip unzip wget 25 | 26 | RUN useradd pyfakefs 27 | 28 | RUN mkdir -p work \ 29 | && wget https://github.com/$github_repo/archive/$github_branch.zip -O archive.zip \ 30 | && unzip archive.zip -d work 31 | RUN WORK_DIR=`ls -d work/*`; mv $WORK_DIR work/pyfakefs 32 | RUN chown -R pyfakefs:pyfakefs work/pyfakefs 33 | WORKDIR work/pyfakefs 34 | RUN pip3 install -r requirements.txt 35 | RUN pip3 install -r extra_requirements.txt 36 | RUN pip3 install -e . 37 | 38 | USER pyfakefs 39 | ENV PYTHONPATH work/pyfakefs 40 | ENV TEST_REAL_FS=1 41 | CMD ["pytest", "pyfakefs/pytest_tests", "pyfakefs/tests"] 42 | -------------------------------------------------------------------------------- /.github/workflows/dockerfiles/Dockerfile_redhat: -------------------------------------------------------------------------------- 1 | # Copyright 2018 John McGehee. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM registry.access.redhat.com/ubi9/ubi 16 | LABEL maintainer="John McGehee" 17 | 18 | ARG github_repo=pytest-dev/pyfakefs 19 | ARG github_branch=main 20 | 21 | RUN yum update --disablerepo=* --enablerepo=ubi-9-appstream-rpms --enablerepo=ubi-9-baseos-rpms -y 22 | RUN yum install -y python3-pip unzip wget 23 | RUN python3 --version 24 | 25 | ENV LANG en_US.UTF-8 26 | ENV LANGUAGE en_US:en 27 | ENV LC_COLLATE C.UTF-8 28 | 29 | RUN useradd pyfakefs 30 | 31 | RUN mkdir -p work \ 32 | && wget https://github.com/$github_repo/archive/$github_branch.zip -O archive.zip \ 33 | && unzip archive.zip -d work 34 | RUN WORK_DIR=`ls -d work/*`; mv $WORK_DIR work/pyfakefs 35 | RUN chown -R pyfakefs:pyfakefs work/pyfakefs 36 | WORKDIR work/pyfakefs 37 | RUN pip3 install -r requirements.txt 38 | RUN pip3 install -r extra_requirements.txt 39 | RUN pip3 install -e . 40 | 41 | USER pyfakefs 42 | ENV PYTHONPATH work/pyfakefs 43 | ENV TEST_REAL_FS=1 44 | CMD ["pytest", "pyfakefs/pytest_tests", "pyfakefs/tests"] 45 | -------------------------------------------------------------------------------- /.github/workflows/dockerfiles/Dockerfile_ubuntu: -------------------------------------------------------------------------------- 1 | # Copyright 2018 John McGehee. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | FROM ubuntu 16 | MAINTAINER jmcgeheeiv@users.noreply.github.com 17 | 18 | RUN apt-get update && apt-get install -y locales 19 | RUN locale-gen en_US.UTF-8 20 | ENV LANG en_US.UTF-8 21 | ENV LANGUAGE en_US:en 22 | ENV LC_ALL en_US.UTF-8 23 | ARG github_repo=pytest-dev/pyfakefs 24 | ARG github_branch=main 25 | 26 | RUN apt-get update && apt-get install -y \ 27 | python3-pip \ 28 | unzip \ 29 | wget \ 30 | python3-venv 31 | 32 | RUN apt-get clean 33 | 34 | RUN useradd pyfakefs 35 | 36 | RUN mkdir -p work \ 37 | && wget https://github.com/$github_repo/archive/$github_branch.zip -O archive.zip \ 38 | && unzip archive.zip -d work 39 | RUN WORK_DIR=`ls -d work/*`; mv $WORK_DIR work/pyfakefs 40 | RUN chown -R pyfakefs:pyfakefs work/pyfakefs 41 | WORKDIR work/pyfakefs 42 | RUN python3 -m venv ../venv 43 | RUN ../venv/bin/pip install -r requirements.txt 44 | RUN ../venv/bin/pip install -r extra_requirements.txt 45 | RUN ../venv/bin/pip install parquet pyarrow 46 | RUN ../venv/bin/pip install -e . 47 | 48 | USER pyfakefs 49 | ENV PYTHONPATH work/pyfakefs 50 | ENV TEST_REAL_FS=1 51 | CMD ["../venv/bin/pytest", "pyfakefs/pytest_tests", "pyfakefs/tests"] 52 | -------------------------------------------------------------------------------- /.github/workflows/dockertests.yml: -------------------------------------------------------------------------------- 1 | name: Dockertests 2 | 3 | on: 4 | [push] 5 | 6 | jobs: 7 | dockertests: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | docker-image: [debian, fedora, ubuntu, redhat] 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Setup docker container 16 | run: | 17 | docker build -t pyfakefs -f $GITHUB_WORKSPACE/.github/workflows/dockerfiles/Dockerfile_${{ matrix.docker-image }} . --build-arg github_repo=$GITHUB_REPOSITORY --build-arg github_branch=$GITHUB_REF_NAME 18 | - name: Run tests 19 | run: docker run -t pyfakefs 20 | -------------------------------------------------------------------------------- /.github/workflows/release-deploy.yml: -------------------------------------------------------------------------------- 1 | name: release-deploy 2 | 3 | on: 4 | release: 5 | types: [ published ] 6 | 7 | jobs: 8 | 9 | deploy: 10 | runs-on: ubuntu-latest 11 | environment: release 12 | permissions: 13 | id-token: write 14 | strategy: 15 | fail-fast: true 16 | matrix: 17 | python-version: [ '3.10' ] 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | 27 | - name: Build package 28 | run: | 29 | python -m pip install --upgrade pip 30 | python -m pip install build 31 | python -m build 32 | 33 | - name: Publish package to PyPI 34 | uses: pypa/gh-action-pypi-publish@release/v1 35 | -------------------------------------------------------------------------------- /.github/workflows/testsuite.yml: -------------------------------------------------------------------------------- 1 | name: Testsuite 2 | 3 | on: 4 | [push, pull_request] 5 | 6 | defaults: 7 | run: 8 | shell: bash 9 | 10 | jobs: 11 | pytype: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check out repository 15 | uses: actions/checkout@v4 16 | - name: Set up Python 17 | uses: actions/setup-python@v5 18 | with: 19 | python-version: "3.10" 20 | - name: install pytype 21 | run: pip install setuptools pytype pytest scandir pathlib2 pandas xlrd django pyarrow 22 | - name: Run pytype 23 | run: | 24 | pytype pyfakefs --keep-going --exclude pyfakefs/tests/* --exclude pyfakefs/pytest_tests/* 25 | 26 | tests: 27 | runs-on: ${{ matrix.os }} 28 | env: 29 | PYTHONWARNDEFAULTENCODING: true 30 | PIP_DISABLE_PIP_VERSION_CHECK: 1 31 | strategy: 32 | fail-fast: false 33 | matrix: 34 | os: [ubuntu-latest, macOS-latest, windows-latest] 35 | python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13", "3.14"] 36 | include: 37 | - python-version: "pypy-3.7" 38 | os: ubuntu-22.04 39 | - python-version: "pypy-3.9" 40 | os: ubuntu-latest 41 | - python-version: "pypy-3.10" 42 | os: ubuntu-latest 43 | - python-version: "3.7" 44 | os: ubuntu-22.04 45 | - python-version: "3.7" 46 | os: windows-latest 47 | 48 | steps: 49 | - uses: actions/checkout@v4 50 | - name: Set up Python ${{ matrix.python-version }} 51 | uses: actions/setup-python@v5 52 | with: 53 | python-version: ${{ matrix.python-version }} 54 | allow-prereleases: true 55 | 56 | - name: Get pip cache dir 57 | id: pip-cache 58 | shell: bash 59 | run: | 60 | python -m pip install --upgrade pip 61 | echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT 62 | 63 | - name: Cache dependencies 64 | id: cache-dep 65 | uses: actions/cache@v4 66 | with: 67 | path: ${{ steps.pip-cache.outputs.dir }} 68 | key: ${{ matrix.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('**/requirements.txt') }}-${{ hashFiles('**/extra_requirements.txt') }}-${{ hashFiles('**/legacy_requirements.txt') }} 69 | restore-keys: | 70 | ${{ matrix.os }}-${{ matrix.python-version }}-pip- 71 | 72 | - name: Install dependencies 73 | run: | 74 | pip install setuptools wheel 75 | pip install -r requirements.txt 76 | - name: Run unit tests without extra packages as non-root user 77 | run: | 78 | export TEST_REAL_FS=1 79 | python -bb -m pyfakefs.tests.all_tests_without_extra_packages 80 | shell: bash 81 | - name: Run unit tests without extra packages as root 82 | run: | 83 | if [[ '${{ matrix.os }}' != 'windows-latest' ]]; then 84 | # provide the same path as non-root to get the correct virtualenv 85 | sudo env "PATH=$PATH" python -m pyfakefs.tests.all_tests_without_extra_packages 86 | fi 87 | shell: bash 88 | - name: Install extra dependencies 89 | if: ${{ matrix.python-version != '3.14' }} 90 | run: | 91 | pip install -r extra_requirements.txt 92 | pip install -r legacy_requirements.txt 93 | pip install zstandard cffi # needed to test #910 94 | shell: bash 95 | - name: Run unit tests with extra packages as non-root user 96 | if: ${{ matrix.python-version != '3.14' }} 97 | run: | 98 | export PYTHON_ZSTANDARD_IMPORT_POLICY=cffi # needed to test #910 99 | python -m pyfakefs.tests.all_tests 100 | shell: bash 101 | - name: Run performance tests 102 | run: | 103 | if [[ '${{ matrix.os }}' != 'macOS-latest' ]]; then 104 | export TEST_PERFORMANCE=1 105 | python -m pyfakefs.tests.performance_test 106 | fi 107 | shell: bash 108 | 109 | pytest-test: 110 | runs-on: ${{ matrix.os }} 111 | env: 112 | PIP_DISABLE_PIP_VERSION_CHECK: 1 113 | strategy: 114 | fail-fast: false 115 | matrix: 116 | os: [ubuntu-latest, macOS-latest, windows-latest] 117 | python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13"] 118 | pytest-version: [6.2.5, 7.0.1, 7.4.4, 8.0.2, 8.3.4] 119 | steps: 120 | - uses: actions/checkout@v4 121 | - name: Set up Python ${{ matrix.python-version }} 122 | uses: actions/setup-python@v5 123 | with: 124 | python-version: ${{ matrix.python-version }} 125 | - name: Install dependencies 126 | run: | 127 | python -m pip install --upgrade pip 128 | python -m pip install -r requirements.txt 129 | python -m pip install -U pytest==${{ matrix.pytest-version }} 130 | python -m pip install pandas parquet pyarrow 131 | python -m pip install -e . 132 | shell: bash 133 | - name: Run pytest tests 134 | run: | 135 | echo "$(python -m pytest pyfakefs/pytest_tests/pytest_plugin_failing_helper.py)" > ./testresult.txt 136 | pytest pyfakefs/pytest_tests 137 | cd pyfakefs/pytest_tests/ns_package 138 | pytest --log-cli-level=INFO test 139 | shell: bash 140 | 141 | dependency-check: 142 | runs-on: ${{ matrix.os }} 143 | strategy: 144 | matrix: 145 | os: [ubuntu-latest, windows-latest] 146 | python-version: ["3.10"] 147 | steps: 148 | - uses: actions/checkout@v4 149 | - name: Set up Python ${{ matrix.python-version }} 150 | uses: actions/setup-python@v5 151 | with: 152 | python-version: ${{ matrix.python-version }} 153 | - name: Install dependencies 154 | run: | 155 | pip install -r requirements.txt 156 | pip install -r extra_requirements.txt 157 | pip install -r legacy_requirements.txt 158 | pip install pytest-find-dependencies 159 | - name: Check dependencies 160 | run: python -m pytest --find-dependencies pyfakefs/tests 161 | shell: bash 162 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info/ 3 | .tox/ 4 | 5 | # Eclipse 6 | .settings 7 | .project 8 | .pydevproject 9 | 10 | # PyCharm 11 | .idea/ 12 | 13 | # pytest 14 | .cache/ 15 | .pytest_cache/ 16 | 17 | # autodoc created by sphinx 18 | gh-pages/ 19 | 20 | # Distribution creation 21 | dist/ 22 | build/ 23 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | default_language_version: 2 | python: "3.10" 3 | 4 | repos: 5 | - repo: "https://github.com/asottile/pyupgrade" 6 | rev: "v3.20.0" 7 | hooks: 8 | - id: "pyupgrade" 9 | name: "Enforce Python 3.7+ idioms" 10 | args: 11 | - "--py37-plus" 12 | 13 | - repo: https://github.com/astral-sh/ruff-pre-commit 14 | rev: "v0.11.12" 15 | hooks: 16 | - id: ruff 17 | args: ["--fix"] 18 | - id: ruff-format 19 | - repo: https://github.com/codespell-project/codespell 20 | rev: v2.4.1 21 | hooks: 22 | - id: codespell 23 | args: 24 | - --ignore-words-list=wronly,afile,assertIn 25 | - repo: https://github.com/asottile/blacken-docs 26 | rev: 1.19.1 27 | hooks: 28 | - id: blacken-docs 29 | additional_dependencies: [ black==24.4.2 ] 30 | - repo: https://github.com/pre-commit/pre-commit-hooks 31 | rev: v5.0.0 32 | hooks: 33 | - id: trailing-whitespace 34 | - id: end-of-file-fixer 35 | - id: fix-encoding-pragma 36 | args: [ --remove ] 37 | - id: check-yaml 38 | - id: debug-statements 39 | language_version: python3 40 | - repo: https://github.com/PyCQA/autoflake 41 | rev: v2.3.1 42 | hooks: 43 | - id: autoflake 44 | name: autoflake 45 | args: ["--in-place", "--remove-unused-variables", "--remove-all-unused-imports"] 46 | language: python 47 | files: \.py$ 48 | - repo: https://github.com/pre-commit/mirrors-mypy 49 | rev: v1.16.0 50 | hooks: 51 | - id: mypy 52 | exclude: (docs|pyfakefs/tests) 53 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # Read the Docs configuration file for Sphinx projects 2 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 3 | 4 | version: 2 5 | 6 | build: 7 | os: ubuntu-22.04 8 | tools: 9 | python: "3.11" 10 | 11 | sphinx: 12 | configuration: docs/conf.py 13 | fail_on_warning: true 14 | 15 | python: 16 | install: 17 | - requirements: docs/requirements.txt 18 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing to pyfakefs 3 | 4 | We welcome any contributions that help to improve pyfakefs for the community. 5 | Contributions may include bug reports, bug fixes, new features, infrastructure enhancements, or 6 | documentation updates. 7 | 8 | ## How to contribute 9 | 10 | ### Reporting Bugs 11 | 12 | If you think you found a bug in pyfakefs, you can [create an issue](https://help.github.com/articles/creating-an-issue/). 13 | Before filing the bug, please check, if it still exists in the [main branch](https://github.com/pytest-dev/pyfakefs). 14 | If you can reproduce the problem, please provide enough information so that it can be reproduced by other developers. 15 | This includes: 16 | * The Operating System 17 | * The Python version 18 | * A minimal example to reproduce the problem (preferably in the form of a failing test) 19 | * The stack trace in case of an unexpected exception. 20 | For better readability, you may use [markdown code formatting](https://help.github.com/articles/creating-and-highlighting-code-blocks/) for any included code. 21 | 22 | ### Proposing Enhancements 23 | 24 | If you need a specific feature that is not implemented, or have an idea for the next 25 | exciting gimmick in pyfakefs, you can also create a respective issue. 26 | Of course - implementing it yourself is the best chance to get it done! 27 | The next item has some information on doing this. 28 | 29 | ### Contributing Code 30 | 31 | The preferred workflow for contributing code is to 32 | [fork](https://help.github.com/articles/fork-a-repo/) the [repository](https://github.com/pytest-dev/pyfakefs) on GitHub, clone it, 33 | develop on a feature branch, and [create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork) when done. 34 | There are a few things to consider for contributing code: 35 | * We ensure the [PEP-8 coding style](https://www.python.org/dev/peps/pep-0008/) 36 | by using [black](https://pypi.org/project/black/) auto-format in a 37 | pre-commit hook; you can locally install 38 | [pre-commit](https://pypi.org/project/pre-commit/) to run the linter 39 | tests on check-in or on demand (`pre-commit run --all-files`) 40 | * Use the [Google documentation style](https://google.github.io/styleguide/pyguide.html) to document new public classes or methods 41 | * Provide unit tests for bug fixes or new functionality - check the existing tests for examples 42 | * Provide meaningful commit messages - it is ok to amend the commits to improve the comments 43 | * Check that the automatic GitHub Action CI tests all pass for your pull request 44 | * Be ready to adapt your changes after a code review 45 | 46 | ### Contributing Documentation 47 | 48 | If you want to improve the existing documentation, you can do this also using a pull request. 49 | You can contribute to: 50 | * the source code documentation using [Google documentation style](https://google.github.io/styleguide/pyguide.html) 51 | * the [README](https://github.com/pytest-dev/pyfakefs/blob/main/README.md) using [markdown syntax](https://help.github.com/articles/basic-writing-and-formatting-syntax/) 52 | * the documentation published on [Read the Docs](https://pytest-pyfakefs.readthedocs.io/en/latest/), 53 | located in the `docs` directory (call `make html` from that directory). 54 | For building the documentation, you will need [sphinx](http://sphinx.pocoo.org/). 55 | * [this file](https://github.com/pytest-dev/pyfakefs/blob/main/CONTRIBUTING.md) 56 | if you want to enhance the contributing guide itself 57 | 58 | Thanks for taking the time to contribute to pyfakefs! 59 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018 John McGehee. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Prerequisites: 16 | # * Install Docker 17 | # * Clone pyfakefs 18 | # 19 | # To build and run the container: 20 | # 21 | # cd pyfakefs 22 | # docker build -t pyfakefs . 23 | # docker run -t pyfakefs 24 | 25 | FROM ubuntu 26 | MAINTAINER jmcgeheeiv@users.noreply.github.com 27 | 28 | # The Ubuntu base container does not specify a locale. 29 | # pyfakefs tests require at least the Latin1 character set. 30 | RUN apt-get update && apt-get install -y locales 31 | RUN locale-gen en_US.UTF-8 32 | ENV LANG en_US.UTF-8 33 | ENV LANGUAGE en_US:en 34 | ENV LC_ALL en_US.UTF-8 35 | 36 | RUN apt-get update && apt-get install -y \ 37 | python3-pip \ 38 | unzip \ 39 | wget 40 | RUN apt-get clean 41 | 42 | RUN useradd pyfakefs 43 | 44 | RUN wget https://github.com/pytest-dev/pyfakefs/archive/main.zip \ 45 | && unzip main.zip \ 46 | && chown -R pyfakefs:pyfakefs /pyfakefs-main 47 | WORKDIR /pyfakefs-main 48 | RUN pip3 install -r requirements.txt 49 | RUN pip3 install -r extra_requirements.txt 50 | 51 | USER pyfakefs 52 | ENV PYTHONPATH /pyfakefs-main 53 | CMD ["python3", "-m", "pyfakefs.tests.all_tests"] 54 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include COPYING 2 | include *.md 3 | include *.ini 4 | include *.txt 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pyfakefs [![PyPI version](https://badge.fury.io/py/pyfakefs.svg)](https://badge.fury.io/py/pyfakefs) [![Python version](https://img.shields.io/pypi/pyversions/pyfakefs.svg)](https://img.shields.io/pypi/pyversions/pyfakefs.svg) ![Testsuite](https://github.com/pytest-dev/pyfakefs/workflows/Testsuite/badge.svg) [![Documentation Status](https://readthedocs.org/projects/pytest-pyfakefs/badge/?version=latest)](https://pytest-pyfakefs.readthedocs.io/en/latest/?badge=latest) [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/pytest-dev/pyfakefs/main.svg)](https://results.pre-commit.ci/latest/github/pytest-dev/pyfakefs/main) 2 | 3 | 4 | pyfakefs implements a fake file system that mocks the Python file system modules. 5 | Using pyfakefs, your tests operate on a fake file system in memory without 6 | touching the real disk. The software under test requires no modification to 7 | work with pyfakefs. 8 | 9 | Pyfakefs creates a new empty in-memory file system at each test start, which replaces 10 | the real filesystem during the test. Think of pyfakefs as making a per-test temporary 11 | directory, except for an entire file system. 12 | 13 | There are several means to achieve this: by using 14 | the `fs` fixture if running pytest, by using `fake_filesystem_unittest.TestCase` as a 15 | base class if using unittest, by using a `fake_filesystem_unittest.Patcher` instance 16 | as a context manager, or by using the `patchfs` decorator. 17 | 18 | 19 | 20 | pyfakefs works with current versions of Linux, Windows and macOS. 21 | 22 | ## Documentation 23 | 24 | This document provides a general overview for pyfakefs. There is more: 25 | 26 | * The documentation at **Read the Docs**: 27 | * The [Release documentation](https://pytest-pyfakefs.readthedocs.io/en/stable) 28 | contains usage documentation for pyfakefs and a description of the 29 | most relevant classes, methods and functions for the last version 30 | released on PyPI 31 | * The [Development documentation](https://pytest-pyfakefs.readthedocs.io/en/latest) 32 | contains the same documentation for the current main branch 33 | * The [Release 3.7 documentation](https://pytest-pyfakefs.readthedocs.io/en/v3.7.2/) 34 | contains usage documentation for the last version of pyfakefs 35 | supporting Python 2.7 36 | * The [Release Notes](https://github.com/pytest-dev/pyfakefs/blob/main/CHANGES.md) 37 | show a list of changes in the latest versions 38 | 39 | ## Usage 40 | The simplest method to use pyfakefs is using the `fs` fixture with `pytest`. 41 | Refer to the 42 | [usage documentation](https://pytest-pyfakefs.readthedocs.io/en/latest/usage.html) 43 | for information on other test scenarios, test customization and 44 | using convenience functions. 45 | 46 | ## Features 47 | Apart from automatically mocking most file-system functions, pyfakefs 48 | provides some additional features: 49 | - mapping files and directories from the real file system into the fake filesystem 50 | - configuration and tracking of the file system size 51 | - pause and resume of patching to be able to use the real file system inside a 52 | test step 53 | - (limited) emulation of other OSes (Linux, macOS or Windows) 54 | - configuration to behave as if running as a non-root user while running 55 | under root 56 | 57 | ## Compatibility 58 | pyfakefs works with CPython 3.7 and above, on Linux, Windows and macOS, and 59 | with PyPy3. 60 | 61 | pyfakefs works with [pytest](http://doc.pytest.org) version 6.2.5 or above, 62 | though a current version is recommended. 63 | 64 | pyfakefs will not work with Python libraries that use C libraries to access the 65 | file system. This is because pyfakefs cannot patch the underlying C libraries' 66 | file access functions--the C libraries will always access the real file 67 | system. Refer to the 68 | [documentation](https://pytest-pyfakefs.readthedocs.io/en/latest/intro.html#limitations) 69 | for more information about the limitations of pyfakefs. 70 | 71 | ## Development 72 | 73 | ### Continuous integration 74 | 75 | pyfakefs is currently automatically tested on Linux, macOS and Windows, with 76 | Python 3.7 to 3.13, and with PyPy3 on Linux, using 77 | [GitHub Actions](https://github.com/pytest-dev/pyfakefs/actions). 78 | 79 | ### Running pyfakefs unit tests 80 | 81 | #### On the command line 82 | pyfakefs unit tests can be run using `pytest` (all tests) or `unittest` 83 | (all tests except `pytest`-specific ones): 84 | 85 | ```bash 86 | $ cd pyfakefs/ 87 | $ export PYTHONPATH=$PWD 88 | 89 | $ python -m pytest pyfakefs 90 | $ python -m pyfakefs.tests.all_tests 91 | ``` 92 | 93 | Similar scripts are called by `tox` and Github Actions. `tox` can be used to 94 | run tests locally against supported python versions: 95 | 96 | ```bash 97 | $ tox 98 | ``` 99 | 100 | #### In a Docker container 101 | 102 | The `Dockerfile` at the repository root will run the tests on the latest 103 | Ubuntu version. Build the container: 104 | ```bash 105 | cd pyfakefs/ 106 | docker build -t pyfakefs . 107 | ``` 108 | Run the unit tests in the container: 109 | ```bash 110 | docker run -t pyfakefs 111 | ``` 112 | 113 | ### Contributing to pyfakefs 114 | 115 | We always welcome contributions to the library. Check out the 116 | [Contributing Guide](https://github.com/pytest-dev/pyfakefs/blob/main/CONTRIBUTING.md) 117 | for more information. 118 | 119 | ## History 120 | pyfakefs.py was initially developed at Google by Mike Bland as a modest fake 121 | implementation of core Python modules. It was introduced to all of Google 122 | in September 2006. Since then, it has been enhanced to extend its 123 | functionality and usefulness. At last count, pyfakefs was used in over 20,000 124 | Python tests at Google. 125 | 126 | Google released pyfakefs to the public in 2011 as Google Code project 127 | [pyfakefs](http://code.google.com/p/pyfakefs/): 128 | * Fork 129 | [jmcgeheeiv-pyfakefs](http://code.google.com/p/jmcgeheeiv-pyfakefs/) added 130 | [direct support for unittest and doctest](../../wiki/Automatically-find-and-patch-file-functions-and-modules) 131 | * Fork 132 | [shiffdane-jmcgeheeiv-pyfakefs](http://code.google.com/p/shiffdane-jmcgeheeiv-pyfakefs/) 133 | added further corrections 134 | 135 | After the [shutdown of Google Code](http://google-opensource.blogspot.com/2015/03/farewell-to-google-code.html) 136 | was announced, [John McGehee](https://github.com/jmcgeheeiv) merged all three Google Code projects together 137 | [here on GitHub](https://github.com/pytest-dev/pyfakefs) where an enthusiastic community actively supports, maintains 138 | and extends pyfakefs. In 2022, the repository has been transferred to 139 | [pytest-dev](https://github.com/pytest-dev) to ensure continuous maintenance. 140 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = ../gh-pages 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help 18 | help: 19 | @echo "Please use \`make ' where is one of" 20 | @echo " html to make standalone HTML files" 21 | @echo " dirhtml to make HTML files named index.html in directories" 22 | @echo " singlehtml to make a single large HTML file" 23 | @echo " pickle to make pickle files" 24 | @echo " json to make JSON files" 25 | @echo " htmlhelp to make HTML files and a HTML help project" 26 | @echo " qthelp to make HTML files and a qthelp project" 27 | @echo " applehelp to make an Apple Help Book" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " epub3 to make an epub3" 31 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 32 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 33 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 34 | @echo " text to make text files" 35 | @echo " man to make manual pages" 36 | @echo " texinfo to make Texinfo files" 37 | @echo " info to make Texinfo files and run them through makeinfo" 38 | @echo " gettext to make PO message catalogs" 39 | @echo " changes to make an overview of all changed/added/deprecated items" 40 | @echo " xml to make Docutils-native XML files" 41 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 42 | @echo " linkcheck to check all external links for integrity" 43 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 44 | @echo " coverage to run coverage check of the documentation (if enabled)" 45 | @echo " dummy to check syntax errors of document sources" 46 | 47 | .PHONY: clean 48 | clean: 49 | rm -rf $(BUILDDIR)/* 50 | 51 | .PHONY: html 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR) 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)." 56 | 57 | .PHONY: dirhtml 58 | dirhtml: 59 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 60 | @echo 61 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 62 | 63 | .PHONY: singlehtml 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | .PHONY: pickle 70 | pickle: 71 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 72 | @echo 73 | @echo "Build finished; now you can process the pickle files." 74 | 75 | .PHONY: json 76 | json: 77 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 78 | @echo 79 | @echo "Build finished; now you can process the JSON files." 80 | 81 | .PHONY: htmlhelp 82 | htmlhelp: 83 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 84 | @echo 85 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 86 | ".hhp project file in $(BUILDDIR)/htmlhelp." 87 | 88 | .PHONY: qthelp 89 | qthelp: 90 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 91 | @echo 92 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 93 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 94 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyfakefs.qhcp" 95 | @echo "To view the help file:" 96 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyfakefs.qhc" 97 | 98 | .PHONY: applehelp 99 | applehelp: 100 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 101 | @echo 102 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 103 | @echo "N.B. You won't be able to view it unless you put it in" \ 104 | "~/Library/Documentation/Help or install it in your application" \ 105 | "bundle." 106 | 107 | .PHONY: devhelp 108 | devhelp: 109 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 110 | @echo 111 | @echo "Build finished." 112 | @echo "To view the help file:" 113 | @echo "# mkdir -p $$HOME/.local/share/devhelp/pyfakefs" 114 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyfakefs" 115 | @echo "# devhelp" 116 | 117 | .PHONY: epub 118 | epub: 119 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 120 | @echo 121 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 122 | 123 | .PHONY: epub3 124 | epub3: 125 | $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 126 | @echo 127 | @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." 128 | 129 | .PHONY: latex 130 | latex: 131 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 132 | @echo 133 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 134 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 135 | "(use \`make latexpdf' here to do that automatically)." 136 | 137 | .PHONY: latexpdf 138 | latexpdf: 139 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 140 | @echo "Running LaTeX files through pdflatex..." 141 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 142 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 143 | 144 | .PHONY: latexpdfja 145 | latexpdfja: 146 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 147 | @echo "Running LaTeX files through platex and dvipdfmx..." 148 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 149 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 150 | 151 | .PHONY: text 152 | text: 153 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 154 | @echo 155 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 156 | 157 | .PHONY: man 158 | man: 159 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 160 | @echo 161 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 162 | 163 | .PHONY: texinfo 164 | texinfo: 165 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 166 | @echo 167 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 168 | @echo "Run \`make' in that directory to run these through makeinfo" \ 169 | "(use \`make info' here to do that automatically)." 170 | 171 | .PHONY: info 172 | info: 173 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 174 | @echo "Running Texinfo files through makeinfo..." 175 | make -C $(BUILDDIR)/texinfo info 176 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 177 | 178 | .PHONY: gettext 179 | gettext: 180 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 181 | @echo 182 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 183 | 184 | .PHONY: changes 185 | changes: 186 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 187 | @echo 188 | @echo "The overview file is in $(BUILDDIR)/changes." 189 | 190 | .PHONY: linkcheck 191 | linkcheck: 192 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 193 | @echo 194 | @echo "Link check complete; look for any errors in the above output " \ 195 | "or in $(BUILDDIR)/linkcheck/output.txt." 196 | 197 | .PHONY: doctest 198 | doctest: 199 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 200 | @echo "Testing of doctests in the sources finished, look at the " \ 201 | "results in $(BUILDDIR)/doctest/output.txt." 202 | 203 | .PHONY: coverage 204 | coverage: 205 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 206 | @echo "Testing of coverage in the sources finished, look at the " \ 207 | "results in $(BUILDDIR)/coverage/python.txt." 208 | 209 | .PHONY: xml 210 | xml: 211 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 212 | @echo 213 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 214 | 215 | .PHONY: pseudoxml 216 | pseudoxml: 217 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 218 | @echo 219 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 220 | 221 | .PHONY: dummy 222 | dummy: 223 | $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy 224 | @echo 225 | @echo "Build finished. Dummy builder generates no files." 226 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # pyfakefs documentation build configuration file, created by 4 | # sphinx-quickstart on Mon Oct 31 20:05:53 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # 19 | import os 20 | import sys 21 | 22 | sys.path.insert(0, os.path.split(os.path.dirname(os.path.abspath(__file__)))[0]) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | "sphinx.ext.autodoc", 35 | "sphinx.ext.githubpages", # puts .nojekyll file into source 36 | "sphinx.ext.napoleon", # enables google style docstrings 37 | ] 38 | 39 | # Add any paths that contain templates here, relative to this directory. 40 | templates_path = ["_templates"] 41 | 42 | # The suffix(es) of source filenames. 43 | # You can specify multiple suffix as a list of string: 44 | # 45 | # source_suffix = ['.rst', '.md'] 46 | source_suffix = ".rst" 47 | 48 | # The encoding of source files. 49 | # 50 | # source_encoding = 'utf-8-sig' 51 | 52 | # The master toctree document. 53 | master_doc = "index" 54 | 55 | # General information about the project. 56 | project = "pyfakefs" 57 | copyright = """2009 Google Inc. All Rights Reserved. 58 | © Copyright 2014 Altera Corporation. All Rights Reserved. 59 | © Copyright 2014-2025 John McGehee and pyfakefs contributors.""" 60 | author = "John McGehee" 61 | 62 | # The version info for the project you're documenting, acts as replacement for 63 | # |version| and |release|, also used in various other places throughout the 64 | # built documents. 65 | # 66 | # The short X.Y version. 67 | version = "5.9" 68 | # The full version, including alpha/beta/rc tags. 69 | release = "5.9.dev0" 70 | 71 | # The language for content autogenerated by Sphinx. Refer to documentation 72 | # for a list of supported languages. 73 | # 74 | # This is also used if you do content translation via gettext catalogs. 75 | # Usually you set "language" from the command line for these cases. 76 | language = "en" 77 | 78 | # There are two options for replacing |today|: either, you set today to some 79 | # non-false value, then it is used: 80 | # 81 | # today = '' 82 | # 83 | # Else, today_fmt is used as the format for a strftime call. 84 | # 85 | # today_fmt = '%B %d, %Y' 86 | 87 | # List of patterns, relative to source directory, that match files and 88 | # directories to ignore when looking for source files. 89 | # This patterns also effect to html_static_path and html_extra_path 90 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 91 | # The reST default role (used for this markup: `text`) to use for all 92 | # documents. 93 | # 94 | # default_role = None 95 | 96 | # If true, '()' will be appended to :func: etc. cross-reference text. 97 | # 98 | # add_function_parentheses = True 99 | 100 | # If true, the current module name will be prepended to all description 101 | # unit titles (such as .. function::). 102 | # 103 | # add_module_names = True 104 | 105 | # If true, sectionauthor and moduleauthor directives will be shown in the 106 | # output. They are ignored by default. 107 | # 108 | # show_authors = False 109 | 110 | autoclass_content = "both" 111 | 112 | autodoc_member_order = "bysource" 113 | 114 | # The name of the Pygments (syntax highlighting) style to use. 115 | pygments_style = "sphinx" 116 | 117 | # A list of ignored prefixes for module index sorting. 118 | # modindex_common_prefix = [] 119 | 120 | # If true, keep warnings as "system message" paragraphs in the built documents. 121 | # keep_warnings = False 122 | 123 | # If true, `todo` and `todoList` produce output, else they produce nothing. 124 | todo_include_todos = False 125 | 126 | # -- Options for HTML output ---------------------------------------------- 127 | 128 | # The theme to use for HTML and HTML Help pages. See the documentation for 129 | # a list of builtin themes. 130 | # 131 | html_theme = "furo" 132 | 133 | # Theme options are theme-specific and customize the look and feel of a theme 134 | # further. For a list of options available for each theme, see the 135 | # documentation. 136 | # 137 | # html_theme_options = {} 138 | 139 | # Add any paths that contain custom themes here, relative to this directory. 140 | html_theme_path = ["."] 141 | 142 | # The name for this set of Sphinx documents. 143 | # " v documentation" by default. 144 | # 145 | # html_title = 'pyfakefs v3.4' 146 | 147 | # A shorter title for the navigation bar. Default is the same as html_title. 148 | # 149 | # html_short_title = None 150 | 151 | # The name of an image file (relative to this directory) to place at the top 152 | # of the sidebar. 153 | # 154 | # html_logo = None 155 | 156 | # The name of an image file (relative to this directory) to use as a favicon of 157 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 158 | # pixels large. 159 | # 160 | # html_favicon = None 161 | 162 | # Add any paths that contain custom static files (such as style sheets) here, 163 | # relative to this directory. They are copied after the builtin static files, 164 | # so a file named "default.css" will overwrite the builtin "default.css". 165 | html_static_path = [] 166 | 167 | # Add any extra paths that contain custom files (such as robots.txt or 168 | # .htaccess) here, relative to this directory. These files are copied 169 | # directly to the root of the documentation. 170 | # 171 | # html_extra_path = [] 172 | 173 | # If not None, a 'Last updated on:' timestamp is inserted at every page 174 | # bottom, using the given strftime format. 175 | # The empty string is equivalent to '%b %d, %Y'. 176 | # 177 | html_last_updated_fmt = "%b %d, %Y" 178 | 179 | # If true, SmartyPants will be used to convert quotes and dashes to 180 | # typographically correct entities. 181 | # 182 | # html_use_smartypants = True 183 | 184 | # Custom sidebar templates, maps document names to template names. 185 | # 186 | # html_sidebars = {} 187 | 188 | # Additional templates that should be rendered to pages, maps page names to 189 | # template names. 190 | # 191 | # html_additional_pages = {} 192 | 193 | # If false, no module index is generated. 194 | # 195 | # html_domain_indices = True 196 | 197 | # If false, no index is generated. 198 | # 199 | # html_use_index = True 200 | 201 | # If true, the index is split into individual pages for each letter. 202 | # 203 | # html_split_index = False 204 | 205 | # If true, links to the reST sources are added to the pages. 206 | # 207 | html_show_sourcelink = False 208 | 209 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 210 | # 211 | html_show_sphinx = False 212 | 213 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 214 | # 215 | # html_show_copyright = True 216 | 217 | # If true, an OpenSearch description file will be output, and all pages will 218 | # contain a tag referring to it. The value of this option must be the 219 | # base URL from which the finished HTML is served. 220 | # 221 | # html_use_opensearch = '' 222 | 223 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 224 | # html_file_suffix = None 225 | 226 | # Language to be used for generating the HTML full-text search index. 227 | # Sphinx supports the following languages: 228 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' 229 | # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' 230 | # 231 | # html_search_language = 'en' 232 | 233 | # A dictionary with options for the search language support, empty by default. 234 | # 'ja' uses this config value. 235 | # 'zh' user can custom change `jieba` dictionary path. 236 | # 237 | # html_search_options = {'type': 'default'} 238 | 239 | # The name of a javascript file (relative to the configuration directory) that 240 | # implements a search results scorer. If empty, the default will be used. 241 | # 242 | # html_search_scorer = 'scorer.js' 243 | 244 | # Output file base name for HTML help builder. 245 | htmlhelp_basename = "pyfakefsdoc" 246 | # -- Options for LaTeX output --------------------------------------------- 247 | 248 | latex_elements = { 249 | # The paper size ('letterpaper' or 'a4paper'). 250 | # 251 | # 'papersize': 'letterpaper', 252 | # 253 | # The font size ('10pt', '11pt' or '12pt'). 254 | # 255 | # 'pointsize': '10pt', 256 | # 257 | # Additional stuff for the LaTeX preamble. 258 | # 259 | # 'preamble': '', 260 | # 261 | # Latex figure (float) alignment 262 | # 263 | # 'figure_align': 'htbp', 264 | } 265 | 266 | # Grouping the document tree into LaTeX files. List of tuples 267 | # (source start file, target name, title, 268 | # author, documentclass [howto, manual, or own class]). 269 | latex_documents = [ 270 | ( 271 | master_doc, 272 | "pyfakefs.tex", 273 | "pyfakefs Documentation", 274 | "John McGehee", 275 | "manual", 276 | ), 277 | ] 278 | 279 | # The name of an image file (relative to this directory) to place at the top of 280 | # the title page. 281 | # 282 | # latex_logo = None 283 | 284 | # For "manual" documents, if this is true, then toplevel headings are parts, 285 | # not chapters. 286 | # 287 | # latex_use_parts = False 288 | 289 | # If true, show page references after internal links. 290 | # 291 | # latex_show_pagerefs = False 292 | 293 | # If true, show URL addresses after external links. 294 | # 295 | # latex_show_urls = False 296 | 297 | # Documents to append as an appendix to all manuals. 298 | # 299 | # latex_appendices = [] 300 | 301 | # It false, will not define \strong, \code, itleref, \crossref ... but only 302 | # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added 303 | # packages. 304 | # 305 | # latex_keep_old_macro_names = True 306 | 307 | # If false, no module index is generated. 308 | # 309 | # latex_domain_indices = True 310 | 311 | 312 | # -- Options for manual page output --------------------------------------- 313 | 314 | # One entry per manual page. List of tuples 315 | # (source start file, name, description, authors, manual section). 316 | man_pages = [(master_doc, "pyfakefs", "pyfakefs Documentation", [author], 1)] 317 | 318 | # If true, show URL addresses after external links. 319 | # 320 | # man_show_urls = False 321 | 322 | 323 | # -- Options for Texinfo output ------------------------------------------- 324 | 325 | # Grouping the document tree into Texinfo files. List of tuples 326 | # (source start file, target name, title, author, 327 | # dir menu entry, description, category) 328 | texinfo_documents = [ 329 | ( 330 | master_doc, 331 | "pyfakefs", 332 | "pyfakefs Documentation", 333 | author, 334 | "pyfakefs", 335 | "One line description of project.", 336 | "Miscellaneous", 337 | ), 338 | ] 339 | 340 | # Documents to append as an appendix to all manuals. 341 | # 342 | # texinfo_appendices = [] 343 | 344 | # If false, no module index is generated. 345 | # 346 | # texinfo_domain_indices = True 347 | 348 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 349 | # 350 | # texinfo_show_urls = 'footnote' 351 | 352 | # If true, do not generate a @detailmenu in the "Top" node's menu. 353 | # 354 | # texinfo_no_detailmenu = False 355 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. pyfakefs documentation master file, created by 2 | sphinx-quickstart on Mon Oct 31 20:05:53 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to the pyfakefs documentation! 7 | ====================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | intro 15 | usage 16 | convenience 17 | customize 18 | troubleshooting 19 | modules 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`search` 26 | -------------------------------------------------------------------------------- /docs/intro.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | `pyfakefs `__ implements a fake file 5 | system that mocks the Python file system modules. 6 | Using pyfakefs, your tests operate on a fake file system in memory without touching the real disk. 7 | The software under test requires no modification to work with pyfakefs. 8 | 9 | ``pyfakefs`` works with CPython 3.7 and above, on Linux, Windows and macOS, 10 | and with PyPy3. 11 | 12 | ``pyfakefs`` works with `pytest `__ version 6.2.5 or above by 13 | providing the `fs` fixture that enables the fake filesystem. 14 | 15 | Installation 16 | ------------ 17 | ``pyfakefs`` is available on `PyPI `__. 18 | The latest released version can be installed from PyPI: 19 | 20 | .. code:: bash 21 | 22 | pip install pyfakefs 23 | 24 | The latest development version (main branch) can be installed from the GitHub sources: 25 | 26 | .. code:: bash 27 | 28 | pip install git+https://github.com/pytest-dev/pyfakefs 29 | 30 | Features 31 | -------- 32 | - Code executed under ``pyfakefs`` works transparently on a memory-based file 33 | system without the need of special commands. The same code that works on 34 | the real filesystem will work on the fake filesystem if running under 35 | ``pyfakefs``. 36 | 37 | - ``pyfakefs`` provides direct support for `pytest` (see :ref:`pytest_plugin`) 38 | and `unittest` (see :ref:`unittest_usage`), but can also be used with 39 | other test frameworks. 40 | 41 | - Each ``pyfakefs`` test starts with an empty (except for the :ref:`os_temporary_directories`) file system, 42 | but it is possible to map files and directories from the real file system into the fake 43 | filesystem if needed (see :ref:`real_fs_access`). 44 | 45 | - No files in the real file system are changed during the tests, even in the 46 | case of writing to mapped real files. 47 | 48 | - ``pyfakefs`` keeps track of the filesystem size if configured. The file system 49 | size can be configured arbitrarily (see :ref:`set-fs-size`). It is also possible to create files 50 | with a defined size without setting contents. 51 | 52 | - It is possible to pause and resume using the fake filesystem, if the 53 | real file system has to be used in a test step (see :ref:`pause_resume`). 54 | 55 | - ``pyfakefs`` defaults to the OS it is running on, but can also be configured 56 | to test code running under another OS (Linux, macOS or Windows, see :ref:`simulate_os`). 57 | 58 | - ``pyfakefs`` can be configured to behave as if running as a root or as a 59 | non-root user, independently from the actual user (see :ref:`allow_root_user`). 60 | 61 | .. _limitations: 62 | 63 | Limitations 64 | ----------- 65 | - ``pyfakefs`` will not work with Python libraries (other than `os` and `io`) that 66 | use C libraries to access the file system, because it cannot patch the 67 | underlying C libraries' file access functions 68 | 69 | - ``pyfakefs`` patches most kinds of importing file system modules automatically, 70 | but there are still some cases where this will not work. 71 | See :ref:`customizing_patcher` for more information and ways to work around 72 | this. 73 | 74 | - ``pyfakefs`` does not retain the MRO for file objects, so you cannot rely on 75 | checks using `isinstance` for these objects (for example, to differentiate 76 | between binary and textual file objects). 77 | 78 | - ``pyfakefs`` is only tested with CPython and the newest PyPy versions, other 79 | Python implementations will probably not work 80 | 81 | - Differences in the behavior in different Linux distributions or different 82 | macOS or Windows versions may not be reflected in the implementation, as 83 | well as some OS-specific low-level file system behavior. The systems used 84 | for automatic tests in GitHub Actions are 85 | considered as reference systems. Additionally, the tests are run in Docker 86 | containers with the latest CentOS, Debian, Fedora and Ubuntu images. 87 | 88 | - ``pyfakefs`` may not work correctly if file system functions are patched by 89 | other means (e.g. using `unittest.mock.patch`) - see 90 | :ref:`usage_with_mock_open` for more information 91 | 92 | - ``pyfakefs`` will not work correctly with 93 | `behave `__ due to the way it loads 94 | the steps, if any filesystem modules are imported globally in the steps or 95 | environment files; as a workaround, you may load them locally inside the 96 | test steps (see `this issue `__) 97 | 98 | - ``pyfakefs`` is not guaranteed to work correctly in multi-threading environments. 99 | Specifically, it does not ensure concurrent write access to a file from different 100 | threads, which is possible under Posix. 101 | 102 | History 103 | ------- 104 | ``pyfakefs`` was initially developed at Google by 105 | `Mike Bland `__ as a modest 106 | fake implementation of core Python modules. It was introduced to all of 107 | Google in September 2006. Since then, it has been enhanced to extend its 108 | functionality and usefulness. At last count, ``pyfakefs`` was used in over 109 | 20,000 Python tests at Google. 110 | 111 | Google released ``pyfakefs`` to the public in 2011 as Google Code project 112 | `pyfakefs `__: 113 | 114 | * Fork `jmcgeheeiv-pyfakefs `__ 115 | added direct support for unittest and doctest as described in 116 | :ref:`auto_patch` 117 | * Fork `shiffdane-jmcgeheeiv-pyfakefs `__ 118 | added further corrections 119 | 120 | After the `shutdown of Google 121 | Code `__ 122 | was announced, `John McGehee `__ merged 123 | all three Google Code projects together `on 124 | GitHub `__ where an enthusiastic 125 | community actively maintains and extends pyfakefs. In 2022, the repository has 126 | been transferred to `pytest-dev `__ to ensure 127 | continuous maintenance. 128 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=../gh-pages 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. epub3 to make an epub3 31 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 32 | echo. text to make text files 33 | echo. man to make manual pages 34 | echo. texinfo to make Texinfo files 35 | echo. gettext to make PO message catalogs 36 | echo. changes to make an overview over all changed/added/deprecated items 37 | echo. xml to make Docutils-native XML files 38 | echo. pseudoxml to make pseudoxml-XML files for display purposes 39 | echo. linkcheck to check all external links for integrity 40 | echo. doctest to run all doctests embedded in the documentation if enabled 41 | echo. coverage to run coverage check of the documentation if enabled 42 | echo. dummy to check syntax errors of document sources 43 | goto end 44 | ) 45 | 46 | if "%1" == "clean" ( 47 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 48 | del /q /s %BUILDDIR%\* 49 | goto end 50 | ) 51 | 52 | 53 | REM Check if sphinx-build is available and fallback to Python version if any 54 | %SPHINXBUILD% 1>NUL 2>NUL 55 | if errorlevel 9009 goto sphinx_python 56 | goto sphinx_ok 57 | 58 | :sphinx_python 59 | 60 | set SPHINXBUILD=python -m sphinx.__init__ 61 | %SPHINXBUILD% 2> nul 62 | if errorlevel 9009 ( 63 | echo. 64 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 65 | echo.installed, then set the SPHINXBUILD environment variable to point 66 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 67 | echo.may add the Sphinx directory to PATH. 68 | echo. 69 | echo.If you don't have Sphinx installed, grab it from 70 | echo.http://sphinx-doc.org/ 71 | exit /b 1 72 | ) 73 | 74 | :sphinx_ok 75 | 76 | 77 | if "%1" == "html" ( 78 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR% 79 | if errorlevel 1 exit /b 1 80 | echo. 81 | echo.Build finished. The HTML pages are in %BUILDDIR%. 82 | goto end 83 | ) 84 | 85 | if "%1" == "dirhtml" ( 86 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 87 | if errorlevel 1 exit /b 1 88 | echo. 89 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 90 | goto end 91 | ) 92 | 93 | if "%1" == "singlehtml" ( 94 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 95 | if errorlevel 1 exit /b 1 96 | echo. 97 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 98 | goto end 99 | ) 100 | 101 | if "%1" == "pickle" ( 102 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 103 | if errorlevel 1 exit /b 1 104 | echo. 105 | echo.Build finished; now you can process the pickle files. 106 | goto end 107 | ) 108 | 109 | if "%1" == "json" ( 110 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 111 | if errorlevel 1 exit /b 1 112 | echo. 113 | echo.Build finished; now you can process the JSON files. 114 | goto end 115 | ) 116 | 117 | if "%1" == "htmlhelp" ( 118 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 119 | if errorlevel 1 exit /b 1 120 | echo. 121 | echo.Build finished; now you can run HTML Help Workshop with the ^ 122 | .hhp project file in %BUILDDIR%/htmlhelp. 123 | goto end 124 | ) 125 | 126 | if "%1" == "qthelp" ( 127 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 128 | if errorlevel 1 exit /b 1 129 | echo. 130 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 131 | .qhcp project file in %BUILDDIR%/qthelp, like this: 132 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pyfakefs.qhcp 133 | echo.To view the help file: 134 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pyfakefs.ghc 135 | goto end 136 | ) 137 | 138 | if "%1" == "devhelp" ( 139 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 140 | if errorlevel 1 exit /b 1 141 | echo. 142 | echo.Build finished. 143 | goto end 144 | ) 145 | 146 | if "%1" == "epub" ( 147 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 148 | if errorlevel 1 exit /b 1 149 | echo. 150 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 151 | goto end 152 | ) 153 | 154 | if "%1" == "epub3" ( 155 | %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 156 | if errorlevel 1 exit /b 1 157 | echo. 158 | echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. 159 | goto end 160 | ) 161 | 162 | if "%1" == "latex" ( 163 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 164 | if errorlevel 1 exit /b 1 165 | echo. 166 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdf" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "latexpdfja" ( 181 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 182 | cd %BUILDDIR%/latex 183 | make all-pdf-ja 184 | cd %~dp0 185 | echo. 186 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 187 | goto end 188 | ) 189 | 190 | if "%1" == "text" ( 191 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 192 | if errorlevel 1 exit /b 1 193 | echo. 194 | echo.Build finished. The text files are in %BUILDDIR%/text. 195 | goto end 196 | ) 197 | 198 | if "%1" == "man" ( 199 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 200 | if errorlevel 1 exit /b 1 201 | echo. 202 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 203 | goto end 204 | ) 205 | 206 | if "%1" == "texinfo" ( 207 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 208 | if errorlevel 1 exit /b 1 209 | echo. 210 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 211 | goto end 212 | ) 213 | 214 | if "%1" == "gettext" ( 215 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 216 | if errorlevel 1 exit /b 1 217 | echo. 218 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 219 | goto end 220 | ) 221 | 222 | if "%1" == "changes" ( 223 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 224 | if errorlevel 1 exit /b 1 225 | echo. 226 | echo.The overview file is in %BUILDDIR%/changes. 227 | goto end 228 | ) 229 | 230 | if "%1" == "linkcheck" ( 231 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 232 | if errorlevel 1 exit /b 1 233 | echo. 234 | echo.Link check complete; look for any errors in the above output ^ 235 | or in %BUILDDIR%/linkcheck/output.txt. 236 | goto end 237 | ) 238 | 239 | if "%1" == "doctest" ( 240 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 241 | if errorlevel 1 exit /b 1 242 | echo. 243 | echo.Testing of doctests in the sources finished, look at the ^ 244 | results in %BUILDDIR%/doctest/output.txt. 245 | goto end 246 | ) 247 | 248 | if "%1" == "coverage" ( 249 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 250 | if errorlevel 1 exit /b 1 251 | echo. 252 | echo.Testing of coverage in the sources finished, look at the ^ 253 | results in %BUILDDIR%/coverage/python.txt. 254 | goto end 255 | ) 256 | 257 | if "%1" == "xml" ( 258 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 259 | if errorlevel 1 exit /b 1 260 | echo. 261 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 262 | goto end 263 | ) 264 | 265 | if "%1" == "pseudoxml" ( 266 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 267 | if errorlevel 1 exit /b 1 268 | echo. 269 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 270 | goto end 271 | ) 272 | 273 | if "%1" == "dummy" ( 274 | %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy 275 | if errorlevel 1 exit /b 1 276 | echo. 277 | echo.Build finished. Dummy builder generates no files. 278 | goto end 279 | ) 280 | 281 | :end 282 | -------------------------------------------------------------------------------- /docs/modules.rst: -------------------------------------------------------------------------------- 1 | Public Modules and Classes 2 | ========================== 3 | .. note:: Only public classes and methods interesting to ``pyfakefs`` 4 | users are shown. Methods that mimic the behavior of standard Python 5 | functions and classes that are only needed internally are not listed. 6 | 7 | Fake filesystem module 8 | ---------------------- 9 | .. automodule:: pyfakefs.helpers 10 | :members: get_uid, set_uid, get_gid, set_gid, reset_ids, is_root 11 | 12 | Fake filesystem classes 13 | ----------------------- 14 | .. autoclass:: pyfakefs.fake_filesystem.FakeFilesystem 15 | :members: add_mount_point, 16 | get_disk_usage, set_disk_usage, change_disk_usage, 17 | add_real_directory, add_real_file, add_real_symlink, add_real_paths, add_package_metadata, 18 | create_dir, create_file, create_symlink, create_link, 19 | get_object, pause, resume 20 | 21 | .. autoclass:: pyfakefs.fake_file.FakeFile 22 | :members: byte_contents, contents, set_contents, 23 | path, size, is_large_file 24 | 25 | .. autoclass:: pyfakefs.fake_file.FakeDirectory 26 | :members: contents, ordered_dirs, size, get_entry, remove_entry 27 | 28 | Unittest module classes 29 | ----------------------- 30 | 31 | .. autoclass:: pyfakefs.fake_filesystem_unittest.TestCaseMixin 32 | :members: fs, setUpPyfakefs, setUpClassPyfakefs, pause, resume 33 | 34 | .. autoclass:: pyfakefs.fake_filesystem_unittest.TestCase 35 | 36 | .. autoclass:: pyfakefs.fake_filesystem_unittest.Patcher 37 | :members: setUp, tearDown, pause, resume, register_cleanup_handler 38 | 39 | .. automodule:: pyfakefs.fake_filesystem_unittest 40 | :members: patchfs 41 | 42 | 43 | Faked module classes 44 | -------------------- 45 | 46 | .. autoclass:: pyfakefs.fake_os.FakeOsModule 47 | 48 | .. autoclass:: pyfakefs.fake_path.FakePathModule 49 | 50 | .. autoclass:: pyfakefs.fake_open.FakeFileOpen 51 | 52 | .. autoclass:: pyfakefs.fake_io.FakeIoModule 53 | 54 | .. autoclass:: pyfakefs.fake_filesystem_shutil.FakeShutilModule 55 | 56 | .. autoclass:: pyfakefs.fake_pathlib.FakePathlibModule 57 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | furo 2 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | Usage 2 | ===== 3 | 4 | Test Scenarios 5 | -------------- 6 | There are several approaches for implementing tests using ``pyfakefs``. 7 | 8 | .. _pytest_plugin: 9 | 10 | Patch using the pytest plugin 11 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 12 | ``pyfakefs`` functions as a `pytest`_ plugin that provides the `fs` fixture, 13 | which is registered at installation time. 14 | Using this fixture automatically patches all file system functions with 15 | the fake file system functions. It also allows to access several 16 | convenience methods (see :ref:`convenience_methods`). 17 | 18 | Here is an example for a simple test: 19 | 20 | .. code:: python 21 | 22 | import os 23 | 24 | 25 | def test_fakefs(fs): 26 | # "fs" is the reference to the fake file system 27 | fs.create_file("/var/data/xx1.txt") 28 | assert os.path.exists("/var/data/xx1.txt") 29 | 30 | If you are bothered by the ``pylint`` warning, 31 | ``C0103: Argument name "fs" doesn't conform to snake_case naming style (invalid-name)``, 32 | you can define a longer name in your ``conftest.py`` and use that in your tests: 33 | 34 | .. code:: python 35 | 36 | import pytest 37 | 38 | 39 | @pytest.fixture 40 | def fake_filesystem(fs): # pylint:disable=invalid-name 41 | """Variable name 'fs' causes a pylint warning. Provide a longer name 42 | acceptable to pylint for use in tests. 43 | """ 44 | yield fs 45 | 46 | Class-, module- and session-scoped fixtures 47 | ........................................... 48 | For convenience, class-, module- and session-scoped fixtures with the same 49 | functionality are provided, named ``fs_class``, ``fs_module`` and ``fs_session``, 50 | respectively. 51 | 52 | .. caution:: If any of these fixtures is active, any other ``fs`` fixture will 53 | not setup / tear down the fake filesystem in the current scope; instead, it 54 | will just serve as a reference to the active fake filesystem. That means that changes 55 | done in the fake filesystem inside a test will remain there until the respective scope 56 | ends (see also :ref:`nested_patcher_invocation`). 57 | 58 | .. _unittest_usage: 59 | 60 | Patch using fake_filesystem_unittest 61 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 62 | If you are using the Python ``unittest`` package, the easiest approach is to 63 | use test classes derived from ``fake_filesystem_unittest.TestCase``. 64 | 65 | If you call ``setUpPyfakefs()`` in your ``setUp()``, ``pyfakefs`` will 66 | automatically find all real file functions and modules, and stub these out 67 | with the fake file system functions and modules: 68 | 69 | .. code:: python 70 | 71 | import os 72 | from pyfakefs.fake_filesystem_unittest import TestCase 73 | 74 | 75 | class ExampleTestCase(TestCase): 76 | def setUp(self): 77 | self.setUpPyfakefs() 78 | 79 | def test_create_file(self): 80 | file_path = "/test/file.txt" 81 | self.assertFalse(os.path.exists(file_path)) 82 | self.fs.create_file(file_path) 83 | self.assertTrue(os.path.exists(file_path)) 84 | 85 | The usage is explained in more detail in :ref:`auto_patch` and 86 | demonstrated in the files `example.py`_ and `example_test.py`_. 87 | 88 | If your setup is the same for all tests in a class, you can use the class setup 89 | method ``setUpClassPyfakefs`` instead: 90 | 91 | .. code:: python 92 | 93 | import os 94 | import pathlib 95 | from pyfakefs.fake_filesystem_unittest import TestCase 96 | 97 | 98 | class ExampleTestCase(TestCase): 99 | @classmethod 100 | def setUpClass(cls): 101 | cls.setUpClassPyfakefs() 102 | # setup the fake filesystem using standard functions 103 | path = pathlib.Path("/test") 104 | path.mkdir() 105 | (path / "file1.txt").touch() 106 | # you can also access the fake fs via fake_fs() if needed 107 | cls.fake_fs().create_file("/test/file2.txt", contents="test") 108 | 109 | def test1(self): 110 | self.assertTrue(os.path.exists("/test/file1.txt")) 111 | self.assertTrue(os.path.exists("/test/file2.txt")) 112 | 113 | def test2(self): 114 | self.assertTrue(os.path.exists("/test/file1.txt")) 115 | file_path = "/test/file3.txt" 116 | # self.fs is the same instance as cls.fake_fs() above 117 | self.fs.create_file(file_path) 118 | self.assertTrue(os.path.exists(file_path)) 119 | 120 | .. note:: This feature cannot be used with a Python version before Python 3.8 due to 121 | a missing feature in ``unittest``. 122 | 123 | .. caution:: If this is used, any changes made in the fake filesystem inside a test 124 | will remain there for all following tests in the test class, if they are not reverted 125 | in the test itself. 126 | 127 | 128 | Patch using fake_filesystem_unittest.Patcher 129 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 130 | If you are using other means of testing like `nose`_, 131 | you can do the patching using ``fake_filesystem_unittest.Patcher``--the class 132 | doing the actual work of replacing the filesystem modules with the fake modules 133 | in the first two approaches. 134 | 135 | The easiest way is to just use ``Patcher`` as a context manager: 136 | 137 | .. code:: python 138 | 139 | from pyfakefs.fake_filesystem_unittest import Patcher 140 | 141 | with Patcher() as patcher: 142 | # access the fake_filesystem object via patcher.fs 143 | patcher.fs.create_file("/foo/bar", contents="test") 144 | 145 | # the following code works on the fake filesystem 146 | with open("/foo/bar") as f: 147 | contents = f.read() 148 | 149 | You can also initialize ``Patcher`` manually: 150 | 151 | .. code:: python 152 | 153 | from pyfakefs.fake_filesystem_unittest import Patcher 154 | 155 | patcher = Patcher() 156 | patcher.setUp() # called in the initialization code 157 | ... 158 | patcher.tearDown() # somewhere in the cleanup code 159 | 160 | Patch using the fake_filesystem_unittest.patchfs decorator 161 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 162 | This is basically a convenience wrapper for the previous method. 163 | If you are not using ``pytest`` and want to use the fake filesystem for a 164 | single function, you can write: 165 | 166 | .. code:: python 167 | 168 | from pyfakefs.fake_filesystem_unittest import patchfs 169 | 170 | 171 | @patchfs 172 | def test_something(fake_fs): 173 | # access the fake_filesystem object via fake_fs 174 | fake_fs.create_file("/foo/bar", contents="test") 175 | 176 | Note that ``fake_fs`` is a positional argument and the argument name does 177 | not matter. If there are additional ``mock.patch`` decorators that also 178 | create positional arguments, the argument order is the same as the decorator 179 | order, as shown here: 180 | 181 | .. code:: python 182 | 183 | @patchfs 184 | @mock.patch("foo.bar") 185 | def test_something(fake_fs, mocked_bar): 186 | assert foo() 187 | 188 | 189 | @mock.patch("foo.bar") 190 | @patchfs 191 | def test_something(mocked_bar, fake_fs): 192 | assert foo() 193 | 194 | .. note:: 195 | Avoid writing the ``patchfs`` decorator *between* ``mock.patch`` operators, 196 | as the order will not be what you expect. Due to implementation details, 197 | all arguments created by ``mock.patch`` decorators are always expected to 198 | be contiguous, regardless of other decorators positioned between them. 199 | 200 | .. caution:: 201 | In previous versions, the keyword argument `fs` has been used instead, 202 | which had to be positioned *after* all positional arguments regardless of 203 | the decorator order. If you upgrade from a version before pyfakefs 4.2, 204 | you may have to adapt the argument order. 205 | 206 | You can also use this to make a single unit test use the fake fs: 207 | 208 | .. code:: python 209 | 210 | class TestSomething(unittest.TestCase): 211 | @patchfs 212 | def test_something(self, fs): 213 | fs.create_file("/foo/bar", contents="test") 214 | 215 | .. _auto_patch: 216 | 217 | Patch file system with unittest and doctest 218 | ------------------------------------------- 219 | The ``fake_filesystem_unittest`` module automatically finds all real file 220 | functions and modules, and stubs them out with the fake file system functions and modules. 221 | The pyfakefs source code contains files that demonstrate this usage model: 222 | 223 | - ``example.py`` is the software under test. In production, it uses the 224 | real file system. 225 | - ``example_test.py`` tests ``example.py``. During testing, the pyfakefs fake 226 | file system is used by ``example_test.py`` and ``example.py`` alike. 227 | 228 | .. note:: This example uses the Python ``unittest`` module for testing, but the 229 | functionality is similar if using the ``fs`` fixture in ``pytest``, 230 | the ``patchfs`` decorator, or the ``Patcher`` class. 231 | 232 | 233 | Software Under Test 234 | ~~~~~~~~~~~~~~~~~~~ 235 | ``example.py`` contains a few functions that manipulate files. For instance: 236 | 237 | .. code:: python 238 | 239 | def create_file(path): 240 | """Create the specified file and add some content to it. Use the open() 241 | built in function. 242 | 243 | For example, the following file operations occur in the fake file system. 244 | In the real file system, we would not even have permission to write /test: 245 | 246 | >>> os.path.isdir('/test') 247 | False 248 | >>> os.mkdir('/test') 249 | >>> os.path.isdir('/test') 250 | True 251 | >>> os.path.exists('/test/file.txt') 252 | False 253 | >>> create_file('/test/file.txt') 254 | >>> os.path.exists('/test/file.txt') 255 | True 256 | >>> with open('/test/file.txt') as f: 257 | ... f.readlines() 258 | ["This is test file '/test/file.txt'.\\n", 'It was created using the open() function.\\n'] 259 | """ 260 | with open(path, "w") as f: 261 | f.write("This is test file '{}'.\n".format(path)) 262 | f.write("It was created using the open() function.\n") 263 | 264 | No functional code in ``example.py`` even hints at a fake file system. In 265 | production, ``create_file()`` invokes the real file functions ``open()`` and 266 | ``write()``. 267 | 268 | Unit Tests and Doctests 269 | ~~~~~~~~~~~~~~~~~~~~~~~ 270 | ``example_test.py`` contains unit tests for ``example.py``. ``example.py`` 271 | contains the doctests, as you can see above. 272 | 273 | The module ``fake_filesystem_unittest`` contains code that finds all real file 274 | functions and modules, and stubs these out with the fake file system functions 275 | and modules: 276 | 277 | .. code:: python 278 | 279 | import os 280 | import unittest 281 | from pyfakefs import fake_filesystem_unittest 282 | 283 | # The module under test is example: 284 | import example 285 | 286 | Doctests 287 | ........ 288 | ``example_test.py`` defines ``load_tests()``, which runs the doctests in 289 | ``example.py``: 290 | 291 | .. code:: python 292 | 293 | def load_tests(loader, tests, ignore): 294 | """Load the pyfakefs/example.py doctest tests into unittest.""" 295 | return fake_filesystem_unittest.load_doctests(loader, tests, ignore, example) 296 | 297 | 298 | Everything, including all imported modules and the test, is stubbed out 299 | with the fake filesystem. Thus you can use familiar file functions like 300 | ``os.mkdir()`` as part of your test fixture and they too will operate on the 301 | fake file system. 302 | 303 | Unit Test Class 304 | ............... 305 | Next comes the ``unittest`` test class. This class is derived from 306 | ``fake_filesystem_unittest.TestCase``, which is in turn derived from 307 | ``unittest.TestClass``: 308 | 309 | .. code:: python 310 | 311 | class TestExample(fake_filesystem_unittest.TestCase): 312 | def setUp(self): 313 | self.setUpPyfakefs() 314 | 315 | def tearDown(self): 316 | # It is no longer necessary to add self.tearDownPyfakefs() 317 | pass 318 | 319 | def test_create_file(self): 320 | """Test example.create_file()""" 321 | # The os module has been replaced with the fake os module so all of the 322 | # following occurs in the fake filesystem. 323 | self.assertFalse(os.path.isdir("/test")) 324 | os.mkdir("/test") 325 | self.assertTrue(os.path.isdir("/test")) 326 | 327 | self.assertFalse(os.path.exists("/test/file.txt")) 328 | example.create_file("/test/file.txt") 329 | self.assertTrue(os.path.exists("/test/file.txt")) 330 | 331 | ... 332 | 333 | 334 | Just add ``self.setUpPyfakefs()`` in ``setUp()``. You need add nothing to 335 | ``tearDown()``. Write your tests as usual. From ``self.setUpPyfakefs()`` to 336 | the end of your ``tearDown()`` method, all file operations will use the fake 337 | file system. 338 | 339 | .. _`example.py`: https://github.com/pytest-dev/pyfakefs/blob/main/pyfakefs/tests/example.py 340 | .. _`example_test.py`: https://github.com/pytest-dev/pyfakefs/blob/main/pyfakefs/tests/example_test.py 341 | .. _`pytest`: https://doc.pytest.org 342 | .. _`nose`: https://docs.nose2.io/en/latest/ 343 | -------------------------------------------------------------------------------- /extra_requirements.txt: -------------------------------------------------------------------------------- 1 | # these are used to test pandas-specific patches to allow 2 | # pyfakefs to work with pandas 3 | # we use the latest version to see any problems with new versions 4 | pandas==1.3.5; python_version == '3.7' # pyup: ignore 5 | pandas==2.0.3; python_version == '3.8' # pyup: ignore 6 | pandas==2.2.3; python_version > '3.8' 7 | xlrd==2.0.1 8 | openpyxl==3.1.3; python_version == '3.7' # pyup: ignore 9 | openpyxl==3.1.5; python_version > '3.7' 10 | -------------------------------------------------------------------------------- /legacy_requirements.txt: -------------------------------------------------------------------------------- 1 | # "pathlib2" and "scandir" are backports of new standard modules, pyfakefs will 2 | # patch them if available when running on older Python versions. 3 | # 4 | # The modules are no longer for all required Python version, and only used for CI tests. 5 | # Note that the usage of these modules is deprecated, and their support 6 | # will be removed in pyfakefs 6.0 7 | pathlib2>=2.3.2 8 | scandir>=1.8; python_version < '3.13' # not (yet) available for Python 3.13 9 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | show_error_codes = True 3 | warn_unused_configs = True 4 | exclude=(docs|pyfakefs/tests) 5 | 6 | [mypy-django.*] 7 | ignore_missing_imports = True 8 | 9 | [mypy-hotshot.*] 10 | ignore_missing_imports = True 11 | 12 | [mypy-openpyxl.*] 13 | ignore_missing_imports = True 14 | 15 | [mypy-pandas.*] 16 | ignore_missing_imports = True 17 | 18 | [mypy-pathlib2.*] 19 | ignore_missing_imports = True 20 | 21 | [mypy-psyco.*] 22 | ignore_missing_imports = True 23 | 24 | [mypy-scandir.*] 25 | ignore_missing_imports = True 26 | 27 | [mypy-setuptools.*] 28 | ignore_missing_imports = True 29 | 30 | [mypy-xlrd.*] 31 | ignore_missing_imports = True 32 | -------------------------------------------------------------------------------- /pyfakefs/__init__.py: -------------------------------------------------------------------------------- 1 | from pyfakefs._version import __version__ # noqa: F401 2 | -------------------------------------------------------------------------------- /pyfakefs/_version.py: -------------------------------------------------------------------------------- 1 | __version__ = "5.9.dev0" 2 | -------------------------------------------------------------------------------- /pyfakefs/fake_filesystem_shutil.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """A fake shutil module implementation that uses fake_filesystem for 16 | unit tests. 17 | Note that only `shutildisk_usage()` is faked, the rest of the functions shall 18 | work fine with the fake file system if `os`/`os.path` are patched. 19 | 20 | :Includes: 21 | FakeShutil: Uses a FakeFilesystem to provide a fake replacement for the 22 | shutil module. 23 | 24 | :Usage: 25 | The fake implementation is automatically involved if using 26 | `fake_filesystem_unittest.TestCase`, pytest fs fixture, 27 | or directly `Patcher`. 28 | """ 29 | 30 | import os 31 | import shutil 32 | import sys 33 | 34 | 35 | class FakeShutilModule: 36 | """Uses a FakeFilesystem to provide a fake replacement 37 | for shutil module. 38 | """ 39 | 40 | @staticmethod 41 | def dir(): 42 | """Return the list of patched function names. Used for patching 43 | functions imported from the module. 44 | """ 45 | return ("disk_usage",) 46 | 47 | def __init__(self, filesystem): 48 | """Construct fake shutil module using the fake filesystem. 49 | 50 | Args: 51 | filesystem: FakeFilesystem used to provide file system information 52 | """ 53 | self.filesystem = filesystem 54 | self._shutil_module = shutil 55 | 56 | def disk_usage(self, path): 57 | """Return the total, used and free disk space in bytes as named tuple 58 | or placeholder holder values simulating unlimited space if not set. 59 | 60 | Args: 61 | path: defines the filesystem device which is queried 62 | """ 63 | return self.filesystem.get_disk_usage(path) 64 | 65 | if sys.version_info >= (3, 12) and sys.platform == "win32": 66 | 67 | def copy2(self, src, dst, *, follow_symlinks=True): 68 | """Since Python 3.12, there is an optimization fow Windows, 69 | using the Windows API. We just remove this and fall back to the previous 70 | implementation. 71 | """ 72 | if self.filesystem.isdir(dst): 73 | dst = self.filesystem.joinpaths(dst, os.path.basename(src)) 74 | 75 | self.copyfile(src, dst, follow_symlinks=follow_symlinks) 76 | self.copystat(src, dst, follow_symlinks=follow_symlinks) 77 | return dst 78 | 79 | def copytree( 80 | self, 81 | src, 82 | dst, 83 | symlinks=False, 84 | ignore=None, 85 | copy_function=shutil.copy2, 86 | ignore_dangling_symlinks=False, 87 | dirs_exist_ok=False, 88 | ): 89 | """Make sure the default argument is patched.""" 90 | if copy_function == shutil.copy2: 91 | copy_function = self.copy2 92 | return self._shutil_module.copytree( 93 | src, 94 | dst, 95 | symlinks, 96 | ignore, 97 | copy_function, 98 | ignore_dangling_symlinks, 99 | dirs_exist_ok, 100 | ) 101 | 102 | def move(self, src, dst, copy_function=shutil.copy2): 103 | """Make sure the default argument is patched.""" 104 | if copy_function == shutil.copy2: 105 | copy_function = self.copy2 106 | return self._shutil_module.move(src, dst, copy_function) 107 | 108 | def __getattr__(self, name): 109 | """Forwards any non-faked calls to the standard shutil module.""" 110 | return getattr(self._shutil_module, name) 111 | -------------------------------------------------------------------------------- /pyfakefs/fake_io.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Uses :py:class:`FakeIoModule` to provide a 16 | fake ``io`` module replacement. 17 | """ 18 | 19 | import _io # pytype: disable=import-error 20 | import io 21 | import sys 22 | from enum import Enum 23 | from typing import ( 24 | List, 25 | Optional, 26 | Callable, 27 | Union, 28 | Any, 29 | AnyStr, 30 | IO, 31 | TYPE_CHECKING, 32 | ) 33 | 34 | from pyfakefs.fake_file import AnyFileWrapper 35 | from pyfakefs.fake_open import fake_open 36 | from pyfakefs.helpers import IS_PYPY, is_called_from_skipped_module 37 | 38 | if TYPE_CHECKING: 39 | from pyfakefs.fake_filesystem import FakeFilesystem 40 | 41 | 42 | class PatchMode(Enum): 43 | """Defines if patching shall be on, off, or in automatic mode. 44 | Currently only used for `patch_open_code` option. 45 | """ 46 | 47 | OFF = 1 48 | AUTO = 2 49 | ON = 3 50 | 51 | 52 | class FakeIoModule: 53 | """Uses FakeFilesystem to provide a fake io module replacement. 54 | 55 | You need a fake_filesystem to use this:: 56 | 57 | filesystem = fake_filesystem.FakeFilesystem() 58 | my_io_module = fake_io.FakeIoModule(filesystem) 59 | """ 60 | 61 | @staticmethod 62 | def dir() -> List[str]: 63 | """Return the list of patched function names. Used for patching 64 | functions imported from the module. 65 | """ 66 | _dir = ["open"] 67 | if sys.version_info >= (3, 8): 68 | _dir.append("open_code") 69 | return _dir 70 | 71 | def __init__(self, filesystem: "FakeFilesystem"): 72 | """ 73 | Args: 74 | filesystem: FakeFilesystem used to provide file system information. 75 | """ 76 | self.filesystem = filesystem 77 | self.skip_names: List[str] = [] 78 | self._io_module = io 79 | 80 | def open( 81 | self, 82 | file: Union[AnyStr, int], 83 | mode: str = "r", 84 | buffering: int = -1, 85 | encoding: Optional[str] = None, 86 | errors: Optional[str] = None, 87 | newline: Optional[str] = None, 88 | closefd: bool = True, 89 | opener: Optional[Callable] = None, 90 | is_fake_open_code: bool = False, 91 | ) -> Union[AnyFileWrapper, IO[Any]]: 92 | """Redirect the call to FakeFileOpen. 93 | See FakeFileOpen.call() for description. 94 | """ 95 | return fake_open( 96 | self.filesystem, 97 | self.skip_names, 98 | file, 99 | mode, 100 | buffering, 101 | encoding, 102 | errors, 103 | newline, 104 | closefd, 105 | opener, 106 | is_fake_open_code, 107 | ) 108 | 109 | if sys.version_info >= (3, 8): 110 | 111 | def open_code(self, path): 112 | """Redirect the call to open. Note that the behavior of the real 113 | function may be overridden by an earlier call to the 114 | PyFile_SetOpenCodeHook(). This behavior is not reproduced here. 115 | """ 116 | if not isinstance(path, str) and not IS_PYPY: 117 | raise TypeError("open_code() argument 'path' must be str, not int") 118 | patch_mode = self.filesystem.patch_open_code 119 | if ( 120 | patch_mode == PatchMode.AUTO 121 | and self.filesystem.exists(path) 122 | or patch_mode == PatchMode.ON 123 | ): 124 | return self.open(path, mode="rb", is_fake_open_code=True) 125 | # mostly this is used for compiled code - 126 | # don't patch these, as the files are probably in the real fs 127 | return self._io_module.open_code(path) 128 | 129 | def __getattr__(self, name): 130 | """Forwards any unfaked calls to the standard io module.""" 131 | return getattr(self._io_module, name) 132 | 133 | 134 | class FakeIoModule2(FakeIoModule): 135 | """Similar to ``FakeIoModule``, but fakes `_io` instead of `io`.""" 136 | 137 | def __init__(self, filesystem: "FakeFilesystem"): 138 | """ 139 | Args: 140 | filesystem: FakeFilesystem used to provide file system information. 141 | """ 142 | super().__init__(filesystem) 143 | self._io_module = _io 144 | 145 | 146 | if sys.platform != "win32": 147 | import fcntl 148 | 149 | class FakeFcntlModule: 150 | """Replaces the fcntl module. Only valid under Linux/MacOS, 151 | currently just mocks the functionality away. 152 | """ 153 | 154 | @staticmethod 155 | def dir() -> List[str]: 156 | """Return the list of patched function names. Used for patching 157 | functions imported from the module. 158 | """ 159 | return ["fcntl", "ioctl", "flock", "lockf"] 160 | 161 | def __init__(self, filesystem: "FakeFilesystem"): 162 | """ 163 | Args: 164 | filesystem: FakeFilesystem used to provide file system 165 | information (currently not used). 166 | """ 167 | self.filesystem = filesystem 168 | self._fcntl_module = fcntl 169 | 170 | def fcntl(self, fd: int, cmd: int, arg: int = 0) -> Union[int, bytes]: 171 | return 0 if isinstance(arg, int) else arg 172 | 173 | def ioctl( 174 | self, fd: int, request: int, arg: int = 0, mutate_flag: bool = True 175 | ) -> Union[int, bytes]: 176 | return 0 if isinstance(arg, int) else arg 177 | 178 | def flock(self, fd: int, operation: int) -> None: 179 | pass 180 | 181 | def lockf( 182 | self, fd: int, cmd: int, len: int = 0, start: int = 0, whence=0 183 | ) -> Any: 184 | pass 185 | 186 | def __getattribute__(self, name): 187 | """Prevents patching of skipped modules.""" 188 | fs: FakeFilesystem = object.__getattribute__(self, "filesystem") 189 | fnctl_module = object.__getattribute__(self, "_fcntl_module") 190 | if fs.patcher: 191 | if is_called_from_skipped_module( 192 | skip_names=fs.patcher.skip_names, 193 | case_sensitive=fs.is_case_sensitive, 194 | ): 195 | # remove the `self` argument for FakeOsModule methods 196 | return getattr(fnctl_module, name) 197 | 198 | return object.__getattribute__(self, name) 199 | 200 | def __getattr__(self, name): 201 | """Forwards any unfaked calls to the standard fcntl module.""" 202 | return getattr(self._fcntl_module, name) 203 | -------------------------------------------------------------------------------- /pyfakefs/fake_legacy_modules.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | import warnings 14 | 15 | 16 | from pyfakefs.fake_pathlib import FakePathlibModule 17 | from pyfakefs.fake_scandir import scandir, walk 18 | 19 | 20 | def legacy_warning(module_name): 21 | msg = ( 22 | f"You are using the legacy package '{module_name}' instead of the " 23 | f"built-in module." 24 | "Patching this package will no longer be supported in pyfakefs >= 6" 25 | ) 26 | warnings.warn(msg, category=DeprecationWarning) 27 | 28 | 29 | class FakePathlib2Module(FakePathlibModule): 30 | """Uses FakeFilesystem to provide a fake pathlib module replacement. 31 | for the `pathlib2` package available on PyPi. 32 | The usage of `pathlib2` is deprecated and will no longer be supported 33 | in future pyfakefs versions. 34 | """ 35 | 36 | has_warned = False 37 | 38 | def __getattribute__(self, name): 39 | attr = object.__getattribute__(self, name) 40 | if hasattr(attr, "__call__") and not FakePathlib2Module.has_warned: 41 | FakePathlib2Module.has_warned = True 42 | legacy_warning("pathlib2") 43 | return attr 44 | 45 | 46 | class FakeScanDirModule: 47 | """Uses FakeFilesystem to provide a fake module replacement 48 | for the `scandir` package available on PyPi. 49 | 50 | The usage of the `scandir` package is deprecated and will no longer be supported 51 | in future pyfakefs versions. 52 | 53 | You need a fake_filesystem to use this: 54 | `filesystem = fake_filesystem.FakeFilesystem()` 55 | `fake_scandir_module = fake_filesystem.FakeScanDirModule(filesystem)` 56 | """ 57 | 58 | @staticmethod 59 | def dir(): 60 | """Return the list of patched function names. Used for patching 61 | functions imported from the module. 62 | """ 63 | return "scandir", "walk" 64 | 65 | def __init__(self, filesystem): 66 | self.filesystem = filesystem 67 | 68 | has_warned = False 69 | 70 | def scandir(self, path="."): 71 | """Return an iterator of DirEntry objects corresponding to the entries 72 | in the directory given by path. 73 | 74 | Args: 75 | path: Path to the target directory within the fake filesystem. 76 | 77 | Returns: 78 | an iterator to an unsorted list of os.DirEntry objects for 79 | each entry in path. 80 | 81 | Raises: 82 | OSError: if the target is not a directory. 83 | """ 84 | if not self.has_warned: 85 | self.__class__.has_warned = True 86 | legacy_warning("scandir") 87 | return scandir(self.filesystem, path) 88 | 89 | def walk(self, top, topdown=True, onerror=None, followlinks=False): 90 | """Perform a walk operation over the fake filesystem. 91 | 92 | Args: 93 | top: The root directory from which to begin walk. 94 | topdown: Determines whether to return the tuples with the root as 95 | the first entry (`True`) or as the last, after all the child 96 | directory tuples (`False`). 97 | onerror: If not `None`, function which will be called to handle the 98 | `os.error` instance provided when `os.listdir()` fails. 99 | followlinks: If `True`, symbolic links are followed. 100 | 101 | Yields: 102 | (path, directories, nondirectories) for top and each of its 103 | subdirectories. See the documentation for the builtin os module 104 | for further details. 105 | """ 106 | if not self.has_warned: 107 | self.__class__.has_warned = True 108 | legacy_warning("scandir") 109 | 110 | return walk(self.filesystem, top, topdown, onerror, followlinks) 111 | -------------------------------------------------------------------------------- /pyfakefs/fake_scandir.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | """A fake implementation for the `scandir` function working with 14 | FakeFilesystem. 15 | Works with both the function integrated into the `os` module since Python 3.5 16 | and the standalone function available in the standalone `scandir` python 17 | package. 18 | """ 19 | 20 | import os 21 | import sys 22 | 23 | from pyfakefs.helpers import to_string, make_string_path 24 | 25 | 26 | class DirEntry(os.PathLike): 27 | """Emulates os.DirEntry. Note that we did not enforce keyword only 28 | arguments.""" 29 | 30 | def __init__(self, filesystem): 31 | """Initialize the dir entry with unset values. 32 | 33 | Args: 34 | filesystem: the fake filesystem used for implementation. 35 | """ 36 | self._filesystem = filesystem 37 | self.name = "" 38 | self.path = "" 39 | self._abspath = "" 40 | self._inode = None 41 | self._islink = False 42 | self._isdir = False 43 | self._statresult = None 44 | self._statresult_symlink = None 45 | 46 | def inode(self): 47 | """Return the inode number of the entry.""" 48 | if self._inode is None: 49 | self.stat(follow_symlinks=False) 50 | return self._inode 51 | 52 | def is_dir(self, follow_symlinks=True): 53 | """Return `True` if this entry is a directory entry. 54 | 55 | Args: 56 | follow_symlinks: If `True`, also return `True` if this entry is a 57 | symlink pointing to a directory. 58 | 59 | Returns: 60 | `True` if this entry is an existing directory entry, or if 61 | ``follow_symlinks`` is set, and this entry points to an existing 62 | directory entry. 63 | """ 64 | return self._isdir and (follow_symlinks or not self._islink) 65 | 66 | def is_file(self, follow_symlinks=True): 67 | """Return `True` if this entry is a regular file entry. 68 | 69 | Args: 70 | follow_symlinks: If `True`, also return `True` if this entry is a 71 | symlink pointing to a regular file. 72 | 73 | Returns: 74 | `True` if this entry is an existing file entry, or if 75 | ``follow_symlinks`` is set, and this entry points to an existing 76 | file entry. 77 | """ 78 | return not self._isdir and (follow_symlinks or not self._islink) 79 | 80 | def is_symlink(self): 81 | """Return `True` if this entry is a symbolic link (even if broken).""" 82 | return self._islink 83 | 84 | def stat(self, follow_symlinks=True): 85 | """Return a `stat_result` object for this entry. 86 | 87 | Args: 88 | follow_symlinks: If `False` and the entry is a symlink, return the 89 | result for the symlink, otherwise for the object it points to. 90 | """ 91 | if follow_symlinks: 92 | if self._statresult_symlink is None: 93 | file_object = self._filesystem.resolve(self._abspath) 94 | self._statresult_symlink = file_object.stat_result.copy() 95 | if self._filesystem.is_windows_fs: 96 | self._statresult_symlink.st_nlink = 0 97 | return self._statresult_symlink 98 | 99 | if self._statresult is None: 100 | file_object = self._filesystem.lresolve(self._abspath) 101 | self._inode = file_object.st_ino 102 | self._statresult = file_object.stat_result.copy() 103 | if self._filesystem.is_windows_fs: 104 | self._statresult.st_nlink = 0 105 | return self._statresult 106 | 107 | def __fspath__(self): 108 | return self.path 109 | 110 | if sys.version_info >= (3, 12): 111 | 112 | def is_junction(self) -> bool: 113 | """Return `True` if this entry is a junction. 114 | Junctions are not a part of posix semantic.""" 115 | if not self._filesystem.is_windows_fs: 116 | return False 117 | file_object = self._filesystem.resolve(self._abspath) 118 | return file_object.is_junction 119 | 120 | 121 | class ScanDirIter: 122 | """Iterator for DirEntry objects returned from `scandir()` 123 | function.""" 124 | 125 | def __init__(self, filesystem, path): 126 | self.filesystem = filesystem 127 | if isinstance(path, int): 128 | if self.filesystem.is_windows_fs: 129 | raise NotImplementedError( 130 | "scandir does not support file descriptor path argument" 131 | ) 132 | self.abspath = self.filesystem.absnormpath( 133 | self.filesystem.get_open_file(path).get_object().path 134 | ) 135 | self.path = "" 136 | self.entry_iter = iter(tuple()) 137 | else: 138 | if path is None: 139 | path = "." 140 | path = make_string_path(path) 141 | self.abspath = self.filesystem.absnormpath(path) 142 | self.path = to_string(path) 143 | entries = self.filesystem.confirmdir(self.abspath, check_exe_perm=False).entries 144 | self.entry_iter = iter(tuple(entries)) 145 | 146 | def __iter__(self): 147 | return self 148 | 149 | def __next__(self): 150 | entry = self.entry_iter.__next__() 151 | dir_entry = DirEntry(self.filesystem) 152 | dir_entry.name = entry 153 | dir_entry.path = self.filesystem.joinpaths(self.path, dir_entry.name) 154 | dir_entry._abspath = self.filesystem.joinpaths(self.abspath, dir_entry.name) 155 | dir_entry._isdir = self.filesystem.isdir(dir_entry._abspath) 156 | dir_entry._islink = self.filesystem.islink(dir_entry._abspath) 157 | return dir_entry 158 | 159 | def __enter__(self): 160 | return self 161 | 162 | def __exit__(self, exc_type, exc_val, exc_tb): 163 | self.close() 164 | 165 | def close(self): 166 | pass 167 | 168 | 169 | def scandir(filesystem, path=""): 170 | """Return an iterator of DirEntry objects corresponding to the entries 171 | in the directory given by path. 172 | 173 | Args: 174 | filesystem: The fake filesystem used for implementation 175 | path: Path to the target directory within the fake filesystem. 176 | 177 | Returns: 178 | an iterator to an unsorted list of os.DirEntry objects for 179 | each entry in path. 180 | 181 | Raises: 182 | OSError: if the target is not a directory. 183 | """ 184 | return ScanDirIter(filesystem, path) 185 | 186 | 187 | def _classify_directory_contents(filesystem, root): 188 | """Classify contents of a directory as files/directories. 189 | 190 | Args: 191 | filesystem: The fake filesystem used for implementation 192 | root: (str) Directory to examine. 193 | 194 | Returns: 195 | (tuple) A tuple consisting of three values: the directory examined, 196 | a list containing all of the directory entries, and a list 197 | containing all of the non-directory entries. 198 | (This is the same format as returned by the `os.walk` generator.) 199 | 200 | Raises: 201 | Nothing on its own, but be ready to catch exceptions generated by 202 | underlying mechanisms like `os.listdir`. 203 | """ 204 | dirs = [] 205 | files = [] 206 | for entry in filesystem.listdir(root): 207 | if filesystem.isdir(filesystem.joinpaths(root, entry)): 208 | dirs.append(entry) 209 | else: 210 | files.append(entry) 211 | return root, dirs, files 212 | 213 | 214 | def walk(filesystem, top, topdown=True, onerror=None, followlinks=False): 215 | """Perform an os.walk operation over the fake filesystem. 216 | 217 | Args: 218 | filesystem: The fake filesystem used for implementation 219 | top: The root directory from which to begin walk. 220 | topdown: Determines whether to return the tuples with the root as 221 | the first entry (`True`) or as the last, after all the child 222 | directory tuples (`False`). 223 | onerror: If not `None`, function which will be called to handle the 224 | `os.error` instance provided when `os.listdir()` fails. 225 | followlinks: If `True`, symbolic links are followed. 226 | 227 | Yields: 228 | (path, directories, nondirectories) for top and each of its 229 | subdirectories. See the documentation for the builtin os module 230 | for further details. 231 | """ 232 | 233 | def do_walk(top_dir, top_most=False): 234 | if not top_most and not followlinks and filesystem.islink(top_dir): 235 | return 236 | try: 237 | top_contents = _classify_directory_contents(filesystem, top_dir) 238 | except OSError as exc: 239 | top_contents = None 240 | if onerror is not None: 241 | onerror(exc) 242 | 243 | if top_contents is not None: 244 | if topdown: 245 | yield top_contents 246 | 247 | for directory in top_contents[1]: 248 | path = filesystem.joinpaths(top_dir, directory) 249 | if not followlinks and filesystem.islink(path): 250 | continue 251 | yield from do_walk(path) 252 | if not topdown: 253 | yield top_contents 254 | 255 | return do_walk(make_string_path(to_string(top)), top_most=True) 256 | -------------------------------------------------------------------------------- /pyfakefs/legacy_packages.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | """Imports external packages that replace or emulate internal packages. 14 | These packages are not needed with any current Python version, 15 | and their support in pyfakefs will be removed in a an upcoming release. 16 | """ 17 | 18 | try: 19 | import pathlib2 20 | except ImportError: 21 | pathlib2 = None 22 | 23 | try: 24 | import scandir as scandir 25 | except ImportError: 26 | scandir = None 27 | -------------------------------------------------------------------------------- /pyfakefs/mox3_stubout.py: -------------------------------------------------------------------------------- 1 | # Copyright 2008 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """ 16 | This is a fork of the pymox library intended to work with Python 3. 17 | The file was modified by quermit@gmail.com and dawid.fatyga@gmail.com 18 | 19 | Previously, pyfakefs used just this file from the mox3 library. 20 | However, mox3 will soon be decommissioned, yet standard mock cannot 21 | be used because of the problem described in pyfakefs #182 and 22 | mock issue 250 (https://github.com/testing-cabal/mock/issues/250). 23 | Therefore just this file was forked from mox3 and incorporated 24 | into pyfakefs. 25 | """ 26 | 27 | import inspect 28 | 29 | 30 | class StubOutForTesting: 31 | """Sample Usage: 32 | 33 | You want os.path.exists() to always return true during testing. 34 | 35 | stubs = StubOutForTesting() 36 | stubs.Set(os.path, 'exists', lambda x: 1) 37 | ... 38 | stubs.UnsetAll() 39 | 40 | The above changes os.path.exists into a lambda that returns 1. Once 41 | the ... part of the code finishes, the UnsetAll() looks up the old value 42 | of os.path.exists and restores it. 43 | 44 | """ 45 | 46 | def __init__(self): 47 | self.cache = [] 48 | self.stubs = [] 49 | 50 | def __del__(self): 51 | self.smart_unset_all() 52 | self.unset_all() 53 | 54 | def smart_set(self, obj, attr_name, new_attr): 55 | """Replace obj.attr_name with new_attr. 56 | 57 | This method is smart and works at the module, class, and instance level 58 | while preserving proper inheritance. It will not stub out C types 59 | however unless that has been explicitly allowed by the type. 60 | 61 | This method supports the case where attr_name is a staticmethod or a 62 | classmethod of obj. 63 | 64 | If obj is an instance, then it is its class that will actually be 65 | stubbed. Note that the method Set() does not do that: if obj is an 66 | instance, it (and not its class) will be stubbed. 67 | 68 | Raises AttributeError if the attribute cannot be found. 69 | """ 70 | if inspect.ismodule(obj) or ( 71 | not inspect.isclass(obj) and attr_name in obj.__dict__ 72 | ): 73 | orig_obj = obj 74 | if attr_name in obj.__dict__: 75 | orig_attr = obj.__dict__[attr_name] 76 | else: 77 | orig_attr = None 78 | 79 | else: 80 | if not inspect.isclass(obj): 81 | mro = list(inspect.getmro(obj.__class__)) 82 | else: 83 | mro = list(inspect.getmro(obj)) 84 | 85 | mro.reverse() 86 | 87 | orig_attr = None 88 | 89 | for cls in mro: 90 | try: 91 | orig_obj = cls 92 | orig_attr = obj.__dict__[attr_name] 93 | except KeyError: 94 | continue 95 | 96 | if orig_attr is None: 97 | raise AttributeError("Attribute not found.") 98 | 99 | self.stubs.append((orig_obj, attr_name, orig_attr)) 100 | orig_obj.__dict__[attr_name] = new_attr 101 | 102 | def smart_unset_all(self): 103 | """Reverses all the SmartSet() calls. 104 | 105 | Restores things to their original definition. Its okay to call 106 | SmartUnsetAll() repeatedly, as later calls have no effect if no 107 | SmartSet() calls have been made. 108 | """ 109 | self.stubs.reverse() 110 | 111 | for obj, attr_name, old_attr in self.stubs: 112 | obj.__dict__[attr_name] = old_attr 113 | 114 | self.stubs = [] 115 | 116 | def set(self, parent, child_name, new_child): 117 | """Replace child_name's old definition with new_child. 118 | 119 | Replace definition in the context of the given parent. The parent could 120 | be a module when the child is a function at module scope. Or the parent 121 | could be a class when a class' method is being replaced. The named 122 | child is set to new_child, while the prior definition is saved away 123 | for later, when unset_all() is called. 124 | 125 | This method supports the case where child_name is a staticmethod or a 126 | classmethod of parent. 127 | """ 128 | old_child = getattr(parent, child_name) 129 | 130 | old_attribute = parent.__dict__.get(child_name) 131 | if old_attribute is not None: 132 | if isinstance(old_attribute, staticmethod): 133 | old_child = staticmethod(old_child) 134 | elif isinstance(old_attribute, classmethod): 135 | old_child = classmethod(old_child.__func__) 136 | 137 | self.cache.append((parent, old_child, child_name)) 138 | parent.__dict__[child_name] = new_child 139 | 140 | def unset_all(self): 141 | """Reverses all the Set() calls. 142 | 143 | Restores things to their original definition. Its okay to call 144 | unset_all() repeatedly, as later calls have no effect if no Set() 145 | calls have been made. 146 | """ 147 | # Undo calls to set() in reverse order, in case set() was called on the 148 | # same arguments repeatedly (want the original call to be last one 149 | # undone) 150 | self.cache.reverse() 151 | 152 | for parent, old_child, child_name in self.cache: 153 | parent.__dict__[child_name] = old_child 154 | self.cache = [] 155 | -------------------------------------------------------------------------------- /pyfakefs/patched_packages.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | """ 14 | Provides patches for some commonly used modules that enable them to work 15 | with pyfakefs. 16 | """ 17 | 18 | import sys 19 | from importlib import reload 20 | 21 | try: 22 | import pandas as pd 23 | 24 | try: 25 | import pandas.io.parsers as parsers 26 | except ImportError: 27 | parsers = None 28 | except ImportError: 29 | pd = None 30 | parsers = None 31 | 32 | 33 | try: 34 | import xlrd 35 | except ImportError: 36 | xlrd = None 37 | 38 | 39 | try: 40 | import django 41 | 42 | try: 43 | from django.core.files import locks 44 | except ImportError: 45 | locks = None 46 | except ImportError: 47 | django = None 48 | locks = None 49 | 50 | # From pandas v 1.2 onwards the python fs functions are used even when the engine 51 | # selected is "c". This means that we don't explicitly have to change the engine. 52 | patch_pandas = parsers is not None and [int(v) for v in pd.__version__.split(".")] < [ 53 | 1, 54 | 2, 55 | 0, 56 | ] 57 | 58 | 59 | def get_modules_to_patch(): 60 | modules_to_patch = {} 61 | if xlrd is not None: 62 | modules_to_patch["xlrd"] = XLRDModule 63 | if locks is not None: 64 | modules_to_patch["django.core.files.locks"] = FakeLocks 65 | return modules_to_patch 66 | 67 | 68 | def get_classes_to_patch(): 69 | classes_to_patch = {} 70 | if patch_pandas: 71 | classes_to_patch["TextFileReader"] = ["pandas.io.parsers"] 72 | return classes_to_patch 73 | 74 | 75 | def reload_handler(name): 76 | if name in sys.modules: 77 | reload(sys.modules[name]) 78 | return True 79 | 80 | 81 | def get_cleanup_handlers(): 82 | handlers = {} 83 | if pd is not None: 84 | handlers["pandas.core.arrays.arrow.extension_types"] = ( 85 | handle_extension_type_cleanup 86 | ) 87 | if django is not None: 88 | for module_name in django_view_modules(): 89 | handlers[module_name] = lambda name=module_name: reload_handler(name) 90 | return handlers 91 | 92 | 93 | def get_fake_module_classes(): 94 | fake_module_classes = {} 95 | if patch_pandas: 96 | fake_module_classes["TextFileReader"] = FakeTextFileReader 97 | return fake_module_classes 98 | 99 | 100 | if xlrd is not None: 101 | 102 | class XLRDModule: 103 | """Patches the xlrd module, which is used as the default Excel file 104 | reader by pandas. Disables using memory mapped files, which are 105 | implemented platform-specific on OS level.""" 106 | 107 | def __init__(self, _): 108 | self._xlrd_module = xlrd 109 | 110 | def open_workbook( 111 | self, 112 | filename=None, 113 | logfile=sys.stdout, 114 | verbosity=0, 115 | use_mmap=False, 116 | file_contents=None, 117 | encoding_override=None, 118 | formatting_info=False, 119 | on_demand=False, 120 | ragged_rows=False, 121 | ): 122 | return self._xlrd_module.open_workbook( 123 | filename, 124 | logfile, 125 | verbosity, 126 | False, 127 | file_contents, 128 | encoding_override, 129 | formatting_info, 130 | on_demand, 131 | ragged_rows, 132 | ) 133 | 134 | def __getattr__(self, name): 135 | """Forwards any unfaked calls to the standard xlrd module.""" 136 | return getattr(self._xlrd_module, name) 137 | 138 | 139 | if patch_pandas: 140 | # we currently need to add fake modules for both the parser module and 141 | # the contained text reader - maybe this can be simplified 142 | 143 | class FakeTextFileReader: 144 | fake_parsers = None 145 | 146 | def __init__(self, filesystem): 147 | if self.fake_parsers is None: 148 | self.__class__.fake_parsers = ParsersModule(filesystem) 149 | 150 | def __call__(self, *args, **kwargs): 151 | return self.fake_parsers.TextFileReader(*args, **kwargs) 152 | 153 | def __getattr__(self, name): 154 | return getattr(self.fake_parsers.TextFileReader, name) 155 | 156 | class ParsersModule: 157 | def __init__(self, _): 158 | self._parsers_module = parsers 159 | 160 | class TextFileReader(parsers.TextFileReader): 161 | def __init__(self, *args, **kwargs): 162 | kwargs["engine"] = "python" 163 | super().__init__(*args, **kwargs) 164 | 165 | def __getattr__(self, name): 166 | """Forwards any unfaked calls to the standard xlrd module.""" 167 | return getattr(self._parsers_module, name) 168 | 169 | 170 | if pd is not None: 171 | 172 | def handle_extension_type_cleanup(_name): 173 | # the module registers two extension types on load 174 | # on reload it raises if the extensions have not been unregistered before 175 | try: 176 | import pyarrow 177 | 178 | # the code to register these types has been in the module 179 | # since it was created (in pandas 1.5) 180 | pyarrow.unregister_extension_type("pandas.interval") 181 | pyarrow.unregister_extension_type("pandas.period") 182 | except ImportError: 183 | pass 184 | return False 185 | 186 | 187 | if locks is not None: 188 | 189 | class FakeLocks: 190 | """django.core.files.locks uses low level OS functions, fake it.""" 191 | 192 | _locks_module = locks 193 | 194 | def __init__(self, _): 195 | pass 196 | 197 | @staticmethod 198 | def lock(f, flags): 199 | return True 200 | 201 | @staticmethod 202 | def unlock(f): 203 | return True 204 | 205 | def __getattr__(self, name): 206 | return getattr(self._locks_module, name) 207 | 208 | 209 | if django is not None: 210 | 211 | def get_all_view_modules(urlpatterns, modules=None): 212 | if modules is None: 213 | modules = set() 214 | for pattern in urlpatterns: 215 | if hasattr(pattern, "url_patterns"): 216 | get_all_view_modules(pattern.url_patterns, modules=modules) 217 | else: 218 | if hasattr(pattern.callback, "cls"): 219 | view = pattern.callback.cls 220 | elif hasattr(pattern.callback, "view_class"): 221 | view = pattern.callback.view_class 222 | else: 223 | view = pattern.callback 224 | modules.add(view.__module__) 225 | return modules 226 | 227 | def django_view_modules(): 228 | try: 229 | all_urlpatterns = __import__( 230 | django.conf.settings.ROOT_URLCONF 231 | ).urls.urlpatterns 232 | return get_all_view_modules(all_urlpatterns) 233 | except Exception: 234 | return set() 235 | -------------------------------------------------------------------------------- /pyfakefs/py.typed: -------------------------------------------------------------------------------- 1 | # Marker file for PEP 561. The pyfakefs package uses inline types. 2 | -------------------------------------------------------------------------------- /pyfakefs/pytest_plugin.py: -------------------------------------------------------------------------------- 1 | """A pytest plugin for using pyfakefs as a fixture 2 | 3 | When pyfakefs is installed, the "fs" fixture becomes available. 4 | 5 | :Usage: 6 | 7 | def my_fakefs_test(fs): 8 | fs.create_file('/var/data/xx1.txt') 9 | assert os.path.exists('/var/data/xx1.txt') 10 | """ 11 | 12 | import py 13 | import pytest 14 | from _pytest import capture 15 | 16 | from pyfakefs.fake_filesystem_unittest import Patcher 17 | 18 | try: 19 | from _pytest import pathlib 20 | except ImportError: 21 | pathlib = None # type:ignore[assignment] 22 | 23 | Patcher.SKIPMODULES.add(py) 24 | Patcher.SKIPMODULES.add(pytest) 25 | Patcher.SKIPMODULES.add(capture) 26 | if pathlib is not None: 27 | Patcher.SKIPMODULES.add(pathlib) 28 | 29 | 30 | @pytest.fixture 31 | def fs(request): 32 | """Fake filesystem.""" 33 | if hasattr(request, "param"): 34 | # pass optional parameters via @pytest.mark.parametrize 35 | patcher = Patcher(*request.param) 36 | else: 37 | patcher = Patcher() 38 | patcher.setUp() 39 | yield patcher.fs 40 | patcher.tearDown() 41 | 42 | 43 | @pytest.fixture(scope="class") 44 | def fs_class(request): 45 | """Class-scoped fake filesystem fixture.""" 46 | if hasattr(request, "param"): 47 | patcher = Patcher(*request.param) 48 | else: 49 | patcher = Patcher() 50 | patcher.setUp() 51 | yield patcher.fs 52 | patcher.tearDown() 53 | 54 | 55 | @pytest.fixture(scope="module") 56 | def fs_module(request): 57 | """Module-scoped fake filesystem fixture.""" 58 | if hasattr(request, "param"): 59 | patcher = Patcher(*request.param) 60 | else: 61 | patcher = Patcher() 62 | patcher.setUp() 63 | yield patcher.fs 64 | patcher.tearDown() 65 | 66 | 67 | @pytest.fixture(scope="session") 68 | def fs_session(request): 69 | """Session-scoped fake filesystem fixture.""" 70 | if hasattr(request, "param"): 71 | patcher = Patcher(*request.param) 72 | else: 73 | patcher = Patcher() 74 | patcher.setUp() 75 | yield patcher.fs 76 | patcher.tearDown() 77 | 78 | 79 | @pytest.hookimpl(tryfirst=True) 80 | def pytest_sessionfinish(session, exitstatus): 81 | """Make sure that the cache is cleared before the final test shutdown.""" 82 | Patcher.clear_fs_cache() 83 | 84 | 85 | @pytest.hookimpl(hookwrapper=True, tryfirst=True) 86 | def pytest_runtest_logreport(report): 87 | """Make sure that patching is not active during reporting.""" 88 | pause = Patcher.PATCHER is not None and report.when == "call" 89 | if pause: 90 | Patcher.PATCHER.pause() 91 | yield 92 | 93 | 94 | @pytest.hookimpl(hookwrapper=True, trylast=True) 95 | def pytest_runtest_setup(item): 96 | if Patcher.PATCHER is not None: 97 | Patcher.PATCHER.resume() 98 | yield 99 | 100 | 101 | @pytest.hookimpl(hookwrapper=True, tryfirst=True) 102 | def pytest_runtest_teardown(item, nextitem): 103 | """Make sure that patching is not active during reporting.""" 104 | if Patcher.PATCHER is not None: 105 | Patcher.PATCHER.pause() 106 | yield 107 | -------------------------------------------------------------------------------- /pyfakefs/pytest_session_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pytest-dev/pyfakefs/44fab74f14ef90ad1580b6719a052b9671fd5f51/pyfakefs/pytest_session_tests/__init__.py -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pytest-dev/pyfakefs/44fab74f14ef90ad1580b6719a052b9671fd5f51/pyfakefs/pytest_tests/__init__.py -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/conftest.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | # Example for a custom pytest fixture with an argument to Patcher. 14 | # Use this as a template if you want to write your own pytest plugin 15 | # with specific Patcher arguments. 16 | # See `pytest_plugin.py` for more information. 17 | 18 | import pytest 19 | 20 | from pyfakefs.fake_filesystem_unittest import Patcher 21 | 22 | # import the fs fixture to be visible if pyfakefs is not installed 23 | from pyfakefs.pytest_plugin import fs, fs_module # noqa: F401 24 | 25 | from pyfakefs.pytest_tests import example # noqa: E402 26 | 27 | 28 | @pytest.fixture 29 | def fs_reload_example(): 30 | """Fake filesystem.""" 31 | patcher = Patcher(modules_to_reload=[example]) 32 | patcher.setUp() 33 | yield patcher.fs 34 | patcher.tearDown() 35 | 36 | 37 | @pytest.fixture 38 | def fake_filesystem(fs): # noqa: F811 39 | """Shows how to use an alias for the fs fixture.""" 40 | yield fs 41 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/data/test.parquet: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pytest-dev/pyfakefs/44fab74f14ef90ad1580b6719a052b9671fd5f51/pyfakefs/pytest_tests/data/test.parquet -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/example.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | # Used as SUT for pytest_fixture_test.py 14 | 15 | from pathlib import Path 16 | 17 | EXAMPLE_FILE = Path("/test") / "file" 18 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/fake_fcntl_test.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | import sys 14 | 15 | if sys.platform == "linux": 16 | import fcntl 17 | 18 | def test_unpatched_attributes_are_forwarded_to_real_fs(fs): 19 | # regression test for #1074 20 | with open("lock_file", "a+") as lock_file: 21 | fcntl.flock(lock_file, fcntl.LOCK_SH) 22 | fcntl.flock(lock_file, fcntl.LOCK_UN) 23 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/hook_test/conftest.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | from pathlib import Path 13 | 14 | import pytest 15 | 16 | 17 | # Used for testing paused patching during reporting. 18 | 19 | 20 | @pytest.hookimpl 21 | def pytest_runtest_logreport(report): 22 | if report.when == "call": 23 | report_path = Path(__file__).parent / "report.txt" 24 | with open(report_path, "w") as f: 25 | f.write("Test") 26 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/hook_test/pytest_hook_test.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | from pathlib import Path 13 | 14 | import pytest 15 | 16 | 17 | @pytest.fixture 18 | def report_path(): 19 | yield Path(__file__).parent / "report.txt" 20 | 21 | 22 | def test_1(fs): 23 | pass 24 | 25 | 26 | def test_2_report_in_real_fs(report_path): 27 | print("test_2_report_in_real_fs") 28 | assert report_path.exists() 29 | report_path.unlink() 30 | 31 | 32 | def test_3(fs): 33 | pass 34 | 35 | 36 | def test_4_report_in_real_fs(report_path): 37 | assert report_path.exists() 38 | report_path.unlink() 39 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/io.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is a test case for pyfakefs issue #708. 3 | It tests the usage of an own module with the same name as a patched filesystem 4 | module, the content is taken from the issue. 5 | """ 6 | 7 | 8 | class InputStream: 9 | def __init__(self, name): 10 | self.name = name 11 | 12 | def read(self): 13 | with open(self.name) as f: 14 | return f.readline() 15 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/lib_using_pathlib.py: -------------------------------------------------------------------------------- 1 | import pathlib 2 | 3 | 4 | def use_pathlib(path: str): 5 | return pathlib.Path(path) 6 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/local_import.py: -------------------------------------------------------------------------------- 1 | def load(path: str) -> str: 2 | from pyfakefs.pytest_tests import lib_using_pathlib 3 | 4 | return lib_using_pathlib.use_pathlib(path) 5 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/ns_package/test/ns_package/test/test_file.py: -------------------------------------------------------------------------------- 1 | import pyfakefs.fake_filesystem 2 | 3 | 4 | def test_foo(fs): 5 | """Regression test for #814 - must run in namespace package with cli logging.""" 6 | fs.os = pyfakefs.fake_filesystem.OSType.WINDOWS 7 | assert True 8 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/ns_package/test/setup.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pytest-dev/pyfakefs/44fab74f14ef90ad1580b6719a052b9671fd5f51/pyfakefs/pytest_tests/ns_package/test/setup.py -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/pytest_check_failed_plugin_test.py: -------------------------------------------------------------------------------- 1 | """Tests that a failed pytest properly displays the call stack. 2 | Uses the output from running pytest with pytest_plugin_failing_helper.py. 3 | Regression test for #381. 4 | """ 5 | 6 | import os 7 | 8 | import pytest 9 | 10 | 11 | @pytest.mark.skipif(not os.path.exists("testresult.txt"), reason="Only run in CI tests") 12 | def test_failed_testresult_stacktrace(): 13 | with open("testresult.txt") as f: 14 | contents = f.read() 15 | # before the fix, a triple question mark has been displayed 16 | # instead of the stacktrace 17 | assert contents 18 | print("contents", contents) 19 | assert "???" not in contents 20 | assert "AttributeError" not in contents 21 | assert "def test_fs(fs):" in contents 22 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/pytest_doctest_test.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is a test case for pyfakefs issue #45. 3 | This problem is resolved by using PyTest version 2.8.6 or above. 4 | 5 | To run these doctests, install pytest and run: 6 | 7 | $ pytest --doctest-modules pytest_doctest_test.py 8 | 9 | Add `-s` option to enable print statements. 10 | """ 11 | 12 | 13 | def make_file_factory(func_name, fake, result): 14 | """Return a simple function with parametrized doctest.""" 15 | 16 | def make_file(name, content=""): 17 | with open(name, "w") as f: 18 | f.write(content) 19 | 20 | make_file.__doc__ = """ 21 | >>> import os 22 | >>> {command} 23 | >>> name, content = 'foo', 'bar' 24 | >>> {func_name}(name, content) 25 | >>> open(name).read() == content 26 | {result} 27 | >>> os.remove(name) # Cleanup 28 | """.format( 29 | command="getfixture('fs')" if fake else "pass", 30 | func_name=func_name, 31 | result=result, 32 | ) 33 | 34 | return make_file 35 | 36 | 37 | passes = make_file_factory("passes", fake=False, result=True) 38 | passes_too = make_file_factory("passes_too", fake=True, result=True) 39 | 40 | passes_too.__doc__ = passes_too.__doc__.replace(">>> os.remove(name)", ">>> pass") 41 | 42 | fails = make_file_factory("fails", fake=False, result=False) 43 | 44 | # Pytest versions below 2.8.6 raise an internal error when running 45 | # these doctests: 46 | crashes = make_file_factory("crashes", fake=True, result=False) 47 | crashes_too = make_file_factory(") SyntaxError", fake=True, result=False) 48 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/pytest_fixture_param_test.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | # Example for a test using a custom pytest fixture with an argument to Patcher 14 | import os 15 | 16 | import pytest 17 | 18 | import pyfakefs.pytest_tests.example as example 19 | 20 | 21 | @pytest.mark.xfail 22 | def test_example_file_failing(fs): 23 | """Test fails because EXAMPLE_FILE is cached in the module 24 | and not patched.""" 25 | fs.create_file(example.EXAMPLE_FILE, contents="stuff here") 26 | check_that_example_file_is_in_fake_fs() 27 | 28 | 29 | @pytest.mark.parametrize("fs", [[None, [example]]], indirect=True) 30 | def test_example_file_passing_using_parametrized_fixture(fs): 31 | """Test passes if using a fixture that reloads the module containing 32 | EXAMPLE_FILE""" 33 | fs.create_file(example.EXAMPLE_FILE, contents="stuff here") 34 | check_that_example_file_is_in_fake_fs() 35 | 36 | 37 | def check_that_example_file_is_in_fake_fs(): 38 | with open(example.EXAMPLE_FILE) as file: 39 | assert file.read() == "stuff here" 40 | with example.EXAMPLE_FILE.open() as file: 41 | assert file.read() == "stuff here" 42 | assert example.EXAMPLE_FILE.read_text() == "stuff here" 43 | assert example.EXAMPLE_FILE.is_file() 44 | 45 | 46 | def test_twice_chdir(fs): 47 | # regression test for #530 - make sure that 48 | # alternative path separators are correctly handled under Windows 49 | fs.create_dir("/absolute/path/to/directory") 50 | os.chdir("/absolute/path/to/directory") 51 | os.chdir("/absolute/path/to/directory") 52 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/pytest_fixture_test.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | # Example for a test using a custom pytest fixture with an argument to Patcher 14 | 15 | import pytest 16 | 17 | import pyfakefs.pytest_tests.example as example 18 | from pyfakefs.fake_filesystem_unittest import Patcher 19 | from pyfakefs.pytest_tests import unhashable 20 | 21 | 22 | @pytest.mark.xfail 23 | def test_example_file_failing(fs): 24 | """Test fails because EXAMPLE_FILE is cached in the module 25 | and not patched.""" 26 | fs.create_file(example.EXAMPLE_FILE, contents="stuff here") 27 | check_that_example_file_is_in_fake_fs() 28 | 29 | 30 | def test_example_file_passing_using_fixture(fs_reload_example): 31 | """Test passes if using a fixture that reloads the module containing 32 | EXAMPLE_FILE""" 33 | fs_reload_example.create_file(example.EXAMPLE_FILE, contents="stuff here") 34 | check_that_example_file_is_in_fake_fs() 35 | 36 | 37 | def test_example_file_passing_using_patcher(): 38 | """Test passes if using a Patcher instance that reloads the module 39 | containing EXAMPLE_FILE""" 40 | with Patcher(modules_to_reload=[example]) as patcher: 41 | patcher.fs.create_file(example.EXAMPLE_FILE, contents="stuff here") 42 | check_that_example_file_is_in_fake_fs() 43 | 44 | 45 | def test_unhashable(fs): 46 | # regression test for #923 47 | print(unhashable) 48 | 49 | 50 | def check_that_example_file_is_in_fake_fs(): 51 | with open(example.EXAMPLE_FILE) as file: 52 | assert file.read() == "stuff here" 53 | with example.EXAMPLE_FILE.open() as file: 54 | assert file.read() == "stuff here" 55 | assert example.EXAMPLE_FILE.read_text() == "stuff here" 56 | assert example.EXAMPLE_FILE.is_file() 57 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/pytest_module_fixture_test.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | import os 13 | 14 | import pytest 15 | 16 | 17 | @pytest.fixture(scope="module", autouse=True) 18 | def use_fs(fs_module): 19 | fs_module.create_file(os.path.join("foo", "bar")) 20 | yield fs_module 21 | 22 | 23 | @pytest.mark.usefixtures("fs") 24 | def test_fs_uses_fs_module1(): 25 | # check that `fs` uses the same filesystem as `fs_module` 26 | assert os.path.exists(os.path.join("foo", "bar")) 27 | 28 | 29 | def test_fs_uses_fs_module2(fs): 30 | # check that patching was not stopped by the first test 31 | assert os.path.exists(os.path.join("foo", "bar")) 32 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/pytest_plugin_failing_helper.py: -------------------------------------------------------------------------------- 1 | """Failing test to test stacktrace output - see 2 | ``pytest_check_failed_plugin_test.py``.""" 3 | 4 | 5 | def test_fs(fs): 6 | assert 1 == 2 7 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/pytest_plugin_test.py: -------------------------------------------------------------------------------- 1 | """Tests that the pytest plugin properly provides the "fs" fixture""" 2 | 3 | import os 4 | import tempfile 5 | 6 | from pyfakefs.fake_filesystem import OSType 7 | from pyfakefs.fake_filesystem_unittest import Pause 8 | import pyfakefs.pytest_tests.io 9 | 10 | 11 | def test_fs_fixture(fs): 12 | fs.create_file("/var/data/xx1.txt") 13 | assert os.path.exists("/var/data/xx1.txt") 14 | 15 | 16 | def test_fs_fixture_alias(fake_filesystem): 17 | fake_filesystem.create_file("/var/data/xx1.txt") 18 | assert os.path.exists("/var/data/xx1.txt") 19 | 20 | 21 | def test_both_fixtures(fs, fake_filesystem): 22 | fake_filesystem.create_file("/var/data/xx1.txt") 23 | fs.create_file("/var/data/xx2.txt") 24 | assert os.path.exists("/var/data/xx1.txt") 25 | assert os.path.exists("/var/data/xx2.txt") 26 | assert fs == fake_filesystem 27 | 28 | 29 | def test_pause_resume(fs): 30 | fake_temp_file = tempfile.NamedTemporaryFile() 31 | assert fs.exists(fake_temp_file.name) 32 | assert os.path.exists(fake_temp_file.name) 33 | fs.pause() 34 | assert fs.exists(fake_temp_file.name) 35 | assert not os.path.exists(fake_temp_file.name) 36 | real_temp_file = tempfile.NamedTemporaryFile() 37 | assert not fs.exists(real_temp_file.name) 38 | assert os.path.exists(real_temp_file.name) 39 | fs.resume() 40 | assert not os.path.exists(real_temp_file.name) 41 | assert os.path.exists(fake_temp_file.name) 42 | 43 | 44 | def test_pause_resume_contextmanager(fs): 45 | fake_temp_file = tempfile.NamedTemporaryFile() 46 | assert fs.exists(fake_temp_file.name) 47 | assert os.path.exists(fake_temp_file.name) 48 | with Pause(fs): 49 | assert fs.exists(fake_temp_file.name) 50 | assert not os.path.exists(fake_temp_file.name) 51 | real_temp_file = tempfile.NamedTemporaryFile() 52 | assert not fs.exists(real_temp_file.name) 53 | assert os.path.exists(real_temp_file.name) 54 | assert not os.path.exists(real_temp_file.name) 55 | assert os.path.exists(fake_temp_file.name) 56 | 57 | 58 | def test_use_own_io_module(fs): 59 | filepath = "foo.txt" 60 | with open(filepath, "w") as f: 61 | f.write("bar") 62 | 63 | stream = pyfakefs.pytest_tests.io.InputStream(filepath) 64 | assert stream.read() == "bar" 65 | 66 | 67 | def test_switch_to_windows(fs): 68 | fs.os = OSType.WINDOWS 69 | assert os.path.exists(tempfile.gettempdir()) 70 | 71 | 72 | def test_switch_to_linux(fs): 73 | fs.os = OSType.LINUX 74 | assert os.path.exists(tempfile.gettempdir()) 75 | 76 | 77 | def test_switch_to_macos(fs): 78 | fs.os = OSType.MACOS 79 | assert os.path.exists(tempfile.gettempdir()) 80 | 81 | 82 | def test_updatecache_problem(fs): 83 | # regression test for #1096 84 | filename = r"C:\source_file" 85 | fs.create_file(filename) 86 | with open(filename): 87 | assert True 88 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/pytest_reload_pandas_test.py: -------------------------------------------------------------------------------- 1 | """Regression test for #947. 2 | Ensures that reloading the `pandas.core.arrays.arrow.extension_types` module succeeds. 3 | """ 4 | 5 | from pathlib import Path 6 | 7 | import pytest 8 | 9 | try: 10 | import pandas as pd 11 | except ImportError: 12 | pd = None 13 | 14 | try: 15 | import parquet 16 | except ImportError: 17 | parquet = None 18 | 19 | 20 | @pytest.mark.skipif( 21 | pd is None or parquet is None, reason="pandas or parquet not installed" 22 | ) 23 | def test_1(fs): 24 | dir_ = Path(__file__).parent / "data" 25 | fs.add_real_directory(dir_) 26 | pd.read_parquet(dir_ / "test.parquet") 27 | 28 | 29 | @pytest.mark.skipif( 30 | pd is None or parquet is None, reason="pandas or parquet not installed" 31 | ) 32 | def test_2(): 33 | dir_ = Path(__file__).parent / "data" 34 | pd.read_parquet(dir_ / "test.parquet") 35 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/test_patch_on_setup.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import pytest 4 | 5 | from pyfakefs.fake_filesystem import FakeFilesystem 6 | 7 | 8 | def pytest_generate_tests(metafunc): 9 | handlers = [a, b, c] 10 | if "handler_class" in metafunc.fixturenames: 11 | metafunc.parametrize("handler_class", handlers) 12 | 13 | 14 | def a(): 15 | pass 16 | 17 | 18 | def b(): 19 | pass 20 | 21 | 22 | def c(): 23 | pass 24 | 25 | 26 | @pytest.fixture 27 | def class_a(): 28 | pass 29 | 30 | 31 | @pytest.fixture 32 | def class_b(): 33 | pass 34 | 35 | 36 | @pytest.fixture 37 | def class_c(): 38 | pass 39 | 40 | 41 | @pytest.fixture 42 | def make_handler(request): 43 | def _make_handler(cls): 44 | return request.getfixturevalue(f"class_{cls.__name__}") 45 | 46 | yield _make_handler 47 | 48 | 49 | @pytest.fixture 50 | def handler_and_check(handler_class, make_handler): 51 | assert Path("/foo/bar").exists() 52 | yield 53 | 54 | 55 | def test_handler_and_check_in_fixture(handler_and_check): 56 | assert Path("/foo/bar").exists() 57 | 58 | 59 | @pytest.fixture(scope="module", autouse=True) 60 | def config(fs_module: FakeFilesystem): 61 | fs_module.create_file("/foo/bar") 62 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/test_reload_local_import.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from pyfakefs.fake_filesystem_unittest import Patcher 4 | from pyfakefs.fake_pathlib import FakePathlibModule 5 | from pyfakefs.helpers import reload_cleanup_handler 6 | from pyfakefs.pytest_tests import local_import 7 | 8 | 9 | @pytest.fixture 10 | def test_fs(): 11 | with Patcher() as patcher: 12 | patcher.cleanup_handlers["pyfakefs.pytest_tests.lib_using_pathlib"] = ( 13 | reload_cleanup_handler 14 | ) 15 | yield patcher.fs 16 | 17 | 18 | class TestReloadCleanupHandler: 19 | def test1(self, test_fs): 20 | path = local_import.load("some_path") 21 | assert isinstance(path, FakePathlibModule.Path) 22 | 23 | def test2(self): 24 | path = local_import.load("some_path") 25 | # will fail without reload handler 26 | assert not isinstance(path, FakePathlibModule.Path) 27 | -------------------------------------------------------------------------------- /pyfakefs/pytest_tests/unhashable.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import types 3 | 4 | 5 | class Unhashable(types.ModuleType): 6 | """ 7 | Unhashable module, used for regression test for #923. 8 | """ 9 | 10 | @property 11 | def Unhashable(self): 12 | return self 13 | 14 | def __eq__(self, other): 15 | raise NotImplementedError("Cannot compare unhashable") 16 | 17 | 18 | if sys.modules[__name__] is not Unhashable: 19 | sys.modules[__name__] = Unhashable("unhashable") 20 | -------------------------------------------------------------------------------- /pyfakefs/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pytest-dev/pyfakefs/44fab74f14ef90ad1580b6719a052b9671fd5f51/pyfakefs/tests/__init__.py -------------------------------------------------------------------------------- /pyfakefs/tests/all_tests.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """A test suite that runs all tests for pyfakefs at once. 16 | Includes tests with external pathlib2 and scandir packages if installed.""" 17 | 18 | import sys 19 | import unittest 20 | 21 | from pyfakefs.tests import ( 22 | dynamic_patch_test, 23 | fake_stat_time_test, 24 | example_test, 25 | fake_filesystem_glob_test, 26 | fake_filesystem_shutil_test, 27 | fake_filesystem_test, 28 | fake_filesystem_unittest_test, 29 | fake_filesystem_vs_real_test, 30 | fake_open_test, 31 | fake_os_test, 32 | fake_pathlib_test, 33 | fake_tempfile_test, 34 | patched_packages_test, 35 | fake_legacy_modules_test, 36 | mox3_stubout_test, 37 | ) 38 | 39 | 40 | class AllTests(unittest.TestSuite): 41 | """A test suite that runs all tests for pyfakefs at once.""" 42 | 43 | def suite(self): # pylint: disable-msg=C6409 44 | loader = unittest.defaultTestLoader 45 | self.addTests( 46 | [ 47 | loader.loadTestsFromModule(fake_filesystem_test), 48 | loader.loadTestsFromModule(fake_filesystem_glob_test), 49 | loader.loadTestsFromModule(fake_filesystem_shutil_test), 50 | loader.loadTestsFromModule(fake_os_test), 51 | loader.loadTestsFromModule(fake_stat_time_test), 52 | loader.loadTestsFromModule(fake_open_test), 53 | loader.loadTestsFromModule(fake_tempfile_test), 54 | loader.loadTestsFromModule(fake_filesystem_vs_real_test), 55 | loader.loadTestsFromModule(fake_filesystem_unittest_test), 56 | loader.loadTestsFromModule(example_test), 57 | loader.loadTestsFromModule(mox3_stubout_test), 58 | loader.loadTestsFromModule(dynamic_patch_test), 59 | loader.loadTestsFromModule(fake_pathlib_test), 60 | loader.loadTestsFromModule(patched_packages_test), 61 | loader.loadTestsFromModule(fake_legacy_modules_test), 62 | ] 63 | ) 64 | return self 65 | 66 | 67 | if __name__ == "__main__": 68 | result = unittest.TextTestRunner(verbosity=2).run(AllTests().suite()) 69 | sys.exit(int(not result.wasSuccessful())) 70 | -------------------------------------------------------------------------------- /pyfakefs/tests/all_tests_without_extra_packages.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | """A test suite that runs all tests for pyfakefs at once. 14 | Excludes tests using external scandir package.""" 15 | 16 | import sys 17 | import unittest 18 | 19 | from pyfakefs import legacy_packages 20 | 21 | legacy_packages.scandir = None 22 | legacy_packages.pathlib2 = None 23 | 24 | from pyfakefs.tests.all_tests import AllTests # noqa: E402 25 | 26 | if __name__ == "__main__": 27 | result = unittest.TextTestRunner(verbosity=2).run(AllTests().suite()) 28 | sys.exit(int(not result.wasSuccessful())) 29 | -------------------------------------------------------------------------------- /pyfakefs/tests/dynamic_patch_test.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | """ 14 | Tests for patching modules loaded after `setUpPyfakefs()`. 15 | """ 16 | 17 | import pathlib 18 | import unittest 19 | 20 | from pyfakefs import fake_filesystem_unittest 21 | 22 | 23 | class TestPyfakefsUnittestBase(fake_filesystem_unittest.TestCase): 24 | def setUp(self): 25 | """Set up the fake file system""" 26 | self.setUpPyfakefs() 27 | 28 | 29 | class DynamicImportPatchTest(TestPyfakefsUnittestBase): 30 | def __init__(self, methodName="runTest"): 31 | super().__init__(methodName) 32 | 33 | def test_os_patch(self): 34 | import os 35 | 36 | os.mkdir("test") 37 | self.assertTrue(self.fs.exists("test")) 38 | self.assertTrue(os.path.exists("test")) 39 | 40 | def test_os_import_as_patch(self): 41 | import os as _os 42 | 43 | _os.mkdir("test") 44 | self.assertTrue(self.fs.exists("test")) 45 | self.assertTrue(_os.path.exists("test")) 46 | 47 | def test_os_path_patch(self): 48 | import os.path 49 | 50 | os.mkdir("test") 51 | self.assertTrue(self.fs.exists("test")) 52 | self.assertTrue(os.path.exists("test")) 53 | 54 | def test_shutil_patch(self): 55 | import shutil 56 | 57 | self.fs.set_disk_usage(100) 58 | self.assertEqual(100, shutil.disk_usage("/").total) 59 | 60 | def test_pathlib_path_patch(self): 61 | file_path = "test.txt" 62 | path = pathlib.Path(file_path) 63 | with path.open("w", encoding="utf8") as f: 64 | f.write("test") 65 | 66 | self.assertTrue(self.fs.exists(file_path)) 67 | file_object = self.fs.get_object(file_path) 68 | self.assertEqual("test", file_object.contents) 69 | 70 | 71 | if __name__ == "__main__": 72 | unittest.main() 73 | -------------------------------------------------------------------------------- /pyfakefs/tests/example.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Altera Corporation. All Rights Reserved. 2 | # Author: John McGehee 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | Example module that is tested in :py:class`pyfakefs.example_test.TestExample`. 18 | This demonstrates the usage of the 19 | :py:class`pyfakefs.fake_filesystem_unittest.TestCase` base class. 20 | 21 | The modules related to file handling are bound to the respective fake modules: 22 | 23 | >>> os #doctest: +ELLIPSIS 24 | 25 | >>> os.path #doctest: +ELLIPSIS 26 | 27 | >>> shutil #doctest: +ELLIPSIS 28 | 29 | 30 | `open()` is an alias for `io.open()` and is bound to `FakeIoModule.open`. 31 | """ 32 | 33 | import glob 34 | import os 35 | import shutil 36 | 37 | try: 38 | import scandir 39 | 40 | has_scandir = True 41 | except ImportError: 42 | scandir = None 43 | has_scandir = False 44 | 45 | 46 | def create_file(path): 47 | """Create the specified file and add some content to it. Use the `open()` 48 | built in function. 49 | 50 | For example, the following file operations occur in the fake file system. 51 | In the real file system, we would not even have permission 52 | to write `/test`: 53 | 54 | >>> os.path.isdir('/test') 55 | False 56 | >>> os.mkdir('/test') 57 | >>> os.path.isdir('/test') 58 | True 59 | >>> os.path.exists('/test/file.txt') 60 | False 61 | >>> create_file('/test/file.txt') 62 | >>> os.path.exists('/test/file.txt') 63 | True 64 | >>> with open('/test/file.txt', encoding='utf8') as f: 65 | ... f.readlines() 66 | ["This is test file '/test/file.txt'.\\n", \ 67 | 'It was created using open().\\n'] 68 | """ 69 | with open(path, "w", encoding="utf8") as f: 70 | f.write(f"This is test file '{path}'.\n") 71 | f.write("It was created using open().\n") 72 | 73 | 74 | def delete_file(path): 75 | """Delete the specified file. 76 | 77 | For example: 78 | 79 | >>> os.mkdir('/test') 80 | >>> os.path.exists('/test/file.txt') 81 | False 82 | >>> create_file('/test/file.txt') 83 | >>> os.path.exists('/test/file.txt') 84 | True 85 | >>> delete_file('/test/file.txt') 86 | >>> os.path.exists('/test/file.txt') 87 | False 88 | """ 89 | os.remove(path) 90 | 91 | 92 | def path_exists(path): 93 | """Return True if the specified file exists. 94 | 95 | For example: 96 | 97 | >>> path_exists('/test') 98 | False 99 | >>> os.mkdir('/test') 100 | >>> path_exists('/test') 101 | True 102 | >>> 103 | >>> path_exists('/test/file.txt') 104 | False 105 | >>> create_file('/test/file.txt') 106 | >>> path_exists('/test/file.txt') 107 | True 108 | """ 109 | return os.path.exists(path) 110 | 111 | 112 | def get_glob(glob_path): 113 | r"""Return the list of paths matching the specified glob expression. 114 | 115 | For example: 116 | 117 | >>> os.mkdir('/test') 118 | >>> create_file('/test/file1.txt') 119 | >>> create_file('/test/file2.txt') 120 | >>> file_names = sorted(get_glob('/test/file*.txt')) 121 | >>> 122 | >>> import sys 123 | >>> if sys.platform.startswith('win'): 124 | ... # Windows style path 125 | ... file_names == [r'/test\file1.txt', r'/test\file2.txt'] 126 | ... else: 127 | ... # UNIX style path 128 | ... file_names == ['/test/file1.txt', '/test/file2.txt'] 129 | True 130 | """ 131 | return glob.glob(glob_path) 132 | 133 | 134 | def rm_tree(path): 135 | """Delete the specified file hierarchy.""" 136 | shutil.rmtree(path) 137 | 138 | 139 | def scan_dir(path): 140 | """Return a list of directory entries for the given path.""" 141 | if has_scandir: 142 | return list(scandir.scandir(path)) 143 | return list(os.scandir(path)) 144 | 145 | 146 | def file_contents(path): 147 | """Return the contents of the given path as byte array.""" 148 | with open(path, "rb") as f: 149 | return f.read() 150 | -------------------------------------------------------------------------------- /pyfakefs/tests/example_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Altera Corporation. All Rights Reserved. 2 | # Author: John McGehee 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | Test the :py:class`pyfakefs.example` module to demonstrate the usage of the 18 | :py:class`pyfakefs.fake_filesystem_unittest.TestCase` base class. 19 | 20 | Fake filesystem functions like `create_file()`, `create_dir()` or 21 | `create_symlink()` are often used to set up file structures at the beginning 22 | of a test. 23 | While you could also use the familiar `open()`, `os.mkdirs()` and similar 24 | functions, these functions can make the test code shorter and more readable. 25 | `create_file()` is particularly convenient because it creates all parent 26 | directories and allows you to specify the contents or the size of the file. 27 | """ 28 | 29 | import io 30 | import os 31 | import sys 32 | import unittest 33 | 34 | from pyfakefs import fake_filesystem_unittest 35 | from pyfakefs.legacy_packages import scandir 36 | from pyfakefs.tests import example # The module under test 37 | 38 | 39 | # Work around pyupgrade auto-rewriting `io.open()` to `open()`. 40 | io_open = io.open 41 | 42 | 43 | def load_tests(loader, tests, ignore): 44 | """Load the pyfakefs/example.py doctest tests into unittest.""" 45 | return fake_filesystem_unittest.load_doctests(loader, tests, ignore, example) 46 | 47 | 48 | class TestExample(fake_filesystem_unittest.TestCase): # pylint: disable=R0904 49 | """Test the example module. 50 | The os and shutil modules have been replaced with the fake modules, 51 | so that all of the calls to os and shutil in the tested example code 52 | occur in the fake filesystem. 53 | """ 54 | 55 | def setUp(self): 56 | """Invoke the :py:class:`pyfakefs.fake_filesystem_unittest.TestCase` 57 | `self.setUp()` method. This defines: 58 | 59 | * Attribute `self.fs`, an instance of 60 | :py:class:`pyfakefs.fake_filesystem.FakeFilesystem`. This is useful 61 | for creating test files. 62 | * Attribute `self.stubs`, an instance of 63 | :py:class:`pyfakefs.mox3_stubout.StubOutForTesting`. Use this if 64 | you need to define additional stubs. 65 | """ 66 | 67 | # This is before setUpPyfakefs(), so still using the real file system 68 | self.filepath = os.path.realpath(__file__) 69 | with io_open(self.filepath, "rb") as f: 70 | self.real_contents = f.read() 71 | 72 | self.setUpPyfakefs() 73 | 74 | def tearDown(self): 75 | # No longer need self.tearDownPyfakefs() 76 | pass 77 | 78 | def test_create_file(self): 79 | """Test example.create_file() which uses `open()` 80 | and `file.write()`. 81 | """ 82 | self.assertFalse(os.path.isdir("/test")) 83 | os.mkdir("/test") 84 | self.assertTrue(os.path.isdir("/test")) 85 | 86 | self.assertFalse(os.path.exists("/test/file.txt")) 87 | example.create_file("/test/file.txt") 88 | self.assertTrue(os.path.exists("/test/file.txt")) 89 | 90 | def test_delete_file(self): 91 | """Test example.delete_file() which uses `os.remove()`.""" 92 | self.fs.create_file("/test/full.txt", contents="First line\nSecond Line\n") 93 | self.assertTrue(os.path.exists("/test/full.txt")) 94 | example.delete_file("/test/full.txt") 95 | self.assertFalse(os.path.exists("/test/full.txt")) 96 | 97 | def test_file_exists(self): 98 | """Test example.path_exists() which uses `os.path.exists()`.""" 99 | self.assertFalse(example.path_exists("/test/empty.txt")) 100 | self.fs.create_file("/test/empty.txt") 101 | self.assertTrue(example.path_exists("/test/empty.txt")) 102 | 103 | def test_get_globs(self): 104 | """Test example.get_glob().""" 105 | self.assertFalse(os.path.isdir("/test")) 106 | self.fs.create_dir("/test/dir1/dir2a") 107 | self.assertTrue(os.path.isdir("/test/dir1/dir2a")) 108 | # os.mkdirs() works, too. 109 | os.makedirs("/test/dir1/dir2b") 110 | self.assertTrue(os.path.isdir("/test/dir1/dir2b")) 111 | 112 | self.assertEqual(example.get_glob("/test/dir1/nonexistent*"), []) 113 | is_windows = sys.platform.startswith("win") 114 | matching_paths = sorted(example.get_glob("/test/dir1/dir*")) 115 | if is_windows: 116 | self.assertEqual(matching_paths, [r"/test/dir1\dir2a", r"/test/dir1\dir2b"]) 117 | else: 118 | self.assertEqual(matching_paths, ["/test/dir1/dir2a", "/test/dir1/dir2b"]) 119 | 120 | def test_rm_tree(self): 121 | """Test example.rm_tree() using `shutil.rmtree()`.""" 122 | self.fs.create_dir("/test/dir1/dir2a") 123 | # os.mkdirs() works, too. 124 | os.makedirs("/test/dir1/dir2b") 125 | self.assertTrue(os.path.isdir("/test/dir1/dir2b")) 126 | self.assertTrue(os.path.isdir("/test/dir1/dir2a")) 127 | 128 | example.rm_tree("/test/dir1") 129 | self.assertFalse(os.path.exists("/test/dir1")) 130 | 131 | def test_os_scandir(self): 132 | """Test example.scandir() which uses `os.scandir()`. 133 | 134 | The os module has been replaced with the fake os module so the 135 | fake filesystem path entries are returned instead of `os.DirEntry` 136 | objects. 137 | """ 138 | self.fs.create_file("/test/text.txt") 139 | self.fs.create_dir("/test/dir") 140 | self.fs.create_file("/linktest/linked") 141 | self.fs.create_symlink("/test/linked_file", "/linktest/linked") 142 | 143 | entries = sorted(example.scan_dir("/test"), key=lambda e: e.name) 144 | self.assertEqual(3, len(entries)) 145 | self.assertEqual("linked_file", entries[1].name) 146 | self.assertTrue(entries[0].is_dir()) 147 | self.assertTrue(entries[1].is_symlink()) 148 | self.assertTrue(entries[2].is_file()) 149 | 150 | @unittest.skipIf(scandir is None, "Testing only if scandir module is installed") 151 | def test_scandir_scandir(self): 152 | """Test example.scandir() which uses `scandir.scandir()`. 153 | 154 | The scandir module has been replaced with the fake_scandir module so 155 | the fake filesystem path entries are returned instead of 156 | `scandir.DirEntry` objects. 157 | """ 158 | self.fs.create_file("/test/text.txt") 159 | self.fs.create_dir("/test/dir") 160 | 161 | entries = sorted(example.scan_dir("/test"), key=lambda e: e.name) 162 | self.assertEqual(2, len(entries)) 163 | self.assertEqual("text.txt", entries[1].name) 164 | self.assertTrue(entries[0].is_dir()) 165 | self.assertTrue(entries[1].is_file()) 166 | 167 | def test_real_file_access(self): 168 | """Test `example.file_contents()` for a real file after adding it using 169 | `add_real_file()`.""" 170 | with self.assertRaises(OSError): 171 | example.file_contents(self.filepath) 172 | self.fs.add_real_file(self.filepath) 173 | self.assertEqual(example.file_contents(self.filepath), self.real_contents) 174 | 175 | 176 | if __name__ == "__main__": 177 | unittest.main() 178 | -------------------------------------------------------------------------------- /pyfakefs/tests/fake_filesystem_glob_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Test for glob using fake_filesystem.""" 16 | 17 | import contextlib 18 | import glob 19 | import os 20 | import sys 21 | import unittest 22 | 23 | from pyfakefs import fake_filesystem_unittest 24 | 25 | 26 | class FakeGlobUnitTest(fake_filesystem_unittest.TestCase): 27 | def setUp(self): 28 | self.setUpPyfakefs() 29 | directory = "./xyzzy" 30 | self.fs.create_dir(directory) 31 | self.fs.create_dir("%s/subdir" % directory) 32 | self.fs.create_dir("%s/subdir2" % directory) 33 | self.fs.create_file("%s/subfile" % directory) 34 | self.fs.create_file("[Temp]") 35 | 36 | def test_glob_empty(self): 37 | self.assertEqual(glob.glob(""), []) 38 | 39 | def test_glob_star(self): 40 | basedir = "/xyzzy" 41 | self.assertEqual( 42 | [ 43 | os.path.join(basedir, "subdir"), 44 | os.path.join(basedir, "subdir2"), 45 | os.path.join(basedir, "subfile"), 46 | ], 47 | sorted(glob.glob("/xyzzy/*")), 48 | ) 49 | 50 | def test_glob_exact(self): 51 | self.assertEqual(["/xyzzy"], glob.glob("/xyzzy")) 52 | self.assertEqual(["/xyzzy/subfile"], glob.glob("/xyzzy/subfile")) 53 | 54 | def test_glob_question(self): 55 | basedir = "/xyzzy" 56 | self.assertEqual( 57 | [ 58 | os.path.join(basedir, "subdir"), 59 | os.path.join(basedir, "subdir2"), 60 | os.path.join(basedir, "subfile"), 61 | ], 62 | sorted(glob.glob("/x?zz?/*")), 63 | ) 64 | 65 | def test_glob_no_magic(self): 66 | self.assertEqual(["/xyzzy"], glob.glob("/xyzzy")) 67 | self.assertEqual(["/xyzzy/subdir"], glob.glob("/xyzzy/subdir")) 68 | 69 | def test_non_existent_path(self): 70 | self.assertEqual([], glob.glob("nonexistent")) 71 | 72 | def test_magic_dir(self): 73 | self.assertEqual(["/[Temp]"], glob.glob("/*emp*")) 74 | 75 | def test_glob1(self): 76 | with ( 77 | contextlib.nullcontext() 78 | if sys.version_info < (3, 13) 79 | else self.assertWarns(DeprecationWarning) 80 | ): 81 | self.assertEqual(["[Temp]"], glob.glob1("/", "*Tem*")) 82 | 83 | def test_has_magic(self): 84 | self.assertTrue(glob.has_magic("[")) 85 | self.assertFalse(glob.has_magic("a")) 86 | 87 | 88 | if __name__ == "__main__": 89 | unittest.main() 90 | -------------------------------------------------------------------------------- /pyfakefs/tests/fake_legacy_modules_test.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | import unittest 14 | 15 | from pyfakefs.fake_filesystem_unittest import TestCase 16 | from pyfakefs.fake_legacy_modules import FakeScanDirModule, FakePathlib2Module 17 | from pyfakefs.legacy_packages import pathlib2, scandir 18 | from pyfakefs.tests.fake_os_test import FakeScandirTest 19 | from pyfakefs.tests.fake_pathlib_test import ( 20 | FakePathlibInitializationTest, 21 | FakePathlibPathFileOperationTest, 22 | FakePathlibFileObjectPropertyTest, 23 | FakePathlibUsageInOsFunctionsTest, 24 | ) 25 | 26 | 27 | class DeprecationWarningTest(TestCase): 28 | def setUp(self): 29 | self.setUpPyfakefs() 30 | 31 | @unittest.skipIf(scandir is None, "The scandir package is not installed") 32 | def test_scandir_warning(self): 33 | FakeScanDirModule.has_warned = False 34 | with self.assertWarns(DeprecationWarning): 35 | scandir.scandir("/") 36 | 37 | @unittest.skipIf(pathlib2 is None, "The pathlib2 package is not installed") 38 | def test_pathlib2_warning(self): 39 | FakePathlib2Module.has_warned = False 40 | with self.assertWarns(DeprecationWarning): 41 | pathlib2.Path("/foo") 42 | 43 | 44 | @unittest.skipIf(scandir is None, "The scandir package is not installed") 45 | class FakeScandirPackageTest(FakeScandirTest): 46 | def used_scandir(self): 47 | import pyfakefs.fake_legacy_modules 48 | 49 | def fake_scan_dir(p): 50 | return pyfakefs.fake_legacy_modules.FakeScanDirModule( 51 | self.filesystem 52 | ).scandir(p) 53 | 54 | return fake_scan_dir 55 | 56 | def test_path_like(self): 57 | unittest.skip("Path-like objects not available in scandir package") 58 | 59 | 60 | class RealScandirPackageTest(FakeScandirPackageTest): 61 | def used_scandir(self): 62 | from scandir import scandir 63 | 64 | return scandir 65 | 66 | def use_real_fs(self): 67 | return True 68 | 69 | 70 | @unittest.skipIf(pathlib2 is None, "The pathlib2 package is not installed") 71 | class FakePathlib2InitializationTest(FakePathlibInitializationTest): 72 | def used_pathlib(self): 73 | return pathlib2 74 | 75 | 76 | class RealPathlib2InitializationTest(FakePathlib2InitializationTest): 77 | def use_real_fs(self): 78 | return True 79 | 80 | 81 | @unittest.skipIf(pathlib2 is None, "The pathlib2 package is not installed") 82 | class FakePathlib2FileObjectPropertyTest(FakePathlibFileObjectPropertyTest): 83 | def used_pathlib(self): 84 | return pathlib2 85 | 86 | 87 | class RealPathlib2FileObjectPropertyTest(FakePathlib2FileObjectPropertyTest): 88 | def use_real_fs(self): 89 | return True 90 | 91 | 92 | @unittest.skipIf(pathlib2 is None, "The pathlib2 package is not installed") 93 | class FakePathlib2PathFileOperationTest(FakePathlibPathFileOperationTest): 94 | def used_pathlib(self): 95 | return pathlib2 96 | 97 | def test_is_junction(self): 98 | unittest.skip("is_junction not available in pathlib2") 99 | 100 | 101 | class RealPathlibPath2FileOperationTest(FakePathlib2PathFileOperationTest): 102 | def use_real_fs(self): 103 | return True 104 | 105 | 106 | @unittest.skipIf(pathlib2 is None, "The pathlib2 package is not installed") 107 | class FakePathlib2UsageInOsFunctionsTest(FakePathlibUsageInOsFunctionsTest): 108 | def used_pathlib(self): 109 | return pathlib2 110 | 111 | 112 | class RealPathlib2UsageInOsFunctionsTest(FakePathlib2UsageInOsFunctionsTest): 113 | def use_real_fs(self): 114 | return True 115 | 116 | 117 | if __name__ == "__main__": 118 | unittest.main(verbosity=2) 119 | -------------------------------------------------------------------------------- /pyfakefs/tests/fake_tempfile_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2009 Google Inc. All Rights Reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Tests that ensure that the `tempfile` module works with `fake_filesystem` 16 | if using `Patcher` (via `fake_filesystem_unittest`). 17 | """ 18 | 19 | import os 20 | import stat 21 | import tempfile 22 | import unittest 23 | 24 | from pyfakefs import fake_filesystem_unittest 25 | 26 | 27 | class FakeTempfileModuleTest(fake_filesystem_unittest.TestCase): 28 | """Test the 'tempfile' module with the fake file system.""" 29 | 30 | def setUp(self): 31 | self.setUpPyfakefs() 32 | 33 | def test_named_temporary_file(self): 34 | obj = tempfile.NamedTemporaryFile() 35 | self.assertTrue(self.fs.get_object(obj.name)) 36 | obj.close() 37 | with self.assertRaises(OSError): 38 | self.fs.get_object(obj.name) 39 | 40 | def test_named_temporary_file_no_delete(self): 41 | obj = tempfile.NamedTemporaryFile(delete=False) 42 | obj.write(b"foo") 43 | obj.close() 44 | file_obj = self.fs.get_object(obj.name) 45 | contents = file_obj.contents 46 | self.assertEqual("foo", contents) 47 | obj = tempfile.NamedTemporaryFile(mode="w", encoding="utf8", delete=False) 48 | obj.write("foo") 49 | obj.close() 50 | file_obj = self.fs.get_object(obj.name) 51 | self.assertEqual("foo", file_obj.contents) 52 | 53 | def test_mkstemp(self): 54 | next_fd = len(self.fs.open_files) 55 | temporary = tempfile.mkstemp() 56 | self.assertEqual(2, len(temporary)) 57 | self.assertTrue( 58 | temporary[1].startswith(os.path.join(tempfile.gettempdir(), "tmp")) 59 | ) 60 | self.assertEqual(next_fd, temporary[0]) 61 | self.assertTrue(self.fs.exists(temporary[1])) 62 | mode = 0o666 if self.fs.is_windows_fs else 0o600 63 | self.assertEqual(self.fs.get_object(temporary[1]).st_mode, stat.S_IFREG | mode) 64 | fh = os.fdopen(temporary[0], "w+b") 65 | self.assertEqual(temporary[0], fh.fileno()) 66 | 67 | def test_mkstemp_dir(self): 68 | """test tempfile.mkstemp(dir=).""" 69 | # expect fail: /dir does not exist 70 | with self.assertRaises(OSError): 71 | tempfile.mkstemp(dir="/dir") 72 | # expect pass: /dir exists 73 | self.fs.create_dir("/dir") 74 | next_fd = len(self.fs.open_files) 75 | temporary = tempfile.mkstemp(dir="/dir") 76 | self.assertEqual(2, len(temporary)) 77 | self.assertEqual(next_fd, temporary[0]) 78 | self.assertTrue( 79 | temporary[1].startswith(os.path.join(self.fs.root_dir_name, "dir", "tmp")) 80 | ) 81 | self.assertTrue(self.fs.exists(temporary[1])) 82 | mode = 0o666 if self.fs.is_windows_fs else 0o600 83 | self.assertEqual(self.fs.get_object(temporary[1]).st_mode, stat.S_IFREG | mode) 84 | 85 | def test_mkdtemp(self): 86 | dirname = tempfile.mkdtemp() 87 | self.assertTrue(dirname) 88 | self.assertTrue(self.fs.exists(dirname)) 89 | self.assertEqual(self.fs.get_object(dirname).st_mode, stat.S_IFDIR | 0o700) 90 | 91 | def test_temporary_directory(self): 92 | with tempfile.TemporaryDirectory() as tmpdir: 93 | self.assertTrue(tmpdir) 94 | self.assertTrue(self.fs.exists(tmpdir)) 95 | self.assertEqual(self.fs.get_object(tmpdir).st_mode, stat.S_IFDIR | 0o700) 96 | 97 | def test_temporary_file(self): 98 | with tempfile.TemporaryFile() as f: 99 | f.write(b"test") 100 | f.seek(0) 101 | self.assertEqual(b"test", f.read()) 102 | 103 | def test_temporay_file_with_dir(self): 104 | with self.assertRaises(FileNotFoundError): 105 | tempfile.TemporaryFile(dir="/parent") 106 | os.mkdir("/parent") 107 | with tempfile.TemporaryFile() as f: 108 | f.write(b"test") 109 | f.seek(0) 110 | self.assertEqual(b"test", f.read()) 111 | 112 | 113 | if __name__ == "__main__": 114 | unittest.main() 115 | -------------------------------------------------------------------------------- /pyfakefs/tests/fixtures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pytest-dev/pyfakefs/44fab74f14ef90ad1580b6719a052b9671fd5f51/pyfakefs/tests/fixtures/__init__.py -------------------------------------------------------------------------------- /pyfakefs/tests/fixtures/config_module.py: -------------------------------------------------------------------------------- 1 | configurable_value = "another value" 2 | -------------------------------------------------------------------------------- /pyfakefs/tests/fixtures/deprecated_property.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | """Used for testing suppression of deprecation warnings while iterating 14 | over modules. The code is modeled after code in xmlbuilder.py in Python 3.6. 15 | See issue #542. 16 | """ 17 | 18 | import warnings 19 | 20 | 21 | class DeprecatedProperty: 22 | def __get__(self, instance, cls): 23 | warnings.warn("async is deprecated", DeprecationWarning, stacklevel=2) 24 | warnings.warn("async will be replaced", FutureWarning, stacklevel=2) 25 | return instance 26 | 27 | 28 | class DeprecationTest: 29 | locals()["async"] = DeprecatedProperty() 30 | -------------------------------------------------------------------------------- /pyfakefs/tests/fixtures/excel_test.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pytest-dev/pyfakefs/44fab74f14ef90ad1580b6719a052b9671fd5f51/pyfakefs/tests/fixtures/excel_test.xlsx -------------------------------------------------------------------------------- /pyfakefs/tests/fixtures/module_with_attributes.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 John McGehee 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """This module is for testing pyfakefs 16 | :py:class:`fake_filesystem_unittest.Patcher`. It defines attributes that have 17 | the same names as file modules, sudh as 'io` and `path`. Since these are not 18 | modules, :py:class:`fake_filesystem_unittest.Patcher` should not patch them. 19 | 20 | Whenever a new module is added to 21 | :py:meth:`fake_filesystem_unittest.Patcher._findModules`, the corresponding 22 | attribute should be added here and in the test 23 | :py:class:`fake_filesystem_unittest_test.TestAttributesWithFakeModuleNames`. 24 | """ 25 | 26 | os = "os attribute value" 27 | path = "path attribute value" 28 | pathlib = "pathlib attribute value" 29 | shutil = "shutil attribute value" 30 | io = "io attribute value" 31 | -------------------------------------------------------------------------------- /pyfakefs/tests/import_as_example.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | """ 14 | Example module that is used for testing modules that import file system modules 15 | to be patched under another name. 16 | """ 17 | 18 | import os as my_os 19 | import pathlib 20 | import sys 21 | from builtins import open as bltn_open 22 | from io import open as io_open 23 | from os import path 24 | from os import stat 25 | from os import stat as my_stat 26 | from os.path import exists, isfile, isdir, islink 27 | from os.path import exists as my_exists 28 | from pathlib import Path 29 | 30 | 31 | def check_if_exists1(filepath): 32 | # test patching module imported under other name 33 | return my_os.path.exists(filepath) 34 | 35 | 36 | def check_if_exists2(filepath): 37 | # tests patching path imported from os 38 | return path.exists(filepath) 39 | 40 | 41 | def check_if_exists3(filepath): 42 | # tests patching Path imported from pathlib 43 | return Path(filepath).exists() 44 | 45 | 46 | def check_if_exists4(filepath, file_exists=my_os.path.exists): 47 | return file_exists(filepath) 48 | 49 | 50 | def check_if_exists5(filepath): 51 | # tests patching `exists` imported from os.path 52 | return exists(filepath) 53 | 54 | 55 | def check_if_exists6(filepath): 56 | # tests patching `exists` imported from os.path as other name 57 | return my_exists(filepath) 58 | 59 | 60 | def check_if_exists7(filepath): 61 | # tests patching pathlib 62 | return pathlib.Path(filepath).exists() 63 | 64 | 65 | def check_if_isfile(filepath): 66 | # tests patching `isfile` imported from os.path 67 | return isfile(filepath) 68 | 69 | 70 | def check_if_isdir(filepath): 71 | # tests patching `isdir` imported from os.path 72 | return isdir(filepath) 73 | 74 | 75 | def check_if_islink(filepath): 76 | # tests patching `islink` imported from os.path 77 | return islink(filepath) 78 | 79 | 80 | def file_stat1(filepath): 81 | # tests patching `stat` imported from os 82 | return stat(filepath) 83 | 84 | 85 | def file_stat2(filepath): 86 | # tests patching `stat` imported from os as other name 87 | return my_stat(filepath) 88 | 89 | 90 | def system_stat(filepath): 91 | if sys.platform == "win32": 92 | from nt import stat as system_stat 93 | else: 94 | from posix import stat as system_stat 95 | return system_stat(filepath) 96 | 97 | 98 | def file_contents1(filepath): 99 | with bltn_open(filepath, encoding="utf8") as f: 100 | return f.read() 101 | 102 | 103 | def file_contents2(filepath): 104 | with io_open(filepath, encoding="utf8") as f: 105 | return f.read() 106 | 107 | 108 | def exists_this_file(): 109 | """Returns True in real fs only""" 110 | return exists(__file__) 111 | 112 | 113 | def open_this_file(): 114 | """Works only in real fs""" 115 | with open(__file__, encoding="utf8"): 116 | pass 117 | 118 | 119 | def return_this_file_path(): 120 | """Works only in real fs""" 121 | return Path(__file__) 122 | 123 | 124 | class TestDefaultArg: 125 | def check_if_exists(self, filepath, file_exists=my_os.path.exists): 126 | # this is a similar case as in the tempfile implementation under Posix 127 | return file_exists(filepath) 128 | -------------------------------------------------------------------------------- /pyfakefs/tests/logsio.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | """ 14 | Example module that is used for a regression test where a module with 15 | a name ending with "io" was skipped from patching (see #569). 16 | """ 17 | 18 | 19 | def file_contents(path): 20 | """Return the contents of the given path as byte array.""" 21 | with open(path, "rb") as f: 22 | return f.read() 23 | -------------------------------------------------------------------------------- /pyfakefs/tests/mox3_stubout_example.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | """ 14 | Example module that is used for testing the functionality of 15 | :py:class`pyfakefs.mox_stubout.StubOutForTesting`. 16 | """ 17 | 18 | import datetime 19 | import math 20 | import os 21 | 22 | 23 | def check_if_exists(filepath): 24 | return os.path.exists(filepath) 25 | 26 | 27 | def fabs(x): 28 | return math.fabs(x) 29 | 30 | 31 | def tomorrow(): 32 | return datetime.date.today() + datetime.timedelta(days=1) 33 | -------------------------------------------------------------------------------- /pyfakefs/tests/mox3_stubout_test.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | """Unit tests for mox3_stubout.""" 14 | 15 | import datetime 16 | import math 17 | import os 18 | import unittest 19 | from os import path 20 | 21 | from pyfakefs import mox3_stubout 22 | from pyfakefs.tests import mox3_stubout_example 23 | 24 | 25 | class NoPanicMath: 26 | real_math = math 27 | 28 | @staticmethod 29 | def fabs(_x): 30 | return 42 31 | 32 | def __getattr__(self, name): 33 | """Forwards any unfaked calls to the standard module.""" 34 | return getattr(self.real_math, name) 35 | 36 | 37 | class ExistingPath: 38 | real_path = path 39 | 40 | @staticmethod 41 | def exists(_path): 42 | return True 43 | 44 | def __getattr__(self, name): 45 | """Forwards any unfaked calls to the standard module.""" 46 | return getattr(self.real_path, name) 47 | 48 | 49 | class GroundhogDate(datetime.date): 50 | @classmethod 51 | def today(cls): 52 | return datetime.date(1993, 2, 2) 53 | 54 | 55 | class StubOutForTestingTest(unittest.TestCase): 56 | def setUp(self): 57 | super().setUp() 58 | self.stubber = mox3_stubout.StubOutForTesting() 59 | 60 | def test_stubout_method_with_set(self): 61 | non_existing_path = "non_existing_path" 62 | self.assertFalse(mox3_stubout_example.check_if_exists(non_existing_path)) 63 | self.stubber.set(os.path, "exists", lambda x: True) 64 | self.assertTrue(mox3_stubout_example.check_if_exists(non_existing_path)) 65 | self.stubber.unset_all() 66 | self.assertFalse(mox3_stubout_example.check_if_exists(non_existing_path)) 67 | 68 | def test_stubout_class_with_set(self): 69 | self.assertGreater(mox3_stubout_example.tomorrow().year, 2000) 70 | 71 | self.stubber.set(datetime, "date", GroundhogDate) 72 | self.assertEqual(mox3_stubout_example.tomorrow(), datetime.date(1993, 2, 3)) 73 | 74 | self.stubber.unset_all() 75 | self.assertGreater(mox3_stubout_example.tomorrow().year, 2000) 76 | 77 | def test_stubout_module_with_set(self): 78 | self.assertEqual(10, mox3_stubout_example.fabs(-10)) 79 | 80 | self.stubber.set(mox3_stubout_example, "math", NoPanicMath) 81 | self.assertEqual(42, mox3_stubout_example.fabs(-10)) 82 | 83 | self.stubber.unset_all() 84 | self.assertEqual(10, mox3_stubout_example.fabs(-10)) 85 | 86 | def test_set_raise_if_unknown_attribute(self): 87 | self.assertRaises( 88 | AttributeError, 89 | self.stubber.set, 90 | os.path, 91 | "exists_not", 92 | lambda x: True, 93 | ) 94 | self.assertRaises( 95 | AttributeError, 96 | self.stubber.set, 97 | datetime, 98 | "tomorrow", 99 | GroundhogDate, 100 | ) 101 | self.assertRaises( 102 | AttributeError, 103 | self.stubber.set, 104 | mox3_stubout_example, 105 | "math1", 106 | NoPanicMath, 107 | ) 108 | 109 | def test_stubout_method_with_smart_set(self): 110 | non_existing_path = "non_existing_path" 111 | self.stubber.smart_set(os.path, "exists", lambda x: True) 112 | self.assertTrue(mox3_stubout_example.check_if_exists(non_existing_path)) 113 | self.stubber.smart_unset_all() 114 | self.assertFalse(mox3_stubout_example.check_if_exists(non_existing_path)) 115 | 116 | def test_stubout_class_with_smart_set(self): 117 | self.stubber.smart_set(datetime, "date", GroundhogDate) 118 | self.assertEqual(mox3_stubout_example.tomorrow(), datetime.date(1993, 2, 3)) 119 | 120 | self.stubber.smart_unset_all() 121 | self.assertGreater(mox3_stubout_example.tomorrow().year, 2000) 122 | 123 | def test_stubout_module_with_smart_set(self): 124 | self.assertEqual(10, mox3_stubout_example.fabs(-10)) 125 | 126 | self.stubber.smart_set(mox3_stubout_example, "math", NoPanicMath) 127 | self.assertEqual(42, mox3_stubout_example.fabs(-10)) 128 | 129 | self.stubber.smart_unset_all() 130 | self.assertEqual(10, mox3_stubout_example.fabs(-10)) 131 | 132 | def test_stubout_submodule_with_smart_set(self): 133 | # this one does not work with Set 134 | non_existing_path = "non_existing_path" 135 | self.assertFalse(mox3_stubout_example.check_if_exists(non_existing_path)) 136 | self.stubber.smart_set(os, "path", ExistingPath) 137 | self.assertTrue(mox3_stubout_example.check_if_exists(non_existing_path)) 138 | self.stubber.smart_unset_all() 139 | self.assertFalse(mox3_stubout_example.check_if_exists(non_existing_path)) 140 | 141 | def test_smart_set_raise_if_unknown_attribute(self): 142 | self.assertRaises( 143 | AttributeError, 144 | self.stubber.smart_set, 145 | os.path, 146 | "exists_not", 147 | lambda x: True, 148 | ) 149 | self.assertRaises( 150 | AttributeError, 151 | self.stubber.smart_set, 152 | datetime, 153 | "tomorrow", 154 | GroundhogDate, 155 | ) 156 | self.assertRaises( 157 | AttributeError, 158 | self.stubber.smart_set, 159 | mox3_stubout_example, 160 | "math1", 161 | NoPanicMath, 162 | ) 163 | 164 | 165 | if __name__ == "__main__": 166 | unittest.main() 167 | -------------------------------------------------------------------------------- /pyfakefs/tests/patched_packages_test.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | """ 14 | Provides patches for some commonly used modules that enable them to work 15 | with pyfakefs. 16 | """ 17 | 18 | import os 19 | import sys 20 | import unittest 21 | 22 | from pyfakefs import fake_filesystem_unittest 23 | from pyfakefs.helpers import IS_PYPY 24 | 25 | try: 26 | import pandas as pd 27 | except ImportError: 28 | pd = None 29 | 30 | try: 31 | import xlrd 32 | except ImportError: 33 | xlrd = None 34 | 35 | try: 36 | import openpyxl 37 | except ImportError: 38 | openpyxl = None 39 | 40 | 41 | @unittest.skipIf( 42 | IS_PYPY and sys.version_info < (3, 8), "Has a problem with older PyPy versions" 43 | ) 44 | class TestPatchedPackages(fake_filesystem_unittest.TestCase): 45 | def setUp(self): 46 | self.setUpPyfakefs() 47 | 48 | if pd is not None: 49 | 50 | def test_read_csv(self): 51 | path = "/foo/bar.csv" 52 | self.fs.create_file(path, contents="1,2,3,4") 53 | df = pd.read_csv(path) 54 | assert (df.columns == ["1", "2", "3", "4"]).all() 55 | 56 | def test_read_table(self): 57 | path = "/foo/bar.csv" 58 | self.fs.create_file(path, contents="1|2|3|4") 59 | df = pd.read_table(path, delimiter="|") 60 | assert (df.columns == ["1", "2", "3", "4"]).all() 61 | 62 | if pd is not None and xlrd is not None: 63 | 64 | def test_read_excel(self): 65 | path = "/foo/bar.xlsx" 66 | src_path = os.path.dirname(os.path.abspath(__file__)) 67 | src_path = os.path.join(src_path, "fixtures", "excel_test.xlsx") 68 | # map the file into another location to be sure that 69 | # the real fs is not used 70 | self.fs.add_real_file(src_path, target_path=path) 71 | df = pd.read_excel(path) 72 | assert (df.columns == [1, 2, 3, 4]).all() 73 | 74 | if pd is not None and openpyxl is not None: 75 | 76 | def test_write_excel(self): 77 | self.fs.create_dir("/foo") 78 | path = "/foo/bar.xlsx" 79 | df = pd.DataFrame([[0, 1, 2, 3]]) 80 | with pd.ExcelWriter(path) as writer: 81 | df.to_excel(writer) 82 | df = pd.read_excel(path) 83 | assert (df.columns == ["Unnamed: 0", 0, 1, 2, 3]).all() 84 | -------------------------------------------------------------------------------- /pyfakefs/tests/performance_test.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | """Shall provide tests to check performance overhead of pyfakefs.""" 13 | 14 | import os 15 | import time 16 | import unittest 17 | 18 | from pyfakefs.fake_filesystem_unittest import TestCase 19 | from pyfakefs.helpers import IS_PYPY 20 | 21 | if os.environ.get("TEST_PERFORMANCE"): 22 | 23 | class SetupPerformanceTest(TestCase): 24 | @classmethod 25 | def setUpClass(cls) -> None: 26 | cls.start_time = time.time() 27 | 28 | @classmethod 29 | def tearDownClass(cls) -> None: 30 | cls.elapsed_time = time.time() - cls.start_time 31 | print( 32 | "Elapsed time per test for cached setup: {:.3f} ms".format( 33 | cls.elapsed_time * 10 34 | ) 35 | ) 36 | 37 | def setUp(self) -> None: 38 | self.setUpPyfakefs() 39 | 40 | class SetupNoCachePerformanceTest(TestCase): 41 | @classmethod 42 | def setUpClass(cls) -> None: 43 | cls.start_time = time.time() 44 | 45 | @classmethod 46 | def tearDownClass(cls) -> None: 47 | cls.elapsed_time = time.time() - cls.start_time 48 | print( 49 | "Elapsed time per test for uncached setup: {:.3f} ms".format( 50 | cls.elapsed_time * 10 51 | ) 52 | ) 53 | 54 | def setUp(self) -> None: 55 | self.setUpPyfakefs(use_cache=False) 56 | 57 | @unittest.skipIf(IS_PYPY, "PyPy times are not comparable") 58 | class TimePerformanceTest(TestCase): 59 | """Make sure performance degradation in setup is noticed. 60 | The numbers are related to the CI builds and may fail in local builds. 61 | """ 62 | 63 | def test_cached_time(self): 64 | self.assertLess(SetupPerformanceTest.elapsed_time, 0.18) 65 | 66 | def test_uncached_time(self): 67 | self.assertLess(SetupNoCachePerformanceTest.elapsed_time, 4) 68 | 69 | def test_setup(self): 70 | pass 71 | 72 | for n in range(100): 73 | test_name = "test_" + str(n) 74 | setattr(SetupPerformanceTest, test_name, test_setup) 75 | test_name = "test_nocache" + str(n) 76 | setattr(SetupNoCachePerformanceTest, test_name, test_setup) 77 | 78 | if __name__ == "__main__": 79 | unittest.main() 80 | -------------------------------------------------------------------------------- /pyfakefs/tests/skipped_pathlib.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | """ 13 | Provides functions for testing additional_skip_names functionality. 14 | """ 15 | 16 | import os 17 | from pathlib import Path 18 | 19 | 20 | def read_pathlib(file_name): 21 | return (Path(__file__).parent / file_name).open("r").read() 22 | 23 | 24 | def read_text_pathlib(file_name): 25 | return (Path(__file__).parent / file_name).read_text() 26 | 27 | 28 | def read_bytes_pathlib(file_name): 29 | return (Path(__file__).parent / file_name).read_bytes() 30 | 31 | 32 | def check_exists_pathlib(): 33 | return os.path.exists(__file__) and Path(__file__).exists() 34 | 35 | 36 | def read_open(file_name): 37 | with open(os.path.join(os.path.dirname(__file__), file_name)) as f: 38 | return f.read() 39 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pytest>=6.2.5 2 | -------------------------------------------------------------------------------- /requirements_dev.txt: -------------------------------------------------------------------------------- 1 | pre-commit==4.0.1 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = pyfakefs 3 | version = attr: pyfakefs.__version__ 4 | author = Google 5 | author_email = google-pyfakefs@google.com 6 | maintainer = John McGehee 7 | maintainer_email = pyfakefs@johnnado.com 8 | license = http://www.apache.org/licenses/LICENSE-2.0 9 | description = pyfakefs implements a fake file system that mocks the Python file system modules. 10 | long_description = file: README.md 11 | long_description_content_type = text/markdown 12 | keywords = 13 | testing 14 | test 15 | file 16 | os 17 | shutil 18 | pathlib 19 | mocking 20 | unittest 21 | pytest 22 | fakes 23 | filesystem 24 | url = https://github.com/pytest-dev/pyfakefs 25 | classifiers = 26 | Development Status :: 5 - Production/Stable 27 | Environment :: Console 28 | Intended Audience :: Developers 29 | License :: OSI Approved :: Apache Software License 30 | Programming Language :: Python :: 3 31 | Programming Language :: Python :: 3.7 32 | Programming Language :: Python :: 3.8 33 | Programming Language :: Python :: 3.9 34 | Programming Language :: Python :: 3.10 35 | Programming Language :: Python :: 3.11 36 | Programming Language :: Python :: 3.12 37 | Programming Language :: Python :: 3.13 38 | Programming Language :: Python :: Implementation :: CPython 39 | Programming Language :: Python :: Implementation :: PyPy 40 | Operating System :: POSIX 41 | Operating System :: MacOS 42 | Operating System :: Microsoft :: Windows 43 | Topic :: Software Development :: Libraries 44 | Topic :: Software Development :: Libraries :: Python Modules 45 | Topic :: Software Development :: Testing 46 | Topic :: System :: Filesystems 47 | Framework :: Pytest 48 | 49 | [bdist_wheel] 50 | universal = 0 51 | 52 | [options] 53 | packages = find: 54 | install_requires = 55 | python_requires = >=3.7 56 | test_suite = pyfakefs.tests 57 | include_package_data = True 58 | 59 | [options.packages.find] 60 | exclude = docs 61 | 62 | [options.package_data] 63 | pyfakefs = py.typed 64 | 65 | [options.entry_points] 66 | pytest11 = 67 | pytest_fakefs = pyfakefs.pytest_plugin 68 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | if __name__ == "__main__": 4 | setup() 5 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py{37,38,39,310,311,312,313} 4 | pypy{37,39,310} 5 | 6 | [testenv] 7 | deps = 8 | -rrequirements.txt 9 | -rextra_requirements.txt 10 | passenv = HOME,USERPROFILE 11 | commands= 12 | python -m pyfakefs.tests.all_tests 13 | python -m pyfakefs.tests.all_tests_without_extra_packages 14 | python -m pytest pyfakefs/pytest_tests/pytest_plugin_test.py 15 | --------------------------------------------------------------------------------